diff --git a/app/Exports/ExpensesExport.php b/app/Exports/ExpensesExport.php index 59d126a..a842ea6 100644 --- a/app/Exports/ExpensesExport.php +++ b/app/Exports/ExpensesExport.php @@ -11,11 +11,11 @@ use Maatwebsite\Excel\Concerns\WithHeadings; class ExpensesExport implements WithHeadings, FromView { + public $begining_balance = 0; + public function view(): View { - $this->begining_balance = 0; - $expenses = Expense::all(); - $today = \Carbon\Carbon::now(); + $today = now(); $query = Expense::query()->orderBy('date_expense', 'ASC'); diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 5ac320c..024e3b4 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -2,11 +2,14 @@ namespace App\Http\Controllers; +use App\Exports\ExpensesExport; use App\Models\Expense; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; +use Maatwebsite\Excel\Facades\Excel; class ExpenseController extends Controller { @@ -24,11 +27,10 @@ class ExpenseController extends Controller $query->where('isIncome', 0)->orderBy('created_at', 'DESC'); } - if ($request->q) { + if ($request->q != null) { $query->where('name', 'like', '%'.$request->q.'%') ->orWhere('description', 'like', '%'.$request->q.'%') - ->orWhere('job_number', 'like', '%'.$request->q.'%') - ->orWhere('amount', 'like', '%'.$request->q.'%'); + ->orWhere('job_number', 'like', '%'.$request->q.'%'); } $endDate = Carbon::now()->toDateString(); @@ -43,17 +45,89 @@ class ExpenseController extends Controller ->whereDate('date_expense', '>=', $startDate); $limit = $request->limit ? $request->limit : 10; - + return inertia('Expense/Index', [ 'expenses' => $query->paginate($limit), '_startDate' => $startDate, '_endDate' => $endDate, - '_limit' => $limit + '_limit' => $limit, + '_q' => $request->q + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'description' => ['required'], + 'date_expense' => ['required', 'date'], + 'amount' => ['required', 'numeric'], + 'is_paid' => ['required', 'in:0,1,2,3'], + 'isIncome' => ['required', 'in:0,1'], + ]); + + if ($request->isIncome === 0) { + $request->validate([ + 'name' => ['required'], + 'job_number' => ['required'], + ]); + } + + Expense::create([ + 'name' => $request->name, + 'description' => $request->description, + 'job_number' => $request->job_number, + 'date_expense' => Carbon::parse($request->date_expense)->toDateString(), + 'amount' => $request->amount, + 'is_paid' => $request->is_paid, + 'isIncome' => $request->isIncome, + ]); + + return redirect()->route('expenses.index'); + } + + public function update(Request $request, Expense $expense) + { + $request->validate([ + 'description' => ['required'], + 'date_expense' => ['required', 'date'], + 'amount' => ['required', 'numeric'], + 'is_paid' => ['required', 'in:0,1,2,3'], + 'isIncome' => ['required', 'in:0,1'], ]); + + if ($request->isIncome === 0) { + $request->validate([ + 'name' => ['required'], + 'job_number' => ['required'], + ]); + } + + $expense->update([ + 'name' => $request->name, + 'description' => $request->description, + 'job_number' => $request->job_number, + 'date_expense' => Carbon::parse($request->date_expense)->toDateString(), + 'amount' => $request->amount, + 'is_paid' => $request->is_paid, + 'isIncome' => $request->isIncome, + ]); + + return redirect()->route('expenses.index'); + } + + public function decision(Expense $expense, $status) + { + $expense->update(['is_paid' => $status]); + return redirect()->route('expenses.index'); } public function destroy(Expense $expense) { $expense->delete(); } + + public function export() + { + return Excel::download(new ExpensesExport, 'expenses.xlsx'); + } } diff --git a/app/Models/Expense.php b/app/Models/Expense.php index e0c4dc9..c65faf9 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -9,11 +9,11 @@ class Expense extends Model { use HasFactory; - const IS_PAID_DRAFT = 1; - const IS_PAID_UNPAID = 1; - const IS_PAID_PAID = 2; - const IS_PAID_APPROVE = 3; - const IS_PAID_REJECT = 4; + const IS_PAID_DRAFT = 0; + const IS_PAID_UNPAID = 0; + const IS_PAID_PAID = 1; + const IS_PAID_APPROVE = 2; + const IS_PAID_REJECT = 3; /** * The attributes that are mass assignable. @@ -30,4 +30,16 @@ class Expense extends Model 'isIncome', 'is_paid', ]; + + protected $appends = ['status']; + + public function getStatusAttribute() + { + return [ + self::IS_PAID_DRAFT => 'Draft', + self::IS_PAID_PAID => 'Paid', + self::IS_PAID_APPROVE => 'Approve', + self::IS_PAID_REJECT => 'Reject', + ][$this->is_paid]; + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ee8ca5b..40c4154 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -23,6 +25,17 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { - // + if (app()->isProduction() == false) { + DB::listen(function ($query) { + Log::info( + $query->sql, + [ + 'bindings' => $query->bindings, + 'time' => $query->time, + 'connectionName' => $query->connectionName + ] + ); + }); + } } } diff --git a/resources/css/app.css b/resources/css/app.css index c903134..7c1d6b5 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -8,7 +8,7 @@ } .react-datepicker-popper { - @apply z-40 w-72 text-sm bg-white shadow px-3 py-2 border-2 border-gray-200 rounded; + @apply z-10 w-72 text-sm bg-white shadow px-3 py-2 border-2 border-gray-200 rounded; } .react-datepicker-left { diff --git a/resources/js/Components/DatePickerInput.jsx b/resources/js/Components/DatePickerInput.jsx index eab2a28..bdfdf8c 100644 --- a/resources/js/Components/DatePickerInput.jsx +++ b/resources/js/Components/DatePickerInput.jsx @@ -35,3 +35,21 @@ export const DatePickerRangeInput = ({ ); }; + +export const DatePickerInput = ({ + value, + onChange, +}) => { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/resources/js/Components/Modal.jsx b/resources/js/Components/Modal.jsx index cf6d39f..2622068 100644 --- a/resources/js/Components/Modal.jsx +++ b/resources/js/Components/Modal.jsx @@ -1,57 +1,25 @@ -import { Fragment } from 'react'; -import { Dialog, Transition } from '@headlessui/react'; +import React from "react"; -export default function Modal({ children, show = false, maxWidth = '2xl', closeable = true, onClose = () => {} }) { - const close = () => { - if (closeable) { - onClose(); - } - }; - - const maxWidthClass = { - sm: 'sm:max-w-sm', - md: 'sm:max-w-md', - lg: 'sm:max-w-lg', - xl: 'sm:max-w-xl', - '2xl': 'sm:max-w-2xl', - }[maxWidth]; +export default function Modal({ isOpen, toggle = () => {}, children, title = ''}) { return ( - - - -
- - - - - {children} - - -
-
- ); -} +
+
+

+ {title} +

+ {children} +
+
+ ) +} \ No newline at end of file diff --git a/resources/js/Pages/Booking/Index.jsx b/resources/js/Pages/Booking/Index.jsx index 69ce38b..d9893d1 100644 --- a/resources/js/Pages/Booking/Index.jsx +++ b/resources/js/Pages/Booking/Index.jsx @@ -161,7 +161,7 @@ export default function Dashboard(props) {
-
+
handleToggleForm()}>Tambah
diff --git a/resources/js/Pages/Expense/FormModal.jsx b/resources/js/Pages/Expense/FormModal.jsx new file mode 100644 index 0000000..adeae6f --- /dev/null +++ b/resources/js/Pages/Expense/FormModal.jsx @@ -0,0 +1,196 @@ +import React, { useEffect, useState } from "react"; +import Modal from "@/Components/Modal"; +import InputError from "@/Components/InputError"; +import { DatePickerInput } from "@/Components/DatePickerInput"; +import { useForm, usePage } from "@inertiajs/inertia-react"; +import { toast } from "react-toastify"; + +export default function FormModal(props) { + const { auth: { user } } = usePage().props + + const { modalState } = props + const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({ + id: null, + name: "", + description: "", + job_number: "", + date_expense: new Date(), + amount: "", + isIncome: 0, + is_paid: 0, + }) + + const setType = (type) => { + setData('isIncome',type) + } + + const handleOnChange = (event) => { + setData(event.target.name, event.target.value) + } + + const handleReset = () => { + modalState.setData(null) + reset() + clearErrors() + } + + const handleClose = () => { + handleReset() + modalState.toggle() + } + + const handleSubmit = () => { + const expense = modalState.data + if(expense !== null) { + put(route('expenses.update', expense), { + onSuccess: () => { + toast.success('item updated') + handleClose() + } + }) + return + } + post(route('expenses.store'), { + onSuccess: () => { + toast.success('item created') + handleClose() + } + }) + } + + const title = data.id ? 'Edit Data' : 'Tambah Data' + + useEffect(() => { + const expense = modalState.data + if (expense !== null) { + setData({ + id: expense?.id, + name: expense?.name, + description: expense?.description, + job_number: expense?.job_number, + date_expense: new Date(expense?.date_expense), + amount: expense?.amount, + isIncome: expense?.isIncome, + is_paid: expense?.is_paid, + }) + return + } + }, [modalState]) + + return ( + + {+user.role === 1 && ( + + )} + + {data.isIncome === 0 && ( + <> +
+ + + +
+
+ + + +
+ + )} + +
+ +