pull/2/head
ajikamaludin 2 years ago
parent 0177c3cc7e
commit d5a3224bf1

@ -2,9 +2,55 @@
namespace App\Http\Controllers;
use App\Models\Booking;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
class BookingController extends Controller
{
//
public function index(Request $request)
{
Booking::where('is_available', 0)
->where(DB::raw('DATE(departure)'), '<', now()->toDateString())
->update(['is_available' => 1]);
$query = Booking::orderBy('departure', 'ASC');
$last_updated = Booking::select('updated_at')->latest('updated_at')->first();
if ($last_updated !== null) {
$last_updated = $last_updated->updated_at;
}
if ($request->q !== null) {
$query->where('master_awb', 'like', '%' . $request->q . '%')
->orWhere('flight_number', 'like', '%' . $request->q . '%')
->orWhere('departure', 'like', '%' . $request->q . '%')
->orWhere('destination', 'like', '%' . $request->q . '%');
}
$startDate = now()->startOfMonth()->toDateString();
$endDate = now()->endOfMonth()->toDateString();
if ($request->startDate != null) {
$startDate = Carbon::parse($request->startDate)->toDateString();
}
if ($request->endDate != null) {
$endDate = Carbon::parse($request->endDate)->toDateString();
}
$query->whereBetween(DB::raw('DATE(departure)'), [$startDate, $endDate]);
$limit = $request->limit ? $request->limit : 10;
return inertia('Booking/Index', [
'booking' => $query->paginate($limit),
'last_updated' => $last_updated,
'_startDate' => $startDate,
'_endDate' => $endDate,
'_limit' => $limit
]);
}
}

@ -2,9 +2,58 @@
namespace App\Http\Controllers;
use App\Models\Expense;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
class ExpenseController extends Controller
{
//
public function index(Request $request)
{
$isAdmin = Auth::user()->role === User::ROLE_CASIER;
$today = Carbon::now();
$query = Expense::query();
if ($isAdmin) {
$query->orderBy('date_expense', 'DESC');
}
if (!$isAdmin) {
$query->where('isIncome', 0)->orderBy('created_at', 'DESC');
}
if ($request->start_date && $request->end_date) {
$startDate = Carbon::parse($request->start_date);
$endDate = Carbon::parse($request->end_date);
$query->whereDate('date_expense', '<=', $endDate)
->whereDate('date_expense', '>=', $startDate);
}
if ($request->q) {
$query->where('name', 'like', '%'.$request->q.'%')
->orWhere('description', 'like', '%'.$request->q.'%')
->orWhere('job_number', 'like', '%'.$request->q.'%')
->orWhere('amount', 'like', '%'.$request->q.'%');
}
if (!$request->start_date) {
$endDate = Carbon::now();
$startDate = $today->subDays(30);
$query->whereDate('date_expense', '<=', $endDate)
->whereDate('date_expense', '>=', $startDate);
}
$limit = $request->limit ? $request->limit : 10;
return inertia('Expense/Index', [
'expenses' => $query->paginate($limit),
'start_date' => $startDate,
'end_date' => $endDate,
'_limit' => $limit
]);
}
}

@ -8,4 +8,21 @@ use Illuminate\Database\Eloquent\Model;
class Booking extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'master_awb',
'flight_number',
'departure',
'destination',
'jumlah_koli',
'kemasan',
'booked',
'used',
'is_available',
];
}

@ -8,4 +8,26 @@ use Illuminate\Database\Eloquent\Model;
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;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'id',
'name',
'job_number',
'description',
'date_expense',
'amount',
'isIncome',
'is_paid',
];
}

@ -12,6 +12,10 @@ class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
const ROLE_CASIER = 1;
const ROLE_OPERATION = 2;
const ROLE_MANAJEMEN = 3;
/**
* The attributes that are mass assignable.
*
@ -21,6 +25,7 @@ class User extends Authenticatable
'name',
'email',
'password',
'role'
];
/**

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if(!Schema::hasColumn('users', 'role')) {
Schema::table('users', function($t){
$t->string('role');
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
};

1196
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -20,5 +20,13 @@
"react-dom": "^18.2.0",
"tailwindcss": "^3.2.1",
"vite": "^4.0.0"
},
"dependencies": {
"daisyui": "^2.46.1",
"moment": "^2.29.4",
"qs": "^6.11.0",
"react-datepicker": "^4.8.0",
"react-toastify": "^9.1.1",
"react-use": "^17.4.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

@ -1,3 +1,100 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.react-datepicker__input-container > input {
@apply block w-full text-base md:text-sm bg-white border border-gray-300 shadow-sm input input-bordered;
}
.react-datepicker-popper {
@apply z-40 w-72 text-sm bg-white shadow px-3 py-2 border-2 border-gray-200 rounded;
}
.react-datepicker-left {
@apply absolute left-0 right-auto top-11 transform-none !important;
}
.react-datepicker-right {
@apply absolute right-0 left-auto top-11 transform-none !important;
}
.react-datepicker__portal {
@apply absolute z-10 w-72 text-sm transform-none bg-white shadow px-3 py-2 top-12 right-0 border-2 border-gray-200 rounded;
}
.react-datepicker__month-container {
@apply flex flex-col;
}
.react-datepicker__month {
@apply flex flex-col;
}
.react-datepicker__current-month {
@apply ml-2.5 text-lg font-semibold text-gray-800;
}
.react-datepicker__week {
@apply flex justify-around;
}
.react-datepicker__day-names {
@apply flex justify-around text-gray-400 font-medium text-center text-xs;
}
.react-datepicker__day-name {
@apply w-8 h-8 flex items-center justify-center py-1 rounded-full;
}
.react-datepicker__navigation {
@apply absolute top-2;
}
.react-datepicker__navigation--previous {
@apply right-12 w-8 h-8 rounded transition flex items-center justify-center hover:bg-gray-200;
}
.react-datepicker__navigation--next {
@apply right-4 w-8 h-8 rounded transition flex items-center justify-center hover:bg-gray-200;
}
.react-datepicker__day {
@apply mb-1 w-8 h-8 flex items-center justify-center py-1 text-sm leading-loose transition text-gray-700 rounded;
}
.react-datepicker__day--disabled {
@apply cursor-not-allowed opacity-40 hover:bg-transparent;
}
.react-datepicker__day--outside-month {
@apply text-gray-300;
}
.react-datepicker__day--in-range {
@apply bg-gray-200;
}
.react-datepicker__day--in-selecting-range {
@apply bg-blue-200;
}
.react-datepicker__day--selecting-range-start {
@apply bg-white border-2 border-blue-500;
}
.react-datepicker__day--selecting-range-end {
@apply bg-white border-2 border-blue-500;
}
.react-datepicker__day--selected {
@apply bg-blue-500 text-white;
}
.react-datepicker__day--range-start {
@apply bg-blue-500 text-white hover:text-gray-700 hover:bg-white;
}
.react-datepicker__day--range-end {
@apply bg-blue-500 text-white hover:text-gray-700 hover:bg-white;
}

@ -1,7 +1,7 @@
export default function ApplicationLogo({ className }) {
return (
<svg className={className} viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg">
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z" />
</svg>
<div className="w-20">
<img src="/img/yamato-cash-portal-icon.png" className="mx-auto" alt="logo Yamato Operation Portal"/>
</div>
);
}

@ -0,0 +1,35 @@
import React, { useEffect } from "react";
import DatePicker from "react-datepicker";
export const DatePickerRangeInput = ({
startDate,
setStartDate,
endDate,
setEndDate,
}) => {
if(typeof(endDate) === 'string') {
endDate = new Date(endDate)
}
return (
<div className="flex space-x-1">
<div className="relative">
<DatePicker
selected={startDate}
onChange={(dates) => {
const [start, end] = dates;
setStartDate(start);
setEndDate(end);
}}
selectsRange
startDate={startDate}
endDate={endDate}
nextMonthButtonLabel=">"
previousMonthButtonLabel="<"
popperClassName="react-datepicker-left"
/>
</div>
</div>
);
};

@ -0,0 +1,63 @@
import { Inertia } from "@inertiajs/inertia";
import qs from "qs";
const PageLink = ({ active, label, url, params }) => {
const className = `mr-1 mb-1 px-4 py-3 border border-solid border-gray-300 rounded text-sm bg-white hover:bg-white ${
active &&
"focus:outline-none focus:border-gray-700 focus:text-gray-700 border-gray-600 bg-gray-600 text-white hover:bg-gray-400"
}`;
const onClick = () => {
Inertia.get(
`${url}&${qs.stringify(params)}`,
{},
{
replace: true,
// preserveState: true,
}
);
};
return (
<div className={className} onClick={onClick}>
<span dangerouslySetInnerHTML={{ __html: label }}></span>
</div>
);
};
// Previous, if on first page
// Next, if on last page
// and dots, if exists (...)
const PageInactive = ({ label }) => {
const className =
"mr-1 mb-1 px-4 py-3 text-sm border rounded border-solid border-gray-300 text-gray";
return (
<div
className={className}
dangerouslySetInnerHTML={{ __html: label }}
/>
);
};
export default ({ links = [], params = null }) => {
// dont render, if there's only 1 page (previous, 1, next)
if (links.length === 3) return null;
return (
<div className="flex flex-wrap mt-6 -mb-1">
{links.map(({ active, label, url }) => {
return url === null ? (
<PageInactive key={label} label={label} />
) : (
<PageLink
key={label}
label={label}
active={active}
url={url}
params={params}
/>
)
})}
</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,
};
}

@ -4,8 +4,9 @@ import Dropdown from '@/Components/Dropdown';
import NavLink from '@/Components/NavLink';
import ResponsiveNavLink from '@/Components/ResponsiveNavLink';
import { Link } from '@inertiajs/inertia-react';
import { ToastContainer } from 'react-toastify';
export default function Authenticated({ auth, header, children }) {
export default function Authenticated({ auth, children }) {
const [showingNavigationDropdown, setShowingNavigationDropdown] = useState(false);
return (
@ -21,9 +22,14 @@ export default function Authenticated({ auth, header, children }) {
</div>
<div className="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<NavLink href={route('dashboard')} active={route().current('dashboard')}>
Dashboard
<NavLink href={route('expenses.index')} active={route().current('expenses.index')}>
Expense Tracking
</NavLink>
{auth.user.role === "2" && (
<NavLink href={route('monitoring-booking.index')} active={route().current('monitoring-booking.index')}>
Monitoring Booking
</NavLink>
)}
</div>
</div>
@ -92,9 +98,14 @@ export default function Authenticated({ auth, header, children }) {
<div className={(showingNavigationDropdown ? 'block' : 'hidden') + ' sm:hidden'}>
<div className="pt-2 pb-3 space-y-1">
<ResponsiveNavLink href={route('dashboard')} active={route().current('dashboard')}>
Dashboard
<ResponsiveNavLink href={route('expenses.index')} active={route().current('expenses.index')}>
Expense Tracking
</ResponsiveNavLink>
{auth.user.role === "2" && (
<ResponsiveNavLink href={route('monitoring-booking.index')} active={route().current('monitoring-booking.index')}>
Monitoring Booking
</ResponsiveNavLink>
)}
</div>
<div className="pt-4 pb-1 border-t border-gray-200">
@ -115,13 +126,23 @@ export default function Authenticated({ auth, header, children }) {
</div>
</nav>
{header && (
<header className="bg-white shadow">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">{header}</div>
</header>
)}
<header className="bg-white shadow">
<div className="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8">Yamato Cash Management</div>
</header>
<main>{children}</main>
<ToastContainer
position="top-right"
autoClose={5000}
theme='colored'
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</div>
);
}

@ -6,7 +6,10 @@ 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="/">
<ApplicationLogo className="w-20 h-20 fill-current text-gray-500" />
<img src="/img/yamato-cash-portal-icon.png" className="w-32 h-auto mx-auto" alt="logo Yamato Operation Portal"/>
<div className="font-bold text-2xl p-8">
Yamato Operation Portal
</div>
</Link>
</div>

@ -3,11 +3,9 @@ import Checkbox from '@/Components/Checkbox';
import GuestLayout from '@/Layouts/GuestLayout';
import InputError from '@/Components/InputError';
import InputLabel from '@/Components/InputLabel';
import PrimaryButton from '@/Components/PrimaryButton';
import TextInput from '@/Components/TextInput';
import { Head, Link, useForm } from '@inertiajs/inertia-react';
import { Head, useForm } from '@inertiajs/inertia-react';
export default function Login({ status, canResetPassword }) {
export default function Login({ status }) {
const { data, setData, post, processing, errors, reset } = useForm({
email: '',
password: '',
@ -40,15 +38,14 @@ export default function Login({ status, canResetPassword }) {
<div>
<InputLabel forInput="email" value="Email" />
<TextInput
id="email"
type="email"
<input
type="text"
className="input input-bordered w-full"
name="email"
value={data.email}
className="mt-1 block w-full"
onChange={onHandleChange}
autoComplete="username"
isFocused={true}
handleChange={onHandleChange}
autoFocus={true}
/>
<InputError message={errors.email} className="mt-2" />
@ -57,14 +54,12 @@ export default function Login({ status, canResetPassword }) {
<div className="mt-4">
<InputLabel forInput="password" value="Password" />
<TextInput
id="password"
<input
type="password"
className="input input-bordered w-full"
name="password"
value={data.password}
className="mt-1 block w-full"
autoComplete="current-password"
handleChange={onHandleChange}
onChange={onHandleChange}
/>
<InputError message={errors.password} className="mt-2" />
@ -78,18 +73,9 @@ export default function Login({ status, canResetPassword }) {
</div>
<div className="flex items-center justify-end mt-4">
{canResetPassword && (
<Link
href={route('password.request')}
className="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Forgot your password?
</Link>
)}
<PrimaryButton className="ml-4" processing={processing}>
<button className='btn' disabled={processing}>
Log in
</PrimaryButton>
</button>
</div>
</form>
</GuestLayout>

@ -0,0 +1,273 @@
import React, { useState, useEffect } from 'react'
import { usePrevious } from "react-use";
import { Inertia } from "@inertiajs/inertia";
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import Pagination from "@/Components/Pagination";
import { DatePickerRangeInput } from "@/Components/DatePickerInput";
import { useModalState } from "@/Hook";
import { Head } from '@inertiajs/inertia-react';
import { formatDate } from "@/Utils";
import { toast } from 'react-toastify';
export default function Dashboard(props) {
const { _startDate, _endDate, _limit } = props
const [startDate, setStartDate] = useState(_startDate)
const [endDate, setEndDate] = useState(_endDate)
const { data: bookings, links } = props.booking;
const [bookingsChecked, setBookingsChecked] = useState(
bookings.map((booking) => {
return {
...booking,
isChecked: false,
};
})
);
const [search, setSearch] = useState("");
const [limit, setLimit] = useState(_limit)
const preValue = usePrevious(`${search}-${startDate}-${endDate}-${limit}`);
const [booking, setBooking] = useState(null);
const [ids, setIds] = useState({});
const formModal = useModalState(false);
const handleEdit = (booking = null) => {
setBooking(booking);
formModal.toggle();
};
const confirmModal = useModalState(false);
const handleDelete = (booking) => {
confirmModal.setData(booking);
confirmModal.toggle();
};
const detailModal = useModalState(false);
const handleDetail = (booking = null) => {
setBooking(booking);
detailModal.toggle();
};
const bookingModal = useModalState(false);
const handleBooking = () => {
bookingModal.toggle();
};
const onDelete = () => {
const booking = confirmModal.data;
if (booking != null) {
Inertia.delete(
route("monitoring-booking.destroy", booking), {
onSuccess: () => toast.success("item delete"),
}
);
}
};
const handleCheckedCheckbox = (e) => {
setBookingsChecked(
bookingsChecked.map((booking) => {
if (booking.id === e.target.defaultValue * 1) {
return {
...booking,
isChecked: !booking.isChecked,
};
} else {
return booking;
}
})
);
};
const handleMouseOverExport = () => {
let params = bookingsChecked
.map((booking) => {
if (booking.isChecked) {
return booking.id;
}
})
.filter((isChecked) => {
return isChecked !== undefined;
});
setIds(params);
};
const handleCheckAll = (e) => {
setBookingsChecked((prevBookingsChecked) => {
return prevBookingsChecked.map((booking) => {
return {
...booking,
isChecked: e.target.checked,
};
});
});
};
const params = { ids };
useEffect(() => {
setBookingsChecked(
bookings.map((booking) => {
return {
...booking,
isChecked: false,
};
})
);
}, [bookings]);
useEffect(() => {
if (preValue) {
Inertia.get(
route(route().current()),
{ q: search, startDate, endDate, limit },
{
replace: true,
preserveState: true,
}
)
}
}, [search, startDate, endDate, limit])
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
>
<Head title="Booking" />
<div className="p-4">
<div className="mx-auto max-w-7xl p-4 bg-white overflow-hidden shadow-sm sm:rounded-lg min-h-screen">
<div className='flex justify-between space-x-0 lg:space-x-1 flex-row mb-2'>
<div className='flex space-x-1'>
<div className='btn'>Tambah</div>
<div className='btn'>Import Excel</div>
</div>
<div className='btn'>Export Excel</div>
</div>
<div className='flex justify-between space-y-1 lg:space-y-0 space-x-0 lg:space-x-1 flex-col lg:flex-row'>
<div>
Terakhir diperbarui: {props.last_updated
? formatDate(props.last_updated).format('DD/MM/Y hh:mm')
: 'Belum ada pembaruan'}
</div>
<div className='flex lg:space-x-2 space-x-0 lg:space-y-0 space-y-1 flex-col lg:flex-row'>
<div className='flex space-x-1'>
<DatePickerRangeInput
startDate={new Date(startDate)}
setStartDate={setStartDate}
endDate={endDate}
setEndDate={setEndDate}
/>
</div>
<div>
<input className='input input-bordered w-full' placeholder='search'/>
</div>
</div>
</div>
<div className="overflow-x-auto mt-2">
<table className="table w-full table-zebra">
<thead>
<tr>
<th>
<input
type="checkbox"
className="checkbox checkbox-xs"
onChange={(e) =>handleCheckAll(e)}
/>
</th>
<th>Master AWB</th>
<th>Flight Number</th>
<th>Departure</th>
<th>Destination</th>
<th>Booked</th>
<th>Packaging</th>
<th>Used</th>
<th>Status</th>
<th>Opsi</th>
</tr>
</thead>
<tbody>
{bookingsChecked.map((booking) => (
<tr key={booking.id}>
<td>
<input
type="checkbox"
className="checkbox checkbox-xs"
onChange={(e) => handleCheckedCheckbox(e)}
checked={booking.isChecked}
value={booking.id}
/>
</td>
<td>{booking.master_awb}</td>
<td>{booking.flight_number}</td>
<td>
{formatDate(booking.departure).format('DD/MM/yyyy')}
</td>
<td>{booking.destination}</td>
<td>{booking.booked}</td>
<td>{booking.kemasan}</td>
<td>{booking.used}</td>
<td>
{+booking.is_available === 0
? 'Available'
: 'Closed'}
</td>
<td className="flex gap-1">
<button
className="btn btn-neutral btn-xs"
onClick={() => handleDetail(booking)}
>
Detail
</button>
<button
className="btn btn-neutral btn-xs"
onClick={() => handleEdit(booking)}
>
Edit
</button>
<button
className="btn btn-neutral btn-xs"
onClick={() => handleDelete(booking)}
>
Hapus
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className='flex w-full'>
<div className="flex mx-auto items-end mt-4">
<Pagination
links={links}
params={{
q: search,
startDate,
endDate,
limit,
}}
/>
<div>
<select
className="select select-bordered w-full max-w-xs"
onChange={(e) => setLimit(e.target.value)}
value={limit}
>
<option value="10">10</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}

@ -0,0 +1,37 @@
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head } from '@inertiajs/inertia-react';
export default function Dashboard(props) {
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
>
<Head title="Expense" />
<div className="p-4">
<div className="mx-auto max-w-7xl p-2 bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className='flex justify-between space-x-0 lg:space-x-1 flex-row mb-2'>
<div className='btn'>Tambah</div>
<div className='btn'>Export Excel</div>
</div>
<div className='flex justify-between space-y-1 lg:space-y-0 space-x-0 lg:space-x-1 flex-col lg:flex-row'>
<div>
<div>Jumlah Record : 100</div>
<div>Jumlah Halaman : 24</div>
</div>
<div className='flex lg:space-x-2 space-x-0 lg:space-y-0 space-y-1 flex-col lg:flex-row'>
<div className='flex space-x-1'>
<input className='input input-bordered w-full lg:w-32' placeholder='start date' defaultValue={'01/10/2023'}/>
<input className='input input-bordered w-full lg:w-32' placeholder='end date'defaultValue={'01/10/2023'}/>
</div>
<div>
<input className='input input-bordered w-full' placeholder='search'/>
</div>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}

@ -0,0 +1,10 @@
import moment from "moment";
export const formatDate = (date) => {
return moment(date);
};
export function formatIDR(amount) {
const idFormatter = new Intl.NumberFormat("id-ID");
return idFormatter.format(amount);
}

@ -5,11 +5,10 @@ import { createRoot } from 'react-dom/client';
import { createInertiaApp } from '@inertiajs/inertia-react';
import { InertiaProgress } from '@inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
import "react-toastify/dist/ReactToastify.css";
createInertiaApp({
title: (title) => `${title} - ${appName}`,
title: (title) => `${title}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.jsx`, import.meta.glob('./Pages/**/*.jsx')),
setup({ el, App, props }) {
const root = createRoot(el);

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="corporate">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

@ -1,5 +1,7 @@
<?php
use App\Http\Controllers\BookingController;
use App\Http\Controllers\ExpenseController;
use App\Http\Controllers\ProfileController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
@ -20,9 +22,16 @@ Route::get('/', function () {
return redirect()->route('login');
});
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', fn () => redirect()->route('expenses.index'))->name('dashboard');
// Expense & Income Page
Route::get('/expenses', [ExpenseController::class, 'index'])->name('expenses.index');
// Monitor Booking
Route::get('/monitoring-booking', [BookingController::class, 'index'])->name('monitoring-booking.index');
});
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');

@ -17,5 +17,5 @@ module.exports = {
},
},
plugins: [require('@tailwindcss/forms')],
plugins: [require('@tailwindcss/forms'),require("daisyui")],
};

Loading…
Cancel
Save