diff --git a/TODO.md b/TODO.md index 4640904..d2e9de8 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,27 @@ # TODO + +## Front + +- [ ] 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 +- [ ] tambah metode topup deposit dengan setor tunai kantor wbb +- [ ] tambah screen untuk daftar setor tunai kantor wbb +- [ ] 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 +- [ ] format nomor transaksi di deposit , transaksi dan poin + +# Back Office + +- [ ] tambah biaya admin di deposit manual transfer +- [ ] info di ubah jadi html +- [ ] tambahan detail customer untuk detail mitra wbb +- [ ] detail customer level untuk tampilan screen level customer di depan +- [ ] rombak fitur affiliasi +- [ ] tambah detail di user admin +- [ ] tambah logo bank +- [ ] tambah setor tunai +- [ ] pengaturan share dapat menggunakan html +- [ ] menu mitrawbb diff --git a/app/Http/Controllers/Customer/CartController.php b/app/Http/Controllers/Customer/CartController.php index 00fac71..ee034df 100644 --- a/app/Http/Controllers/Customer/CartController.php +++ b/app/Http/Controllers/Customer/CartController.php @@ -19,11 +19,12 @@ class CartController extends Controller * has payed button * show payment method -> deposit, poin, paylater */ - public function index() + public function index(Request $request) { - $carts = collect(session('carts') ?? []); + $customer = $request->user('customer'); + $carts = $customer->carts->load(['voucher.locationProfile.location']); $total = $carts->sum(function ($item) { - return $item['quantity'] * $item['voucher']->validate_price; + return $item->quantity * $item->voucher->validate_price; }); $customer = Customer::find(auth()->id()); @@ -42,51 +43,40 @@ class CartController extends Controller */ public function store(Request $request, Voucher $voucher) { - $operator = $request->param ?? 'add'; - $voucher->load(['location']); - - $carts = collect(session('carts') ?? []); - if ($carts->count() > 0) { - $item = $carts->firstWhere('id', $voucher->id); - if ($item == null) { - $carts->add(['id' => $voucher->id, 'quantity' => 1, 'voucher' => $voucher]); - session(['carts' => $carts->toArray()]); - session()->flash('message', ['type' => 'success', 'message' => 'voucher ditambahkan ke keranjang']); - } else { - $carts = $carts->map(function ($item) use ($voucher, $operator) { - if ($item['id'] == $voucher->id) { - if ($operator == 'delete') { - return ['id' => null]; - } - if ($operator == 'add') { - $quantity = $item['quantity'] + 1; - } - if ($operator == 'sub') { - $quantity = $item['quantity'] - 1; - if ($quantity <= 0) { - $quantity = 1; - } - } - - return [ - ...$item, - 'quantity' => $quantity, - ]; - } - - return $item; - }); - $carts = $carts->whereNotNull('id')->toArray(); - session(['carts' => $carts]); + $operator = $request->param ?? 'add'; //delete, sub, add + $customer = $request->user('customer'); + + $item = $customer->carts()->where(['entity_id' => $voucher->id])->first(); + if ($item !== null) { + if ($operator == 'delete') { + $item->delete(); + session()->flash('message', ['type' => 'success', 'message' => 'voucher dihapus dari keranjang', 'cart' => 1]); + } + if ($operator == 'add') { + // bisa tambah filter stock vouchernya + $item->update([ + 'quantity' => $item->quantity + 1 + ]); } + if ($operator == 'sub') { + if ($item->quantity - 1 != 0) { + $item->update([ + 'quantity' => $item->quantity - 1 + ]); + } + } + } else { + $customer->carts()->create([ + 'entity_id' => $voucher->id, + 'quantity' => 1 + ]); - return; + session()->flash('message', ['type' => 'success', 'message' => 'voucher ditambahkan ke keranjang', 'cart' => 1]); } - session(['carts' => [ - ['id' => $voucher->id, 'quantity' => 1, 'voucher' => $voucher], - ]]); - session()->flash('message', ['type' => 'success', 'message' => 'voucher ditambahkan ke keranjang']); + if ($request->direct != '') { + return redirect()->route('cart.index'); + } } /** @@ -166,13 +156,13 @@ class CartController extends Controller if ($bonus != null) { $poin = $customer->poins()->create([ 'debit' => $bonus->bonus_poin, - 'description' => 'Bonus Pembelian #'.$sale->code, + 'description' => 'Bonus Pembelian #' . $sale->code, ]); $poin->update_customer_balance(); } - $description = 'Pembayaran #'.$sale->code; + $description = 'Pembayaran #' . $sale->code; if ($customer->deposit_balance < $total) { if ($customer->deposit_balance > 0) { diff --git a/app/Http/Controllers/Customer/PoinExchangeController.php b/app/Http/Controllers/Customer/PoinExchangeController.php index f65ce8a..a342c99 100644 --- a/app/Http/Controllers/Customer/PoinExchangeController.php +++ b/app/Http/Controllers/Customer/PoinExchangeController.php @@ -15,13 +15,13 @@ class PoinExchangeController extends Controller public function index(Request $request) { $locations = Location::get(); - $vouchers = Voucher::with(['location']) - ->where(function ($q) { + $vouchers = Voucher::with(['locationProfile']) + ->whereHas('locationProfile', function ($q) { $q->where('price_poin', '!=', 0) ->where('price_poin', '!=', null); }) ->where('is_sold', Voucher::UNSOLD) - ->groupBy('batch_id') + ->groupBy('location_profile_id') ->orderBy('updated_at', 'desc'); if ($request->location_id != '') { @@ -52,10 +52,10 @@ class PoinExchangeController extends Controller DB::beginTransaction(); $sale = $customer->sales()->create([ - 'code' => 'Tukar poin '.str()->upper(str()->random(5)), + 'code' => 'Tukar poin ' . str()->upper(str()->random(5)), 'date_time' => now(), 'amount' => 0, - 'payed_with' => Sale::PAYED_WITH_poin, + 'payed_with' => Sale::PAYED_WITH_POIN, ]); $voucher = $voucher->shuffle_unsold(); diff --git a/app/Http/Middleware/HandleInertiaCustomerRequests.php b/app/Http/Middleware/HandleInertiaCustomerRequests.php index 6c54c1c..d8b1012 100644 --- a/app/Http/Middleware/HandleInertiaCustomerRequests.php +++ b/app/Http/Middleware/HandleInertiaCustomerRequests.php @@ -31,17 +31,12 @@ class HandleInertiaCustomerRequests extends Middleware */ public function share(Request $request): array { - $carts = collect(session('carts') ?? []); $cart_count = 0; - if ($carts->count() > 0) { - foreach ($carts as $cart) { - $cart_count += $cart['quantity']; - } - } - $notification_count = 0; + if (auth('customer')->check()) { $notification_count = Notification::where('entity_id', auth()->id())->where('is_read', Notification::UNREAD)->count(); + $cart_count = auth('customer')->user()->carts()->sum('quantity'); } return array_merge(parent::share($request), [ diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 083be54..d0a9bb9 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -209,6 +209,11 @@ class Customer extends Authenticatable return $this->hasMany(CustomerRefferal::class); } + public function carts() + { + return $this->hasMany(CustomerCart::class); + } + public function allowPay($total): array { $allowProcess = false; diff --git a/app/Models/CustomerCart.php b/app/Models/CustomerCart.php index f6d2364..6a2aff6 100644 --- a/app/Models/CustomerCart.php +++ b/app/Models/CustomerCart.php @@ -13,4 +13,9 @@ class CustomerCart extends Model 'quantity', 'additional_info_json', ]; + + public function voucher() + { + return $this->belongsTo(Voucher::class, 'entity_id'); + } } diff --git a/app/Models/DepositHistory.php b/app/Models/DepositHistory.php index 31fd35b..738bdba 100644 --- a/app/Models/DepositHistory.php +++ b/app/Models/DepositHistory.php @@ -76,10 +76,10 @@ class DepositHistory extends Model { return Attribute::make(get: function () { if ($this->credit == 0) { - return 'Rp'.number_format($this->debit, is_float($this->debit) ? 2 : 0, ',', '.'); + return 'Rp ' . number_format($this->debit, is_float($this->debit) ? 2 : 0, ',', '.'); } - return '-Rp'.number_format($this->credit, is_float($this->credit) ? 2 : 0, ',', '.'); + return '-Rp ' . number_format($this->credit, is_float($this->credit) ? 2 : 0, ',', '.'); }); } @@ -115,14 +115,14 @@ class DepositHistory extends Model } Notification::create([ 'entity_type' => User::class, - 'description' => $this->customer->fullname.' melakukan deposit manual sebesar : '.$this->amount.$status, + 'description' => $this->customer->fullname . ' melakukan deposit manual sebesar : ' . $this->amount . $status, ]); } if ($this->payment_channel == Setting::PAYMENT_MIDTRANS) { Notification::create([ 'entity_type' => User::class, - 'description' => $this->customer->fullname.' melakukan deposit via midtrans sebesar : '.$this->amount, + 'description' => $this->customer->fullname . ' melakukan deposit via midtrans sebesar : ' . $this->amount, ]); } } @@ -131,7 +131,7 @@ class DepositHistory extends Model { Notification::create([ 'entity_id' => $this->customer_id, - 'description' => 'Deposit '.$this->description.' sebesar '.$this->amount.' sudah sukses diterima', + 'description' => 'Deposit ' . $this->description . ' sebesar ' . $this->amount . ' sudah sukses diterima', ]); } } diff --git a/package-lock.json b/package-lock.json index fcec9ed..82d52bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "qs": "^6.11.0", "react-chartjs-2": "^5.2.0", "react-datepicker": "^4.8.0", + "react-hot-toast": "^2.4.1", "react-icons": "^4.7.1", "react-number-format": "^5.1.2", "react-toastify": "^9.1.1", @@ -1715,6 +1716,14 @@ "node": ">=4" } }, + "node_modules/goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2426,6 +2435,21 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-icons": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", diff --git a/package.json b/package.json index cbb067a..a430516 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "qs": "^6.11.0", "react-chartjs-2": "^5.2.0", "react-datepicker": "^4.8.0", + "react-hot-toast": "^2.4.1", "react-icons": "^4.7.1", "react-number-format": "^5.1.2", "react-toastify": "^9.1.1", diff --git a/resources/css/app.css b/resources/css/app.css index 2395c33..1d4c3ba 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,6 +1,7 @@ @tailwind base; @tailwind components; @tailwind utilities; + /* Make clicks pass-through */ #nprogress { pointer-events: none; @@ -25,11 +26,11 @@ width: 100px; height: 100%; box-shadow: 0 0 10px #003bf1, 0 0 5px #003bf1; - opacity: 1.0; + opacity: 1; -webkit-transform: rotate(3deg) translate(0px, -4px); - -ms-transform: rotate(3deg) translate(0px, -4px); - transform: rotate(3deg) translate(0px, -4px); + -ms-transform: rotate(3deg) translate(0px, -4px); + transform: rotate(3deg) translate(0px, -4px); } /* Remove these to get rid of the spinner */ @@ -52,7 +53,7 @@ border-radius: 50%; -webkit-animation: nprogress-spinner 400ms linear infinite; - animation: nprogress-spinner 400ms linear infinite; + animation: nprogress-spinner 400ms linear infinite; } .nprogress-custom-parent { @@ -72,37 +73,48 @@ } @-webkit-keyframes nprogress-spinner { - 0% { -webkit-transform: rotate(0deg); } - 100% { -webkit-transform: rotate(360deg); } + 0% { + -webkit-transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + } } + @keyframes nprogress-spinner { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } } /* width */ ::-webkit-scrollbar { - width: 5px; - height: 70%; + width: 5px; + height: 70%; } /* Track */ ::-webkit-scrollbar-track { - background: rgb(136 136 136 / 28%); + background: rgb(136 136 136 / 28%); } /* Handle */ ::-webkit-scrollbar-thumb { - background: #888; + background: #888; } /* Handle on hover */ ::-webkit-scrollbar-thumb:hover { - background: rgb(241 241 241 / 12%); + background: rgb(241 241 241 / 12%); } .react-datepicker__input-container input { - @apply block w-full text-base md:text-sm bg-white border dark:bg-gray-700 shadow-sm ; + @apply block w-full text-base md:text-sm bg-white border dark:bg-gray-700 shadow-sm; } .react-datepicker-popper { @@ -141,36 +153,47 @@ .react-datepicker__month-0 { @apply w-1/3; } + .react-datepicker__month-1 { @apply w-1/3; } + .react-datepicker__month-2 { @apply w-1/3; } + .react-datepicker__month-3 { @apply w-1/3; } + .react-datepicker__month-4 { @apply w-1/3; } + .react-datepicker__month-5 { @apply w-1/3; } + .react-datepicker__month-6 { @apply w-1/3; } + .react-datepicker__month-7 { @apply w-1/3; } + .react-datepicker__month-8 { @apply w-1/3; } + .react-datepicker__month-9 { @apply w-1/3; } + .react-datepicker__month-10 { @apply w-1/3; } + .react-datepicker__month-11 { @apply w-1/3; } @@ -246,17 +269,19 @@ .react-datepicker__month-text { @apply mb-1 p-1 flex items-center justify-center text-sm leading-loose transition text-gray-700 dark:text-gray-100 rounded hover:bg-blue-500 hover:text-white dark:hover:text-white; } + .react-datepicker__month--selected { @apply bg-blue-500 text-white; } + .react-datepicker-year-header { @apply ml-2.5 mb-2 text-lg font-semibold text-gray-800 dark:text-gray-100; } .react-datepicker__aria-live { - @apply hidden + @apply hidden; } .min-h-100 { max-height: 45rem; -} \ No newline at end of file +} diff --git a/resources/css/spinner.css b/resources/css/spinner.css index 62280e4..202857e 100644 --- a/resources/css/spinner.css +++ b/resources/css/spinner.css @@ -27,7 +27,7 @@ width: 100px; height: 100%; box-shadow: 0 0 10px #003bf1, 0 0 5px #003bf1; - opacity: 1.0; + opacity: 1; -webkit-transform: rotate(3deg) translate(0px, -4px); -ms-transform: rotate(3deg) translate(0px, -4px); @@ -71,15 +71,14 @@ box-sizing: border-box; border: solid 3px transparent !important; - border-top-color: #F1A410 !important; - border-left-color: #F1A410 !important; + border-top-color: #f1a410 !important; + border-left-color: #f1a410 !important; border-radius: 50px !important; -webkit-animation: nprogress-spinner 400ms linear infinite; animation: nprogress-spinner 400ms linear infinite; } - .nprogress-custom-parent { display: flex; align-items: center; @@ -94,4 +93,4 @@ .nprogress-custom-parent #nprogress .spinner, .nprogress-custom-parent #nprogress .bar { position: absolute; -} \ No newline at end of file +} diff --git a/resources/js/Customer/Cart/VoucherCard.jsx b/resources/js/Customer/Cart/VoucherCard.jsx index a5894a7..0e6d5ce 100644 --- a/resources/js/Customer/Cart/VoucherCard.jsx +++ b/resources/js/Customer/Cart/VoucherCard.jsx @@ -17,12 +17,14 @@ export default function VoucherCard({ item: { voucher, quantity } }) { return (