oke dah , kurang share , reminder send mail sama calender
parent
df9ea70806
commit
cffa0e509a
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Models\Document;
|
||||
use App\Models\TypeDoc;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DocumentController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Document::with(['department', 'type'])->orderBy('created_at');
|
||||
|
||||
if ($request->q != null || $request->q != '') {
|
||||
$query->where('no_doc', 'like', '%'.$request->q.'%')
|
||||
->orWhere('company_name', 'like', '%'.$request->q.'%')
|
||||
->orWhere('pic_name', 'like', '%'.$request->q.'%')
|
||||
->orWhere('email', 'like', '%'.$request->q.'%');
|
||||
}
|
||||
|
||||
if ($request->department_id != ''){
|
||||
$query->where('department_id', $request->department_id);
|
||||
}
|
||||
|
||||
if ($request->status != ''){
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
if ($request->type_doc_id != ''){
|
||||
$query->where('type_doc_id', $request->type_doc_id);
|
||||
}
|
||||
|
||||
return inertia('Document/Index', [
|
||||
'docs' => $query->paginate(10),
|
||||
'types' => TypeDoc::all(),
|
||||
'departments' => Department::all(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return inertia('Document/Form', [
|
||||
'types' => TypeDoc::all(),
|
||||
'departments' => Department::all(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'no_doc' => 'required|string',
|
||||
'company_name' => 'required|string',
|
||||
'first_person_name' => 'required|string',
|
||||
'second_person_name' => 'required|string',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date',
|
||||
'type_doc_id' => 'required|exists:type_docs,id',
|
||||
'department_id' => 'required|exists:departments,id',
|
||||
'pic_name' => 'required|string',
|
||||
'email' => 'required|email',
|
||||
// 'document' => 'required|file',
|
||||
'note' => 'nullable',
|
||||
'status' => 'required|numeric',
|
||||
]);
|
||||
|
||||
$lastDocs = Document::orderBy('created_at', 'desc')->first();
|
||||
$lastDocs = $lastDocs ? $lastDocs : Document::make(['no' => 0]);
|
||||
$docs = Document::make([
|
||||
'no' => $lastDocs->no + 1,
|
||||
'no_doc' => $request->no_doc,
|
||||
'company_name' => $request->company_name,
|
||||
'first_person_name' => $request->first_person_name,
|
||||
'second_person_name' => $request->second_person_name,
|
||||
'start_date' => $request->start_date,
|
||||
'end_date' => $request->end_date,
|
||||
'pic_name' => $request->pic_name,
|
||||
'email' => $request->email,
|
||||
'note' => $request->note,
|
||||
'type_doc_id' => $request->type_doc_id,
|
||||
'department_id' => $request->department_id,
|
||||
'status' => $request->status,
|
||||
'user_id' => auth()->user()->id,
|
||||
]);
|
||||
|
||||
// $file = $request->file('document');
|
||||
// $file->store('documents', 'public');
|
||||
$docs->document = '';
|
||||
|
||||
$docs->save();
|
||||
|
||||
return redirect()->route('docs.index')
|
||||
->with('message', ['type' => 'success', 'message' => 'The data has beed saved']);
|
||||
}
|
||||
|
||||
public function edit(Document $doc)
|
||||
{
|
||||
return inertia('Document/Form', [
|
||||
'types' => TypeDoc::all(),
|
||||
'departments' => Department::all(),
|
||||
'doc' => $doc
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, Document $doc)
|
||||
{
|
||||
$request->validate([
|
||||
'no_doc' => 'required|string',
|
||||
'company_name' => 'required|string',
|
||||
'first_person_name' => 'required|string',
|
||||
'second_person_name' => 'required|string',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date',
|
||||
'type_doc_id' => 'required|exists:type_docs,id',
|
||||
'department_id' => 'required|exists:departments,id',
|
||||
'pic_name' => 'required|string',
|
||||
'email' => 'required|email',
|
||||
'document' => 'nullable|file',
|
||||
'note' => 'nullable',
|
||||
'status' => 'required|numeric',
|
||||
]);
|
||||
|
||||
|
||||
$doc->fill([
|
||||
'no_doc' => $request->no_doc,
|
||||
'company_name' => $request->company_name,
|
||||
'first_person_name' => $request->first_person_name,
|
||||
'second_person_name' => $request->second_person_name,
|
||||
'start_date' => $request->start_date,
|
||||
'end_date' => $request->end_date,
|
||||
'pic_name' => $request->pic_name,
|
||||
'email' => $request->email,
|
||||
'note' => $request->note,
|
||||
'type_doc_id' => $request->type_doc_id,
|
||||
'department_id' => $request->department_id,
|
||||
'status' => $request->status,
|
||||
]);
|
||||
|
||||
$file = $request->file('document');
|
||||
if($file != null) {
|
||||
$file->store('documents', 'public');
|
||||
$doc->document = $file->hashName();
|
||||
}
|
||||
|
||||
$doc->save();
|
||||
|
||||
return redirect()->route('docs.index')
|
||||
->with('message', ['type' => 'success', 'message' => 'The data has beed saved']);
|
||||
}
|
||||
|
||||
public function show(Document $doc)
|
||||
{
|
||||
return inertia('Document/Detail', [
|
||||
'doc' => $doc->load(['department', 'type', 'creator']),
|
||||
'doc_url' => asset('document/'.$doc->document),
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Document $doc)
|
||||
{
|
||||
$doc->delete();
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Document;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class GeneralController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return inertia('Dashboard', [
|
||||
'count_active' => Document::where('status', Document::ACTIVE)->count(),
|
||||
'count_update' => Document::where('status', Document::UPDATE)->count(),
|
||||
'count_expired' => Document::where('status', Document::EXPIRED)->count(),
|
||||
'count_total' => Document::count()
|
||||
]);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
|
||||
|
||||
export default function InputFile({ file, isError, inputRef, handleChange }) {
|
||||
return (
|
||||
<div className="btn-group w-full">
|
||||
<input
|
||||
readOnly={true}
|
||||
className={`input input-bordered w-full ${
|
||||
isError && 'input-error'
|
||||
}`}
|
||||
value={file ? file.name : ''}
|
||||
/>
|
||||
<div
|
||||
className="btn btn-active w-1/6"
|
||||
onClick={() => {
|
||||
console.log(inputRef.current.click())
|
||||
}}
|
||||
>
|
||||
Pilih File
|
||||
</div>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
className="hidden"
|
||||
name="document"
|
||||
onChange={(e) => handleChange(e)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
export const IconMenu = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const IconFilter = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
|
||||
</svg>
|
||||
)
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
import React from 'react'
|
||||
import { Link, Head } from '@inertiajs/inertia-react'
|
||||
|
||||
import DocStatusItem from './DocStatusItem'
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
|
||||
import InputLabel from '@/Components/InputLabel'
|
||||
import TextInput from '@/Components/TextInput'
|
||||
|
||||
|
||||
export default function FormDocument(props) {
|
||||
const { doc, doc_url }= props
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
auth={props.auth}
|
||||
errors={props.errors}
|
||||
flash={props.flash}
|
||||
>
|
||||
<Head title="Document - Form" />
|
||||
|
||||
<div className="flex flex-col w-full px-6 lg:px-8 space-y-2">
|
||||
<div className="card bg-base-100 w-full">
|
||||
<div className="card-body">
|
||||
<p className='font-bold text-2xl mb-4'>Dokumen</p>
|
||||
<div className="overflow-x-auto">
|
||||
<div>
|
||||
<div>
|
||||
<InputLabel forInput="no_doc" value="No Dokumen" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="no_doc"
|
||||
value={doc.no_doc}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="type" value="Jenis" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="no_doc"
|
||||
value={doc.type.name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="company_name" value="Nama Perusahaan" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="company_name"
|
||||
value={doc.company_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="first_person_name" value="Nama Pihak Pertama" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="first_person_name"
|
||||
value={doc.first_person_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="second_person_name" value="Nama Pihak Kedua" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="second_person_name"
|
||||
value={doc.second_person_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="start_date" value="Tanggal Mulai" />
|
||||
<TextInput
|
||||
type="date"
|
||||
name="start_date"
|
||||
value={doc.start_date}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="end_date" value="Tanggal Berakhir" />
|
||||
<TextInput
|
||||
type="date"
|
||||
name="end_date"
|
||||
value={doc.end_date}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="type" value="Deparment" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="type"
|
||||
value={doc.department.name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="pic_name" value="Nama PIC" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="pic_name"
|
||||
value={doc.pic_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="email" value="Email" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="email"
|
||||
value={doc.email}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="note" value="Catatan" />
|
||||
<TextInput
|
||||
type="textarea"
|
||||
name="note"
|
||||
value={doc.note}
|
||||
className="mt-1 block w-full"
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="document" value="Dokumen" />
|
||||
<a href={doc_url} className='btn btn-outline'>Download</a>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="status" value="Status" />
|
||||
<DocStatusItem status={doc.status}/>
|
||||
</div>
|
||||
<div className="flex items-center justify-end mt-4">
|
||||
<Link href={route('docs.index')} className="btn btn-outline">
|
||||
Kembali
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</AuthenticatedLayout>
|
||||
)
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import { statuses } from "@/utils";
|
||||
import React from "react";
|
||||
|
||||
export default function DocStatusItem({ status }) {
|
||||
const stat = statuses.find(i => i.key == status)
|
||||
return (
|
||||
<p style={{color: stat.color}}>{stat.value}</p>
|
||||
)
|
||||
}
|
@ -0,0 +1,278 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { Link, Head, useForm } from '@inertiajs/inertia-react'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import {statuses} from '@/utils'
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
|
||||
import PrimaryButton from '@/Components/PrimaryButton'
|
||||
import InputLabel from '@/Components/InputLabel'
|
||||
import TextInput from '@/Components/TextInput'
|
||||
import InputError from '@/Components/InputError'
|
||||
import InputFile from '@/Components/InputFile'
|
||||
|
||||
|
||||
export default function FormDocument(props) {
|
||||
const { types, departments, doc }= props
|
||||
|
||||
const { data, setData, post, processing, errors, reset } = useForm({
|
||||
no_doc: '',
|
||||
email: '',
|
||||
type_doc_id: '1',
|
||||
department_id: '1',
|
||||
company_name: '',
|
||||
first_person_name: '',
|
||||
second_person_name: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
pic_name: '',
|
||||
note: '',
|
||||
document: null,
|
||||
document_name: '',
|
||||
status: 0,
|
||||
});
|
||||
|
||||
const inputDocument = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
if(doc !== undefined) {
|
||||
setData({
|
||||
no_doc: doc.no_doc,
|
||||
email: doc.email,
|
||||
type_doc_id: doc.type_doc_id,
|
||||
department_id: doc.department_id,
|
||||
company_name: doc.company_name,
|
||||
first_person_name: doc.first_person_name,
|
||||
second_person_name: doc.second_person_name,
|
||||
start_date: doc.start_date,
|
||||
end_date: doc.end_date,
|
||||
pic_name: doc.pic_name,
|
||||
note: doc.note,
|
||||
document: null,
|
||||
document_name: doc.document,
|
||||
status: doc.status,
|
||||
})
|
||||
}
|
||||
}, [doc]);
|
||||
|
||||
const onHandleChange = (event) => {
|
||||
setData(event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value);
|
||||
};
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if(doc !== undefined) {
|
||||
post(route('docs.update', doc), {
|
||||
onError: () => toast.error('please recheck the data')
|
||||
});
|
||||
return
|
||||
}
|
||||
post(route('docs.store'), {
|
||||
onError: () => toast.error('please recheck the data')
|
||||
});
|
||||
};
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
auth={props.auth}
|
||||
errors={props.errors}
|
||||
flash={props.flash}
|
||||
>
|
||||
<Head title="Document - Form" />
|
||||
|
||||
<div className="flex flex-col w-full px-6 lg:px-8 space-y-2">
|
||||
<div className="card bg-base-100 w-full">
|
||||
<div className="card-body">
|
||||
<p className='font-bold text-2xl mb-4'>Dokumen</p>
|
||||
<div className="overflow-x-auto">
|
||||
<form onSubmit={submit}>
|
||||
<div>
|
||||
<InputLabel forInput="no_doc" value="No Dokumen" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="no_doc"
|
||||
value={data.no_doc}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.no_doc}
|
||||
/>
|
||||
<InputError message={errors.no_doc}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="type" value="Jenis" />
|
||||
<select
|
||||
className="mt-1 select select-bordered w-full"
|
||||
name="type_doc_id"
|
||||
onChange={onHandleChange}
|
||||
value={data.type_doc_id}
|
||||
>
|
||||
{types.map(type => (
|
||||
<option key={type.id} value={type.id}>{type.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<InputError message={errors.type_doc_id}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="company_name" value="Nama Perusahaan" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="company_name"
|
||||
value={data.company_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.company_name}
|
||||
/>
|
||||
<InputError message={errors.company_name}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="first_person_name" value="Nama Pihak Pertama" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="first_person_name"
|
||||
value={data.first_person_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.first_person_name}
|
||||
/>
|
||||
<InputError message={errors.first_person_name}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="second_person_name" value="Nama Pihak Kedua" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="second_person_name"
|
||||
value={data.second_person_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.second_person_name}
|
||||
/>
|
||||
<InputError message={errors.second_person_name}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="start_date" value="Tanggal Mulai" />
|
||||
<TextInput
|
||||
type="date"
|
||||
name="start_date"
|
||||
value={data.start_date}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.start_date}
|
||||
/>
|
||||
<InputError message={errors.start_date}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="end_date" value="Tanggal Berakhir" />
|
||||
<TextInput
|
||||
type="date"
|
||||
name="end_date"
|
||||
value={data.end_date}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.end_date}
|
||||
/>
|
||||
<InputError message={errors.end_date}/>
|
||||
</div>
|
||||
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="type" value="Deparment" />
|
||||
<select
|
||||
className="mt-1 select select-bordered w-full"
|
||||
name="department_id"
|
||||
onChange={onHandleChange}
|
||||
value={data.department_id}
|
||||
>
|
||||
{departments.map(dep => (
|
||||
<option key={dep.id} value={dep.id}>{dep.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<InputError message={errors.type_doc_id}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="pic_name" value="Nama PIC" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="pic_name"
|
||||
value={data.pic_name}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.pic_name}
|
||||
/>
|
||||
<InputError message={errors.pic_name}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="email" value="Email" />
|
||||
<TextInput
|
||||
type="text"
|
||||
name="email"
|
||||
value={data.email}
|
||||
className="mt-1 block w-full"
|
||||
autoComplete={"false"}
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.email}
|
||||
/>
|
||||
<InputError message={errors.email}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="note" value="Catatan" />
|
||||
<TextInput
|
||||
type="textarea"
|
||||
name="note"
|
||||
value={data.note}
|
||||
className="mt-1 block w-full"
|
||||
handleChange={onHandleChange}
|
||||
isError={errors.note}
|
||||
/>
|
||||
<InputError message={errors.note}/>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="document" value="Dokumen" />
|
||||
<InputFile
|
||||
file={data.document}
|
||||
isError={errors.document}
|
||||
inputRef={inputDocument}
|
||||
handleChange={e => setData('document', e.target.files[0])}
|
||||
/>
|
||||
<InputError message={errors.document}/>
|
||||
{doc !== undefined && (
|
||||
<p className='text-sm'>file saved is found, reupload to replace</p>
|
||||
)}
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="status" value="Status" />
|
||||
<select
|
||||
className="mt-1 select select-bordered w-full"
|
||||
name="status"
|
||||
onChange={onHandleChange}
|
||||
value={data.status}
|
||||
>
|
||||
{statuses.map(status => (
|
||||
<option key={`status-${status.key}`} value={status.key}>{status.value}</option>
|
||||
))}
|
||||
</select>
|
||||
<InputError message={errors.status}/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-4">
|
||||
<PrimaryButton processing={processing}>
|
||||
Simpan
|
||||
</PrimaryButton>
|
||||
|
||||
<Link href={route('docs.index')} className="btn btn-outline">
|
||||
Batal
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</AuthenticatedLayout>
|
||||
)
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { usePrevious } from 'react-use'
|
||||
import { Head, Link } from '@inertiajs/inertia-react'
|
||||
import { Inertia } from '@inertiajs/inertia'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { useModalState } from '@/Hooks'
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
|
||||
import Pagination from '@/Components/Pagination'
|
||||
import ModalConfirm from '@/Components/ModalConfirm'
|
||||
import ModalFilter from './ModalFilter'
|
||||
import DocStatusItem from './DocStatusItem'
|
||||
import { IconFilter, IconMenu } from '@/Icons'
|
||||
|
||||
export default function Document(props) {
|
||||
const { types, departments } = props
|
||||
const { data: docs, links } = props.docs
|
||||
|
||||
const [search, setSearch] = useState({q: ''})
|
||||
const preValue = usePrevious(search)
|
||||
|
||||
const confirmModal = useModalState(false)
|
||||
const handleDelete = (doc) => {
|
||||
confirmModal.setData(doc)
|
||||
confirmModal.toggle()
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
const doc = confirmModal.data
|
||||
if(doc != null) {
|
||||
Inertia.delete(route('docs.destroy', doc), {
|
||||
onSuccess: () => toast.success('The Data has been deleted'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const filterModal = useModalState(false)
|
||||
|
||||
const handleFilter = (filter) => {
|
||||
setSearch({
|
||||
...search,
|
||||
...filter
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (preValue) {
|
||||
Inertia.get(
|
||||
route(route().current()),
|
||||
search,
|
||||
{
|
||||
replace: true,
|
||||
preserveState: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
}, [search])
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
auth={props.auth}
|
||||
errors={props.errors}
|
||||
flash={props.flash}
|
||||
>
|
||||
<Head title="Document" />
|
||||
<div className="flex flex-col w-full sm:px-6 lg:px-8 space-y-2">
|
||||
<div className="card bg-base-100 w-full">
|
||||
<div className="card-body">
|
||||
<div className="flex flex-col md:flex-row w-full mb-4 justify-between space-y-1 md:space-y-0">
|
||||
<Link
|
||||
className="btn btn-neutral"
|
||||
href={route('docs.create')}
|
||||
>
|
||||
Tambah
|
||||
</Link>
|
||||
<div className='flex flex-row'>
|
||||
<div className="form-control w-full">
|
||||
<input
|
||||
type="text"
|
||||
className="input input-bordered"
|
||||
value={search.q}
|
||||
onChange={(e) =>
|
||||
handleFilter({q: e.target.value})
|
||||
}
|
||||
placeholder="Search"
|
||||
/>
|
||||
</div>
|
||||
<div className='tooltip' data-tip="filter status etc">
|
||||
<div className='btn btn-outline' onClick={() => filterModal.toggle()}>
|
||||
<IconFilter/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-x-auto pb-52">
|
||||
<table className="table w-full table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>No. Dokumen</th>
|
||||
<th>Nama PIC</th>
|
||||
<th>Jenis</th>
|
||||
<th>Deparment</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{docs?.map((doc) => (
|
||||
<tr key={doc.id}>
|
||||
<th>{doc.no}</th>
|
||||
<td>{doc.no_doc}</td>
|
||||
<td>{doc.pic_name}</td>
|
||||
<td>{doc.type.name}</td>
|
||||
<td>{doc.department.name}</td>
|
||||
<td><DocStatusItem status={doc.status}/></td>
|
||||
<td className='text-right'>
|
||||
<div className="dropdown dropdown-left">
|
||||
<label tabIndex={0} className="btn btn-sm m-1 px-1"><IconMenu/></label>
|
||||
<ul tabIndex={0} className="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li>
|
||||
<Link href={route('docs.show', doc)}>Detail</Link>
|
||||
</li>
|
||||
<li>
|
||||
<div>Share</div>
|
||||
</li>
|
||||
<li>
|
||||
<Link href={route('docs.edit', doc)}>Edit</Link>
|
||||
</li>
|
||||
<li onClick={() => handleDelete(doc)} className="bg-error ">
|
||||
<div>Delete</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<Pagination links={links} params={search}/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ModalConfirm
|
||||
isOpen={confirmModal.isOpen}
|
||||
toggle={confirmModal.toggle}
|
||||
onConfirm={onDelete}
|
||||
/>
|
||||
<ModalFilter
|
||||
isOpen={filterModal.isOpen}
|
||||
toggle={filterModal.toggle}
|
||||
filter={search}
|
||||
types={types}
|
||||
departments={departments}
|
||||
handleSetFilter={handleFilter}
|
||||
/>
|
||||
</AuthenticatedLayout>
|
||||
)
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
import React, { useState } from 'react'
|
||||
import { statuses } from '@/utils'
|
||||
import InputLabel from '@/Components/InputLabel'
|
||||
|
||||
export default function ModalFilter(props) {
|
||||
const { isOpen, toggle, handleSetFilter, types, departments, filter } = props
|
||||
const [type, setType] = useState('')
|
||||
const [dep, setDep] = useState('')
|
||||
const [status, setStatus] = useState('')
|
||||
|
||||
const onClickFilter = () => {
|
||||
toggle()
|
||||
handleSetFilter({
|
||||
...filter,
|
||||
type_doc_id: type,
|
||||
status: status,
|
||||
department_id: dep
|
||||
})
|
||||
}
|
||||
|
||||
const onClickReset = () => {
|
||||
toggle()
|
||||
setType('')
|
||||
setDep('')
|
||||
setStatus('')
|
||||
handleSetFilter({
|
||||
...filter,
|
||||
type_doc_id: '',
|
||||
status: '',
|
||||
department_id: ''
|
||||
})
|
||||
}
|
||||
|
||||
const onClickExport = () => {
|
||||
// call export url
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="modal modal-bottom sm:modal-middle pb-10"
|
||||
style={
|
||||
isOpen
|
||||
? {
|
||||
opacity: 1,
|
||||
pointerEvents: 'auto',
|
||||
visibility: 'visible',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<div className="modal-box">
|
||||
<label htmlFor="my-modal-3" className="btn btn-sm btn-circle absolute right-2 top-2" onClick={toggle}>✕</label>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="type" value="Jenis" />
|
||||
<select
|
||||
className="mt-1 select select-bordered w-full"
|
||||
name="type_doc_id"
|
||||
onChange={(e) => setType(e.target.value)}
|
||||
value={type}
|
||||
>
|
||||
<option key={`type.id`} value={``}> - </option>
|
||||
{types.map(type => (
|
||||
<option key={type.id} value={type.id}>{type.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="type" value="Deparment" />
|
||||
<select
|
||||
className="mt-1 select select-bordered w-full"
|
||||
name="department_id"
|
||||
onChange={(e) => setDep(e.target.value)}
|
||||
value={dep}
|
||||
>
|
||||
<option key={`dep.id`} value={``}> - </option>
|
||||
{departments.map(dep => (
|
||||
<option key={dep.id} value={dep.id}>{dep.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<InputLabel forInput="status" value="Status" />
|
||||
<select
|
||||
className="mt-1 select select-bordered w-full"
|
||||
name="status"
|
||||
onChange={(e) => setStatus(e.target.value)}
|
||||
value={status}
|
||||
>
|
||||
<option key={`status.id`} value={``}> - </option>
|
||||
{statuses.map(status => (
|
||||
<option key={`status-${status.key}`} value={status.key}>{status.value}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className='flex justify-center mt-4 space-x-4'>
|
||||
<div className='btn btn-outline' onClick={onClickFilter}>Filter</div>
|
||||
<div className='btn btn-outline' onClick={onClickReset}>Reset</div>
|
||||
<div className='btn btn-info btn-outline' onClick={onClickExport}>Export</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
export const statuses = [
|
||||
{
|
||||
key: 0,
|
||||
value: 'Aktif',
|
||||
color: 'green'
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
value: 'Update',
|
||||
color: 'rgb(229, 195, 24)'
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
value: 'Expired',
|
||||
color: 'red'
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue