update dashboard

dev
ajikamaludin 1 year ago
parent 9a17047a2b
commit 7a01a60b20
Signed by: ajikamaludin
GPG Key ID: 476C9A2B4B794EBB

@ -30,10 +30,24 @@ class GeneralController extends Controller
}
$charts = Sale::selectRaw('SUM(total) as stotal, date')
->whereBetween('date', [$startDate, $endDate])
->orderBy('date', 'asc')
->groupBy('date')
->get();
->whereBetween('date', [$startDate, $endDate])
->orderBy('date', 'asc')
->groupBy('date')
->get();
// $dounat = SaleItem::selectRaw('product_id, SUM(quantity) as qty')
// ->with('product.category')
// ->join('products', 'products.id', '=', 'sale_items.product_id')
// ->groupBy('sale_items.product_id')
// ->get();
$dounat = SaleItem::selectRaw('product_id, SUM(quantity) as qty')
->with('product.category')
->join('products', 'products.id', '=', 'sale_items.product_id')
->join('sales', 'sales.id', '=', 'sale_items.sale_id')
->whereBetween('sales.date', [now()->startOfMonth()->format('m/d/Y'), now()->endOfMonth()->format('m/d/Y')])
->groupBy('sale_items.product_id')
->get();
$favoriteProducts = SaleItem::selectRaw('product_id, sum(quantity) as qty')
->groupBy('product_id')
@ -54,6 +68,7 @@ class GeneralController extends Controller
'total_product' => $totalProduct,
'total_customer' => $totalCustomer,
'sale_days' => $charts,
'favorite_categories' => $dounat,
'list_favorite_product' => $favoriteProducts,
'list_customer' => $transactionCustomers
]);

@ -17,7 +17,7 @@ services:
- "/etc/localtime:/etc/localtime:ro"
mem_limit: 512m
mem_reservation: 128M
# cpus: 0.5
cpus: 0.5
networks:
- simplepos
nginx:

@ -1,7 +1,7 @@
import React from 'react';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head } from '@inertiajs/react';
import { formatIDR } from '@/utils';
import React from 'react'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import { Head } from '@inertiajs/react'
import { formatIDR } from '@/utils'
import {
Chart as ChartJS,
CategoryScale,
@ -9,25 +9,41 @@ import {
BarElement,
Title,
Tooltip,
ArcElement,
Legend,
} from 'chart.js'
import { Bar } from 'react-chartjs-2'
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip)
import { Bar, Doughnut } from 'react-chartjs-2'
import moment from 'moment'
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
ArcElement,
Legend
)
export default function Dashboard(props) {
const {
total_sale_today, total_item_today, total_product, total_customer,
const {
total_sale_today,
total_item_today,
total_product,
total_customer,
sale_days,
list_favorite_product,
list_customer
list_customer,
favorite_categories,
} = props
const options = {
responsive: true,
};
}
const data = {
labels: sale_days.map((item) => item.date),
labels: sale_days.map((item) =>
moment(item.date).format('DD MMM YYYY')
),
datasets: [
{
label: 'Penjualan',
@ -40,12 +56,71 @@ export default function Dashboard(props) {
'rgba(75, 192, 192, 1)',
'rgba(54, 162, 235, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 99, 132, 1)'
'rgba(255, 99, 132, 1)',
],
},
],
}
const names = favorite_categories.map((c) => `${c.product.name} - ${c.qty}`)
const count = favorite_categories.map((c) => c.qty)
const dataDounat = {
labels: names,
datasets: [
{
label: '# Jumlah',
data: count,
backgroundColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)',
'#b91c1c',
'#c2410c',
'#b45309',
'#15803d',
'#047857',
'#0f766e',
'#0369a1',
'#1d4ed8',
'#6d28d9',
'#a21caf',
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)',
'#b91c1c',
'#c2410c',
'#b45309',
'#15803d',
'#047857',
'#0f766e',
'#0369a1',
'#1d4ed8',
'#6d28d9',
'#a21caf',
],
borderWidth: 1,
},
],
}
const optionsDounat = {
responsive: true,
plugins: {
legend: {
display: true,
position: 'right',
},
},
}
return (
<AuthenticatedLayout
auth={props.auth}
@ -58,36 +133,63 @@ export default function Dashboard(props) {
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className='px-2 w-full grid grid-cols-2 md:grid-cols-4 gap-2'>
<div className="px-2 w-full grid grid-cols-2 md:grid-cols-4 gap-2">
<div className="p-4 overflow-hidden shadow sm:rounded-lg bg-white">
<div className="text-xl">Total Penjualan <br/>Hari Ini</div>
<div className='text-3xl font-bold'>{total_sale_today}</div>
<div className="text-xl">
Total Penjualan <br />
Hari Ini
</div>
<div className="text-3xl font-bold">
{total_sale_today}
</div>
</div>
<div className="p-4 overflow-hidden shadow sm:rounded-lg bg-white">
<div className="text-xl">Total Barang Terjual <br/>Hari Ini</div>
<div className='text-3xl font-bold'>{total_item_today}</div>
<div className="text-xl">
Total Barang Terjual <br />
Hari Ini
</div>
<div className="text-3xl font-bold">
{total_item_today}
</div>
</div>
<div className="p-4 overflow-hidden shadow sm:rounded-lg bg-white">
<div className="text-xl">Jumlah Barang</div>
<div className='text-3xl font-bold'>{total_product}</div>
<div className="text-3xl font-bold">
{total_product}
</div>
</div>
<div className="p-4 overflow-hidden shadow sm:rounded-lg bg-white">
<div className="text-xl">Jumlah Pelanggan</div>
<div className='text-3xl font-bold'>{total_customer}</div>
<div className="text-3xl font-bold">
{total_customer}
</div>
</div>
</div>
{/* Chart : jumlah transaksi 7 hari terkahir */}
<div className="overflow-auto bg-white p-4 mt-4">
<div className='text-xl pb-4'>
Penjualan 7 Hari Terakhir
<div className="w-full flex flex-row mt-4 space-x-2">
<div className="flex-1 overflow-auto bg-white p-4">
<div className="text-xl pb-4">
Penjualan 7 Hari Terakhir
</div>
<Bar
options={options}
data={data}
className="max-h-96"
/>
</div>
<div className="overflow-auto bg-white p-4">
<div className="text-xl pb-4">
Top Product of The Month
</div>
<Doughnut
data={dataDounat}
options={optionsDounat}
/>
</div>
<Bar options={options} data={data} className='max-h-96' />
</div>
{/* list produk paling laris dengan jumlah penjualan */}
<div className='overflow-auto bg-white p-4 mt-4'>
<div className='text-xl pb-4'>
Barang Terlaris
</div>
<div className="overflow-auto bg-white p-4 mt-4">
<div className="text-xl pb-4">Barang Terlaris</div>
<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">
@ -104,9 +206,15 @@ export default function Dashboard(props) {
</tr>
</thead>
<tbody>
{list_favorite_product.map(product => (
<tr className="bg-white border-b dark:bg-gray-800 dark:border-gray-700" key={product.product_id}>
<td scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{list_favorite_product.map((product) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={product.product_id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{product.product.code}
</td>
<td className="py-4 px-6">
@ -122,10 +230,8 @@ export default function Dashboard(props) {
</div>
</div>
{/* list customer yang bertransaksi dengan total transaksi */}
<div className='overflow-auto bg-white p-4 mt-4'>
<div className='text-xl pb-4'>
Pelanggan Hari Ini
</div>
<div className="overflow-auto bg-white p-4 mt-4">
<div className="text-xl pb-4">Pelanggan Hari Ini</div>
<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">
@ -139,10 +245,18 @@ export default function Dashboard(props) {
</tr>
</thead>
<tbody>
{list_customer.map(customer => (
<tr className="bg-white border-b dark:bg-gray-800 dark:border-gray-700" key={customer.customer_id}>
<td scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{customer.customer !== null ? customer.customer.name : 'Umum'}
{list_customer.map((customer) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={customer.customer_id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{customer.customer !== null
? customer.customer.name
: 'Umum'}
</td>
<td className="py-4 px-6">
{formatIDR(customer.stotal)}
@ -155,7 +269,6 @@ export default function Dashboard(props) {
</div>
</div>
</div>
</AuthenticatedLayout>
);
)
}

@ -1,20 +1,23 @@
import React, { useEffect, useState } from 'react';
import { Head, Link, router, useForm } from '@inertiajs/react';
import { usePrevious } from 'react-use';
import { HiXCircle } from 'react-icons/hi';
import React, { useEffect, useState } from 'react'
import { Head, Link, router, useForm } from '@inertiajs/react'
import { usePrevious } from 'react-use'
import { HiXCircle } from 'react-icons/hi'
import { dateToString, formatIDR } from '@/utils';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import SearchInput from '@/Components/SearchInput';
import Button from '@/Components/Button';
import FormInputDate from '@/Components/FormInputDate';
import Pagination from '@/Components/Pagination';
import FormInput from '@/Components/FormInput';
import CustomerSelectionInput from '../Customer/SelectionInput';
import { Spinner } from 'flowbite-react';
import { dateToString, formatIDR } from '@/utils'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import SearchInput from '@/Components/SearchInput'
import Button from '@/Components/Button'
import FormInputDate from '@/Components/FormInputDate'
import Pagination from '@/Components/Pagination'
import FormInput from '@/Components/FormInput'
import CustomerSelectionInput from '../Customer/SelectionInput'
import { Spinner } from 'flowbite-react'
export default function Sale(props) {
const { _products: { data: products, links}, _page } = props
const {
_products: { data: products, links },
_page,
} = props
const [loading, setLoading] = useState(false)
const [search, setSearch] = useState('')
@ -23,34 +26,43 @@ export default function Sale(props) {
const { data, setData, post, processing, errors } = useForm({
date: dateToString(new Date()),
customer_id: null,
items: []
items: [],
})
const addItem = (product) => {
const isExist = data.items.find(item => item.id === product.id)
const isExist = data.items.find((item) => item.id === product.id)
if (isExist) {
return
}
setData('items', data.items.concat({
...product,
qty: 1
}))
setData(
'items',
data.items.concat({
...product,
qty: 1,
})
)
}
const removeItem = (id) => {
setData('items', data.items.filter(item => item.id !== id))
setData(
'items',
data.items.filter((item) => item.id !== id)
)
}
const setQuantityItem = (id, qty) => {
setData('items', data.items.map(item => {
if (item.id === id) {
return {
...item,
qty: qty,
setData(
'items',
data.items.map((item) => {
if (item.id === id) {
return {
...item,
qty: qty,
}
}
}
return item
}))
return item
})
)
}
const handleSubmit = () => {
@ -67,16 +79,18 @@ export default function Sale(props) {
{
replace: true,
preserveState: true,
onSuccess: () => {
setLoading(false)
onSuccess: () => {
setLoading(false)
},
}
)
}
}, [search])
console.log(data)
const total = data.items.reduce((amt, item) => amt + (+item.qty * +item.price), 0)
const total = data.items.reduce(
(amt, item) => amt + +item.qty * +item.price,
0
)
return (
<AuthenticatedLayout
@ -90,26 +104,26 @@ export default function Sale(props) {
<div className="mx-auto sm:px-6 lg:px-8 w-full">
<div className="flex flex-row p-6 shadow-sm sm:rounded-lg bg-white w-full space-x-2">
<div className='w-full md:w-7/12'>
<div className='mb-4'>
<div className="w-full md:w-7/12">
<div className="mb-4">
<SearchInput
onChange={e => setSearch(e.target.value)}
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
</div>
{loading ? (
<div className='w-full flex flex-row justify-center mt-28'>
<Spinner size="xl"/>
<div className="w-full flex flex-row justify-center mt-28">
<Spinner size="xl" />
</div>
) : (
<div className='grid grid-cols-4 gap-2 text-center'>
{products.map(item => (
<div
className='rounded bg-gray-300 hover:bg-gray-200 shadow-lg px-4 py-2 flex flex-col justify-between'
<div className="grid grid-cols-4 gap-2 text-center">
{products.map((item) => (
<div
className="rounded bg-gray-300 hover:bg-gray-200 shadow-lg px-4 py-2 flex flex-col justify-between"
key={item.id}
onClick={() => addItem(item)}
>
<div className='font-bold'>
<div className="font-bold">
{item.name}
</div>
<div className="rounded-md bg-gray-800 p-0.5 text-white">
@ -119,13 +133,13 @@ export default function Sale(props) {
))}
</div>
)}
<div className='w-full mt-4 justify-center'>
<div className='mx-auto w-fit'>
<Pagination links={links} params={params}/>
<div className="w-full mt-4 justify-center">
<div className="mx-auto w-fit">
<Pagination links={links} params={params} />
</div>
</div>
</div>
<div className='w-full md:w-5/12 flex flex-col'>
<div className="w-full md:w-5/12 flex flex-col">
<CustomerSelectionInput
placeholder="Pelanggan"
itemSelected={data.customer_id}
@ -134,58 +148,58 @@ export default function Sale(props) {
/>
<FormInputDate
selected={data.date}
onChange={(date) => setData("date", date)}
placeholder='Tanggal'
onChange={(date) => setData('date', date)}
placeholder="Tanggal"
error={errors.date}
/>
<div className='my-4 h-[350px] overflow-y-scroll'>
<div className='flex flex-row justify-between space-x-2 space-y-2 rounded-md shadow items-center p-2'>
<div className='w-2/3'>
Nama
</div>
<div className='w-1/3'>
Jumlah
</div>
<div className=''>
Subtotal
</div>
<div className='text-transparent'>
<div className='h-5 w-5'></div>
<div className="my-4 h-[350px] overflow-y-scroll">
<div className="flex flex-row justify-between space-x-2 space-y-2 rounded-md shadow items-center p-2">
<div className="w-2/3">Nama</div>
<div className="w-1/3">Jumlah</div>
<div className="">Subtotal</div>
<div className="text-transparent">
<div className="h-5 w-5"></div>
</div>
</div>
{data.items.map(item => (
<div
className='flex flex-row justify-between space-x-2 space-y-2 rounded-md shadow items-center p-2'
{data.items.map((item) => (
<div
className="flex flex-row justify-between space-x-2 space-y-2 rounded-md shadow items-center p-2"
key={item.id}
>
<div className='w-2/3'>
{item.name}
</div>
<div className='w-1/3 text-right'>
<div className="w-2/3">{item.name}</div>
<div className="w-1/3 text-right">
<FormInput
type="number"
min="1"
value={item.qty}
onChange={(e) => setQuantityItem(item.id, e.target.value)}
onChange={(e) =>
setQuantityItem(
item.id,
e.target.value
)
}
className="text-right"
/>
</div>
<div className='text-right w-1/3'>
<div className="text-right w-1/3">
{formatIDR(item.qty * item.price)}
</div>
<div className='text-red-500' onClick={() => removeItem(item.id)}>
<HiXCircle className='h-5 w-5'/>
<div
className="text-red-500"
onClick={() => removeItem(item.id)}
>
<HiXCircle className="h-5 w-5" />
</div>
</div>
))}
</div>
<div className='w-full flex flex-row justify-between font-bold text-2xl'>
<div className="w-full flex flex-row justify-between font-bold text-2xl">
<div>Total:</div>
<div>{formatIDR(total)}</div>
</div>
<Button
disable={processing}
onClick={e => handleSubmit()}
onClick={(e) => handleSubmit()}
>
Simpan
</Button>
@ -193,5 +207,5 @@ export default function Sale(props) {
</div>
</div>
</AuthenticatedLayout>
);
}
)
}

Loading…
Cancel
Save