absensi done

dev
ajikamaludin 2 years ago
parent 4ff9f62274
commit 3a30d72203
Signed by: ajikamaludin
GPG Key ID: 476C9A2B4B794EBB

@ -21,4 +21,9 @@ class GeneralController extends Controller
{
return Inertia::render('Karyawan/Index');
}
public function absensi()
{
return Inertia::render('Absensi/Index');
}
}

@ -35,6 +35,11 @@ class HandleInertiaRequests extends Middleware
public function share(Request $request)
{
return array_merge(parent::share($request), [
'app_creator' => [
'nama' => 'Aji Kamaludin',
'email' => 'aji.kamaludin2021@gmail.com',
'github' => 'github.com/ajikamaludin'
],
'auth' => [
'user' => session('user'),
],

@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.select {
@apply border mt-1 border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-28 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500
}

@ -1,13 +1,13 @@
import React from 'react'
export function Modal({ isOpen, toggleModal = () => {}, children }) {
export function Modal({ isOpen, toggleModal = () => {}, children, size = 'lg' }) {
return (
<div
className={`overflow-y-auto overflow-x-hidden bg-opacity-50 dark:bg-opacity-50 bg-gray-500 dark:bg-gray-400 fixed flex right-0 left-0 top-0 z-50 justify-center items-center h-full md:inset-0 ${
isOpen ? 'visible' : 'hidden'
}`}
>
<div className={`md:relative fixed bottom-0 md:px-4 w-full max-w-lg h-auto`}>
<div className={`md:relative fixed bottom-0 md:px-4 w-full max-w-${size} h-auto`}>
<div className="relative overflow-y-auto max-h-screen bg-white rounded-b-none rounded-t-lg md:rounded-lg shadow dark:bg-gray-800 pb-10 md:pb-0 ">
<div className="flex justify-end p-2">
<button

@ -105,7 +105,7 @@ export default function Authenticated({ auth, children }) {
</div>
</nav>
<div className='flex flex-row md:mt-5 mx-5'>
<div className='flex flex-row md:mt-5 mx-auto max-w-7xl'>
<div className='w-auto hidden md:block'>
<aside className="w-64" aria-label="Sidebar">
<div className="overflow-y-auto py-4 px-3 bg-white rounded dark:bg-gray-800">
@ -126,7 +126,7 @@ export default function Authenticated({ auth, children }) {
</Link>
</li>
<li>
<Link href={route('dashboard')} className="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
<Link href={route('absensi')} className="flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
<span className="flex-1 ml-3 whitespace-nowrap">Data Absensi</span>
</Link>
</li>

@ -0,0 +1,128 @@
import React, { useEffect, useState } from 'react'
import { toast } from 'react-toastify'
import { Modal } from '@/Components/Modal'
import { useForm } from '@inertiajs/inertia-react'
import Button from '@/Components/Button'
import Input from '@/Components/Input'
import { getAll } from '@/Services/Karyawan'
import { create } from '@/Services/Absensi'
export default function FormModal({ modalState, periode, refresh }) {
const [loading, setLoading] = useState(false)
const { data, setData, reset } = useForm({
users: []
})
useEffect(() => {
getAll()
.then(items => setData({
users: items.filter(item => item.data.jabatan.id != "1").map(item => {
return {
id: item.id,
name: item.data.name,
nik: item.data.nik,
jenisKelamin: item.data.jenisKelamin,
jabatan: item.data.jabatan.nama,
jabatan_id: item.data.jabatan.id,
hadir: 0,
sakit: 0,
alfa: 0,
}
})
}))
},[modalState])
const onHandleChange = (item, event) => {
setData({
users: data.users.map(user => {
if (user.id == item.id) {
user[event.target.name] = event.target.value
}
return user
})
})
}
const submit = (e) => {
e.preventDefault()
setLoading(true)
create({
...data,
periode: periode,
})
.then(() => {
reset()
modalState.toggle()
})
.finally(() => setLoading(false))
}
return (
<Modal isOpen={modalState.isOpen} toggleModal={modalState.toggle} size="4xl">
<div className="text-lg font-bold">Absensi Karyawan</div>
<div className="text-md">Periode : {periode}</div>
<div className="overflow-x-auto relative pt-5">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" className="py-3 px-6">
NIK
</th>
<th scope="col" className="py-3 px-6">
Nama
</th>
<th scope="col" className="py-3 px-6">
Jenis Kelamin
</th>
<th scope="col" className="py-3 px-6">
Jabatan
</th>
<th scope="col" className="py-3 px-6">Hadir</th>
<th scope="col" className="py-3 px-6">Sakit</th>
<th scope="col" className="py-3 px-6">Alfa</th>
</tr>
</thead>
<tbody>
{data.users.map(item => (
<tr key={item.id} className="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<th scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{item.nik}
</th>
<td className="py-4 px-6">
{item.name}
</td>
<td className="py-4 px-6">
{item.jenisKelamin}
</td>
<td className="py-4 px-6">
{item.jabatan}
</td>
<td>
<Input
name="hadir"
value={item.hadir}
handleChange={(e) => onHandleChange(item, e)}/>
</td>
<td>
<Input
name="sakit"
value={item.sakit}
handleChange={(e) => onHandleChange(item, e)}/>
</td>
<td>
<Input
name="alfa"
value={item.alfa}
handleChange={(e) => onHandleChange(item, e)}/>
</td>
</tr>
))}
</tbody>
</table>
</div>
<Button className={'mt-4'} onClick={submit} disabled={loading} processing={loading}>
Save
</Button>
</Modal>
)
}

@ -0,0 +1,132 @@
import React, { useEffect, useState } from 'react';
import Authenticated from '@/Layouts/Authenticated';
import { Head, useForm } from '@inertiajs/inertia-react';
import { useModalState } from '@/Hooks'
import Button from '@/Components/Button';
import FormModal from './FormModal';
import { getByPeriode } from '@/Services/Absensi';
import { toast } from 'react-toastify';
export default function Karyawan(props) {
const formModal = useModalState(false)
const month = (new Date()).getMonth()
const year = (new Date()).getFullYear()
const months = [1,2,3,4,5,6,7,8,9,10,11,12]
const years = [+year-2, +year-1, year, +year+1, +year+2]
const {data, setData} = useForm({
month: month,
year: year
})
const onHandleChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
: event.target.value
)
}
const [absensi, setAbsensi] = useState({})
const onClickShow = () => {
setAbsensi({})
getByPeriode(`${data.month}_${data.year}`)
.then(items => {
if(items.length <= 0) {
toast.error("No data found")
return
}
setAbsensi(items[0])
})
}
return (
<Authenticated
auth={props.auth}
errors={props.errors}
>
<Head title="Absensi" />
<div className="py-0">
<div className="max-w-7xl sm:px-6">
<div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6 bg-white border-b border-gray-200">
<div className='flex flex-col md:flex-row space-y-1 md:space-y-0 justify-between'>
<div className='flex space-x-1 items-center'>
<p>Periode Data :</p>
<select className='select' value={data.month} name="month" onChange={onHandleChange}>
<option value="">Bulan</option>
{months.map(month => (
<option key={month}>{month}</option>
))}
</select>
<select className='select' value={data.year} name="year" onChange={onHandleChange}>
<option>Tahun</option>
{years.map(year => (
<option key={year}>{year}</option>
))}
</select>
</div>
<div className='flex space-x-1'>
<Button onClick={onClickShow}>Tampilkan Data</Button>
<Button onClick={formModal.toggle}>Tambah Absensi</Button>
</div>
</div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mt-5">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" className="py-3 px-6">
NIK
</th>
<th scope="col" className="py-3 px-6">
Nama
</th>
<th scope="col" className="py-3 px-6">
Jenis Kelamin
</th>
<th scope="col" className="py-3 px-6">
Jabatan
</th>
<th scope="col" className="py-3 px-6">Hadir</th>
<th scope="col" className="py-3 px-6">Sakit</th>
<th scope="col" className="py-3 px-6">Alfa</th>
</tr>
</thead>
<tbody>
{absensi.data?.users?.map(item => (
<tr key={item.id} className="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<th scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{item.nik}
</th>
<td className="py-4 px-6">
{item.name}
</td>
<td className="py-4 px-6">
{item.jenisKelamin}
</td>
<td className="py-4 px-6">
{item.jabatan}
</td>
<td>
{item.hadir}
</td>
<td>
{item.sakit}
</td>
<td>
{item.alfa}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
<FormModal modalState={formModal} periode={`${data.month}_${data.year}`}/>
</Authenticated>
);
}

@ -10,9 +10,24 @@ export default function FormModal({ modalState, refresh }) {
const [loading, setLoading] = useState(false)
const { data, setData, reset } = useForm({
nama: '',
tunjangan: 0
gajiPokok: 0,
tunjangan: 0,
feePenjualan: 0,
transport: 0,
uangMakan: 0,
bonus: 0,
total: 0
})
const total = +data.gajiPokok + +data.tunjangan + +data.feePenjualan + +data.transport + +data.uangMakan + +data.bonus
useEffect(() => {
setData({
...data,
total: total
})
}, [total])
useEffect(() => {
if (modalState.isOpen === false) {
reset()
@ -71,6 +86,18 @@ export default function FormModal({ modalState, refresh }) {
handleChange={onHandleChange}
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Gaji Pokok
</span>
<Input
placeholder="081XXX"
name="gajiPokok"
value={data.gajiPokok}
handleChange={onHandleChange}
type="number"
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Tunjangan
@ -83,6 +110,54 @@ export default function FormModal({ modalState, refresh }) {
type="number"
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Fee Penjualan
</span>
<Input
placeholder="081XXX"
name="feePenjualan"
value={data.feePenjualan}
handleChange={onHandleChange}
type="number"
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Uang Transport
</span>
<Input
placeholder="081XXX"
name="transport"
value={data.transport}
handleChange={onHandleChange}
type="number"
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Uang Makan
</span>
<Input
placeholder="081XXX"
name="uangMakan"
value={data.uangMakan}
handleChange={onHandleChange}
type="number"
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Bonus
</span>
<Input
placeholder="081XXX"
name="bonus"
value={data.bonus}
handleChange={onHandleChange}
type="number"
/>
</label>
<Button className={'mt-4'} onClick={submit} disabled={loading} processing={loading}>
Save
</Button>

@ -6,6 +6,7 @@ import Button from '@/Components/Button';
import FormModal from './FormModal';
import { getAll, deleteById } from '@/Services/Jabatan';
import { toast } from 'react-toastify';
import { formatIDR } from '@/Utils';
export default function Jabatan(props) {
const formModal = useModalState(false)
@ -53,9 +54,27 @@ export default function Jabatan(props) {
<th scope="col" className="py-3 px-6">
Nama
</th>
<th scope="col" className="py-3 px-6">
Gaji Pokok
</th>
<th scope="col" className="py-3 px-6">
Tunjangan
</th>
<th scope="col" className="py-3 px-6">
Fee Penjualan
</th>
<th scope="col" className="py-3 px-6">
Tunjangan Transport
</th>
<th scope="col" className="py-3 px-6">
Uang Makan
</th>
<th scope="col" className="py-3 px-6">
Bonus
</th>
<th scope="col" className="py-3 px-6">
Total
</th>
<th></th>
</tr>
</thead>
@ -66,7 +85,25 @@ export default function Jabatan(props) {
{item.data.nama}
</th>
<td className="py-4 px-6">
Rp. {item.data.tunjangan}
Rp. {formatIDR(item.data.gajiPokok)}
</td>
<td className="py-4 px-6">
Rp. {formatIDR(item.data.tunjangan)}
</td>
<td className="py-4 px-6">
Rp. {formatIDR(item.data.feePenjualan)}
</td>
<td className="py-4 px-6">
Rp. {formatIDR(item.data.transport)}
</td>
<td className="py-4 px-6">
Rp. {formatIDR(item.data.uangMakan)}
</td>
<td className="py-4 px-6">
Rp. {formatIDR(item.data.bonus)}
</td>
<td className="py-4 px-6">
Rp. {formatIDR(item.data.total)}
</td>
<th>
<div className='flex space-x-1'>

@ -0,0 +1,57 @@
import db from "@/firebase";
import { collection, getDocs, doc, addDoc, deleteDoc, updateDoc, query, where } from "firebase/firestore";
const COLLECTION = "absensi"
async function getAll() {
const collect = collection(db, COLLECTION)
const data = await getDocs(collect)
const lists = data.docs.map(doc => {
return {
data: doc.data(),
id: doc.id
}
})
return lists
}
async function getByPeriode(periode) {
const collect = query(collection(db, COLLECTION), where("periode", "==", periode))
const data = await getDocs(collect)
if (data.size <= 0) {
return []
}
const lists = data.docs.map(doc => {
return {
data: doc.data(),
id: doc.id
}
})
return lists
}
async function create(payload) {
const docRef = await addDoc(collection(db, COLLECTION), payload)
return docRef.id
}
async function update(payload, id){
const docRef = doc(db, COLLECTION, id)
const result = await updateDoc(docRef, payload);
return result
}
async function deleteById(id) {
console.log(id)
const docRef = doc(db, COLLECTION, id)
const result = await deleteDoc(docRef);
return result
}
export {
getAll,
create,
update,
deleteById,
getByPeriode
}

@ -0,0 +1,4 @@
export function formatIDR(amount) {
const idFormatter = new Intl.NumberFormat('id-ID')
return idFormatter.format(amount)
}

@ -31,5 +31,6 @@ Route::middleware([IsSessionAuth::class])->group(function () {
Route::get('/dashboard', [GeneralController::class, 'dashboard'])->name('dashboard');
Route::get('/jabatan', [GeneralController::class, 'jabatan'])->name('jabatan');
Route::get('/karyawan', [GeneralController::class, 'karyawan'])->name('karyawan');
Route::get('/absensi', [GeneralController::class, 'absensi'])->name('absensi');
Route::post('/logout', [AuthController::class, 'destroy'])->name('logout');
});

Loading…
Cancel
Save