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