crud jabatan done

dev
ajikamaludin 2 years ago
parent 0397720ad4
commit 401f8575a8
Signed by: ajikamaludin
GPG Key ID: 476C9A2B4B794EBB

@ -14,6 +14,6 @@ class GeneralController extends Controller
public function jabatan()
{
return Inertia::render('Jabatan');
return Inertia::render('Jabatan/Index');
}
}

54
package-lock.json generated

@ -5,7 +5,8 @@
"packages": {
"": {
"dependencies": {
"firebase": "^9.9.1"
"firebase": "^9.9.1",
"react-toastify": "^9.0.7"
},
"devDependencies": {
"@headlessui/react": "^1.4.2",
@ -1547,6 +1548,14 @@
"wrap-ansi": "^7.0.0"
}
},
"node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -2443,8 +2452,7 @@
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/jsesc": {
"version": "2.5.2",
@ -2539,7 +2547,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@ -2699,7 +2706,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -2989,7 +2995,6 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@ -3002,7 +3007,6 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@ -3021,6 +3025,18 @@
"node": ">=0.10.0"
}
},
"node_modules/react-toastify": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.7.tgz",
"integrity": "sha512-UG5P/2F/fYdbK+v3XRWHM6xfMkWeLM+k2swKvwU8oO9DXx31GkeoH7Z6slBTkavZ0VjoQ+n/YD6xOBqEwN7HRg==",
"dependencies": {
"clsx": "^1.1.1"
},
"peerDependencies": {
"react": ">=16",
"react-dom": ">=16"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -3152,7 +3168,6 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@ -4787,6 +4802,11 @@
"wrap-ansi": "^7.0.0"
}
},
"clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -5365,8 +5385,7 @@
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"jsesc": {
"version": "2.5.2",
@ -5440,7 +5459,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
@ -5552,8 +5570,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"object-hash": {
"version": "3.0.0",
@ -5730,7 +5747,6 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@ -5740,7 +5756,6 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@ -5753,6 +5768,14 @@
"integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
"dev": true
},
"react-toastify": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.7.tgz",
"integrity": "sha512-UG5P/2F/fYdbK+v3XRWHM6xfMkWeLM+k2swKvwU8oO9DXx31GkeoH7Z6slBTkavZ0VjoQ+n/YD6xOBqEwN7HRg==",
"requires": {
"clsx": "^1.1.1"
}
},
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -5842,7 +5865,6 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"

@ -23,6 +23,7 @@
"vite": "^3.0.0"
},
"dependencies": {
"firebase": "^9.9.1"
"firebase": "^9.9.1",
"react-toastify": "^9.0.7"
}
}

@ -1,8 +1,9 @@
import React from 'react';
export default function Button({ type = 'submit', className = '', processing, children }) {
export default function Button({ type = 'submit', className = '', processing, children, onClick }) {
return (
<button
onClick={onClick}
type={type}
className={
`inline-flex items-center px-4 py-2 bg-gray-900 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest active:bg-gray-900 transition ease-in-out duration-150 ${

@ -25,7 +25,7 @@ export default function Input({
name={name}
value={value}
className={
`border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm ` +
`w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm` +
className
}
ref={input}

@ -0,0 +1,40 @@
import React from 'react'
export function Modal({ isOpen, toggleModal = () => {}, children }) {
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="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
type="button"
onClick={() => toggleModal()}
className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
></path>
</svg>
</button>
</div>
<div className="pt-2 p-6 text-gray-500 dark:text-gray-400">
{children}
</div>
</div>
</div>
</div>
)
}

@ -0,0 +1,31 @@
import { useState, useEffect } from 'react'
export function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => {
clearTimeout(handler)
}
}, [value, delay])
return debouncedValue
}
export function useModalState(state = false) {
const [isOpen, setIsOpen] = useState(state)
const toggle = () => {
setIsOpen(!isOpen)
}
const [data, setData] = useState(null)
return {
isOpen,
toggle,
setIsOpen,
data,
setData,
}
}

@ -1,4 +1,6 @@
import React, { useState } from 'react';
import 'react-toastify/dist/ReactToastify.css'
import { ToastContainer } from 'react-toastify'
import Dropdown from '@/Components/Dropdown';
import ResponsiveNavLink from '@/Components/ResponsiveNavLink';
import { Link } from '@inertiajs/inertia-react';
@ -6,7 +8,6 @@ import { Link } from '@inertiajs/inertia-react';
export default function Authenticated({ auth, children }) {
const [showingNavigationDropdown, setShowingNavigationDropdown] = useState(false);
return (
<div className="min-h-screen bg-gray-100">
<nav className="bg-white border-b border-gray-100">
@ -15,7 +16,7 @@ export default function Authenticated({ auth, children }) {
<div className="flex">
<div className="shrink-0 flex items-center">
<Link href="/" className='text-3xl font-bold'>
Payroll App
Koro Koro Family Karaoke
</Link>
</div>
</div>
@ -158,6 +159,17 @@ export default function Authenticated({ auth, children }) {
<main>{children}</main>
</div>
</div>
<ToastContainer
position="top-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
rtl={false}
closeOnClick
pauseOnFocusLoss
draggable
pauseOnHover
/>
</div>
);
}

@ -7,7 +7,7 @@ export default function Guest({ children }) {
<div className="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
<div>
<Link href="/" className='text-3xl font-bold'>
Payroll App
Koro Koro Family Karaoke
</Link>
</div>

@ -13,7 +13,7 @@ export default function Dashboard(props) {
<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">You're logged in!</div>
<div className="p-6 bg-white border-b border-gray-200">Hai, {props.auth.user.name}</div>
</div>
</div>
</div>

@ -1,89 +0,0 @@
import React from 'react';
import Authenticated from '@/Layouts/Authenticated';
import { Head } from '@inertiajs/inertia-react';
import Button from '@/Components/Button';
export default function Jabatan(props) {
return (
<Authenticated
auth={props.auth}
errors={props.errors}
>
<Head title="Jabatan" />
<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">
<Button>Tambah</Button>
<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">
Product name
</th>
<th scope="col" className="py-3 px-6">
Color
</th>
<th scope="col" className="py-3 px-6">
Category
</th>
<th scope="col" className="py-3 px-6">
Price
</th>
</tr>
</thead>
<tbody>
<tr 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">
Apple MacBook Pro 17"
</th>
<td className="py-4 px-6">
Sliver
</td>
<td className="py-4 px-6">
Laptop
</td>
<td className="py-4 px-6">
$2999
</td>
</tr>
<tr 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">
Microsoft Surface Pro
</th>
<td className="py-4 px-6">
White
</td>
<td className="py-4 px-6">
Laptop PC
</td>
<td className="py-4 px-6">
$1999
</td>
</tr>
<tr className="bg-white dark:bg-gray-800">
<th scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
Magic Mouse 2
</th>
<td className="py-4 px-6">
Black
</td>
<td className="py-4 px-6">
Accessories
</td>
<td className="py-4 px-6">
$99
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</Authenticated>
);
}

@ -0,0 +1,91 @@
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 { create, update } from '@/Services/Jabatan'
export default function FormModal({ modalState, refresh }) {
const [loading, setLoading] = useState(false)
const { data, setData, reset } = useForm({
nama: '',
tunjangan: 0
})
useEffect(() => {
if (modalState.isOpen === false) {
reset()
modalState.setData(null)
}
if (modalState.data !== null) {
setData(modalState.data.data)
}
}, [modalState])
const onHandleChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
: event.target.value
)
}
const submit = (e) => {
e.preventDefault()
setLoading(true)
if (modalState.data !== null) {
update(data, modalState.data.id)
.finally(() => {
reset()
toast.success("berhasil update")
setLoading(false)
modalState.toggle()
refresh()
})
} else {
create(data)
.then((id) => console.log(id))
.finally(() => {
reset()
toast.success("berhasil simpan")
setLoading(false)
modalState.toggle()
refresh()
})
}
}
return (
<Modal isOpen={modalState.isOpen} toggleModal={modalState.toggle}>
<div className="text-lg font-bold">Jabatan</div>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Nama
</span>
<Input
placeholder="John Doe"
name="nama"
value={data.nama}
handleChange={onHandleChange}
/>
</label>
<label className="block text-sm mt-4">
<span className="text-gray-700 dark:text-gray-400">
Tunjangan
</span>
<Input
placeholder="081XXX"
name="tunjangan"
value={data.tunjangan}
handleChange={onHandleChange}
type="number"
/>
</label>
<Button className={'mt-4'} onClick={submit} disabled={loading} processing={loading}>
Save
</Button>
</Modal>
)
}

@ -0,0 +1,95 @@
import React, { useEffect, useState } from 'react';
import Authenticated from '@/Layouts/Authenticated';
import { Head } from '@inertiajs/inertia-react';
import { useModalState } from '@/Hooks'
import Button from '@/Components/Button';
import FormModal from './FormModal';
import { getAll, deleteById } from '@/Services/Jabatan';
import { toast } from 'react-toastify';
export default function Jabatan(props) {
const formModal = useModalState(false)
const [items, setItems] = useState([])
const hanldeDeleteClick = (item) => {
const con = confirm("delete item?")
if (con) {
deleteById(item.id)
.then(() => toast.success('berhasil hapus'))
.finally(() => fetchData())
}
}
const handleEditClick = (item) => {
formModal.setData(item)
formModal.toggle()
}
const fetchData = () => {
getAll()
.then(items => setItems(items))
}
useEffect(() => {
fetchData()
}, [])
return (
<Authenticated
auth={props.auth}
errors={props.errors}
>
<Head title="Jabatan" />
<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">
<Button onClick={formModal.toggle}>Tambah</Button>
<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">
ID
</th>
<th scope="col" className="py-3 px-6">
Nama
</th>
<th scope="col" className="py-3 px-6">
Tunjangan
</th>
<th></th>
</tr>
</thead>
<tbody>
{items.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.id}
</th>
<th scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{item.data.nama}
</th>
<td className="py-4 px-6">
Rp. {item.data.tunjangan}
</td>
<th>
<div className='flex space-x-1'>
<Button onClick={() => handleEditClick(item)}>Edit</Button>
<Button onClick={() => hanldeDeleteClick(item)}>Hapus</Button>
</div>
</th>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<FormModal modalState={formModal} refresh={fetchData}/>
</Authenticated>
);
}

@ -1,19 +1,41 @@
import db from "@/firebase";
import { collection, getDocs, doc, setDoc } from "firebase/firestore";
import { collection, getDocs, doc, addDoc, deleteDoc, updateDoc } from "firebase/firestore";
async function getAllJabatan() {
const collect = collection(db, 'jabatan')
const COLLECTION = "jabatan"
async function getAll() {
const collect = collection(db, COLLECTION)
const data = await getDocs(collect)
const lists = data.docs.map(doc => doc.data())
const lists = data.docs.map(doc => {
return {
data: doc.data(),
id: doc.id
}
})
return lists
}
async function createJabatan(payload) {
const docRef = await setDoc(doc(db, "jabatan"), payload);
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 {
getAllJabatan,
createJabatan
getAll,
create,
update,
deleteById
}
Loading…
Cancel
Save