done crud employee
parent
1d8cc39b1f
commit
c8df4033a5
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Employee;
|
||||||
|
|
||||||
|
class EmployeeQueryController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->q != null) {
|
||||||
|
$query = Employee::where('name', 'like', '%'.$request->q.'%')->orWhere('whatsapp', 'like', '%'.$request->q.'%')->orderBy('id');
|
||||||
|
} else {
|
||||||
|
$query = Employee::orderBy('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->paginate(10);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Employee;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class EmployeeController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->q != null) {
|
||||||
|
$query = Employee::where('name', 'like', '%'.$request->q.'%')->orWhere('whatsapp', 'like', '%'.$request->q.'%')->orderBy('id');
|
||||||
|
} else {
|
||||||
|
$query = Employee::orderBy('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
return inertia('Employees', [
|
||||||
|
'employees' => $query->paginate(10),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string',
|
||||||
|
'whatsapp' => 'nullable|string',
|
||||||
|
'photo' => 'nullable|image'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$employee = Employee::make($request->only(['name', 'whatsapp']));
|
||||||
|
$photo = $request->file('photo');
|
||||||
|
if ($photo != null) {
|
||||||
|
$photo->store('public');
|
||||||
|
$employee->photo = $photo->hashName();
|
||||||
|
}
|
||||||
|
|
||||||
|
$employee->save();
|
||||||
|
|
||||||
|
return redirect()->route('employees.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param int $id
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function update(Request $request, Employee $employee)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string',
|
||||||
|
'whatsapp' => 'nullable|string',
|
||||||
|
'photo' => 'nullable|image'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$employee->fill($request->only(['name', 'whatsapp']));
|
||||||
|
|
||||||
|
$photo = $request->file('photo');
|
||||||
|
if ($photo != null) {
|
||||||
|
if ($employee->photo != null) {
|
||||||
|
Storage::delete('public/'.$employee->photo);
|
||||||
|
$employee->photo = null;
|
||||||
|
}
|
||||||
|
$photo->store('public');
|
||||||
|
$employee->photo = $photo->hashName();
|
||||||
|
}
|
||||||
|
|
||||||
|
$employee->save();
|
||||||
|
|
||||||
|
return redirect()->route('employees.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function destroy(Employee $employee)
|
||||||
|
{
|
||||||
|
if ($employee->photo != null) {
|
||||||
|
Storage::delete('public/'.$employee->photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
$employee->delete();
|
||||||
|
|
||||||
|
return redirect()->route('employees.index');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react'
|
||||||
|
import { useForm } from '@inertiajs/inertia-react'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
export default function FormEmployeeModal(props) {
|
||||||
|
const { isOpen, toggle = () => {}, employee = null } = props
|
||||||
|
|
||||||
|
const { data, setData, post, processing, errors, clearErrors } =
|
||||||
|
useForm({
|
||||||
|
name: '',
|
||||||
|
whatsapp: '',
|
||||||
|
photo: null,
|
||||||
|
img_alt: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const inputPhoto = useRef()
|
||||||
|
|
||||||
|
const handleOnChange = (event) => {
|
||||||
|
setData(event.target.name, event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setData({
|
||||||
|
name: '',
|
||||||
|
whatsapp: '',
|
||||||
|
photo: null,
|
||||||
|
img_alt: null,
|
||||||
|
})
|
||||||
|
clearErrors()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
handleReset()
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (employee !== null) {
|
||||||
|
post(route('employees.update', employee), {
|
||||||
|
forceFormData: true,
|
||||||
|
onSuccess: () =>
|
||||||
|
Promise.all([
|
||||||
|
handleReset(),
|
||||||
|
toggle(),
|
||||||
|
toast.success('The Data has been changed'),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
post(route('employees.store'), {
|
||||||
|
onSuccess: () =>
|
||||||
|
Promise.all([
|
||||||
|
handleReset(),
|
||||||
|
toggle(),
|
||||||
|
toast.success('The Data has been saved'),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setData({
|
||||||
|
name: employee?.name ? employee.name : '',
|
||||||
|
whatsapp: employee?.whatsapp ? employee.whatsapp : '',
|
||||||
|
img_alt: employee?.photo_url ? employee.photo_url : null,
|
||||||
|
})
|
||||||
|
}, [employee])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="modal"
|
||||||
|
style={
|
||||||
|
isOpen
|
||||||
|
? {
|
||||||
|
opacity: 1,
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
visibility: 'visible',
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="modal-box">
|
||||||
|
<h1 className="font-bold text-2xl pb-8">Barang</h1>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Nama</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="nama"
|
||||||
|
className={`input input-bordered ${
|
||||||
|
errors.name && 'input-error'
|
||||||
|
}`}
|
||||||
|
name="name"
|
||||||
|
value={data.name}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text-alt">{errors.name}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Whatsapp</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="whatsapp"
|
||||||
|
className={`input input-bordered ${
|
||||||
|
errors.whatsapp && 'input-error'
|
||||||
|
}`}
|
||||||
|
name="whatsapp"
|
||||||
|
value={data.whatsapp}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text-alt">
|
||||||
|
{errors.whatsapp}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Foto</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className={`input input-bordered ${
|
||||||
|
errors.photo && 'input-error'
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
console.log(inputPhoto.current.click())
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data.photo ? data.photo.name : ''}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref={inputPhoto}
|
||||||
|
type="file"
|
||||||
|
className="hidden"
|
||||||
|
name="photo"
|
||||||
|
onChange={(e) => setData('photo', e.target.files[0])}
|
||||||
|
accept="image/png, image/jpeg, image/jpg"
|
||||||
|
/>
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text-alt">{errors.photo}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
{data.img_alt !== null && <img src={data.img_alt} />}
|
||||||
|
</div>
|
||||||
|
<div className="modal-action">
|
||||||
|
<div
|
||||||
|
onClick={handleSubmit}
|
||||||
|
className="btn btn-primary"
|
||||||
|
disabled={processing}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={handleCancel}
|
||||||
|
className="btn btn-secondary"
|
||||||
|
disabled={processing}
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,24 +1,158 @@
|
|||||||
import React from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import Authenticated from '@/Layouts/Authenticated'
|
|
||||||
import { Head } from '@inertiajs/inertia-react'
|
import { Head } from '@inertiajs/inertia-react'
|
||||||
|
import { Inertia } from '@inertiajs/inertia'
|
||||||
|
import { usePrevious } from 'react-use'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
import { useModalState } from '@/Hooks'
|
||||||
|
import Authenticated from '@/Layouts/Authenticated'
|
||||||
|
import Pagination from '@/Components/Pagination'
|
||||||
|
import ModalConfirm from '@/Components/ModalConfirm'
|
||||||
|
import FormEmployeeModal from '@/Modals/FormEmployeeModal'
|
||||||
|
|
||||||
export default function Employees(props) {
|
export default function Employees(props) {
|
||||||
return (
|
const { data: employees, links } = props.employees
|
||||||
<Authenticated
|
|
||||||
auth={props.auth}
|
const [search, setSearch] = useState('')
|
||||||
errors={props.errors}
|
const preValue = usePrevious(search)
|
||||||
header={
|
|
||||||
<h2 className="font-semibold text-xl text-gray-800 leading-tight">
|
const [employee, setEmployee] = useState(null)
|
||||||
Keryawan
|
const formModal = useModalState(false)
|
||||||
</h2>
|
const toggle = (employee = null) => {
|
||||||
}
|
setEmployee(employee)
|
||||||
>
|
formModal.toggle()
|
||||||
<Head title="Employees" />
|
}
|
||||||
<div className="py-12">
|
|
||||||
<div className="flex flex-row w-full sm:px-6 lg:px-8 space-x-4">
|
const confirmModal = useModalState(false)
|
||||||
|
const handleDelete = (employee) => {
|
||||||
</div>
|
confirmModal.setData(employee)
|
||||||
</div>
|
confirmModal.toggle()
|
||||||
</Authenticated>
|
}
|
||||||
)
|
|
||||||
|
const onDelete = () => {
|
||||||
|
const employee = confirmModal.data
|
||||||
|
if (employee != null) {
|
||||||
|
Inertia.delete(route('employees.destroy', employee), {
|
||||||
|
onSuccess: () => toast.success('The Data has been deleted'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (preValue) {
|
||||||
|
Inertia.get(
|
||||||
|
route(route().current()),
|
||||||
|
{ q: search },
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
preserveState: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [search])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authenticated
|
||||||
|
auth={props.auth}
|
||||||
|
errors={props.errors}
|
||||||
|
header={
|
||||||
|
<h2 className="font-semibold text-xl text-gray-800 leading-tight">
|
||||||
|
Keryawan
|
||||||
|
</h2>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Head title="Employees" />
|
||||||
|
<div className="py-12">
|
||||||
|
<div className="flex flex-col w-full sm:px-6 lg:px-8 space-y-2">
|
||||||
|
<div className="card bg-white w-full">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="flex w-full mb-4 justify-between">
|
||||||
|
<div
|
||||||
|
className="btn btn-neutral"
|
||||||
|
onClick={() => toggle()}
|
||||||
|
>
|
||||||
|
Tambah
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input input-bordered"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) =>
|
||||||
|
setSearch(e.target.value)
|
||||||
|
}
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="table w-full table-zebra">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Id</th>
|
||||||
|
<th>Nama</th>
|
||||||
|
<th>Whatsapp</th>
|
||||||
|
<th>Foto</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{employees?.map((employee) => (
|
||||||
|
<tr key={employee.id}>
|
||||||
|
<th>{employee.id}</th>
|
||||||
|
<td>{employee.name}</td>
|
||||||
|
<td>{employee.whatsapp}</td>
|
||||||
|
<td>
|
||||||
|
{employee.photo_url !==
|
||||||
|
null && (
|
||||||
|
<img
|
||||||
|
width="100px"
|
||||||
|
src={
|
||||||
|
employee.photo_url
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="text-right">
|
||||||
|
<div
|
||||||
|
className="btn btn-primary mx-1"
|
||||||
|
onClick={() =>
|
||||||
|
toggle(employee)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="btn btn-secondary mx-1"
|
||||||
|
onClick={() =>
|
||||||
|
handleDelete(
|
||||||
|
employee
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<Pagination links={links} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FormEmployeeModal
|
||||||
|
isOpen={formModal.isOpen}
|
||||||
|
toggle={toggle}
|
||||||
|
employee={employee}
|
||||||
|
/>
|
||||||
|
<ModalConfirm
|
||||||
|
isOpen={confirmModal.isOpen}
|
||||||
|
toggle={confirmModal.toggle}
|
||||||
|
onConfirm={onDelete}
|
||||||
|
/>
|
||||||
|
</Authenticated>
|
||||||
|
)
|
||||||
}
|
}
|
Loading…
Reference in New Issue