diff --git a/app/Http/Controllers/GeneralController.php b/app/Http/Controllers/GeneralController.php index 5f76820..30f3512 100644 --- a/app/Http/Controllers/GeneralController.php +++ b/app/Http/Controllers/GeneralController.php @@ -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 ]); diff --git a/docker-compose.yml b/docker-compose.yml index d2b0437..c40e656 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: diff --git a/resources/js/Pages/Dashboard.jsx b/resources/js/Pages/Dashboard.jsx index 8aff3c4..786eb8e 100644 --- a/resources/js/Pages/Dashboard.jsx +++ b/resources/js/Pages/Dashboard.jsx @@ -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 (
-
+
-
Total Penjualan
Hari Ini
-
{total_sale_today}
+
+ Total Penjualan
+ Hari Ini +
+
+ {total_sale_today} +
-
Total Barang Terjual
Hari Ini
-
{total_item_today}
+
+ Total Barang Terjual
+ Hari Ini +
+
+ {total_item_today} +
Jumlah Barang
-
{total_product}
+
+ {total_product} +
Jumlah Pelanggan
-
{total_customer}
+
+ {total_customer} +
{/* Chart : jumlah transaksi 7 hari terkahir */} -
-
- Penjualan 7 Hari Terakhir +
+
+
+ Penjualan 7 Hari Terakhir +
+ +
+
+
+ Top Product of The Month +
+
-
{/* list produk paling laris dengan jumlah penjualan */} -
-
- Barang Terlaris -
+
+
Barang Terlaris
@@ -104,9 +206,15 @@ export default function Dashboard(props) { - {list_favorite_product.map(product => ( - - +
+ {list_favorite_product.map((product) => ( +
{product.product.code} @@ -122,10 +230,8 @@ export default function Dashboard(props) { {/* list customer yang bertransaksi dengan total transaksi */} -
-
- Pelanggan Hari Ini -
+
+
Pelanggan Hari Ini
@@ -139,10 +245,18 @@ export default function Dashboard(props) { - {list_customer.map(customer => ( - - +
- {customer.customer !== null ? customer.customer.name : 'Umum'} + {list_customer.map((customer) => ( +
+ {customer.customer !== null + ? customer.customer.name + : 'Umum'} {formatIDR(customer.stotal)} @@ -155,7 +269,6 @@ export default function Dashboard(props) { - - ); + ) } diff --git a/resources/js/Pages/Sale/Form.jsx b/resources/js/Pages/Sale/Form.jsx index 3e7f9ff..9a3437f 100644 --- a/resources/js/Pages/Sale/Form.jsx +++ b/resources/js/Pages/Sale/Form.jsx @@ -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 (
-
-
+
+
setSearch(e.target.value)} + onChange={(e) => setSearch(e.target.value)} value={search} />
{loading ? ( -
- +
+
) : ( -
- {products.map(item => ( -
+ {products.map((item) => ( +
addItem(item)} > -
+
{item.name}
@@ -119,13 +133,13 @@ export default function Sale(props) { ))}
)} -
-
- +
+
+
-
+
setData("date", date)} - placeholder='Tanggal' + onChange={(date) => setData('date', date)} + placeholder="Tanggal" error={errors.date} /> -
-
-
- Nama -
-
- Jumlah -
-
- Subtotal -
-
-
+
+
+
Nama
+
Jumlah
+
Subtotal
+
+
- {data.items.map(item => ( -
( +
-
- {item.name} -
-
+
{item.name}
+
setQuantityItem(item.id, e.target.value)} + onChange={(e) => + setQuantityItem( + item.id, + e.target.value + ) + } className="text-right" />
-
+
{formatIDR(item.qty * item.price)}
-
removeItem(item.id)}> - +
removeItem(item.id)} + > +
))}
-
+
Total:
{formatIDR(total)}
@@ -193,5 +207,5 @@ export default function Sale(props) {
- ); -} \ No newline at end of file + ) +}