notifikasi , bit fix dashboard

dev
Aji Kamaludin 1 year ago
parent d4b2d9253b
commit 612ccc0ac1
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -18,11 +18,7 @@
- [x] Setting Bonus Coin (memasukan amount bonus coin yang didapat dengan level dan harga voucher) - bonus coin
- [x] Voucher Sales (index: customer, total, jumlah voucher, detail: customer, list voucher, payment)
- [x] Dashboard (gafik hasil penjualan : disorting tanggal, lokasi dan customer)
[total voucher] [total customer] [total customer verified] [total deposit]
[total voucher terjual bulan xxx] [jumlah voucher terjual bulan xxx] [total voucher terjual hari ini ] [jumlah voucher terjual hari ini]
[cart penjualan per tanggal, pilih range tanggal (range default 7), bisa filter by lokasi dan customer]
[list customer dengan total beli hari ini]
- [ ] Notification (manual deposit, deposit success, stock voucher, sale)
- [x] Notification ([x]manual deposit, [x]deposit success, [x]stock voucher, [x]sale)
- [ ] View Customer Coin History
- [ ] Voucher - harga per level
- [ ] Voucher - harga coin
@ -48,5 +44,5 @@
- [x] Customer View Coin History
- [x] Verified Akun
- [x] Paylater: index paylater, payment cart, deposite repay
- [x] Notification ([x] purchase success, [x] deposit success)
- [ ] Coin Explorer: list voucher, modal voucher to excange
- [ ] Notification (purchase success, deposit success)

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Notification;
use Illuminate\Http\Request;
class NotificationController extends Controller
{
public function update(Notification $notif)
{
if ($notif->id == null) {
(new Notification())->mark_all_as_read();
return;
}
$notif->mark_as_read();
}
}

@ -155,8 +155,10 @@ class CartController extends Controller
]);
$voucher->update(['is_sold' => Voucher::SOLD]);
$voucher->check_stock_notification();
}
}
$sale->create_notification();
$bonus = CoinReward::where('customer_level_id', $customer->customer_level_id)
->where('amount_buy', '<=', $total)

@ -65,6 +65,7 @@ class DepositController extends Controller
$deposit->update(['payment_token' => $token]);
}
$deposit->create_notification();
DB::commit();
@ -97,6 +98,8 @@ class DepositController extends Controller
'is_valid' => DepositHistory::STATUS_WAIT_APPROVE,
]);
$deposit->create_notification();
session()->flash('message', ['type' => 'success', 'message' => 'Upload berhasil, silahkan tunggu untuk approve']);
}
@ -147,6 +150,8 @@ class DepositController extends Controller
$deposit->update_customer_balance();
$customer = Customer::find($deposit->customer_id);
$customer->repayPaylater($deposit);
$deposit->create_notification();
$deposit->create_notification_user();
} elseif ($request->transaction_status == 'pending') {
$deposit->fill(['payment_status' => DepositHistory::STATUS_WAIT_PAYMENT]);
} else {

@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
use App\Models\Banner;
use App\Models\Info;
use App\Models\Location;
use App\Models\Notification;
use App\Models\Voucher;
use Illuminate\Http\Request;
@ -40,4 +41,13 @@ class HomeController extends Controller
'banner' => $banner,
]);
}
public function notification()
{
Notification::where('entity_id', auth()->id())->where('is_read', Notification::UNREAD)->update(['is_read' => Notification::READ]);
return inertia('Index/Notification', [
'notification' => Notification::where('entity_id', auth()->id())->orderBy('updated_at', 'desc')->paginate()
]);
}
}

@ -59,6 +59,7 @@ class DepositController extends Controller
$customer = Customer::find($deposit->customer_id);
$customer->repayPaylater($deposit);
$deposit->create_notification_user();
}
DB::commit();

@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers;
use App\Models\Notification;
use Illuminate\Http\Request;
class NotificationController extends Controller
{
public function index()
{
$query = Notification::where('entity_type', \App\Models\User::class)
->orderBy('is_read', 'asc')
->orderBy('created_at', 'desc');
return inertia('Notification/Index', [
'query' => $query->paginate(),
]);
}
}

@ -20,6 +20,10 @@ class VoucherController extends Controller
->orWhere('profile', 'like', "%$request->q%");
}
if ($request->location_id != '') {
$query->where('location_id', $request->location_id);
}
return inertia('Voucher/Index', [
'query' => $query->paginate(),
]);

@ -2,6 +2,7 @@
namespace App\Http\Middleware;
use App\Models\Notification;
use Illuminate\Http\Request;
use Inertia\Middleware;
@ -37,6 +38,11 @@ class HandleInertiaCustomerRequests extends Middleware
}
}
$notification_count = 0;
if (auth('customer')->check()) {
$notification_count = Notification::where('entity_id', auth()->id())->where('is_read', Notification::UNREAD)->count();
}
return array_merge(parent::share($request), [
'app_name' => env('APP_NAME', 'App Name'),
'auth' => [
@ -45,7 +51,7 @@ class HandleInertiaCustomerRequests extends Middleware
'flash' => [
'message' => fn () => $request->session()->get('message') ?? ['type' => null, 'message' => null],
],
'notification_count' => 0,
'notification_count' => $notification_count,
'cart_count' => $cart_count,
]);
}

@ -2,6 +2,7 @@
namespace App\Http\Middleware;
use App\Models\Notification;
use Illuminate\Http\Request;
use Inertia\Middleware;
@ -38,6 +39,8 @@ class HandleInertiaRequests extends Middleware
],
'app_name' => env('APP_NAME', 'App Name'),
'csrf_token' => csrf_token(),
'notifications' => Notification::where('entity_type', \App\Models\User::class)->orderBy('created_at', 'desc')->limit(10)->get(),
'count_unread_notifications' => Notification::where('entity_type', \App\Models\User::class)->where('is_read', Notification::UNREAD)->count(),
]);
}
}

@ -105,4 +105,33 @@ class DepositHistory extends Model
$customer = Customer::find($this->customer_id);
$customer->update(['deposit_balance' => $customer->deposit_balance + $this->debit - $this->credit]);
}
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 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,
]);
}
}
public function create_notification_user()
{
Notification::create([
'entity_id' => $this->customer_id,
'description' => 'Deposit ' . $this->description . ' sebesar ' . $this->amount . ' sudah sukses diterima',
]);
}
}

@ -2,12 +2,39 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Carbon;
class Notification extends Model
{
const UNREAD = 0;
const READ = 1;
protected $fillable = [
'entity_type',
'entity_id',
'description',
'is_read',
];
protected $appends = [
'format_created_at'
];
public function mark_as_read()
{
$this->update(['is_read' => self::READ]);
}
public function mark_all_as_read()
{
Notification::where('is_read', self::UNREAD)->where('entity_type', User::class)->update(['is_read' => self::READ]);
}
public function formatCreatedAt(): Attribute
{
return Attribute::make(get: function () {
return Carbon::parse($this->created_at)->locale('id')->translatedFormat('d F Y H:i:s');
});
}
}

@ -67,4 +67,17 @@ class Sale extends Model
return 'Rp' . number_format($this->amount, 0, ',', '.');
});
}
public function create_notification()
{
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan pembelian ' . $this->items()->count() . ' voucher sebesar ' . $this->display_amount,
]);
Notification::create([
'entity_id' => auth()->id(),
'description' => 'Transaksi pembelian anda #' . $this->code . ' sebesar ' . $this->display_amount . ' berhasil',
]);
}
}

@ -98,4 +98,21 @@ class Voucher extends Model
return $voucher;
}
public function check_stock_notification()
{
$count = Voucher::where([
['is_sold', '=', self::UNSOLD],
['batch_id', '=', $this->batch_id]
])->count();
$treshold = Setting::getByKey('VOUCHER_STOCK_NOTIFICATION');
if ($count <= $treshold) {
Notification::create([
'entity_type' => User::class,
'description' => "stok voucher " . $this->location->name . " ( " . $this->profile . " ) " . "tersisa : " . $count,
]);
}
}
}

@ -1,50 +1,60 @@
import React, { useState, useContext, Fragment } from 'react';
import { Link } from '@inertiajs/react';
import { Transition } from '@headlessui/react';
import React, { useState, useContext, Fragment } from 'react'
import { Link } from '@inertiajs/react'
import { Transition } from '@headlessui/react'
const DropDownContext = React.createContext();
const DropDownContext = React.createContext()
const Dropdown = ({ children }) => {
const [open, setOpen] = useState(false);
const [open, setOpen] = useState(false)
const toggleOpen = () => {
setOpen((previousState) => !previousState);
};
setOpen((previousState) => !previousState)
}
return (
<DropDownContext.Provider value={{ open, setOpen, toggleOpen }}>
<div className="relative">{children}</div>
</DropDownContext.Provider>
);
};
)
}
const Trigger = ({ children }) => {
const { open, setOpen, toggleOpen } = useContext(DropDownContext);
const { open, setOpen, toggleOpen } = useContext(DropDownContext)
return (
<>
<div onClick={toggleOpen}>{children}</div>
{open && <div className="fixed inset-0 z-40" onClick={() => setOpen(false)}></div>}
{open && (
<div
className="fixed inset-0 z-40"
onClick={() => setOpen(false)}
></div>
)}
</>
);
};
)
}
const Content = ({ align = 'right', width = '48', contentClasses = 'py-1 bg-white', children }) => {
const { open, setOpen } = useContext(DropDownContext);
const Content = ({
align = 'right',
width = '48',
contentClasses = 'py-1 bg-white',
children,
}) => {
const { open, setOpen } = useContext(DropDownContext)
let alignmentClasses = 'origin-top';
let alignmentClasses = 'origin-top'
if (align === 'left') {
alignmentClasses = 'origin-top-left left-0';
alignmentClasses = 'origin-top-left left-0'
} else if (align === 'right') {
alignmentClasses = 'origin-top-right right-0';
alignmentClasses = 'origin-top-right right-0'
}
let widthClasses = '';
let widthClasses = 'w-96'
if (width === '48') {
widthClasses = 'w-48';
widthClasses = 'w-48'
}
return (
@ -63,12 +73,19 @@ const Content = ({ align = 'right', width = '48', contentClasses = 'py-1 bg-whit
className={`absolute z-50 mt-2 rounded-md shadow-lg ${alignmentClasses} ${widthClasses}`}
onClick={() => setOpen(false)}
>
<div className={`rounded-md ring-1 ring-black ring-opacity-5 dark:bg-gray-700 ` + contentClasses}>{children}</div>
<div
className={
`rounded-md ring-1 ring-black ring-opacity-5 dark:bg-gray-700 ` +
contentClasses
}
>
{children}
</div>
</div>
</Transition>
</>
);
};
)
}
const DropdownLink = ({ href, method, as, children }) => {
return (
@ -80,11 +97,11 @@ const DropdownLink = ({ href, method, as, children }) => {
>
{children}
</Link>
);
};
)
}
Dropdown.Trigger = Trigger;
Dropdown.Content = Content;
Dropdown.Link = DropdownLink;
Dropdown.Trigger = Trigger
Dropdown.Content = Content
Dropdown.Link = DropdownLink
export default Dropdown;
export default Dropdown

@ -0,0 +1,70 @@
import React, { useState } from 'react'
import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout'
import { HiChevronLeft } from 'react-icons/hi2'
export default function Index({
auth: { user },
notification: { data, next_page_url },
}) {
const [_notification, setCoins] = useState(data)
const handleNextPage = () => {
router.get(
next_page_url,
{},
{
replace: true,
preserveState: true,
only: ['notification'],
onSuccess: (res) => {
setCoins(_notification.concat(res.props.notification.data))
},
}
)
}
return (
<CustomerLayout>
<Head title="Coin" />
<div className="flex flex-col w-full min-h-[calc(90dvh)]">
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('home.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />
</div>
<div className="text-2xl px-5 font-bold">Notifikasi</div>
<div className="w-full">
<div className="flex flex-col py-10 space-y-5 px-5">
{_notification.map((notification) => (
<div
key={notification.id}
className="flex flex-row pb-2 items-center justify-between border-b"
>
<div className="flex flex-col">
<div className="font-bold">
{notification.format_created_at}
</div>
<div className="font-thin">
{notification.description}
</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>
</div>
</CustomerLayout>
)
}

@ -1,8 +1,12 @@
import React from 'react'
import { HiOutlineCash, HiOutlineBell } from 'react-icons/hi'
import BalanceBanner from './BalanceBanner'
import { router, usePage } from '@inertiajs/react'
export default function UserBanner({ user }) {
const {
props: { notification_count },
} = usePage()
return (
<div>
{/* user */}
@ -16,11 +20,16 @@ export default function UserBanner({ user }) {
</div>
</div>
</div>
<div className="flex flex-row">
<div
className="flex flex-row"
onClick={() => {
router.get(route('notification.index'))
}}
>
<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">
0
{notification_count}
</div>
</div>
</div>

@ -13,7 +13,7 @@ import { useModalState } from '@/hooks'
import ModalConfirm from '@/Components/ModalConfirm'
import BalanceBanner from '../Index/BalanceBanner'
export default function Index({ auth: { user } }) {
export default function Index({ auth: { user }, notification_count }) {
const confirmModal = useModalState()
const handleLogoutClick = () => {
@ -56,11 +56,16 @@ export default function Index({ auth: { user } }) {
</div>
</div>
</div>
<div className="flex flex-row">
<div
className="flex flex-row"
onClick={() => {
router.get(route('notification.index'))
}}
>
<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">
1
{notification_count}
</div>
</div>
</div>
@ -127,7 +132,12 @@ export default function Index({ auth: { user } }) {
<div>Transaksi</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">
<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('notification.index'))
}}
>
<div>Notifikasi</div>
<HiChevronRight className="h-5 w-5" />
</div>

@ -1,19 +1,35 @@
import React, { useState, useEffect } from 'react';
import { ToastContainer, toast } from 'react-toastify';
import ApplicationLogo from '@/Components/Defaults/ApplicationLogo';
import Dropdown from '@/Components/Defaults/Dropdown';
import { Link } from '@inertiajs/react';
import { Breadcrumb } from 'flowbite-react';
import React, { useState, useEffect } from 'react'
import { ToastContainer, toast } from 'react-toastify'
import ApplicationLogo from '@/Components/Defaults/ApplicationLogo'
import Dropdown from '@/Components/Defaults/Dropdown'
import { Link, usePage } from '@inertiajs/react'
import { Breadcrumb } from 'flowbite-react'
import { HiMenu, HiChevronDown, HiHome } from 'react-icons/hi'
import { router } from '@inertiajs/react';
import SidebarNav from './Partials/SidebarNav';
import { router } from '@inertiajs/react'
import SidebarNav from './Partials/SidebarNav'
import { HiOutlineBell } from 'react-icons/hi2'
export default function Authenticated({ auth, children, flash, page = '', action = ''}) {
const [showingNavigationDropdown, setShowingNavigationDropdown] = useState(false);
export default function Authenticated({
auth,
children,
flash,
page = '',
action = '',
}) {
const {
props: { count_unread_notifications, notifications },
} = usePage()
const [showingNavigationDropdown, setShowingNavigationDropdown] =
useState(false)
const handleNotification = (notif) => {
fetch(route('api.notification.update', notif))
router.get(route(route().current()))
}
useEffect(() => {
if (flash.message !== null) {
toast(flash.message.message, {type: flash.message.type})
toast(flash.message.message, { type: flash.message.type })
}
}, [flash])
@ -28,10 +44,63 @@ export default function Authenticated({ auth, children, flash, page = '', action
<ApplicationLogo className="block pt-2 h-12 w-full font-bold text-2xl fill-current" />
</Link>
</div>
</div>
<div className="hidden sm:flex sm:items-center sm:ml-6">
<div className="ml-3 relative">
<Dropdown>
<Dropdown.Trigger>
<div className="flex flex-row">
<HiOutlineBell className="h-6 w-6" />
<div>
<div className="bg-blue-300 text-blue-600 rounded-lg px-1 text-xs -ml-2">
{count_unread_notifications}
</div>
</div>
</div>
</Dropdown.Trigger>
<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`}
onClick={() =>
handleNotification(notif)
}
key={notif.id}
>
{notif.description}
</div>
))}
{notifications.length > 0 && (
<div className="w-full flex flex-row justify-between px-3">
<div
className="text-xs hover:text-blue-500 hover:underline"
onClick={() =>
router.get(
route(
'notifications.index'
)
)
}
>
lihat semua
</div>
<div
className="text-xs hover:text-blue-500 hover:underline"
onClick={() =>
handleNotification()
}
>
tandai semua dibaca
</div>
</div>
)}
</Dropdown.Content>
</Dropdown>
</div>
<div className="ml-3 relative">
<Dropdown>
<Dropdown.Trigger>
@ -41,14 +110,22 @@ export default function Authenticated({ auth, children, flash, page = '', action
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150 dark:bg-gray-700 dark:hover:text-gray-50 dark:text-gray-200 gap-2"
>
{auth.user.name}
<HiChevronDown/>
<HiChevronDown />
</button>
</span>
</Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Link href={route('profile.edit')}>Profile</Dropdown.Link>
<Dropdown.Link href={route('logout')} method="post" as="button">
<Dropdown.Link
href={route('profile.edit')}
>
Profile
</Dropdown.Link>
<Dropdown.Link
href={route('logout')}
method="post"
as="button"
>
Log Out
</Dropdown.Link>
</Dropdown.Content>
@ -58,45 +135,48 @@ export default function Authenticated({ auth, children, flash, page = '', action
<div className="-mr-2 flex items-center sm:hidden space-x-2">
<button
onClick={() => setShowingNavigationDropdown((previousState) => !previousState)}
onClick={() =>
setShowingNavigationDropdown(
(previousState) => !previousState
)
}
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out"
>
<HiMenu/>
<HiMenu />
</button>
</div>
</div>
</div>
</nav>
<div className='flex-1 flex flex-row'>
<div className={`w-fit ${showingNavigationDropdown ? 'absolute h-screen z-10' : 'md:block hidden'}`}>
<SidebarNav user={auth.user}/>
<div className="flex-1 flex flex-row">
<div
className={`w-fit ${
showingNavigationDropdown
? 'absolute h-screen z-10'
: 'md:block hidden'
}`}
>
<SidebarNav user={auth.user} />
</div>
<main className='w-full'>
<main className="w-full">
{page !== '' && (
<Breadcrumb
className="bg-gray-200 py-3 px-5 mb-2 dark:bg-gray-700"
>
<Breadcrumb className="bg-gray-200 py-3 px-5 mb-2 dark:bg-gray-700">
<Breadcrumb.Item
onClick={() => router.visit(route('dashboard'))}
icon={HiHome}
>
<p className='mt-0.5'>{page}</p>
<p className="mt-0.5">{page}</p>
</Breadcrumb.Item>
{action !== '' && (
<Breadcrumb.Item>
{action}
</Breadcrumb.Item>
<Breadcrumb.Item>{action}</Breadcrumb.Item>
)}
</Breadcrumb>
)}
<div className='py-4'>
{children}
</div>
<div className="py-4">{children}</div>
</main>
</div>
<ToastContainer />
</div>
);
)
}

@ -103,7 +103,7 @@ export default function Dashboard(props) {
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="grid grid-cols-4 gap-2">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-2">
<div className="border rounded-md shadow bg-white p-4 flex flex-col">
<div className="text-gray-600 text-xl">
Total Voucher
@ -171,8 +171,8 @@ export default function Dashboard(props) {
</div>
<div className="w-full flex flex-row mt-4 space-x-2 border rounded-md shadow">
<div className="flex-1 overflow-auto bg-white p-4">
<div className="w-full flex flex-row justify-between mb-4">
<div className="tex-gray-500 text-xl pb-4">
<div className="w-full flex flex-col md:flex-row justify-between mb-4">
<div className="text-gray-500 text-xl pb-4">
Penjualan
</div>
@ -205,7 +205,7 @@ export default function Dashboard(props) {
</div>
</div>
<div className="bg-white rounded-md shadow border mt-4 w-full">
<div className="tex-gray-500 text-xl px-3 py-4">
<div className="text-gray-500 text-xl px-3 py-4">
Deposit Hari Ini
</div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4 px-2">

@ -0,0 +1,110 @@
import React from 'react'
import { Head, router } from '@inertiajs/react'
import { useModalState } from '@/hooks'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import { HiPencilSquare } from 'react-icons/hi2'
import { HiPencilAlt } from 'react-icons/hi'
export default function Index(props) {
const {
query: { links, data },
} = props
const formModal = useModalState()
const handleNotification = (notif) => {
fetch(route('api.notification.update', notif))
router.get(route(route().current()))
}
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
page={'Notifikasi'}
action={''}
>
<Head title="Notifikasi" />
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex justify-between">
{/* {canCreate && (
<Button
size="sm"
onClick={() => toggleFormModal()}
>
Tambah
</Button>
)} */}
</div>
<div className="overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th
scope="col"
className="py-3 px-6"
>
Deskripsi
</th>
<th
scope="col"
className="py-3 px-6"
>
Dibaca
</th>
<th
scope="col"
className="py-3 px-6"
/>
</tr>
</thead>
<tbody>
{data.map((notification) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={notification.id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{notification.description}
</td>
<td className="py-4 px-6">
{+notification.is_read === 1
? 'Sudah dibaca'
: 'Belum dibaca'}
</td>
<td className="py-4 px-6 flex justify-end items-center space-x-2">
<HiPencilAlt />
<div
className="text-gray-600 hover:underline"
onClick={() =>
handleNotification(
notification
)
}
>
Tandai dibaca
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="w-full flex items-center justify-center">
<Pagination links={links} />
</div>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
)
}

@ -10,6 +10,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import ModalConfirm from '@/Components/ModalConfirm'
import SearchInput from '@/Components/SearchInput'
import LocationSelectionInput from '../Location/SelectionInput'
import { hasPermission } from '@/utils'
export default function Index(props) {
@ -18,8 +19,9 @@ export default function Index(props) {
auth,
} = props
const [location, setLocation] = useState(null)
const [search, setSearch] = useState('')
const preValue = usePrevious(search)
const preValue = usePrevious(`${search}${location}`)
const confirmModal = useModalState()
@ -34,19 +36,19 @@ export default function Index(props) {
}
}
const params = { q: search }
const params = { q: search, location_id: location }
useEffect(() => {
if (preValue) {
router.get(
route(route().current()),
{ q: search },
{ q: search, location_id: location },
{
replace: true,
preserveState: true,
}
)
}
}, [search])
}, [search, location])
const canCreate = hasPermission(auth, 'create-voucher')
const canUpdate = hasPermission(auth, 'update-voucher')
@ -78,7 +80,12 @@ export default function Index(props) {
</Link>
</div>
)}
<div className="flex items-center">
<div className="flex flex-row space-x-2 items-center">
<LocationSelectionInput
itemSelected={location}
onItemSelected={(id) => setLocation(id)}
placeholder={'filter lokasi'}
/>
<SearchInput
onChange={(e) => setSearch(e.target.value)}
value={search}

@ -1,6 +1,7 @@
<?php
use App\Http\Controllers\AccountController;
use App\Http\Controllers\NotificationController;
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\BannerController;
use App\Http\Controllers\CoinRewardController;
@ -127,5 +128,8 @@ Route::middleware(['http_secure_aware', 'inertia.admin'])
// sale
Route::get('/sales', [SaleController::class, 'index'])->name('sale.index');
Route::get('/sales/{sale}', [SaleController::class, 'show'])->name('sale.show');
// notification
Route::get('/notifications', [NotificationController::class, 'index'])->name('notifications.index');
});
});

@ -4,6 +4,7 @@ use App\Http\Controllers\Api\LocationController;
use App\Http\Controllers\Api\RoleController;
use App\Http\Controllers\Customer\DepositController;
use App\Http\Controllers\Api\CustomerController;
use App\Http\Controllers\Api\NotificationController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
@ -26,6 +27,7 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
Route::get('/roles', [RoleController::class, 'index'])->name('api.role.index');
Route::get('/locations', [LocationController::class, 'index'])->name('api.location.index');
Route::get('/customers', [CustomerController::class, 'index'])->name('api.customer.index');
Route::get('/notifications/{notif?}', [NotificationController::class, 'update'])->name('api.notification.update');
// midtrans
Route::post('mindtrans/notification', [DepositController::class, 'mindtrans_notification'])->name('api.midtrans.notification');

@ -63,6 +63,9 @@ Route::middleware(['http_secure_aware', 'guard_should_customer', 'inertia.custom
// 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');
});
Route::middleware('guest:customer')->group(function () {

Loading…
Cancel
Save