crud and import voucher done
parent
b709a716e4
commit
78144c25bc
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LocationController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Location::orderBy('updated_at', 'desc');
|
||||
|
||||
if ($request->q != '') {
|
||||
$query->where('name', 'like', "%$request->q%");
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Voucher;
|
||||
use App\Services\GeneralService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class VoucherController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Voucher::with(['location'])->orderBy('updated_at', 'desc');
|
||||
|
||||
if ($request->q != '') {
|
||||
$query->where('username', 'like', "%$request->q%")
|
||||
->orWhere('comment', 'like', "%$request->q%")
|
||||
->orWhere('profile', 'like', "%$request->q%");
|
||||
}
|
||||
|
||||
return inertia('Voucher/Index', [
|
||||
'query' => $query->paginate()
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return inertia('Voucher/Form');
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'nullable|string',
|
||||
'description' => 'nullable|string',
|
||||
'location_id' => 'required|exists:locations,id',
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
'discount' => 'required|numeric',
|
||||
'display_price' => 'required|numeric',
|
||||
'quota' => 'required|string',
|
||||
'profile' => 'required|string',
|
||||
'comment' => 'required|string',
|
||||
'expired' => 'required|numeric',
|
||||
'expired_unit' => 'required|string',
|
||||
]);
|
||||
|
||||
Voucher::create([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'location_id' => $request->location_id,
|
||||
'username' => $request->username,
|
||||
'password' => $request->password,
|
||||
'discount' => $request->discount,
|
||||
'display_price' => $request->display_price,
|
||||
'quota' => $request->quota,
|
||||
'profile' => $request->profile,
|
||||
'comment' => $request->comment,
|
||||
'expired' => $request->expired,
|
||||
'expired_unit' => $request->expired_unit,
|
||||
]);
|
||||
|
||||
return redirect()->route('voucher.index')
|
||||
->with('message', ['type' => 'success', 'message' => 'Item has beed saved']);
|
||||
}
|
||||
|
||||
public function edit(Voucher $voucher)
|
||||
{
|
||||
return inertia('Voucher/Form', [
|
||||
'voucher' => $voucher,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, Voucher $voucher)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'nullable|string',
|
||||
'description' => 'nullable|string',
|
||||
'location_id' => 'required|exists:locations,id',
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
'discount' => 'required|numeric',
|
||||
'display_price' => 'required|numeric',
|
||||
'quota' => 'required|string',
|
||||
'profile' => 'required|string',
|
||||
'comment' => 'required|string',
|
||||
'expired' => 'required|numeric',
|
||||
'expired_unit' => 'required|string',
|
||||
]);
|
||||
|
||||
$voucher->update([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'location_id' => $request->location_id,
|
||||
'username' => $request->username,
|
||||
'password' => $request->password,
|
||||
'discount' => $request->discount,
|
||||
'display_price' => $request->display_price,
|
||||
'quota' => $request->quota,
|
||||
'profile' => $request->profile,
|
||||
'comment' => $request->comment,
|
||||
'expired' => $request->expired,
|
||||
]);
|
||||
|
||||
return redirect()->route('voucher.index')
|
||||
->with('message', ['type' => 'success', 'message' => 'Item has beed updated']);
|
||||
}
|
||||
|
||||
public function destroy(Voucher $voucher)
|
||||
{
|
||||
$voucher->delete();
|
||||
|
||||
return redirect()->route('voucher.index')
|
||||
->with('message', ['type' => 'success', 'message' => 'Item has beed deleted']);
|
||||
}
|
||||
|
||||
public function form_import()
|
||||
{
|
||||
return inertia('Voucher/Import');
|
||||
}
|
||||
|
||||
public function import(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'script' => 'required|string',
|
||||
'location_id' => 'required|exists:locations,id',
|
||||
'discount' => 'required|numeric',
|
||||
'display_price' => 'required|numeric',
|
||||
'expired' => 'required|numeric',
|
||||
'expired_unit' => 'required|string',
|
||||
]);
|
||||
|
||||
$vouchers = GeneralService::script_parser($request->script);
|
||||
|
||||
DB::beginTransaction();
|
||||
foreach ($vouchers as $voucher) {
|
||||
Voucher::create([
|
||||
'location_id' => $request->location_id,
|
||||
'username' => $voucher['username'],
|
||||
'password' => $voucher['password'],
|
||||
'discount' => $request->discount,
|
||||
'display_price' => $request->display_price,
|
||||
'quota' => $voucher['quota'],
|
||||
'profile' => $voucher['profile'],
|
||||
'comment' => $voucher['comment'],
|
||||
'expired' => $request->expired,
|
||||
'expired_unit' => $request->expired_unit,
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return redirect()->route('voucher.index')
|
||||
->with('message', ['type' => 'success', 'message' => 'Items has beed saved']);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class GeneralService
|
||||
{
|
||||
public static function script_parser($script)
|
||||
{
|
||||
$data = [];
|
||||
$lines = explode("\n", $script);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$item = self::line_parser($line);
|
||||
if ($item != null) {
|
||||
$data[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function line_parser($line)
|
||||
{
|
||||
$item = null;
|
||||
|
||||
$commands = explode(' ', $line);
|
||||
foreach ($commands as $command) {
|
||||
if (str_contains($command, 'name')) {
|
||||
$d = explode('=', $command);
|
||||
$item['username'] = str_replace('"', '', $d[1]);
|
||||
}
|
||||
if (str_contains($command, 'password')) {
|
||||
$d = explode('=', $command);
|
||||
$item['password'] = str_replace('"', '', $d[1]);
|
||||
}
|
||||
if (str_contains($command, 'profile')) {
|
||||
$d = explode('=', $command);
|
||||
$item['profile'] = str_replace('"', '', $d[1]);
|
||||
}
|
||||
if (str_contains($command, 'comment')) {
|
||||
$d = explode('=', $command);
|
||||
$item['comment'] = str_replace('"', '', $d[1]);
|
||||
}
|
||||
if (str_contains($command, 'limit-bytes-total')) {
|
||||
$d = explode('=', $command);
|
||||
$item['quota'] = (int) str_replace('"', '', $d[1]);
|
||||
}
|
||||
}
|
||||
info('item', [$item]);
|
||||
|
||||
return $item;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Midtrans\Config;
|
||||
use Midtrans\Snap;
|
||||
|
||||
class MidtransService
|
||||
{
|
||||
protected $order;
|
||||
|
||||
public function __construct($order, $serverKey)
|
||||
{
|
||||
Config::$serverKey = $serverKey;
|
||||
Config::$isProduction = app()->isProduction();
|
||||
Config::$isSanitized = true;
|
||||
Config::$is3ds = true;
|
||||
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
public function getSnapToken()
|
||||
{
|
||||
$items = $this->order->items->map(function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'price' => $item->amount,
|
||||
'quantity' => $item->quantity,
|
||||
'name' => $item->item->order_detail,
|
||||
];
|
||||
});
|
||||
|
||||
if ($this->order->total_discount > 0) {
|
||||
$items->add([
|
||||
'id' => 'Discount',
|
||||
'price' => -$this->order->total_discount,
|
||||
'quantity' => 1,
|
||||
'name' => 'DISCOUNT',
|
||||
]);
|
||||
}
|
||||
|
||||
$params = [
|
||||
'transaction_details' => [
|
||||
'order_id' => $this->order->order_code,
|
||||
'gross_amount' => $this->order->total_amount,
|
||||
],
|
||||
'item_details' => $items->toArray(),
|
||||
'customer_details' => [
|
||||
'name' => $this->order->customer->name,
|
||||
'email' => $this->order->customer->email,
|
||||
'phone' => $this->order->customer->phone,
|
||||
'address' => $this->order->customer->address,
|
||||
],
|
||||
];
|
||||
|
||||
$snapToken = Snap::getSnapToken($params);
|
||||
|
||||
return $snapToken;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
add name="8ga6xmzzkn" password="8ga6xmzzkn" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8gd5874ncu" password="8gd5874ncu" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax37" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax38" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax39" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax10" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax11" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax12" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax13" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax14" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8ga6xmzzkn1" password="8ga6xmzzkn" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8gd5874ncu2" password="8gd5874ncu" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax373" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax384" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax395" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax106" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax117" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax128" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax139" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8g5t5tax1410" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="YvTdU80eaq" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="IckwYPBq7m" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="25HH4VlPoN" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="Sc5LesGx1S" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="XiMhZXBeyb" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="kaEXtorL8V" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="EfKbqsSuSs" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="3QZJaf5bcJ" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="vPXQF5ZZb3" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="yYKLQFjJll" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="sw7Oi5WznU" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="DYcTEEyZCz" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="m5XTZNFYmq" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="E4k5tXWBsH" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8ky5sMmDUB" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="7Cbm4U52AN" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="Vdkw5NU312" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="NW6GXomQyF" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="SsilG1ZkkM" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="8D8oPBRfhm" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="0GL2GwD9fl" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="Z1f3ZpplaD" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="E1MVCSIvjR" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="1S2xkQ9ofv" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="VdX0x5GjS9" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="xTIRAtrkM4" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="RYvmb03wOD" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="Ji6AEYaJmF" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="yLHQlK79cZ" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="BBvTG8TWv0" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="sMQ4jlCVJB" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="iaQNtPMcQH" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="HsUIUagLX4" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="xUrvtZpRL3" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="Yyh0gjfmQR" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="FHMFQd9MpW" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="pliCYDGkSd" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="nkNvbK2W6e" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="jIGd68CJjM" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
add name="Jm3WDPCNSw" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"
|
||||
|
||||
foreach(range(1,40) as $r) {
|
||||
echo 'add name="'. Str::random(10) .'" password="8g5t5tax37" profile="NEW-8GB-Kuota-7726-MB" comment="vc-203-06.17.22-a1" limit-bytes-total="8101298176"'."\n";
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
import React, { useRef, useEffect, useState } from 'react'
|
||||
import { useDebounce } from '@/hooks'
|
||||
import { usePage } from '@inertiajs/react'
|
||||
import axios from 'axios'
|
||||
import { HiChevronDown, HiChevronUp, HiX } from 'react-icons/hi'
|
||||
import { Spinner } from 'flowbite-react'
|
||||
|
||||
export default function SelectionInput(props) {
|
||||
const ref = useRef()
|
||||
const {
|
||||
props: { auth },
|
||||
} = usePage()
|
||||
|
||||
const {
|
||||
label = '',
|
||||
itemSelected = null,
|
||||
onItemSelected = () => {},
|
||||
disabled = false,
|
||||
placeholder = '',
|
||||
error = '',
|
||||
all = 0,
|
||||
} = props
|
||||
|
||||
const [showItems, setShowItem] = useState([])
|
||||
|
||||
const [isSelected, setIsSelected] = useState(true)
|
||||
const [selected, setSelected] = useState(null)
|
||||
|
||||
const [query, setQuery] = useState('')
|
||||
const q = useDebounce(query, 300)
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const toggle = () => {
|
||||
setQuery('')
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
|
||||
const onInputMouseDown = () => {
|
||||
setIsSelected(false)
|
||||
setQuery('')
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
|
||||
const handleSelectItem = (item) => {
|
||||
setIsSelected(true)
|
||||
onItemSelected(item.id)
|
||||
setSelected(item.name)
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
const removeItem = () => {
|
||||
setIsSelected(false)
|
||||
setSelected('')
|
||||
onItemSelected(null)
|
||||
}
|
||||
|
||||
const filterItems = (value) => {
|
||||
setIsSelected(false)
|
||||
setQuery(value)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen === true) {
|
||||
const checkIfClickedOutside = (e) => {
|
||||
if (isOpen && ref.current && !ref.current.contains(e.target)) {
|
||||
setIsOpen(false)
|
||||
if (selected !== null) {
|
||||
setIsSelected(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', checkIfClickedOutside)
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', checkIfClickedOutside)
|
||||
}
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const fetch = (q = '') => {
|
||||
setLoading(true)
|
||||
axios
|
||||
.get(route('api.location.index', { q: q, all: all }), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Authorization: 'Bearer ' + auth.user.jwt_token,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
setShowItem(response.data)
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
// every select item open
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
fetch(q)
|
||||
}
|
||||
}, [q, isOpen])
|
||||
|
||||
// once page load
|
||||
useEffect(() => {
|
||||
fetch()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setSelected('')
|
||||
}
|
||||
}, [disabled])
|
||||
|
||||
useEffect(() => {
|
||||
if (itemSelected !== null) {
|
||||
const item = showItems.find((item) => item.id === itemSelected)
|
||||
if (item) {
|
||||
setSelected(item.name)
|
||||
setIsSelected(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
setIsSelected(false)
|
||||
}, [itemSelected, loading])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSelected && selected === '') {
|
||||
setSelected('')
|
||||
setIsSelected(false)
|
||||
}
|
||||
}, [isSelected])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center" ref={ref}>
|
||||
<div className="w-full flex flex-col items-center">
|
||||
<div className="w-full">
|
||||
<div className="flex flex-col relative">
|
||||
{label !== '' && (
|
||||
<label
|
||||
htmlFor="first_name"
|
||||
className="mb-2 block text-sm font-medium text-gray-900 dark:text-white"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={`p-1.5 bg-gray-50 dark:bg-gray-700 flex border rounded-lg
|
||||
${
|
||||
error
|
||||
? 'border-red-500'
|
||||
: 'border-gray-300 dark:border-gray-600'
|
||||
}
|
||||
${disabled ? 'bg-gray-50' : ''}`}
|
||||
>
|
||||
<input
|
||||
className="block w-full text-sm bg-gray-50 text-gray-900 dark:border-gray-700 border cursor-pointer dark:text-gray-300 outline-none border-transparent dark:bg-gray-700 dark:placeholder-gray-400 px-1"
|
||||
onMouseDown={onInputMouseDown}
|
||||
placeholder={placeholder}
|
||||
value={`${
|
||||
isSelected
|
||||
? selected === null
|
||||
? ''
|
||||
: selected
|
||||
: query
|
||||
}`}
|
||||
onChange={(e) =>
|
||||
filterItems(e.target.value)
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{isSelected && (
|
||||
<div
|
||||
onClick={
|
||||
disabled ? () => {} : removeItem
|
||||
}
|
||||
>
|
||||
<button className="cursor-pointer w-6 h-6 text-red-300 outline-none focus:outline-none">
|
||||
<HiX />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div onClick={disabled ? () => {} : toggle}>
|
||||
<button className="cursor-pointer w-6 h-6 text-gray-300 outline-none focus:outline-none">
|
||||
{isOpen ? (
|
||||
<HiChevronUp />
|
||||
) : (
|
||||
<HiChevronDown />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<p className="mb-2 text-sm text-red-600 dark:text-red-500">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div
|
||||
className="absolute mt-1 shadow-lg bg-gray-50 dark:bg-gray-700 dark:text-gray-200 top-100 z-40 w-full lef-0 rounded overflow-y-auto"
|
||||
style={{ maxHeight: '300px', top: '100%' }}
|
||||
>
|
||||
<div className="flex flex-col w-full">
|
||||
{loading ? (
|
||||
<div>
|
||||
<div className="flex w-full items-center p-2 pl-2 border-transparent border-l-2 relative hover:border-neutral-content">
|
||||
<div className="w-full items-center justify-center flex">
|
||||
<div className="mx-2 my-5">
|
||||
<Spinner className="mr-2" />
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{showItems.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() =>
|
||||
handleSelectItem(item)
|
||||
}
|
||||
>
|
||||
<div className="flex w-full items-center p-2 pl-2 border-transparent border-l-2 relative hover:border-neutral-content hover:bg-gray-400 bg-opacity-10 dark:hover:bg-gray-200 dark:hover:bg-opacity-10 dark:hover:text-gray-100">
|
||||
<div className="w-full items-center flex">
|
||||
<div className="mx-2">
|
||||
<span>
|
||||
{item.name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{showItems.length <= 0 && (
|
||||
<div>
|
||||
<div className="flex w-full items-center p-2 pl-2 border-transparent border-l-2 relative hover:border-neutral-content">
|
||||
<div className="w-full items-center justify-center flex">
|
||||
<div className="mx-2 my-5">
|
||||
<span>
|
||||
No Items
|
||||
Found
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
|
||||
import FormInput from '@/Components/FormInput'
|
||||
import Button from '@/Components/Button'
|
||||
import { Head, useForm } from '@inertiajs/react'
|
||||
import FormInputWith from '@/Components/FormInputWith'
|
||||
import LocationSelectionInput from '../Location/SelectionInput'
|
||||
|
||||
export default function Form(props) {
|
||||
const { voucher } = props
|
||||
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
username: '',
|
||||
password: '',
|
||||
discount: 0,
|
||||
display_price: 0,
|
||||
quota: '',
|
||||
profile: '',
|
||||
comment: '',
|
||||
expired: '',
|
||||
expired_unit: 'Hari',
|
||||
location_id: null,
|
||||
})
|
||||
|
||||
const handleOnChange = (event) => {
|
||||
setData(
|
||||
event.target.name,
|
||||
event.target.type === 'checkbox'
|
||||
? event.target.checked
|
||||
? 1
|
||||
: 0
|
||||
: event.target.value
|
||||
)
|
||||
}
|
||||
const handleSubmit = () => {
|
||||
if (isEmpty(voucher) === false) {
|
||||
post(route('voucher.update', voucher))
|
||||
return
|
||||
}
|
||||
post(route('voucher.store'))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmpty(voucher) === false) {
|
||||
setData({
|
||||
username: voucher.username,
|
||||
password: voucher.password,
|
||||
discount: voucher.discount,
|
||||
display_price: voucher.display_price,
|
||||
quota: voucher.quota,
|
||||
profile: voucher.profile,
|
||||
comment: voucher.comment,
|
||||
expired: voucher.expired,
|
||||
expired_unit: voucher.expired_unit,
|
||||
location_id: voucher.location_id,
|
||||
})
|
||||
}
|
||||
}, [voucher])
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
auth={props.auth}
|
||||
errors={props.errors}
|
||||
flash={props.flash}
|
||||
page={'Voucher'}
|
||||
action={'Form'}
|
||||
>
|
||||
<Head title="Voucher" />
|
||||
|
||||
<div>
|
||||
<div className="mx-auto sm:px-6 lg:px-8">
|
||||
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col ">
|
||||
<div className="text-xl font-bold mb-4">Voucher</div>
|
||||
<LocationSelectionInput
|
||||
label="Lokasi"
|
||||
itemSelected={data.location_id}
|
||||
onItemSelected={(id) => setData('location_id', id)}
|
||||
error={errors.location_id}
|
||||
/>
|
||||
<div className="mt-2" />
|
||||
<FormInput
|
||||
name="username"
|
||||
value={data.username}
|
||||
onChange={handleOnChange}
|
||||
label="Username"
|
||||
error={errors.username}
|
||||
/>
|
||||
<FormInput
|
||||
name="password"
|
||||
value={data.password}
|
||||
onChange={handleOnChange}
|
||||
label="Password"
|
||||
error={errors.password}
|
||||
/>
|
||||
<FormInput
|
||||
type="number"
|
||||
name="display_price"
|
||||
value={data.display_price}
|
||||
onChange={handleOnChange}
|
||||
label="Harga"
|
||||
error={errors.display_price}
|
||||
/>
|
||||
<FormInputWith
|
||||
type="number"
|
||||
rightItem={<div className="text-sm">%</div>}
|
||||
name="discount"
|
||||
value={data.discount}
|
||||
onChange={handleOnChange}
|
||||
error={errors.discount}
|
||||
formClassName={'pr-10'}
|
||||
label="Discount"
|
||||
max={100}
|
||||
min={0}
|
||||
/>
|
||||
<FormInput
|
||||
type="number"
|
||||
name="quota"
|
||||
value={data.quota}
|
||||
onChange={handleOnChange}
|
||||
label="Kuota (bytes)"
|
||||
error={errors.quota}
|
||||
/>
|
||||
<FormInput
|
||||
name="profile"
|
||||
value={data.profile}
|
||||
onChange={handleOnChange}
|
||||
label="Profile"
|
||||
error={errors.profile}
|
||||
/>
|
||||
<FormInput
|
||||
name="comment"
|
||||
value={data.comment}
|
||||
onChange={handleOnChange}
|
||||
label="Comment"
|
||||
error={errors.comment}
|
||||
/>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-900 dark:text-white">
|
||||
Masa Aktif
|
||||
</label>
|
||||
<div className="w-full flex flex-row space-x-2 items-center">
|
||||
<FormInput
|
||||
type="number"
|
||||
name="expired"
|
||||
value={data.expired}
|
||||
onChange={handleOnChange}
|
||||
// label="Masa Aktif"
|
||||
error={errors.expired}
|
||||
className="flex-1"
|
||||
/>
|
||||
<div>
|
||||
<select
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||
onChange={handleOnChange}
|
||||
value={data.expired_unit}
|
||||
name="expired_unit"
|
||||
>
|
||||
<option value="Jam">Jam</option>
|
||||
<option value="Hari">Hari</option>
|
||||
<option value="Minggu">Minggu</option>
|
||||
<option value="Bulan">Bulan</option>
|
||||
<option value="Tahun">Tahun</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
processing={processing}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
)
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
|
||||
import FormInput from '@/Components/FormInput'
|
||||
import Button from '@/Components/Button'
|
||||
import { Head, useForm } from '@inertiajs/react'
|
||||
import FormInputWith from '@/Components/FormInputWith'
|
||||
import LocationSelectionInput from '../Location/SelectionInput'
|
||||
import TextArea from '@/Components/TextArea'
|
||||
|
||||
export default function Import(props) {
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
script: '',
|
||||
discount: 0,
|
||||
display_price: 0,
|
||||
expired: '',
|
||||
expired_unit: 'Hari',
|
||||
location_id: null,
|
||||
})
|
||||
|
||||
const handleOnChange = (event) => {
|
||||
setData(
|
||||
event.target.name,
|
||||
event.target.type === 'checkbox'
|
||||
? event.target.checked
|
||||
? 1
|
||||
: 0
|
||||
: event.target.value
|
||||
)
|
||||
}
|
||||
const handleSubmit = () => {
|
||||
post(route('voucher.import'))
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
auth={props.auth}
|
||||
errors={props.errors}
|
||||
flash={props.flash}
|
||||
page={'Voucher'}
|
||||
action={'Import'}
|
||||
>
|
||||
<Head title="Voucher" />
|
||||
|
||||
<div>
|
||||
<div className="mx-auto sm:px-6 lg:px-8">
|
||||
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col ">
|
||||
<div className="text-xl font-bold mb-4">Voucher</div>
|
||||
<LocationSelectionInput
|
||||
label="Lokasi"
|
||||
itemSelected={data.location_id}
|
||||
onItemSelected={(id) => setData('location_id', id)}
|
||||
error={errors.location_id}
|
||||
/>
|
||||
<div className="mt-2" />
|
||||
<FormInput
|
||||
type="number"
|
||||
name="display_price"
|
||||
value={data.display_price}
|
||||
onChange={handleOnChange}
|
||||
label="Harga"
|
||||
error={errors.display_price}
|
||||
/>
|
||||
<FormInputWith
|
||||
type="number"
|
||||
rightItem={<div className="text-sm">%</div>}
|
||||
name="discount"
|
||||
value={data.discount}
|
||||
onChange={handleOnChange}
|
||||
error={errors.discount}
|
||||
formClassName={'pr-10'}
|
||||
label="Discount"
|
||||
max={100}
|
||||
min={0}
|
||||
/>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-900 dark:text-white">
|
||||
Masa Aktif
|
||||
</label>
|
||||
<div className="w-full flex flex-row space-x-2 items-center">
|
||||
<FormInput
|
||||
type="number"
|
||||
name="expired"
|
||||
value={data.expired}
|
||||
onChange={handleOnChange}
|
||||
// label="Masa Aktif"
|
||||
error={errors.expired}
|
||||
className="flex-1"
|
||||
/>
|
||||
<div>
|
||||
<select
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||
onChange={handleOnChange}
|
||||
value={data.expired_unit}
|
||||
name="expired_unit"
|
||||
>
|
||||
<option value="Jam">Jam</option>
|
||||
<option value="Hari">Hari</option>
|
||||
<option value="Minggu">Minggu</option>
|
||||
<option value="Bulan">Bulan</option>
|
||||
<option value="Tahun">Tahun</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TextArea
|
||||
name="script"
|
||||
value={data.script}
|
||||
onChange={handleOnChange}
|
||||
label="Script"
|
||||
error={errors.script}
|
||||
rows={16}
|
||||
/>
|
||||
<div className="mt-8">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
processing={processing}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
)
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Link, router } from '@inertiajs/react'
|
||||
import { usePrevious } from 'react-use'
|
||||
import { Head } from '@inertiajs/react'
|
||||
import { Button, Dropdown } from 'flowbite-react'
|
||||
import { HiPencil, HiTrash } from 'react-icons/hi'
|
||||
import { useModalState } from '@/hooks'
|
||||
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
|
||||
import Pagination from '@/Components/Pagination'
|
||||
import ModalConfirm from '@/Components/ModalConfirm'
|
||||
import SearchInput from '@/Components/SearchInput'
|
||||
import { hasPermission } from '@/utils'
|
||||
|
||||
export default function Index(props) {
|
||||
const {
|
||||
query: { links, data },
|
||||
auth,
|
||||
} = props
|
||||
|
||||
const [search, setSearch] = useState('')
|
||||
const preValue = usePrevious(search)
|
||||
|
||||
const confirmModal = useModalState()
|
||||
|
||||
const handleDeleteClick = (voucher) => {
|
||||
confirmModal.setData(voucher)
|
||||
confirmModal.toggle()
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
if (confirmModal.data !== null) {
|
||||
router.delete(route('voucher.destroy', confirmModal.data.id))
|
||||
}
|
||||
}
|
||||
|
||||
const params = { q: search }
|
||||
useEffect(() => {
|
||||
if (preValue) {
|
||||
router.get(
|
||||
route(route().current()),
|
||||
{ q: search },
|
||||
{
|
||||
replace: true,
|
||||
preserveState: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
}, [search])
|
||||
|
||||
const canCreate = hasPermission(auth, 'create-voucher')
|
||||
const canUpdate = hasPermission(auth, 'update-voucher')
|
||||
const canDelete = hasPermission(auth, 'delete-voucher')
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
auth={props.auth}
|
||||
errors={props.errors}
|
||||
flash={props.flash}
|
||||
page={'Voucher'}
|
||||
action={''}
|
||||
>
|
||||
<Head title="Voucher" />
|
||||
|
||||
<div>
|
||||
<div className="mx-auto sm:px-6 lg:px-8 ">
|
||||
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
|
||||
<div className="flex justify-between">
|
||||
{canCreate && (
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Link href={route('voucher.create')}>
|
||||
<Button size="sm">Tambah</Button>
|
||||
</Link>
|
||||
<Link href={route('voucher.import')}>
|
||||
<Button size="sm" outline>
|
||||
Import
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
<SearchInput
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
value={search}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-auto">
|
||||
<div>
|
||||
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
|
||||
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6"
|
||||
>
|
||||
No
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6"
|
||||
>
|
||||
Username
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6"
|
||||
>
|
||||
Password
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6"
|
||||
>
|
||||
Profile
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6"
|
||||
>
|
||||
Comment
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6"
|
||||
>
|
||||
Kuota
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3 px-6 w-1/8"
|
||||
/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((voucher, index) => (
|
||||
<tr
|
||||
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
|
||||
key={voucher.id}
|
||||
>
|
||||
<td
|
||||
scope="row"
|
||||
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{index + 1}
|
||||
</td>
|
||||
<td
|
||||
scope="row"
|
||||
className="py-4 px-6"
|
||||
>
|
||||
{voucher.username}
|
||||
</td>
|
||||
<td
|
||||
scope="row"
|
||||
className="py-4 px-6"
|
||||
>
|
||||
{voucher.password}
|
||||
</td>
|
||||
<td
|
||||
scope="row"
|
||||
className="py-4 px-6"
|
||||
>
|
||||
{voucher.profile}
|
||||
</td>
|
||||
<td
|
||||
scope="row"
|
||||
className="py-4 px-6"
|
||||
>
|
||||
{voucher.comment}
|
||||
</td>
|
||||
<td
|
||||
scope="row"
|
||||
className="py-4 px-6"
|
||||
>
|
||||
{voucher.display_quota}
|
||||
</td>
|
||||
<td className="py-4 px-6 flex justify-end">
|
||||
<Dropdown
|
||||
label={'Opsi'}
|
||||
floatingArrow={true}
|
||||
arrowIcon={true}
|
||||
dismissOnClick={true}
|
||||
size={'sm'}
|
||||
>
|
||||
{canUpdate && (
|
||||
<Dropdown.Item>
|
||||
<Link
|
||||
href={route(
|
||||
'voucher.edit',
|
||||
voucher
|
||||
)}
|
||||
className="flex space-x-1 items-center"
|
||||
>
|
||||
<HiPencil />
|
||||
<div>
|
||||
Ubah
|
||||
</div>
|
||||
</Link>
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
{canDelete && (
|
||||
<Dropdown.Item
|
||||
onClick={() =>
|
||||
handleDeleteClick(
|
||||
voucher
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex space-x-1 items-center">
|
||||
<HiTrash />
|
||||
<div>
|
||||
Hapus
|
||||
</div>
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
</Dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="w-full flex items-center justify-center">
|
||||
<Pagination links={links} params={params} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ModalConfirm modalState={confirmModal} onConfirm={onDelete} />
|
||||
</AuthenticatedLayout>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue