upgrade deposit

dev
Aji Kamaludin 1 year ago
parent 92ca57e425
commit 3f12da970a
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -2,12 +2,12 @@
## Front
- [x] mengubah metode pembayaran deposit dengan daftar bank seperti deposit dan tampil logo bank
- [ ] tampilan keranjang jadi lebih seperti tokped dengan metode pembayaran deposit atau hutang jika tersedia
- [ ] tampilan transaksi deposit, hutang (mitra wbb), dan poin jadi satu tampilan
- [ ] mengubah metode pembayaran deposit dengan daftar bank seperti deposit dan tampil logo bank
- [x] tampilan transaksi deposit, hutang (mitra wbb), dan poin jadi satu tampilan
- [ ] tambah metode topup deposit dengan setor tunai kantor wbb
- [ ] tambah screen untuk daftar setor tunai kantor wbb
- [ ] halaman untuk menampilkan level customer
- [x] tambah screen untuk daftar setor tunai kantor wbb
- [x] halaman untuk menampilkan level customer
- [ ] expired time 2jam di deposit manual maupun kantor wbb
- [ ] ubah username dan password di detail transaksi dengan kode voucher saja
- [ ] [BUG] pembelian voucher lebih dari 1 mendapat kode yang sama

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Account;
use Illuminate\Http\Request;
@ -34,7 +35,7 @@ class AccountController extends Controller
$file = $request->file('logo');
$file->store('uploads', 'public');
Account::create([
'name' => $request->name,
'bank_name' => $request->bank_name,

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Banner;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\CustomerLevel;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\CustomerLevel;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\DepositHistory;
use Illuminate\Http\Request;
@ -12,7 +13,7 @@ class DepositController extends Controller
{
public function index(Request $request)
{
$query = DepositHistory::with(['customer', 'account'])
$query = DepositHistory::with(['customer', 'account', 'editor'])
->where('credit', 0)
->orderBy('is_valid', 'desc')
->orderBy('updated_at', 'desc');
@ -50,9 +51,14 @@ class DepositController extends Controller
],
]);
if ($request->status == DepositHistory::STATUS_REJECT) {
$request->validate(['reject_reason' => 'required|string']);
}
DB::beginTransaction();
$deposit->update([
'is_valid' => $request->status,
'note' => $request->reject_reason
]);
if ($request->status == DepositHistory::STATUS_VALID) {
$deposit->update_customer_balance();

@ -1,8 +1,10 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\DepositLocation;
use App\Services\GeneralService;
use Illuminate\Http\Request;
class DepositLocationController extends Controller
@ -45,8 +47,8 @@ class DepositLocationController extends Controller
'gmap_url' => $request->gmap_url,
'image' => $file->hashName('uploads'),
'description' => $request->description,
'open_hour' => $request->open_hour,
'close_hour' => $request->close_hour,
'open_hour' => GeneralService::parserToHour($request->open_hour),
'close_hour' => GeneralService::parserToHour($request->close_hour),
'is_active' => $request->is_active,
]);
@ -88,8 +90,8 @@ class DepositLocationController extends Controller
'gmap_url' => $request->gmap_url,
'image' => $location->image,
'description' => $request->description,
'open_hour' => $request->open_hour,
'close_hour' => $request->close_hour,
'open_hour' => GeneralService::parserToHour($request->open_hour),
'close_hour' => GeneralService::parserToHour($request->close_hour),
'is_active' => $request->is_active,
]);

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\DepositHistory;
use App\Models\Sale;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Info;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Location;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\CustomerLevel;
use App\Models\LocationProfile;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Notification;
class NotificationController extends Controller

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\CustomerLevel;
use App\Models\PoinReward;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\ProfileUpdateRequest;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Http\RedirectResponse;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Permission;
use App\Models\Role;
use App\Models\RolePermission;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Sale;
use Illuminate\Http\Request;

@ -1,9 +1,11 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\CustomerLevel;
use App\Models\Setting;
use App\Services\GeneralService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
@ -24,13 +26,25 @@ class SettingController extends Controller
$request->validate([
'OPEN_WEBSITE_NAME' => 'required|string',
'SHARE_TEXT' => 'required|string',
'ENABLE_CASH_DEPOSIT' => 'required|in:0,1',
'ENABLE_MANUAL_TRANSFER' => 'required|in:0,1',
'MAX_MANUAL_TRANSFER_TIMEOUT' => 'required|numeric',
'MANUAL_TRANSFER_OPEN_HOUR' => 'required|string',
'MANUAL_TRANSFER_CLOSE_HOUR' => 'required|string',
'MAX_POINT_EXPIRED' => 'required|numeric',
]);
DB::beginTransaction();
foreach ($request->input() as $key => $value) {
$inputs = $request->except(['MANUAL_TRANSFER_OPEN_HOUR', 'MANUAL_TRANSFER_CLOSE_HOUR']);
foreach ($inputs as $key => $value) {
Setting::where('key', $key)->update(['value' => $value]);
}
$hours = $request->only(['MANUAL_TRANSFER_OPEN_HOUR', 'MANUAL_TRANSFER_CLOSE_HOUR']);
foreach ($hours as $key => $value) {
Setting::where('key', $key)->update(['value' => GeneralService::parserToHour($value)]);
}
Cache::flush();
DB::commit();

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@ -69,7 +70,7 @@ class UserController extends Controller
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,'.$user->id,
'email' => 'required|email|unique:users,email,' . $user->id,
'password' => 'nullable|string|max:255',
'username' => 'required|alpha_dash|unique:users,username,' . $user->id,
'phone_wa' => 'required|string',

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\CustomerLevel;
use Illuminate\Http\Request;

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\CustomerLevel;
use App\Models\Location;
use App\Models\LocationProfile;

@ -202,7 +202,7 @@ class CartController extends Controller
session()->remove('carts');
return redirect()->route('transactions.show', $sale)
return redirect()->route('transactions.sale.show', $sale)
->with('message', ['type' => 'success', 'message' => 'pembelian berhasil']);
}
}

@ -20,7 +20,8 @@ class DepositController extends Controller
{
$histories = DepositHistory::where('customer_id', auth()->id())
->orderBy('updated_at', 'desc')
->orderBy('is_valid', 'desc');
->orderBy('is_valid', 'desc')
->where('type', DepositHistory::TYPE_DEPOSIT);
return inertia('Deposit/Index', [
'histories' => $histories->paginate(20),
@ -40,7 +41,7 @@ class DepositController extends Controller
'amount' => 'required|numeric|min:10000',
'payment' => [
'required',
Rule::in([Setting::PAYMENT_MANUAL, Setting::PAYMENT_MIDTRANS]),
Rule::in([Setting::PAYMENT_MANUAL, Setting::PAYMENT_MIDTRANS, Setting::PAYMENT_CASH_DEPOSIT]),
],
]);
@ -48,11 +49,11 @@ class DepositController extends Controller
$deposit = DepositHistory::make([
'customer_id' => auth()->id(),
'debit' => $request->amount,
'description' => 'Top Up #'.Str::random(5),
'payment_channel' => $request->payment,
'type' => DepositHistory::TYPE_DEPOSIT,
]);
if ($request->payment == Setting::PAYMENT_MANUAL) {
if (in_array($request->payment, [Setting::PAYMENT_MANUAL, Setting::PAYMENT_CASH_DEPOSIT])) {
$deposit->is_valid = DepositHistory::STATUS_WAIT_UPLOAD;
$deposit->save();
}
@ -65,17 +66,18 @@ class DepositController extends Controller
$deposit->update(['payment_token' => $token]);
}
$deposit->create_notification();
DB::commit();
return redirect()->route('customer.deposit.show', ['deposit' => $deposit->id, 'direct' => 'true']);
return redirect()->route('transactions.deposit.show', ['deposit' => $deposit->id, 'direct' => 'true']);
}
public function show(Request $request, DepositHistory $deposit)
{
return inertia('Deposit/Detail', [
'deposit' => $deposit,
'deposit' => $deposit->load(['account']),
'accounts' => Account::get(),
'midtrans_client_key' => Setting::getByKey('MIDTRANS_CLIENT_KEY'),
'is_production' => app()->isProduction(),
@ -131,7 +133,7 @@ class DepositController extends Controller
DB::commit();
return redirect()->route('customer.deposit.show', ['deposit' => $deposit->id]);
return redirect()->route('transactions.deposit.show', ['deposit' => $deposit->id]);
}
public function mindtrans_notification(Request $request)

@ -0,0 +1,17 @@
<?php
namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\DepositLocation;
use Illuminate\Http\Request;
class DepositLocationController extends Controller
{
public function index()
{
return inertia('DepositLocation/Index', [
'locations' => DepositLocation::orderBy('updated_at', 'desc')->paginate(20),
]);
}
}

@ -3,7 +3,7 @@
namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\poinHistory;
use App\Models\PoinHistory;
class PoinController extends Controller
{
@ -17,7 +17,7 @@ class PoinController extends Controller
]);
}
public function show(poinHistory $poin)
public function show(PoinHistory $poin)
{
return inertia('Poin/Detail', [
'poin' => $poin,

@ -84,7 +84,7 @@ class PoinExchangeController extends Controller
$poin->update_customer_balance();
DB::commit();
return redirect()->route('transactions.show', $sale)
return redirect()->route('transactions.sale.show', $sale)
->with('message', ['type' => 'success', 'message' => 'penukaran berhasil']);
}
}

@ -2,6 +2,7 @@
namespace App\Models;
use App\Services\GeneralService;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Carbon;
@ -19,10 +20,15 @@ class DepositHistory extends Model
const STATUS_REJECT = 5;
const TYPE_DEPOSIT = 0;
const TYPE_REPAYMENT = 1;
protected $fillable = [
'debit',
'credit',
'description',
'note',
'customer_id',
'related_type',
'related_id',
@ -34,6 +40,7 @@ class DepositHistory extends Model
'payment_response',
'payment_channel',
'payment_type',
'type',
];
protected $appends = [
@ -44,6 +51,17 @@ class DepositHistory extends Model
'image_prove_url',
];
protected static function booted(): void
{
static::creating(function (DepositHistory $model) {
if ($model->description == null) {
if ($model->type == DepositHistory::TYPE_DEPOSIT) {
$model->description = GeneralService::generateDepositCode();
}
}
});
}
public function status(): Attribute
{
return Attribute::make(get: function () {
@ -109,6 +127,17 @@ class DepositHistory extends Model
public function create_notification()
{
if ($this->payment_channel == Setting::PAYMENT_MANUAL) {
$status = '';
if ($this->is_valid == self::STATUS_WAIT_APPROVE) {
$status = ' (bukti bayar di upload, membutuhkan konfirmasi)';
}
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan deposit transfer manual sebesar : ' . $this->amount . $status,
]);
}
if ($this->payment_channel == Setting::PAYMENT_CASH_DEPOSIT) {
$status = '';
if ($this->is_valid == self::STATUS_WAIT_APPROVE) {
$status = ' (bukti bayar di upload, membutuhkan konfirmasi)';

@ -33,22 +33,7 @@ class DepositLocation extends Model
protected function operationalHour(): Attribute
{
return Attribute::make(get: function () {
return $this->parser($this->open_hour) . ' - ' . $this->parser($this->close_hour);
return $this->open_hour . ' - ' . $this->close_hour;
});
}
private function parser($time)
{
$r = '';
$time = explode(':', $time);
foreach ($time as $t) {
if ($t == 0) {
$r .= '00:';
} else {
$r .= $t . ':';
}
}
return substr($r, 0, -1);
}
}

@ -19,6 +19,8 @@ class Setting extends Model
const PAYMENT_PAYLATER = 'PAYLATER';
const PAYMENT_CASH_DEPOSIT = 'CASH_DEPOSIT';
protected $fillable = [
'key',
'value',

@ -37,7 +37,7 @@ class Voucher extends Model
{
if (count(self::$instance) == 0) {
self::$instance = [
'customer' => Customer::find(auth()->id())
'customer' => Customer::find(auth()->guard('customer')->id())
];
}

@ -2,7 +2,11 @@
namespace App\Services;
use App\Models\DepositHistory;
use App\Models\DepositLocation;
use App\Models\Setting;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
class GeneralService
{
@ -55,13 +59,51 @@ class GeneralService
public static function getEnablePayment()
{
$payment = [
['name' => Setting::PAYMENT_MANUAL, 'logo' => null, 'display_name' => 'Transfer Manual'],
];
$payment = [];
$midtrans_enable = Setting::getByKey('MIDTRANS_ENABLED');
if ($midtrans_enable == 1) {
$payment[] = ['name' => Setting::PAYMENT_MIDTRANS, 'logo' => asset(Setting::getByKey('MIDTRANS_LOGO'))];
$payment[] = [
'name' => Setting::PAYMENT_MIDTRANS,
'logo' => asset(Setting::getByKey('MIDTRANS_LOGO')),
'display_name' => 'Midtrans',
'admin_fee' => Setting::getByKey('MIDTRANS_ADMIN_FEE'),
];
}
$enable = Setting::getByKey('ENABLE_MANUAL_TRANSFER');
if ($enable == 1) {
$openHour = Carbon::createFromFormat('H:i', Setting::getByKey('MANUAL_TRANSFER_OPEN_HOUR'));
$closeHour = Carbon::createFromFormat('H:i', Setting::getByKey('MANUAL_TRANSFER_CLOSE_HOUR'));
if (now()->between($openHour, $closeHour)) {
$payment[] = [
'name' => Setting::PAYMENT_MANUAL,
'logo' => null,
'display_name' => 'Transfer Manual',
'admin_fee' => 0
];
}
}
$enable = Setting::getByKey('ENABLE_CASH_DEPOSIT');
if ($enable == 1) {
$locations = DepositLocation::all(); // 30 partner
$enables = [];
foreach ($locations as $location) {
$openHour = Carbon::createFromFormat('H:i', $location->open_hour);
$closeHour = Carbon::createFromFormat('H:i', $location->close_hour);
$enables[] = now()->between($openHour, $closeHour);
}
if (in_array(true, $enables)) {
$payment[] = [
'name' => Setting::PAYMENT_CASH_DEPOSIT,
'logo' => null,
'display_name' => 'Setor Tunai di Kantor WBB',
'admin_fee' => 0
];
}
}
return $payment;
@ -73,4 +115,40 @@ class GeneralService
// poin
// paylater
}
public static function parserToHour($time)
{
$r = '';
$time = explode(':', $time);
foreach ($time as $t) { //00 : 00
if ($t < 10) {
$r .= '0' . (int) $t . ':';
} else {
$r .= $t . ':';
}
}
return substr($r, 0, -1);
}
public static function generateDepositCode()
{
$code = DepositHistory::where('type', DepositHistory::TYPE_DEPOSIT)->count() + 1;
return 'Invoice #DSR' . now()->format('dmy') . GeneralService::formatNumberCode($code);
}
public static function formatNumberCode($number)
{
if ($number < 10) {
return '000' . $number;
}
if ($number < 100) {
return '00' . $number;
}
if ($number < 1000) {
return '0' . $number;
}
return $number;
}
}

@ -3,6 +3,7 @@
namespace App\Services;
use App\Models\DepositHistory;
use App\Models\Setting;
use Midtrans\Config;
use Midtrans\Snap;
@ -22,17 +23,31 @@ class MidtransService
public function getSnapToken()
{
$items = [
[
'id' => $this->deposit->id,
'price' => $this->deposit->debit,
'quantity' => 1,
'name' => $this->deposit->description,
]
];
$adminFee = Setting::getByKey('MIDTRANS_ADMIN_FEE');
if ($adminFee > 0) {
$items[] = [
'id' => 'tambahan_biaya_admin',
'price' => $adminFee,
'quantity' => 1,
'name' => 'tambahan_biaya_admin',
];
}
$params = [
'transaction_details' => [
'order_id' => $this->deposit->id,
'gross_amount' => $this->deposit->debit,
],
'item_details' => [[
'id' => $this->deposit->id,
'price' => $this->deposit->debit,
'quantity' => 1,
'name' => $this->deposit->description,
]],
'item_details' => $items,
'customer_details' => [
'name' => $this->deposit->customer->fullname,
'email' => $this->deposit->customer->email,
@ -40,7 +55,7 @@ class MidtransService
'address' => $this->deposit->customer->address,
],
'callbacks' => [
'finish' => route('customer.deposit.show', ['deposit' => $this->deposit->id]),
'finish' => route('transactions.deposit.show', ['deposit' => $this->deposit->id]),
],
];

@ -16,12 +16,14 @@ return new class extends Migration
$table->decimal('debit', 20, 2)->default(0);
$table->decimal('credit', 20, 2)->default(0);
$table->text('description')->nullable();
$table->string('description')->nullable();
$table->text('note')->nullable();
$table->ulid('customer_id')->nullable();
$table->ulid('account_id')->nullable();
$table->string('related_type')->nullable();
$table->string('related_id')->nullable();
$table->smallInteger('is_valid')->default(0);
$table->smallInteger('type')->default(0);
$table->string('image_prove')->nullable();
$table->string('payment_channel')->nullable();
$table->string('payment_token')->nullable();

@ -60,8 +60,9 @@ class DummySeeder extends Seeder
public function account()
{
$banks = [
['name' => 'BTPN', 'bank_name' => 'BTPN', 'holder_name' => 'Aji Kamaludin', 'account_number' => '187391738129', 'logo' => 'sample/logo-jenius.png'],
['name' => 'Jago', 'bank_name' => 'Bank Jago', 'holder_name' => 'Aji Kamaludin', 'account_number' => '718297389172', 'logo' => 'sample/logo-jago.png'],
['name' => 'BTPN', 'bank_name' => 'BTPN', 'holder_name' => 'Aji Kamaludin', 'account_number' => '187391738129', 'logo' => 'sample/logo-jenius.png', 'admin_fee' => 0],
['name' => 'Jago', 'bank_name' => 'Bank Jago', 'holder_name' => 'Aji Kamaludin', 'account_number' => '718297389172', 'logo' => 'sample/logo-jago.png', 'admin_fee' => 2500],
['name' => 'BNI', 'bank_name' => 'Bank Negara Indoneisa', 'holder_name' => 'Aji Kamaludin', 'account_number' => '718297389172', 'logo' => 'sample/logo-bni.png', 'admin_fee' => 6500],
];
foreach ($banks as $bank) {
@ -70,7 +71,8 @@ class DummySeeder extends Seeder
'bank_name' => $bank['bank_name'],
'holder_name' => $bank['holder_name'],
'account_number' => $bank['account_number'],
'logo' => $bank['logo']
'logo' => $bank['logo'],
'admin_fee' => $bank['admin_fee']
]);
}
}

@ -22,8 +22,7 @@ class InstallationSeed extends Seeder
$settings = [
// general
['key' => 'OPEN_WEBSITE_NAME', 'value' => 'Welcome to Voucher App', 'type' => 'text'],
['key' => 'SHARE_TEXT', 'value' => '', 'type' => 'text'],
['key' => 'SHARE_TEXT', 'value' => '<p>Baru Beli Voucher nih</p>', 'type' => 'text'],
// affilate
['key' => 'AFFILATE_ENABLED', 'value' => '0', 'type' => 'checkbox'],
@ -41,9 +40,9 @@ class InstallationSeed extends Seeder
['key' => 'MIDTRANS_ADMIN_FEE', 'value' => '2500', 'type' => 'text'],
// deposit
['key' => 'ENABLE_CASH_DEPOSIT', 'value' => '0', 'type' => 'text'],
['key' => 'ENABLE_MANUAL_TRANSFER', 'value' => '0', 'type' => 'text'],
['key' => 'MAX_MANUAL_TRANSFER_TIMEOUT', 'value' => '2', 'type' => 'text'], //dalam jam
['key' => 'ENABLE_CASH_DEPOSIT', 'value' => '0', 'type' => 'text'], // deposit by location (on/off)
['key' => 'ENABLE_MANUAL_TRANSFER', 'value' => '0', 'type' => 'text'], // transfer manual (on/off)
['key' => 'MAX_MANUAL_TRANSFER_TIMEOUT', 'value' => '2', 'type' => 'text'], // dalam jam
['key' => 'MANUAL_TRANSFER_OPEN_HOUR', 'value' => '06:00', 'type' => 'text'],
['key' => 'MANUAL_TRANSFER_CLOSE_HOUR', 'value' => '23:00', 'type' => 'text'],
['key' => 'MAX_POINT_EXPIRED', 'value' => '90', 'type' => 'text'], //dalam hari

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

@ -1,26 +1,35 @@
import React from "react";
import React from 'react'
export default function Checkbox({ value, name, onChange, label = '', error, disabled = false }) {
export default function Checkbox({
value,
name,
onChange,
label = '',
error,
disabled = false,
}) {
return (
<>
<div className="flex items-center mb-1">
<input
<input
type="checkbox"
checked={value}
onChange={onChange}
name={name}
disabled={disabled}
className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"/>
className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
/>
{label !== '' && (
<label className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">
{label}
</label>
)}
</div>
{error && (
<p className="mb-2 text-sm text-red-600 dark:text-red-500">{error}</p>
<p className="mb-2 text-sm text-red-600 dark:text-red-500">
{error}
</p>
)}
</>
)
}
}

@ -29,7 +29,7 @@ export default function Index({
}
const handleTopUp = () => {
router.get(route('customer.deposit.topup'))
router.get(route('transactions.deposit.topup'))
}
return (

@ -0,0 +1,75 @@
import { usePage, router, Link } from '@inertiajs/react'
export default function HeaderTrx({ enable = 'deposit' }) {
const {
props: {
auth: { user },
},
} = usePage()
return (
<>
<div className="w-full pt-10 px-5">
<div className="text-base">{user.fullname}</div>
</div>
<div className="flex flex-row justify-between items-center px-5">
<div>
<div className="font-bold text-3xl">
Rp {user.display_deposit}
</div>
</div>
<div>
<div
className="px-3 py-2 border rounded-full bg-blue-700 text-white hover:bg-transparent hover:text-black"
onClick={() =>
router.get(route('transactions.deposit.topup'))
}
>
Top Up
</div>
</div>
</div>
<div className="px-5 pb-5 border-b">
<div className="flex flex-row items-center text-gray-600 text-sm">
<div>{user.display_poin} poin</div>
<div className="pl-1 text-xs">
( kadaluarsa pada 20 juni 2023 )
</div>
</div>
</div>
<div className="w-full">
<div className="flex flex-row gap-2 px-5 py-5">
<Link
href={route('transactions.deposit.index')}
className={`px-3 py-1 rounded-full border ${
enable === 'deposit'
? 'bg-blue-700 text-white'
: 'bg-blue-100'
}`}
>
Deposit
</Link>
<Link
href={route('transactions.sale.index')}
className={`px-3 py-1 rounded-full border ${
enable === 'trx'
? 'bg-blue-700 text-white'
: 'bg-blue-100'
}`}
>
Pembelian
</Link>
<Link
href={route('transactions.poin.index')}
className={`px-3 py-1 rounded-full border ${
enable === 'poin'
? 'bg-blue-700 text-white'
: 'bg-blue-100'
}`}
>
Poin
</Link>
</div>
</div>
</>
)
}

@ -1,17 +1,19 @@
import React, { useState, useEffect } from 'react'
import { Head, router, useForm, usePage } from '@inertiajs/react'
import { isEmpty } from 'lodash'
import {
HiChevronLeft,
HiClipboard,
HiClipboardDocumentList,
HiOutlineClipboardDocumentCheck,
HiOutlineClipboardDocumentList,
} from 'react-icons/hi2'
import { toastSuccess } from '../utils'
import CustomerLayout from '@/Layouts/CustomerLayout'
import { formatIDR } from '@/utils'
import FormFile from '@/Components/FormFile'
import { isEmpty } from 'lodash'
import Alert from '@/Components/Alert'
import { toast } from 'react-toastify'
import { formatIDR } from '@/utils'
import { STATUS_REJECT } from '@/constant'
const PayButton = () => {
const {
@ -106,6 +108,7 @@ const FormUpload = () => {
props: { accounts, deposit, flash },
} = usePage()
const [imageUrl, setImageUrl] = useState(deposit.image_prove_url)
const [account, setAccount] = useState(null)
const { data, setData, errors, processing, post } = useForm({
account_id: '',
@ -117,6 +120,7 @@ const FormUpload = () => {
if (id === '') {
setData('account_id', '')
setAccount(null)
return
}
const account = accounts.find((acc) => acc.id === id)
setData('account_id', account.id)
@ -124,15 +128,17 @@ const FormUpload = () => {
}
const handleCopyToClipboard = (text) => {
toast.success('copied to clipboard')
navigator.clipboard.writeText(account.account_number)
toastSuccess('copied to clipboard')
navigator.clipboard.writeText(text)
}
const handleSubmit = () => {
if (processing) {
return
}
post(route('customer.deposit.update', deposit), {
post(route('transactions.deposit.update', deposit), {
replace: true,
preserveState: true,
onSuccess: () =>
setTimeout(
() => router.get(route(route().current(), deposit)),
@ -141,6 +147,31 @@ const FormUpload = () => {
})
}
useEffect(() => {
if (deposit.account !== null) {
handleSelectAccount(deposit.account.id)
}
}, [deposit])
if (isEmpty(imageUrl) == false) {
return (
<div className="px-5 mt-2">
<div className="font-bold">Bukti Transfer</div>
<img
src={`${imageUrl}`}
className="w-full h-52 mb-1"
alt="bukti transfer"
/>
<div
className="mt-10 w-full px-4 py-2 border rounded-full bg-blue-600 text-white hover:bg-white hover:text-black"
onClick={() => setImageUrl(null)}
>
Ubah Bukti Transfer
</div>
</div>
)
}
return (
<div className="px-5 mt-4">
<div className="my-4">
@ -149,26 +180,59 @@ const FormUpload = () => {
{flash.message.message}
</span>
</Alert>
<div className="mb-1 text-sm">Bank </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={(e) => handleSelectAccount(e.target.value)}
value={data.account_id}
name="account_id"
>
<option value=""></option>
{accounts.map((account) => (
<option value={account.id} key={account.id}>
{account.name} - {account.bank_name}
</option>
))}
</select>
<div className="mb-1 font-bold">Bank</div>
{account !== null ? (
<div className="flex flex-row w-full gap-2">
<div
className="px-3 py-2 border rounded-md flex-1 flex flex-row items-center gap-1 shadow-md hover:bg-gray-100"
onClick={() => handleSelectAccount(account.id)}
>
<div className="w-1/3">
<img
src={account.logo_url}
alt="logo bank"
className="h-10"
/>
</div>
<div>
{account.name} - {account.bank_name}
</div>
</div>
<div
className="text-center border flex flex-row rounded-md shadow-md items-center justify-center px-3 py-2 hover:bg-gray-100"
onClick={() => handleSelectAccount('')}
>
<div>Ubah</div>
</div>
</div>
) : (
<div className="flex flex-col gap-2">
{accounts.map((account) => (
<div
key={account.id}
className="px-3 py-2 border rounded-md flex flex-row w-full items-center gap-1 shadow-md hover:bg-gray-100"
onClick={() => handleSelectAccount(account.id)}
>
<div className="w-1/3">
<img
src={account.logo_url}
alt="logo bank"
className="h-10"
/>
</div>
<div>
{account.name} - {account.bank_name}
</div>
</div>
))}
</div>
)}
</div>
{data.account_id !== '' && (
{account !== null && (
<>
<div className="my-5">
<div className="bg-blue-50 text-blue-800 p-3 border rounded-md border-blue-400">
<div>Silahkan transfer nominal di atas ke</div>
<div className="bg-blue-50 text-blue-700 p-3 border rounded-md">
<div>
<span className="font-bold">
{account.bank_name}
@ -189,28 +253,90 @@ const FormUpload = () => {
}
>
<div>Nomor Rekening : </div>
<div className="font-bold">
<div className="font-bold pr-2">
{account.account_number}
</div>
<HiClipboardDocumentList className="text-blue-600" />
<HiOutlineClipboardDocumentList className=" w-5 h-5 text-blue-600" />
</div>
<div className="font-bold mt-5">Rincian</div>
<table className="w-full">
<tbody>
<tr>
<td className="w-1/3">
Jumlah Deposit
</td>
<td>: </td>
<td className="text-right">
<span className="font-bold">
{deposit.amount}
</span>
</td>
<td className="w-5" />
</tr>
<tr>
<td>Biaya Admin</td>
<td>: </td>
<td className="text-right">
<span className="font-bold">
{+account.admin_fee === 0 ? (
'Gratis'
) : (
<>
Rp.{' '}
{formatIDR(
+account.admin_fee
)}
</>
)}
</span>
</td>
<td className="w-5" />
</tr>
<tr
onClick={() =>
handleCopyToClipboard(
+account.admin_fee +
+deposit.debit
)
}
>
<td>Total Transfer</td>
<td> : </td>
<td className="text-right">
<span className="font-bold">
Rp.{' '}
{formatIDR(
+account.admin_fee +
+deposit.debit
)}
</span>
</td>
<td className="w-5">
<HiOutlineClipboardDocumentList className=" w-5 h-5 text-blue-600" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="-mb-3">
<div className="font-bold">Bukti Transfer</div>
{isEmpty(data.image_url) == false && (
<img
src={`${data.image_url}`}
className="w-full h-52 mb-1"
alt="bukti transfer"
/>
)}
</div>
<FormFile
label={'Bukti Transfer'}
onChange={(e) => setData('image', e.target.files[0])}
error={errors.image}
preview={
isEmpty(data.image_url) == false && (
<img
src={`${data.image_url}`}
className="w-full h-52 mb-1"
alt="bukti transfer"
/>
)
}
/>
<div className="mb-1 -mt-4 text-sm font-medium text-gray-500">
upload gambar dalam ekstensi jpg, png, jpeg
</div>
<div
className="mt-10 w-full px-4 py-2 border rounded-full bg-blue-600 text-white hover:bg-white hover:text-black"
@ -225,6 +351,17 @@ const FormUpload = () => {
}
const ActionSection = ({ deposit }) => {
if (deposit.is_valid === STATUS_REJECT) {
return (
<div className="w-full px-5">
<div className="my-5">
<div className="bg-red-50 text-red-700 p-3 border rounded-md">
{deposit.note}
</div>
</div>
</div>
)
}
return (
<div className="w-full">
{deposit.payment_channel === 'MIDTRANS' ? (
@ -244,7 +381,7 @@ export default function Detail({ deposit }) {
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('customer.deposit.index'))
router.get(route('transactions.deposit.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />

@ -1,7 +1,8 @@
import React, { useState } from 'react'
import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout'
import { formatIDR } from '@/utils'
import { HiOutlineQuestionMarkCircle } from 'react-icons/hi2'
import HeaderTrx from '../Components/HeaderTrx'
export default function Index({
auth: { user },
@ -28,31 +29,9 @@ export default function Index({
<CustomerLayout>
<Head title="Top Up" />
<div className="flex flex-col w-full min-h-[calc(90dvh)]">
<div className="w-full pt-10 px-5">
<div className="text-base">{user.fullname}</div>
</div>
<div className="flex flex-row justify-between items-center pb-10 border-b px-5">
<div>
<div className="font-semibold text-xl text-gray-400">
Saldo
</div>
<div className="font-bold text-3xl">
Rp {user.display_deposit}
</div>
</div>
<div>
<div
className="px-3 py-2 border rounded-full bg-blue-700 text-white hover:bg-transparent hover:text-black"
onClick={() =>
router.get(route('customer.deposit.topup'))
}
>
Top Up
</div>
</div>
</div>
<HeaderTrx />
<div className="w-full">
<div className="flex flex-col py-10 space-y-5 px-5">
<div className="flex flex-col space-y-5 px-5">
{deposites.map((history) => (
<div
key={history.id}
@ -60,7 +39,7 @@ export default function Index({
onClick={() =>
router.get(
route(
'customer.deposit.show',
'transactions.deposit.show',
history.id
)
)
@ -80,7 +59,7 @@ export default function Index({
</div>
{+history.is_valid !== 0 && (
<div
className={`text-xs px-2 py-1 rounded-full border ${history.status.color} text-white`}
className={`text-xs px-2 py-1 rounded-full border text-white ${history.status.color}`}
>
{history.status.text}
</div>

@ -1,11 +1,11 @@
import React from 'react'
import { Head, router, useForm } from '@inertiajs/react'
import { HiChevronLeft } from 'react-icons/hi2'
import { Head, Link, router, useForm } from '@inertiajs/react'
import { HiCheck, HiChevronLeft, HiQuestionMarkCircle } from 'react-icons/hi2'
import { formatIDR } from '@/utils'
import { CASH_DEPOSIT } from '@/Customer/utils'
import CustomerLayout from '@/Layouts/CustomerLayout'
import FormInput from '@/Components/FormInput'
import Alert from '@/Components/Alert'
import { formatIDR } from '@/utils'
import FormInputNumeric from '@/Components/FormInputNumeric'
export default function Topup({ payments }) {
@ -33,11 +33,21 @@ export default function Topup({ payments }) {
}`
}
const isActivePaymentAdminFee = (payment) => {
return `text-xs ${
payment === data.payment ? 'text-white' : 'text-gray-400'
}`
}
const handleSetPayment = (payment) => {
setData('payment', payment.name)
}
const handleSubmit = () => {
if (processing) {
return
}
post(route('customer.deposit.topup'))
post(route('transactions.deposit.topup'))
}
return (
@ -47,7 +57,7 @@ export default function Topup({ payments }) {
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('customer.deposit.index'))
router.get(route('transactions.deposit.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />
@ -86,33 +96,78 @@ export default function Topup({ payments }) {
)}
<div className="mb-2" />
<div className="w-full flex flex-col space-y-2">
{payments.length <= 0 && (
<Alert type="error">
Sistem pembayaran non-aktif{' '}
</Alert>
)}
{payments.map((payment) => (
<div
className="flex flex-col w-full"
key={payment.name}
className={isActivePayment(payment.name)}
onClick={() => setData('payment', payment.name)}
>
<input type="radio" />
{payment.logo === null ? (
<p>{payment.display_name}</p>
) : (
<img
src={payment.logo}
className="h-7 object-cover"
/>
<div
className={isActivePayment(payment.name)}
onClick={() => handleSetPayment(payment)}
>
{payment.name === data.payment ? (
<div className="w-5 h-5 rounded-md border">
<HiCheck />
</div>
) : (
<div className="w-5 h-5 rounded-md border"></div>
)}
<div className="flex flex-col">
{payment.logo === null ? (
<p>{payment.display_name}</p>
) : (
<img
src={payment.logo}
className="h-7 pt-1 object-cover"
/>
)}
{+payment.admin_fee !== 0 && (
<p
className={isActivePaymentAdminFee(
payment.name
)}
>
biaya admin:{' '}
{formatIDR(payment.admin_fee)}
</p>
)}
</div>
</div>
{payment.name === CASH_DEPOSIT && (
<Link
href={route(
'customer.deposit-location.index'
)}
className="flex flex-row items-center w-full text-sm text-gray-400 py-2 gap-1"
>
<div>Daftar lokasi setor tunai</div>
<div className="text-blue-400">
ada disini
</div>
<div>
<HiQuestionMarkCircle />
</div>
</Link>
)}
</div>
))}
</div>
</div>
</div>
<div className="fixed bottom-20 right-0 w-full">
<div
onClick={handleSubmit}
className="border bg-blue-700 text-white px-5 py-2 mx-auto rounded-full hover:text-black hover:bg-white max-w-sm"
>
Bayar
</div>
<div className="fixed bottom-20 right-0 w-full px-2">
{payments.length > 0 && (
<div
onClick={handleSubmit}
className="bg-blue-700 text-white px-5 py-2 mx-auto rounded-full hover:text-black hover:bg-white max-w-sm"
>
Bayar
</div>
)}
</div>
</CustomerLayout>
)

@ -0,0 +1,104 @@
import React, { useState } from 'react'
import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout'
import { HiChevronLeft } from 'react-icons/hi2'
import { BsWhatsapp } from 'react-icons/bs'
import { GrMap } from 'react-icons/gr'
export default function Index({ locations: { data, next_page_url } }) {
const [locations, setLocations] = useState(data)
const handleNextPage = () => {
router.get(
next_page_url,
{},
{
replace: true,
preserveState: true,
only: ['locations'],
onSuccess: (res) => {
setLocations(locations.concat(res.props.locations.data))
},
}
)
}
return (
<CustomerLayout>
<Head title="Lokasi Setor Tunai" />
<div className="flex flex-col min-h-[calc(90dvh)]">
<div
className="w-full px-5 py-5 flex flex-row items-center"
onClick={() => {
router.get(route('transactions.deposit.topup'))
}}
>
<div>
<HiChevronLeft className="font-bold h-5 w-5" />
</div>
<div className="pl-4 text-xl font-bold">
Lokasi Setor Tunai
</div>
</div>
<div className="w-full px-2 flex flex-col gap-1">
{locations.map((location) => (
<div
key={location.id}
className="border rounded-lg px-2 py-2 flex flex-row w-full gap-2"
>
<img
src={location.image_url}
className="object-fill h-36 w-32"
/>
<div className="flex flex-col w-full">
<div className="font-bold">{location.name}</div>
<div>
<a
target="_blank"
href={`https://wa.me/+62${location.phone}`}
>
<span>+62{location.phone}</span>
</a>
</div>
<div>
Alamat : <span>{location.address}</span>
</div>
<div>
Jam Buka :{' '}
<span className="font-bold">
{location.operational_hour}
</span>
</div>
<div className="pt-2 flex flex-row gap-2 items-center">
<a
target="_blank"
href={`https://wa.me/+62${location.phone}`}
className="flex flex-row gap-2 border px-2 py-1 rounded items-center text-green-500 hover:bg-green-400 hover:text-white"
>
<BsWhatsapp className="w-5 h-5" />
<span>Whatsapp</span>
</a>
<a
target="_blank"
href={location.gmap_url}
className="flex flex-row gap-2 border px-2 py-1 rounded items-center text-gray-500 hover:bg-gray-400 hover:text-white"
>
<GrMap className="w-5 h-5" />
<span>Lokasi</span>
</a>
</div>
</div>
</div>
))}
{next_page_url !== null && (
<div
onClick={handleNextPage}
className="w-full text-center px-2 py-1 border mt-5 hover:bg-blue-600 hover:text-white"
>
Load more
</div>
)}
</div>
</div>
</CustomerLayout>
)
}

@ -9,7 +9,9 @@ export default function BalanceBanner({ user }) {
<div className="flex flex-row w-full shadow py-2 px-2 rounded bg-white items-center justify-between">
<div
className="flex flex-col w-full"
onClick={() => router.get(route('customer.deposit.index'))}
onClick={() =>
router.get(route('transactions.deposit.index'))
}
>
<div className="text-xs flex flex-row items-center space-x-1 text-gray-400">
<HiOutlineCash />

@ -50,7 +50,9 @@ export default function Index({
<div
className="px-3 py-2 border rounded-full bg-blue-700 text-white hover:bg-transparent hover:text-black"
onClick={() =>
router.get(route('customer.deposit.topup'))
router.get(
route('transactions.deposit.topup')
)
}
>
Bayar Tagihan

@ -12,7 +12,7 @@ export default function Detail({ poin }) {
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('customer.poin.index'))
router.get(route('transactions.poin.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />

@ -1,6 +1,19 @@
import React, { useState } from 'react'
import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout'
import HeaderTrx from '../Components/HeaderTrx'
const EmptyHere = () => {
return (
<div className="w-full px-5 text-center flex flex-col my-auto">
<div className="font-bold text-xl">Transaksi kosong</div>
<div className="text-gray-400">
Yuk kumpulkan poin sebanyak banyaknya dengan share link refferal
kamu
</div>
</div>
)
}
export default function Index({
auth: { user },
@ -27,19 +40,8 @@ export default function Index({
<CustomerLayout>
<Head title="poin" />
<div className="flex flex-col w-full min-h-[calc(90dvh)]">
<div className="w-full pt-10 px-5">
<div className="text-base">{user.fullname}</div>
</div>
<div className="flex flex-row justify-between items-center pb-10 border-b px-5">
<div>
<div className="font-semibold text-xl text-gray-400">
poin
</div>
<div className="font-bold text-3xl">
{user.display_poin}
</div>
</div>
</div>
<HeaderTrx enable="poin" />
{_poins.length <= 0 && <EmptyHere />}
<div className="w-full">
<div className="flex flex-col py-10 space-y-5 px-5">
{_poins.map((poin) => (
@ -48,7 +50,7 @@ export default function Index({
className="flex flex-row pb-2 items-center justify-between border-b"
onClick={() =>
router.get(
route('customer.poin.show', poin.id)
route('transactions.poin.show', poin.id)
)
}
>

@ -138,6 +138,12 @@ export default function Index({ auth: { user }, flash }) {
label="password confirm"
/>
</div>
<div className="w-full text-sm font-medium">
<div className="mb-2">email </div>
<div className="px-2 py-2.5 border rounded-md bg-gray-200">
{user.email}
</div>
</div>
<div className="w-full">
<FormFile
label={'Profile Image'}
@ -154,6 +160,7 @@ export default function Index({ auth: { user }, flash }) {
}
/>
</div>
<div className="w-full flex flex-row justify-between">
<Button processing={processing} onClick={handleSubmit}>
Simpan

@ -114,22 +114,8 @@ export default function Index({ auth: { user }, notification_count }) {
<div
className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100"
onClick={() =>
router.get(route('customer.deposit.index'))
router.get(route('transactions.deposit.index'))
}
>
<div>Deposit Saldo</div>
<HiChevronRight className="h-5 w-5" />
</div>
<div
className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100"
onClick={() => router.get(route('customer.poin.index'))}
>
<div>Riwayat poin</div>
<HiChevronRight className="h-5 w-5" />
</div>
<div
className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100"
onClick={() => router.get(route('transactions.index'))}
>
<div>Transaksi</div>
<HiChevronRight className="h-5 w-5" />

@ -12,7 +12,7 @@ export default function Detail({ sale }) {
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('transactions.index'))
router.get(route('transactions.sale.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />

@ -1,6 +1,7 @@
import React, { useState } from 'react'
import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout'
import HeaderTrx from '../Components/HeaderTrx'
const EmptyHere = () => {
return (
@ -34,10 +35,8 @@ export default function Index({ query: { data, next_page_url } }) {
return (
<CustomerLayout>
<Head title="Transaksi" />
<div className="flex flex-col min-h-[calc(95dvh)]">
<div className="py-5 text-2xl px-5 font-bold">
Transaksi Pembelian
</div>
<div className="flex flex-col min-h-[calc(90dvh)]">
<HeaderTrx enable="trx" />
{sales.length <= 0 && <EmptyHere />}
<div className="w-full">
<div className="flex flex-col space-y-5 px-5">
@ -47,7 +46,7 @@ export default function Index({ query: { data, next_page_url } }) {
className="flex flex-row pb-2 items-center justify-between border-b"
onClick={() =>
router.get(
route('transactions.show', sale.id)
route('transactions.sale.show', sale.id)
)
}
>

@ -5,3 +5,5 @@ export const toastSuccess = (message) => {
return <div onClick={() => toast.dismiss(t.id)}>{message}</div>
})
}
export const CASH_DEPOSIT = 'CASH_DEPOSIT'

@ -105,7 +105,7 @@ export default function CustomerLayout({ children }) {
className={`pb-1 pt-2 hover:bg-blue-200 flex flex-col items-center w-full ${isActive(
'transactions.*'
)}`}
onClick={() => handleOnClick('transactions.index')}
onClick={() => handleOnClick('transactions.deposit.index')}
>
<HiArrowPathRoundedSquare className="h-6 w-6" />
<div className="text-xs">Transaksi</div>

@ -6,6 +6,7 @@ import FormInput from '@/Components/FormInput'
import RoleSelectionInput from '../Role/SelectionInput'
import { isEmpty } from 'lodash'
import { STATUS_APPROVE, STATUS_REJECT } from '@/constant'
export default function FormModal(props) {
const { modalState } = props
@ -27,6 +28,7 @@ export default function FormModal(props) {
customer_name: '',
customer_phone: '',
description: '',
reject_reason: '',
})
const handleOnChange = (event) => {
@ -73,6 +75,7 @@ export default function FormModal(props) {
deposit.customer.phone ?? deposit.customer.email
} )`,
description: deposit.description,
reject_reason: deposit.note,
})
return
}
@ -120,6 +123,13 @@ export default function FormModal(props) {
<td>:</td>
<td className={data.text_color}>{data.status_text}</td>
</tr>
{+data.is_valid === STATUS_REJECT && (
<tr>
<td className="font-bold">Alasan Penolakan</td>
<td>:</td>
<td>{data.reject_reason}</td>
</tr>
)}
</tbody>
</table>
@ -131,31 +141,49 @@ export default function FormModal(props) {
/>
</div>
)}
{+data.is_valid !== 0 && (
<>
<div className="my-4">
<div className="mb-1 text-sm">Status </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.status}
name="status"
>
<option value=""></option>
<option value={0}>Approve</option>
<option value={5}>Reject</option>
</select>
</div>
<div className="flex items-center">
<Button onClick={handleSubmit} processing={processing}>
Simpan
</Button>
<Button onClick={handleClose} type="secondary">
Batal
</Button>
</div>
</>
)}
{+data.is_valid !== STATUS_APPROVE &&
+data.is_valid !== STATUS_REJECT && (
<>
<div className="my-4">
<div className="mb-1 text-sm">Status </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.status}
name="status"
>
<option value=""> -- pilih status -- </option>
<option value={STATUS_APPROVE}>Approve</option>
<option value={STATUS_REJECT}>Reject</option>
</select>
{errors.status && (
<div className="text-sm text-red-500">
{errors.status}
</div>
)}
</div>
{+data.status === STATUS_REJECT && (
<FormInput
label="Alasan penolakan"
name="reject_reason"
value={data.reject_reason}
onChange={handleOnChange}
error={errors.reject_reason}
/>
)}
<div className="flex items-center">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
<Button onClick={handleClose} type="secondary">
Batal
</Button>
</div>
</>
)}
</Modal>
)
}

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import { router } from '@inertiajs/react'
import { Link, router } from '@inertiajs/react'
import { usePrevious } from 'react-use'
import { Head } from '@inertiajs/react'
import { HiEye } from 'react-icons/hi2'
@ -63,6 +63,12 @@ export default function Index(props) {
<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"
>
#
</th>
<th
scope="col"
className="py-3 px-6"
@ -81,17 +87,18 @@ export default function Index(props) {
>
Tanggal
</th>
<th
scope="col"
className="py-3 px-6"
>
Deskripsi
Status
</th>
<th
scope="col"
className="py-3 px-6"
>
Status
Approver
</th>
<th
scope="col"
@ -109,22 +116,33 @@ export default function Index(props) {
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{deposit.customer.name}
{deposit.description}
</td>
<td className="py-4 px-6">
{deposit.amount}
<Link
className="hover:underline"
href={route(
'customer.edit',
deposit.customer.id
)}
>
{deposit.customer.name}
</Link>
</td>
<td className="py-4 px-6">
{deposit.format_created_at}
{deposit.amount}
</td>
<td className="py-4 px-6">
{deposit.description}
{deposit.format_created_at}
</td>
<td
className={`py-4 px-6 ${deposit.status.text_color}`}
>
{deposit.status.text}
</td>
<td className="py-4 px-6">
{deposit.editor?.name}
</td>
<td className="py-4 px-6 flex justify-center">
{canUpdate && (
<div

@ -3,6 +3,9 @@ import { Head, router, useForm } from '@inertiajs/react'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import FormInput from '@/Components/FormInput'
import Checkbox from '@/Components/Checkbox'
import FormInputNumeric from '@/Components/FormInputNumeric'
import FormInputTime from '@/Components/FormInputTime'
import Button from '@/Components/Button'
import { extractValue } from './utils'
@ -13,6 +16,21 @@ export default function General(props) {
const { data, setData, post, reset, processing, errors } = useForm({
OPEN_WEBSITE_NAME: extractValue(setting, 'OPEN_WEBSITE_NAME'),
SHARE_TEXT: extractValue(setting, 'SHARE_TEXT'),
ENABLE_CASH_DEPOSIT: extractValue(setting, 'ENABLE_CASH_DEPOSIT'),
ENABLE_MANUAL_TRANSFER: extractValue(setting, 'ENABLE_MANUAL_TRANSFER'),
MAX_MANUAL_TRANSFER_TIMEOUT: extractValue(
setting,
'MAX_MANUAL_TRANSFER_TIMEOUT'
),
MANUAL_TRANSFER_OPEN_HOUR: extractValue(
setting,
'MANUAL_TRANSFER_OPEN_HOUR'
),
MANUAL_TRANSFER_CLOSE_HOUR: extractValue(
setting,
'MANUAL_TRANSFER_CLOSE_HOUR'
),
MAX_POINT_EXPIRED: extractValue(setting, 'MAX_POINT_EXPIRED'),
})
const handleOnChange = (event) => {
@ -27,11 +45,7 @@ export default function General(props) {
}
const handleSubmit = () => {
post(route('setting.update'), {
onSuccess: () => {
setTimeout(() => router.get(route(route().current())), 3000)
},
})
post(route('setting.update'))
}
return (
@ -55,6 +69,7 @@ export default function General(props) {
label="Nama Website"
error={errors.OPEN_WEBSITE_NAME}
/>
<div className="py-4">
<label className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
Share Text Voucher
@ -83,6 +98,67 @@ export default function General(props) {
</Suspense>
</div>
</div>
<div className="p-2 border rounded-xl mt-2">
<Checkbox
label="Aktifkan Setor Tunai"
value={+data.ENABLE_CASH_DEPOSIT === 1}
onChange={handleOnChange}
name="ENABLE_CASH_DEPOSIT"
/>
<Checkbox
label="Aktifkan Transfer Manual"
value={+data.ENABLE_MANUAL_TRANSFER === 1}
onChange={handleOnChange}
name="ENABLE_MANUAL_TRANSFER"
/>
<FormInputNumeric
name="MAX_MANUAL_TRANSFER_TIMEOUT"
value={data.MAX_MANUAL_TRANSFER_TIMEOUT}
onChange={handleOnChange}
label="Waktu Maksimal Transfer (Jam)"
error={errors.MAX_MANUAL_TRANSFER_TIMEOUT}
/>
<div className="my-2 flex flex-row gap-2 items-center">
<div>
<FormInputTime
name="MANUAL_TRANSFER_OPEN_HOUR"
value={data.MANUAL_TRANSFER_OPEN_HOUR}
onChange={(h) =>
setData(
'MANUAL_TRANSFER_OPEN_HOUR',
h
)
}
label="Jam Buka"
error={errors.MANUAL_TRANSFER_OPEN_HOUR}
/>
</div>
<div> - </div>
<div>
<FormInputTime
name="MANUAL_TRANSFER_CLOSE_HOUR"
value={data.MANUAL_TRANSFER_CLOSE_HOUR}
onChange={(h) =>
setData(
'MANUAL_TRANSFER_CLOSE_HOUR',
h
)
}
label="Jam Tutup"
error={
errors.MANUAL_TRANSFER_CLOSE_HOUR
}
/>
</div>
</div>
<FormInputNumeric
name="MAX_POINT_EXPIRED"
value={data.MAX_POINT_EXPIRED}
onChange={handleOnChange}
label="Kadaluarsa Poin Tidak digunakan (Hari)"
error={errors.MAX_POINT_EXPIRED}
/>
</div>
<div className="mt-4">
<Button

@ -1 +1,5 @@
export const DEFAULT_EXPIRED_UNIT = 'Hari'
export const STATUS_APPROVE = 0
export const STATUS_REJECT = 5

@ -1,25 +1,25 @@
<?php
use App\Http\Controllers\AccountController;
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\BannerController;
use App\Http\Controllers\CustomerController;
use App\Http\Controllers\CustomerLevelController;
use App\Http\Controllers\DepositController;
use App\Http\Controllers\DepositLocationController;
use App\Http\Controllers\GeneralController;
use App\Http\Controllers\InfoController;
use App\Http\Controllers\LocationController;
use App\Http\Controllers\LocationProfileController;
use App\Http\Controllers\NotificationController;
use App\Http\Controllers\PoinRewardController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\RoleController;
use App\Http\Controllers\SaleController;
use App\Http\Controllers\SettingController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\VerificationController;
use App\Http\Controllers\VoucherController;
use App\Http\Controllers\Admin\AccountController;
use App\Http\Controllers\Admin\BannerController;
use App\Http\Controllers\Admin\CustomerController;
use App\Http\Controllers\Admin\CustomerLevelController;
use App\Http\Controllers\Admin\DepositController;
use App\Http\Controllers\Admin\DepositLocationController;
use App\Http\Controllers\Admin\GeneralController;
use App\Http\Controllers\Admin\InfoController;
use App\Http\Controllers\Admin\LocationController;
use App\Http\Controllers\Admin\LocationProfileController;
use App\Http\Controllers\Admin\NotificationController;
use App\Http\Controllers\Admin\PoinRewardController;
use App\Http\Controllers\Admin\ProfileController;
use App\Http\Controllers\Admin\RoleController;
use App\Http\Controllers\Admin\SaleController;
use App\Http\Controllers\Admin\SettingController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Admin\VerificationController;
use App\Http\Controllers\Admin\VoucherController;
use Illuminate\Support\Facades\Route;
Route::middleware(['http_secure_aware', 'inertia.admin'])

@ -4,6 +4,7 @@ use App\Http\Controllers\Customer\CustomerLevelController;
use App\Http\Controllers\Customer\AuthController;
use App\Http\Controllers\Customer\CartController;
use App\Http\Controllers\Customer\DepositController;
use App\Http\Controllers\Customer\DepositLocationController;
use App\Http\Controllers\Customer\HomeController;
use App\Http\Controllers\Customer\PaylaterController;
use App\Http\Controllers\Customer\PoinController;
@ -51,32 +52,37 @@ Route::middleware(['http_secure_aware', 'guard_should_customer', 'inertia.custom
Route::get('paylater/trx/{paylater}', [PaylaterController::class, 'show'])->name('customer.paylater.show');
// deposite
Route::get('deposit', [DepositController::class, 'index'])->name('customer.deposit.index');
Route::get('deposit/topup', [DepositController::class, 'create'])->name('customer.deposit.topup');
Route::post('deposit/topup', [DepositController::class, 'store']);
Route::get('deposit/trx/{deposit}', [DepositController::class, 'show'])->name('customer.deposit.show');
Route::post('deposit/trx/{deposit}', [DepositController::class, 'update'])->name('customer.deposit.update');
Route::get('trx/deposit', [DepositController::class, 'index'])->name('transactions.deposit.index');
Route::get('trx/deposit/topup', [DepositController::class, 'create'])->name('transactions.deposit.topup');
Route::post('trx/deposit/topup', [DepositController::class, 'store']);
Route::get('trx/deposit/{deposit}', [DepositController::class, 'show'])->name('transactions.deposit.show');
Route::post('trx/deposit/{deposit}', [DepositController::class, 'update'])->name('transactions.deposit.update');
// transaction
Route::get('trx/sale', [TransactionController::class, 'index'])->name('transactions.sale.index');
Route::get('trx/sale/{sale}', [TransactionController::class, 'show'])->name('transactions.sale.show');
// poin
Route::get('trx/poin', [PoinController::class, 'index'])->name('transactions.poin.index');
Route::get('trx/poin/{poin}', [PoinController::class, 'show'])->name('transactions.poin.show');
// poin exchange
Route::get('poin/exchanges', [PoinExchangeController::class, 'index'])->name('customer.poin.exchange');
Route::get('poin/exchanges/{voucher}', [PoinExchangeController::class, 'exchange'])->name('customer.poin.exchange.process');
Route::get('poin', [PoinController::class, 'index'])->name('customer.poin.index');
Route::get('poin/{poin}', [PoinController::class, 'show'])->name('customer.poin.show');
// cart
Route::get('cart', [CartController::class, 'index'])->name('cart.index');
Route::post('cart/process', [CartController::class, 'purchase'])->name('cart.purchase');
Route::post('cart/{voucher}', [CartController::class, 'store'])->name('cart.store');
// transaction
Route::get('sale/trx', [TransactionController::class, 'index'])->name('transactions.index');
Route::get('sale/trx/{sale}', [TransactionController::class, 'show'])->name('transactions.show');
// notification
Route::get('notifications', [HomeController::class, 'notification'])->name('notification.index');
// customer level
Route::get('customer-level', [CustomerLevelController::class, 'index'])->name('customer.customer-level.index');
// cash deposit location
Route::get('cash-deposit-locations', [DepositLocationController::class, 'index'])->name('customer.deposit-location.index');
});
Route::middleware('guest:customer')->group(function () {

Loading…
Cancel
Save