parent
commit
86398614b9

+ 12
- 1
app/Http/Controllers/DashboardController.php Vedi File

@@ -2,6 +2,9 @@
2 2
 
3 3
 namespace App\Http\Controllers;
4 4
 
5
+use App\Services\ProductService;
6
+use App\Services\PurchaseService;
7
+use App\Services\SaleService;
5 8
 use Illuminate\Http\Request;
6 9
 
7 10
 class DashboardController extends Controller
@@ -14,6 +17,14 @@ class DashboardController extends Controller
14 17
      */
15 18
     public function __invoke(Request $request)
16 19
     {
17
-        return inertia("Dashboards/Index");
20
+        return inertia("Dashboards/Index", [
21
+            "productCounts" => [
22
+                SaleService::saleAmount(),
23
+                PurchaseService::purchaseAmount(),
24
+                ProductService::productAmount(),
25
+                ProductService::stockProductAmount(),
26
+            ],
27
+            "productFavorites" => SaleService::bestSelling(),
28
+        ]);
18 29
     }
19 30
 }

+ 11
- 0
app/Services/ProductService.php Vedi File

@@ -3,6 +3,7 @@
3 3
 namespace App\Services;
4 4
 
5 5
 use App\Models\Product;
6
+use Illuminate\Support\Facades\DB;
6 7
 
7 8
 class ProductService
8 9
 {
@@ -12,4 +13,14 @@ class ProductService
12 13
             $product->stockProducts()->exists() ||
13 14
             $product->purchaseDetails()->exists();
14 15
     }
16
+
17
+    public static function productAmount()
18
+    {
19
+        return QueryService::queryAmount("products", "Produk");
20
+    }
21
+
22
+    public static function stockProductAmount()
23
+    {
24
+        return QueryService::queryAmount("stock_products", "Stok Produk");
25
+    }
15 26
 }

+ 6
- 0
app/Services/PurchaseService.php Vedi File

@@ -3,6 +3,7 @@
3 3
 namespace App\Services;
4 4
 
5 5
 use App\Models\Purchase;
6
+use Illuminate\Support\Facades\DB;
6 7
 
7 8
 class PurchaseService
8 9
 {
@@ -12,4 +13,9 @@ class PurchaseService
12 13
             return $purchaseDetail->price * $purchaseDetail->qty;
13 14
         });
14 15
     }
16
+
17
+    public static function purchaseAmount()
18
+    {
19
+        return QueryService::queryAmount("purchases", "Pembelian");
20
+    }
15 21
 }

+ 26
- 0
app/Services/QueryService.php Vedi File

@@ -0,0 +1,26 @@
1
+<?php
2
+
3
+namespace App\Services;
4
+
5
+use Illuminate\Support\Facades\DB;
6
+
7
+class QueryService
8
+{
9
+    public static function queryAmount(string $table, string $title)
10
+    {
11
+        return DB::table($table)
12
+            ->selectRaw(
13
+                "COUNT(*) as amount,
14
+                (SELECT COUNT(*) FROM products WHERE DATE(updated_at) = CURDATE()) as amountToday"
15
+            )
16
+            ->get()
17
+            ->transform(
18
+                fn($sale) => [
19
+                    "title" => $title,
20
+                    "amount" => $sale->amount,
21
+                    "amountToday" => $sale->amountToday,
22
+                ]
23
+            )
24
+            ->first();
25
+    }
26
+}

+ 32
- 2
app/Services/SaleService.php Vedi File

@@ -2,9 +2,8 @@
2 2
 
3 3
 namespace App\Services;
4 4
 
5
-use App\Models\Ppn;
6 5
 use App\Models\Sale;
7
-use App\Services\HelperService;
6
+use Illuminate\Support\Facades\DB;
8 7
 
9 8
 class SaleService
10 9
 {
@@ -14,4 +13,35 @@ class SaleService
14 13
             return $saleDetail->price * $saleDetail->qty;
15 14
         });
16 15
     }
16
+
17
+    public static function saleAmount()
18
+    {
19
+        return QueryService::queryAmount("sales", "Penjualan");
20
+    }
21
+
22
+    public static function bestSelling()
23
+    {
24
+        return DB::table("sale_details")
25
+            ->selectRaw(
26
+                "product_number, products.name as title, SUM(qty) as qty, products.profit"
27
+            )
28
+            ->join(
29
+                "products",
30
+                "products.number",
31
+                "=",
32
+                "sale_details.product_number"
33
+            )
34
+            ->groupByRaw("product_number")
35
+            ->orderByRaw("qty DESC")
36
+            ->limit(5)
37
+            ->get();
38
+    }
39
+
40
+    public static function saleStatistic()
41
+    {
42
+        return DB::table("sales")
43
+            ->selectRaw("COUNT(*) as amount")
44
+            ->groupByRaw("MONTH(sales.created_at)")
45
+            ->get();
46
+    }
17 47
 }

+ 1
- 1
config/database.php Vedi File

@@ -56,7 +56,7 @@ return [
56 56
             'collation' => 'utf8mb4_unicode_ci',
57 57
             'prefix' => '',
58 58
             'prefix_indexes' => true,
59
-            'strict' => true,
59
+            'strict' => false,
60 60
             'engine' => null,
61 61
             'options' => extension_loaded('pdo_mysql') ? array_filter([
62 62
                 PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),

+ 35
- 0
resources/js/pages/Dashboards/Components/CardCount.vue Vedi File

@@ -0,0 +1,35 @@
1
+<script setup>
2
+defineProps({
3
+  products: {
4
+    required: true,
5
+    type: Array,
6
+  },
7
+})
8
+</script>
9
+
10
+<template>
11
+  <div v-for="product in products" class="col-12 lg:col-6 xl:col-3">
12
+    <div class="card mb-0">
13
+      <div class="flex justify-content-between mb-3">
14
+        <div>
15
+          <span class="block text-500 font-medium mb-3">{{
16
+            product.title
17
+          }}</span>
18
+          <div class="text-900 font-medium text-xl">
19
+            {{ product.amount }}
20
+          </div>
21
+        </div>
22
+        <div
23
+          class="flex align-items-center justify-content-center bg-blue-100 border-round"
24
+          style="width: 2.5rem; height: 2.5rem"
25
+        >
26
+          <i class="pi pi-th-large text-blue-500 text-xl"></i>
27
+        </div>
28
+      </div>
29
+      <span class="text-green-500 font-medium"
30
+        >{{ product.amountToday }} {{ product.title.toLowerCase() }}
31
+      </span>
32
+      <span class="text-500"> hari ini</span>
33
+    </div>
34
+  </div>
35
+</template>

+ 45
- 0
resources/js/pages/Dashboards/Components/CardProductFavorite.vue Vedi File

@@ -0,0 +1,45 @@
1
+<script setup>
2
+defineProps({
3
+  products: {
4
+    required: true,
5
+    type: Array,
6
+  },
7
+})
8
+</script>
9
+
10
+<template>
11
+  <div class="card">
12
+    <div class="flex justify-content-between align-items-center">
13
+      <h5>Produk Terlaris</h5>
14
+    </div>
15
+
16
+    <ul class="list-none p-0 m-0">
17
+      <li
18
+        v-for="product in products"
19
+        class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
20
+      >
21
+        <span class="text-900 font-medium mr-2 mb-1 md:mb-0">{{
22
+          product.title
23
+        }}</span>
24
+
25
+        <div class="mt-2 md:mt-0 flex flex-column align-items-end">
26
+          <div class="mt-1 text-600 text-xs">Profit</div>
27
+
28
+          <div
29
+            class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
30
+            style="height: 8px"
31
+          >
32
+            <div
33
+              class="bg-orange-500 h-full"
34
+              :style="{ width: `${product.profit}% ` }"
35
+            ></div>
36
+          </div>
37
+
38
+          <span class="text-orange-500 ml-3 font-medium"
39
+            >%{{ product.profit }}</span
40
+          >
41
+        </div>
42
+      </li>
43
+    </ul>
44
+  </div>
45
+</template>

+ 49
- 218
resources/js/pages/Dashboards/Index.vue Vedi File

@@ -1,8 +1,29 @@
1 1
 <script setup>
2
+import CardCount from './Components/CardCount.vue'
3
+import CardProductFavorite from './Components/CardProductFavorite.vue'
2 4
 import DashboardLayout from '@/layouts/Dashboard/DashboardLayout.vue'
3 5
 
4
-const lineData = {
5
-  labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
6
+const props = defineProps([
7
+  'productCounts',
8
+  'productFavorites',
9
+  'saleStatistic',
10
+])
11
+
12
+const purchaseAndSale = {
13
+  labels: [
14
+    'Jan',
15
+    'Feb',
16
+    'Mar',
17
+    'Apr',
18
+    'May',
19
+    'Jun',
20
+    'Jul',
21
+    'Aug',
22
+    'Sep',
23
+    'Oct',
24
+    'Nov',
25
+    'Dec',
26
+  ],
6 27
   datasets: [
7 28
     {
8 29
       label: 'Pembelian',
@@ -24,7 +45,20 @@ const lineData = {
24 45
 }
25 46
 
26 47
 const barData = {
27
-  labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
48
+  labels: [
49
+    'Jan',
50
+    'Feb',
51
+    'Mar',
52
+    'Apr',
53
+    'May',
54
+    'Jun',
55
+    'Jul',
56
+    'Aug',
57
+    'Sep',
58
+    'Oct',
59
+    'Nov',
60
+    'Dec',
61
+  ],
28 62
   datasets: [
29 63
     {
30 64
       label: 'Bulan lalu',
@@ -52,6 +86,8 @@ const barData = {
52 86
 }
53 87
 
54 88
 const lineOptions = null
89
+
90
+const productCounts = props.productCounts
55 91
 </script>
56 92
 
57 93
 <template>
@@ -59,83 +95,7 @@ const lineOptions = null
59 95
     <div class="grid">
60 96
       <div class="col-12">
61 97
         <div class="grid">
62
-          <div class="col-12 lg:col-6 xl:col-3">
63
-            <div class="card mb-0">
64
-              <div class="flex justify-content-between mb-3">
65
-                <div>
66
-                  <span class="block text-500 font-medium mb-3">Produk</span>
67
-                  <div class="text-900 font-medium text-xl">152</div>
68
-                </div>
69
-                <div
70
-                  class="flex align-items-center justify-content-center bg-blue-100 border-round"
71
-                  style="width: 2.5rem; height: 2.5rem"
72
-                >
73
-                  <i class="pi pi-th-large text-blue-500 text-xl"></i>
74
-                </div>
75
-              </div>
76
-              <span class="text-green-500 font-medium">24 produk </span>
77
-              <span class="text-500">hari ini</span>
78
-            </div>
79
-          </div>
80
-
81
-          <div class="col-12 lg:col-6 xl:col-3">
82
-            <div class="card mb-0">
83
-              <div class="flex justify-content-between mb-3">
84
-                <div>
85
-                  <span class="block text-500 font-medium mb-3">Pembelian</span>
86
-                  <div class="text-900 font-medium text-xl">284</div>
87
-                </div>
88
-                <div
89
-                  class="flex align-items-center justify-content-center bg-yellow-100 border-round"
90
-                  style="width: 2.5rem; height: 2.5rem"
91
-                >
92
-                  <i class="pi pi-shopping-cart text-yellow-500 text-xl"></i>
93
-                </div>
94
-              </div>
95
-              <span class="text-green-500 font-medium">28 pembelian </span>
96
-              <span class="text-500">hari ini</span>
97
-            </div>
98
-          </div>
99
-
100
-          <div class="col-12 lg:col-6 xl:col-3">
101
-            <div class="card mb-0">
102
-              <div class="flex justify-content-between mb-3">
103
-                <div>
104
-                  <span class="block text-500 font-medium mb-3">Penjualan</span>
105
-                  <div class="text-900 font-medium text-xl">284</div>
106
-                </div>
107
-                <div
108
-                  class="flex align-items-center justify-content-center bg-purple-100 border-round"
109
-                  style="width: 2.5rem; height: 2.5rem"
110
-                >
111
-                  <i class="pi pi-shopping-cart text-purple-500 text-xl"></i>
112
-                </div>
113
-              </div>
114
-              <span class="text-green-500 font-medium">8 penjualan </span>
115
-              <span class="text-500">hari ini</span>
116
-            </div>
117
-          </div>
118
-
119
-          <div class="col-12 lg:col-6 xl:col-3">
120
-            <div class="card mb-0">
121
-              <div class="flex justify-content-between mb-3">
122
-                <div>
123
-                  <span class="block text-500 font-medium mb-3"
124
-                    >Stok Barang</span
125
-                  >
126
-                  <div class="text-900 font-medium text-xl">2848</div>
127
-                </div>
128
-                <div
129
-                  class="flex align-items-center justify-content-center bg-orange-100 border-round"
130
-                  style="width: 2.5rem; height: 2.5rem"
131
-                >
132
-                  <i class="pi pi-box text-orange-500 text-xl"></i>
133
-                </div>
134
-              </div>
135
-              <span class="text-green-500 font-medium">0 barang baru </span>
136
-              <span class="text-500">hari ini</span>
137
-            </div>
138
-          </div>
98
+          <CardCount :products="productCounts" />
139 99
         </div>
140 100
       </div>
141 101
 
@@ -144,148 +104,17 @@ const lineOptions = null
144 104
           <div class="col-12">
145 105
             <div class="card">
146 106
               <h5>Pembelian dan Penjualan</h5>
147
-              <Chart type="line" :data="lineData" :options="lineOptions" />
107
+
108
+              <Chart
109
+                type="line"
110
+                :data="purchaseAndSale"
111
+                :options="lineOptions"
112
+              />
148 113
             </div>
149 114
           </div>
150 115
 
151 116
           <div class="col-12">
152
-            <div class="card">
153
-              <div class="flex justify-content-between align-items-center mb-5">
154
-                <h5>Produk Terlaris</h5>
155
-              </div>
156
-              <ul class="list-none p-0 m-0">
157
-                <li
158
-                  class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
159
-                >
160
-                  <div>
161
-                    <span class="text-900 font-medium mr-2 mb-1 md:mb-0"
162
-                      >Space T-Shirt</span
163
-                    >
164
-                    <div class="mt-1 text-600">Clothing</div>
165
-                  </div>
166
-                  <div class="mt-2 md:mt-0 flex align-items-center">
167
-                    <div
168
-                      class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
169
-                      style="height: 8px"
170
-                    >
171
-                      <div
172
-                        class="bg-orange-500 h-full"
173
-                        style="width: 50%"
174
-                      ></div>
175
-                    </div>
176
-                    <span class="text-orange-500 ml-3 font-medium">%50</span>
177
-                  </div>
178
-                </li>
179
-                <li
180
-                  class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
181
-                >
182
-                  <div>
183
-                    <span class="text-900 font-medium mr-2 mb-1 md:mb-0"
184
-                      >Portal Sticker</span
185
-                    >
186
-                    <div class="mt-1 text-600">Accessories</div>
187
-                  </div>
188
-                  <div
189
-                    class="mt-2 md:mt-0 ml-0 md:ml-8 flex align-items-center"
190
-                  >
191
-                    <div
192
-                      class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
193
-                      style="height: 8px"
194
-                    >
195
-                      <div class="bg-cyan-500 h-full" style="width: 16%"></div>
196
-                    </div>
197
-                    <span class="text-cyan-500 ml-3 font-medium">%16</span>
198
-                  </div>
199
-                </li>
200
-                <li
201
-                  class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
202
-                >
203
-                  <div>
204
-                    <span class="text-900 font-medium mr-2 mb-1 md:mb-0"
205
-                      >Supernova Sticker</span
206
-                    >
207
-                    <div class="mt-1 text-600">Accessories</div>
208
-                  </div>
209
-                  <div
210
-                    class="mt-2 md:mt-0 ml-0 md:ml-8 flex align-items-center"
211
-                  >
212
-                    <div
213
-                      class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
214
-                      style="height: 8px"
215
-                    >
216
-                      <div class="bg-pink-500 h-full" style="width: 67%"></div>
217
-                    </div>
218
-                    <span class="text-pink-500 ml-3 font-medium">%67</span>
219
-                  </div>
220
-                </li>
221
-                <li
222
-                  class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
223
-                >
224
-                  <div>
225
-                    <span class="text-900 font-medium mr-2 mb-1 md:mb-0"
226
-                      >Wonders Notebook</span
227
-                    >
228
-                    <div class="mt-1 text-600">Office</div>
229
-                  </div>
230
-                  <div
231
-                    class="mt-2 md:mt-0 ml-0 md:ml-8 flex align-items-center"
232
-                  >
233
-                    <div
234
-                      class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
235
-                      style="height: 8px"
236
-                    >
237
-                      <div class="bg-green-500 h-full" style="width: 35%"></div>
238
-                    </div>
239
-                    <span class="text-green-500 ml-3 font-medium">%35</span>
240
-                  </div>
241
-                </li>
242
-                <li
243
-                  class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
244
-                >
245
-                  <div>
246
-                    <span class="text-900 font-medium mr-2 mb-1 md:mb-0"
247
-                      >Mat Black Case</span
248
-                    >
249
-                    <div class="mt-1 text-600">Accessories</div>
250
-                  </div>
251
-                  <div
252
-                    class="mt-2 md:mt-0 ml-0 md:ml-8 flex align-items-center"
253
-                  >
254
-                    <div
255
-                      class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
256
-                      style="height: 8px"
257
-                    >
258
-                      <div
259
-                        class="bg-purple-500 h-full"
260
-                        style="width: 75%"
261
-                      ></div>
262
-                    </div>
263
-                    <span class="text-purple-500 ml-3 font-medium">%75</span>
264
-                  </div>
265
-                </li>
266
-                <li
267
-                  class="flex flex-column md:flex-row md:align-items-center md:justify-content-between mb-4"
268
-                >
269
-                  <div>
270
-                    <span class="text-900 font-medium mr-2 mb-1 md:mb-0"
271
-                      >Robots T-Shirt</span
272
-                    >
273
-                    <div class="mt-1 text-600">Clothing</div>
274
-                  </div>
275
-                  <div
276
-                    class="mt-2 md:mt-0 ml-0 md:ml-8 flex align-items-center"
277
-                  >
278
-                    <div
279
-                      class="surface-300 border-round overflow-hidden w-10rem lg:w-6rem"
280
-                      style="height: 8px"
281
-                    >
282
-                      <div class="bg-teal-500 h-full" style="width: 40%"></div>
283
-                    </div>
284
-                    <span class="text-teal-500 ml-3 font-medium">%40</span>
285
-                  </div>
286
-                </li>
287
-              </ul>
288
-            </div>
117
+            <CardProductFavorite :products="productFavorites" />
289 118
           </div>
290 119
         </div>
291 120
       </div>
@@ -295,6 +124,7 @@ const lineOptions = null
295 124
           <div class="col-12">
296 125
             <div class="card">
297 126
               <h5>Pembelian</h5>
127
+
298 128
               <Chart type="bar" :data="barData" :options="lineOptions" />
299 129
             </div>
300 130
           </div>
@@ -302,6 +132,7 @@ const lineOptions = null
302 132
           <div class="col-12">
303 133
             <div class="card">
304 134
               <h5>Pendapatan</h5>
135
+
305 136
               <Chart type="bar" :data="barData" :options="lineOptions" />
306 137
             </div>
307 138
           </div>