fixing tampilan depan

dev
Aji Kamaludin 1 year ago
parent 29b2c1df0c
commit 59aff206e5
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -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

@ -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) {

@ -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();

@ -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), [

@ -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;

@ -13,4 +13,9 @@ class CustomerCart extends Model
'quantity',
'additional_info_json',
];
public function voucher()
{
return $this->belongsTo(Voucher::class, 'entity_id');
}
}

@ -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',
]);
}
}

24
package-lock.json generated

@ -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",

@ -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",

@ -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;
}
}

@ -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;
}
}

@ -17,12 +17,14 @@ export default function VoucherCard({ item: { voucher, quantity } }) {
return (
<div className="px-3 py-1 shadow-md rounded border border-gray-100">
<div className="text-base font-bold">{voucher.location.name}</div>
<div className="text-base font-bold">
{voucher.location_profile.location.name}
</div>
<div className="w-full border border-dashed"></div>
<div className="flex flex-row justify-between items-center">
<div>
<div className="text-xs text-gray-400 py-1">
{voucher.profile}
{voucher.location_profile.display_note}
</div>
<div className="text-xl font-bold">
IDR {formatIDR(voucher.validate_price)}
@ -40,10 +42,10 @@ export default function VoucherCard({ item: { voucher, quantity } }) {
</div>
<div className="flex flex-col justify-end text-right">
<div className="text-3xl font-bold">
{voucher.display_quota}
{voucher.location_profile.quota}
</div>
<div className="text-gray-400">
{voucher.display_expired}
{voucher.location_profile.diplay_expired}
</div>
</div>
</div>
@ -59,14 +61,14 @@ export default function VoucherCard({ item: { voucher, quantity } }) {
className="text-red-700 w-6 h-6 rounded-full border mr-4 hover:bg-red-700"
onClick={handleDelete}
/>
<HiPlusCircle
<HiMinusCircle
className="text-gray-400 w-6 h-6 rounded-full border hover:bg-gray-400"
onClick={handleAdd}
onClick={handleSub}
/>
<div>{quantity}</div>
<HiMinusCircle
<HiPlusCircle
className="text-gray-400 w-6 h-6 rounded-full border hover:bg-gray-400"
onClick={handleSub}
onClick={handleAdd}
/>
</div>
</div>

@ -0,0 +1,23 @@
export default function BottomSheet({ isOpen, toggle, children }) {
return (
<>
<div
className={`${
isOpen ? '' : 'hidden'
} overflow-y-auto overflow-x-hidden fixed bottom-0 right-0 z-50 w-full justify-center flex delay-150 transition ease-in-out duration-1000`}
>
<div
className="fixed bottom-0 right-0 bg-opacity-50 dark:bg-opacity-90 bg-gray-900 w-full h-full dark:bg-gray-900"
onClick={() => toggle()}
></div>
<div
className={`fixex bottom-0 relative w-full max-w-md md:h-auto`}
>
<div className="relative bg-white rounded-t-lg p-2 dark:bg-gray-700 text-base dark:text-gray-400">
{children}
</div>
</div>
</div>
</>
)
}

@ -6,6 +6,7 @@ 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 }) {
const { data, setData, post, processing, errors, reset, clearErrors } =
@ -71,7 +72,7 @@ export default function Topup({ payments }) {
<div className="border-b flex-1"></div>
</div>
<div className="w-full px-5">
<FormInput
<FormInputNumeric
placeholder="masukan nominal, minimal 10.000"
value={data.amount}
onChange={(e) => setData('amount', e.target.value)}

@ -1,6 +1,5 @@
import Reactm, { useState, useEffect } from 'react'
import { Head, router, usePage } from '@inertiajs/react'
import { HiOutlineBell } from 'react-icons/hi2'
import { Head, router } from '@inertiajs/react'
import Carousel from 'nuka-carousel'
import { handleBanner, ALL, FAVORITE } from './utils'
@ -8,30 +7,7 @@ import CustomerLayout from '@/Layouts/CustomerLayout'
import UserBanner from './Partials/UserBanner'
import AllVoucher from './IndexPartials/AllVoucher'
import FavoriteVoucher from './IndexPartials/FavoriteVoucher'
const GuestBanner = () => {
const {
props: { setting, notification_count },
} = usePage()
return (
<div>
{/* user */}
<div className="flex flex-row justify-between items-center px-5 py-6 text-lg bg-blue-600">
<div className="flex flex-col text-white">
<div className="font-bold">{setting.OPEN_WEBSITE_NAME}</div>
</div>
<div className="flex flex-row">
<HiOutlineBell className="text-white w-7 h-7" />
<div>
<div className="bg-white text-blue-700 rounded-lg px-1 text-xs -ml-2.5">
{notification_count}
</div>
</div>
</div>
</div>
</div>
)
}
import GuestBanner from './Partials/GuestBanner'
export default function Index(props) {
const {

@ -6,7 +6,7 @@ import { HiOutlineCash } from 'react-icons/hi'
export default function BalanceBanner({ user }) {
return (
<div
className="flex flex-row px-5 pb-3 text-base bg-blue-600"
className="flex flex-row px-5 pb-3 text-base bg-primary-900"
onClick={() => router.get(route('customer.deposit.index'))}
>
<div className="flex flex-row w-full shadow py-2 px-2 rounded bg-white items-center justify-between">

@ -0,0 +1,26 @@
import { usePage } from '@inertiajs/react'
import { HiOutlineBell } from 'react-icons/hi2'
export default function GuestBanner() {
const {
props: { setting, notification_count },
} = usePage()
return (
<div>
{/* user */}
<div className="flex flex-row justify-between items-center px-5 py-6 text-lg bg-blue-900">
<div className="flex flex-col text-white">
<div className="font-bold">{setting.OPEN_WEBSITE_NAME}</div>
</div>
<div className="flex flex-row">
<HiOutlineBell className="text-white w-7 h-7" />
<div>
<div className="bg-white text-blue-700 rounded-lg px-1 text-xs -ml-2.5">
{notification_count}
</div>
</div>
</div>
</div>
</div>
)
}

@ -11,7 +11,7 @@ export default function UserBanner({ user }) {
return (
<div>
{/* user */}
<div className="flex flex-row justify-between items-center px-5 py-6 text-lg bg-blue-600">
<div className="flex flex-row justify-between items-center px-5 py-6 text-lg bg-primary-900">
<div className="flex flex-col text-white">
<div className="font-bold">{user.name}</div>
<div className="flex flex-row items-center space-x-1 -mt-1">

@ -1,14 +1,14 @@
import Alert from '@/Components/Alert'
import BottomSheet from '@/Customer/Components/BottomSheet'
import { useModalState } from '@/hooks'
import { formatIDR } from '@/utils'
import { router } from '@inertiajs/react'
export default function VoucherCard({ voucher }) {
const addCart = () => {
router.post(route('cart.store', voucher))
}
const Voucher = ({ voucher, onClick }) => {
return (
<div
className="px-3 py-1 shadow-md rounded border border-gray-100 hover:bg-gray-50"
onClick={addCart}
onClick={onClick}
>
<div className="w-full flex flex-row justify-between">
<div className="text-base font-bold">
@ -23,7 +23,7 @@ export default function VoucherCard({ voucher }) {
{voucher.location_profile.display_note}
</div>
<div className="text-xl font-bold">
IDR {formatIDR(voucher.validate_price)}
Rp {formatIDR(voucher.validate_price)}
</div>
{+voucher.discount !== 0 && (
<div className="flex flex-row space-x-2 items-center text-xs pb-2">
@ -48,3 +48,62 @@ export default function VoucherCard({ voucher }) {
</div>
)
}
const ModalChoose = (props) => {
const { state, voucher } = props
const onDirectBuy = () => {
router.post(route('cart.store', voucher), { direct: 1 })
state.toggle()
}
const addToCarts = () => {
router.post(route('cart.store', voucher))
state.toggle()
}
return (
<BottomSheet isOpen={state.isOpen} toggle={() => state.toggle()}>
<Voucher voucher={voucher} />
{voucher.location_profile.display_note !== null && (
<div
className="p-4 my-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400"
role="alert"
>
{voucher.location_profile.display_note}
</div>
)}
<div className="flex flex-row justify-between gap-2 mt-2">
<div
className="w-full text-center px-3 py-2 rounded-lg bg-white border border-blue-700 text-blue-700 hover:bg-blue-100"
onClick={onDirectBuy}
>
Beli Langsung
</div>
<div
className="w-full text-center px-3 py-2 rounded-lg bg-blue-700 border border-blue-900 text-white hover:bg-blue-900"
onClick={addToCarts}
>
+ Keranjang
</div>
</div>
</BottomSheet>
)
}
export default function VoucherCard({ voucher }) {
const chooseModalState = useModalState()
const onVoucherChoose = () => {
chooseModalState.toggle()
}
return (
<>
<div onClick={() => onVoucherChoose()}>
<Voucher voucher={voucher} />
</div>
<ModalChoose state={chooseModalState} voucher={voucher} />
</>
)
}

@ -1,64 +1,57 @@
import { formatIDR } from '@/utils'
import { router } from '@inertiajs/react'
import { useState } from 'react'
import BottomSheet from '../Components/BottomSheet'
const ExchangeModal = ({ show, voucher, setShow }) => {
return (
<div
className={`fixed z-10 top-0 left-0 h-full w-full -mt-4 ${
show ? '' : 'invisible'
} `}
>
<div
className="max-w-md mx-auto h-full bg-gray-500 bg-opacity-70 -mt-2"
onClick={() => setShow(false)}
>
<div className="flex flex-col h-full my-auto justify-center px-4">
<div className="px-3 py-1 shadow-md rounded border bg-white border-gray-100 hover:bg-gray-50">
<div className="text-base font-bold">
{voucher.location.name}
<BottomSheet isOpen={show} toggle={() => setShow(false)}>
<div className="flex flex-col h-full my-auto justify-center px-2 mt-2">
<div className="px-3 py-1 shadow-md rounded border bg-white border-gray-100 hover:bg-gray-50">
<div className="text-base font-bold">
{voucher.location_profile.name}
</div>
<div className="w-full border border-dashed"></div>
<div className="flex flex-row justify-between items-center">
<div>
<div className="text-xs text-gray-400 py-1">
{voucher.location_profile.display_note}
</div>
<div className="text-xl font-bold">
{formatIDR(voucher.location_profile.price_poin)}{' '}
poin{' '}
</div>
</div>
<div className="w-full border border-dashed"></div>
<div className="flex flex-row justify-between items-center">
<div>
<div className="text-xs text-gray-400 py-1">
{voucher.profile}
</div>
<div className="text-xl font-bold">
{formatIDR(voucher.price_poin)} poin
</div>
<div className="flex flex-col justify-end text-right">
<div className="text-3xl font-bold">
{voucher.location_profile.quota}
</div>
<div className="flex flex-col justify-end text-right">
<div className="text-3xl font-bold">
{voucher.display_quota}
</div>
<div className="text-gray-400 ">
{voucher.display_expired}
</div>
<div className="text-gray-400 ">
{voucher.location_profile.display_expired}
</div>
</div>
</div>
<div className="flex flex-row space-x-3">
<div
className="w-full mt-2 px-3 py-1 shadow-md rounded border-blue-700 bg-blue-600 text-white hover:bg-white hover:text-black"
onClick={() =>
router.get(
route(
'customer.poin.exchange.process',
voucher
)
)
}
>
Tukarkan
</div>
<div className="w-full mt-2 px-3 py-1 shadow-md rounded border-white bg-white hover:bg-gray-200">
Batal
</div>
</div>
<div className="flex flex-row space-x-3 mt-2">
<div
className="w-full text-center px-3 py-2 rounded-lg bg-blue-700 border border-blue-900 text-white hover:bg-blue-900"
onClick={() =>
router.get(
route('customer.poin.exchange.process', voucher)
)
}
>
Tukarkan
</div>
<div
className="w-full text-center px-3 py-2 rounded-lg bg-white border border-blue-700 text-blue-700 hover:bg-blue-100"
onClick={() => setShow(false)}
>
Batal
</div>
</div>
</div>
</div>
</BottomSheet>
)
}
@ -71,24 +64,25 @@ export default function VoucherCard({ voucher }) {
onClick={() => setShow(true)}
>
<div className="text-base font-bold">
{voucher.location.name}
{voucher.location_profile.name}
</div>
<div className="w-full border border-dashed"></div>
<div className="flex flex-row justify-between items-center">
<div>
<div className="text-xs text-gray-400 py-1">
{voucher.profile}
{voucher.location_profile.display_note}
</div>
<div className="text-xl font-bold">
{formatIDR(voucher.price_poin)} poin
{formatIDR(voucher.location_profile.price_poin)}{' '}
poin
</div>
</div>
<div className="flex flex-col justify-end text-right">
<div className="text-3xl font-bold">
{voucher.display_quota}
{voucher.location_profile.quota}
</div>
<div className="text-gray-400 ">
{voucher.display_expired}
{voucher.location_profile.display_expired}
</div>
</div>
</div>

@ -35,7 +35,7 @@ export default function Index({ auth: { user }, notification_count }) {
<div className="flex flex-col min-h-[calc(95dvh)]">
<div>
{/* user */}
<div className="flex flex-row justify-between items-center px-5 py-6 text-lg bg-blue-600">
<div className="flex flex-row justify-between items-center px-5 py-6 text-lg bg-primary-900">
<div className="flex flex-row items-center space-x-2">
{user.image_url !== null ? (
<img

@ -61,17 +61,23 @@ export default function Authenticated({
<Dropdown.Content width="64">
{notifications.map((notif) => (
<div
className={`${
+notif.is_read === 0 &&
'font-bold'
} px-4 py-2 hover:bg-gray-100 border-b`}
className={`px-4 py-2 hover:bg-gray-100 border-b`}
onClick={() =>
handleNotification(notif)
}
key={notif.id}
>
<div>{notif.description}</div>
<div>{notif.created_at}</div>
<div
className={`${
+notif.is_read === 0 &&
'font-bold'
}`}
>
{notif.description}
</div>
<div className="text-xs">
{notif.format_created_at}
</div>
</div>
))}
{notifications.length > 0 && (

@ -1,5 +1,5 @@
import React, { useEffect } from 'react'
import { ToastContainer, toast } from 'react-toastify'
import React, { useEffect, useState } from 'react'
import toast, { Toaster } from 'react-hot-toast'
import { router, usePage } from '@inertiajs/react'
import { HiOutlineHome } from 'react-icons/hi'
@ -19,22 +19,40 @@ export default function CustomerLayout({ children }) {
},
} = usePage()
const [bounce, setBouce] = useState(false)
const handleOnClick = (r) => {
router.get(route(r, { direct: 1 }))
}
const isActive = (r) => {
if (route().current(r)) {
return 'text-blue-700 font-bold'
return 'text-primary-900 font-bold'
}
return 'text-gray-600 font-light'
}
const clearAnimate = () => {
setBouce(false)
}
useEffect(() => {
if (flash.message !== null) {
toast(flash.message.message, { type: flash.message.type })
let se
if (flash.message !== null && flash.message.type !== null) {
toast.success((t) => {
return (
<div onClick={() => toast.dismiss(t.id)}>
{flash.message.message}
</div>
)
})
if (+flash.message.cart === 1) {
setBouce(true)
se = setTimeout(clearAnimate, 3000)
}
}
return () => clearTimeout(se)
}, [flash])
return (
@ -58,7 +76,11 @@ export default function CustomerLayout({ children }) {
)}`}
onClick={() => handleOnClick('cart.index')}
>
<div className="flex flex-row">
<div
className={`flex flex-row ${
bounce && 'motion-safe:animate-bounce'
}`}
>
<HiOutlineShoppingCart className="h-6 w-6" />
<div>
<div className="bg-blue-300 text-blue-600 rounded-lg px-1 text-xs -ml-2">
@ -104,7 +126,17 @@ export default function CustomerLayout({ children }) {
<div className="text-xs">Menu</div>
</div>
</div>
<ToastContainer />
<Toaster
position="bottom-center"
containerStyle={{ bottom: 70 }}
toastOptions={{
duration: 2000,
style: {
background: 'rgba(0, 0, 0, 0.7)',
color: 'white',
},
}}
/>
</div>
)
}

@ -2,7 +2,6 @@ import './bootstrap'
import '../css/app.css'
import '../css/spinner.css'
import 'flowbite'
import 'react-toastify/dist/ReactToastify.css'
import React from 'react'
import NProgress from 'nprogress'

@ -1,4 +1,6 @@
#!/bin/bash
npm run build
rsync -arP -e 'ssh -p 225' --exclude=node_modules --exclude=database/database.sqlite --exclude=.git --exclude=.env --exclude=public/hot . arm@ajikamaludin.id:/home/arm/projects/www/voucher
ssh -p 225 arm@ajikamaludin.id -C docker exec php82 php /var/www/voucher/artisan migrate:refresh --seed
ssh -p 225 arm@ajikamaludin.id -C docker exec php82 php /var/www/voucher/artisan migrate:refresh --seed

@ -17,6 +17,34 @@ module.exports = {
sans: ['Nunito', ...defaultTheme.fontFamily.sans],
},
},
colors: {
primary: {
50: '#edf7ff',
100: '#d6ebff',
200: '#b6deff',
300: '#84cbff',
400: '#4aaeff',
500: '#2089ff',
600: '#0867ff',
700: '#024ff3',
800: '#0940c4',
900: '#0e3995',
950: '#0e255d',
},
secondary: {
50: '#fffceb',
100: '#fdf4c8',
200: '#fbe88c',
300: '#f9d650',
400: '#f7c328',
500: '#f1a410',
600: '#d57d0a',
700: '#b1580c',
800: '#904510',
900: '#763911',
950: '#441c04',
},
},
},
plugins: [require('@tailwindcss/forms'), require('flowbite/plugin')],

Loading…
Cancel
Save