Browse Source

feat: master expense

Muhammad Iqbal Afandi 4 years ago
parent
commit
c7ce474ad8

+ 135
- 0
app/Http/Controllers/ExpenseController.php View File

@@ -0,0 +1,135 @@
1
+<?php
2
+
3
+namespace App\Http\Controllers;
4
+
5
+use App\Http\Controllers\Controller;
6
+use App\Http\Requests\Expense\StoreExpenseRequest;
7
+use App\Models\Expense;
8
+use Illuminate\Database\QueryException;
9
+use Illuminate\Http\Request;
10
+use Illuminate\Support\Facades\DB;
11
+use Inertia\Inertia;
12
+
13
+class ExpenseController extends Controller
14
+{
15
+    /**
16
+     * Display a listing of the resource.
17
+     *
18
+     * @return \Inertia\Response
19
+     */
20
+    public function index()
21
+    {
22
+        return inertia('expense/Index', [
23
+            'filters' => request()->all('startDate', 'endDate', 'outlet'),
24
+            'expenses' => Expense::filter(request()->only('startDate', 'endDate', 'outlet'))
25
+                ->latest()
26
+                ->paginate(10)
27
+                ->withQueryString()
28
+                ->through(fn($expense) => [
29
+                    'id' => $expense->id,
30
+                    'createdAt' => $expense->created_at,
31
+                    'amount' => $expense->amount,
32
+                    'user' => $expense->user->name,
33
+                ]),
34
+        ]);
35
+    }
36
+
37
+    /**
38
+     * Show the form for creating a new resource.
39
+     *
40
+     * @return \Illuminate\Http\Response
41
+     */
42
+    public function create()
43
+    {
44
+        return inertia('expense/Create');
45
+    }
46
+
47
+    /**
48
+     * Store a newly created resource in storage.
49
+     *
50
+     * @param  \Illuminate\Http\Request  $request
51
+     * @return \Illuminate\Http\Response
52
+     */
53
+    public function store(StoreExpenseRequest $request)
54
+    {
55
+        DB::beginTransaction();
56
+
57
+        try {
58
+            $expense = Expense::create([
59
+                'description' => $request->description,
60
+                'amount' => $request->amount,
61
+                'user_id' => $request->user()->id,
62
+            ]);
63
+
64
+            $expense->mutation()->create([
65
+                'type' => 2,
66
+                'amount' => $expense->getRawOriginal('amount'),
67
+            ]);
68
+
69
+            DB::commit();
70
+
71
+            return back()->with('success', __('messages.success.store.expense'));
72
+        } catch (QueryException $e) {
73
+
74
+            DB::rollBack();
75
+
76
+            return back()->with('error', __('messages.error.store.expense'));
77
+        }
78
+    }
79
+
80
+    /**
81
+     * Display the specified resource.
82
+     *
83
+     * @param  Expense  $expense
84
+     * @return \Inertia\Response
85
+     */
86
+    public function show(Expense $expense)
87
+    {
88
+        return inertia('expense/Show', [
89
+            'expense' => [
90
+                'created_at' => $expense->created_at,
91
+                'amount' => $expense->amount,
92
+                'description' => $expense->description,
93
+                'user' => [
94
+                    'name' => $expense->user->name,
95
+                    'phone' => $expense->user->phone,
96
+                    'email' => $expense->user->email,
97
+                ],
98
+            ],
99
+        ]);
100
+    }
101
+
102
+    /**
103
+     * Show the form for editing the specified resource.
104
+     *
105
+     * @param  int  $id
106
+     * @return \Illuminate\Http\Response
107
+     */
108
+    public function edit($id)
109
+    {
110
+        //
111
+    }
112
+
113
+    /**
114
+     * Update the specified resource in storage.
115
+     *
116
+     * @param  \Illuminate\Http\Request  $request
117
+     * @param  int  $id
118
+     * @return \Illuminate\Http\Response
119
+     */
120
+    public function update(Request $request, $id)
121
+    {
122
+        //
123
+    }
124
+
125
+    /**
126
+     * Remove the specified resource from storage.
127
+     *
128
+     * @param  int  $id
129
+     * @return \Illuminate\Http\Response
130
+     */
131
+    public function destroy($id)
132
+    {
133
+        //
134
+    }
135
+}

+ 31
- 0
app/Http/Requests/Expense/StoreExpenseRequest.php View File

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace App\Http\Requests\Expense;
4
+
5
+use Illuminate\Foundation\Http\FormRequest;
6
+
7
+class StoreExpenseRequest extends FormRequest
8
+{
9
+    /**
10
+     * Determine if the user is authorized to make this request.
11
+     *
12
+     * @return bool
13
+     */
14
+    public function authorize()
15
+    {
16
+        return true;
17
+    }
18
+
19
+    /**
20
+     * Get the validation rules that apply to the request.
21
+     *
22
+     * @return array
23
+     */
24
+    public function rules()
25
+    {
26
+        return [
27
+            'description' => 'required',
28
+            'amount' => 'required|numeric',
29
+        ];
30
+    }
31
+}

+ 36
- 0
app/Models/Expense.php View File

@@ -2,6 +2,9 @@
2 2
 
3 3
 namespace App\Models;
4 4
 
5
+use App\Services\CurrencyFormatService;
6
+use Carbon\Carbon;
7
+use Illuminate\Database\Eloquent\Casts\Attribute;
5 8
 use Illuminate\Database\Eloquent\Factories\HasFactory;
6 9
 use Illuminate\Database\Eloquent\Model;
7 10
 
@@ -14,4 +17,37 @@ class Expense extends Model
14 17
         'amount',
15 18
         'user_id',
16 19
     ];
20
+
21
+    protected function createdAt(): Attribute
22
+    {
23
+        return Attribute::make(
24
+            get:fn($value) => Carbon::parse($value)->translatedFormat('l d/m/Y')
25
+        );
26
+    }
27
+
28
+    protected function amount(): Attribute
29
+    {
30
+        return Attribute::make(
31
+            get:fn($value) => (new CurrencyFormatService)->setRupiahFormat($value, true)
32
+        );
33
+    }
34
+
35
+    public function user()
36
+    {
37
+        return $this->belongsTo(User::class);
38
+    }
39
+
40
+    public function mutation()
41
+    {
42
+        return $this->hasOne(Mutation::class);
43
+    }
44
+
45
+    public function scopeFilter($query, array $filters)
46
+    {
47
+        $query->when($filters['startDate'] ?? null, function ($query, $date) {
48
+            $query->whereDate('created_at', '>=', $date);
49
+        })->when($filters['endDate'] ?? null, function ($query, $date) {
50
+            $query->whereDate('created_at', '<=', $date);
51
+        });
52
+    }
17 53
 }

+ 23
- 0
app/Services/CurrencyFormatService.php View File

@@ -0,0 +1,23 @@
1
+<?php
2
+
3
+namespace App\Services;
4
+
5
+class CurrencyFormatService
6
+{
7
+    public function setRupiahFormat(int $number, bool $sign = false)
8
+    {
9
+        if ($sign) {
10
+            if ($number < 0) {
11
+                return '-Rp' . number_format(abs($number), 0, ',', '.');
12
+            } else {
13
+                return 'Rp' . number_format($number, 0, ',', '.');
14
+            }
15
+        } else {
16
+            if ($number < 0) {
17
+                return '-' . number_format(abs($number), 0, ',', '.');
18
+            } else {
19
+                return number_format($number, 0, ',', '.');
20
+            }
21
+        }
22
+    }
23
+}

+ 317
- 0
package-lock.json View File

@@ -20,6 +20,7 @@
20 20
         "primeflex": "^3.1.3",
21 21
         "primeicons": "^5.0.0",
22 22
         "primevue": "^3.12.1",
23
+        "quill": "^1.3.7",
23 24
         "resolve-url-loader": "^5.0.0",
24 25
         "sass": "^1.49.8",
25 26
         "sass-loader": "^12.1.0",
@@ -3112,6 +3113,15 @@
3112 3113
         "wrap-ansi": "^7.0.0"
3113 3114
       }
3114 3115
     },
3116
+    "node_modules/clone": {
3117
+      "version": "2.1.2",
3118
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
3119
+      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
3120
+      "dev": true,
3121
+      "engines": {
3122
+        "node": ">=0.8"
3123
+      }
3124
+    },
3115 3125
     "node_modules/clone-deep": {
3116 3126
       "version": "4.0.1",
3117 3127
       "dev": true,
@@ -3682,6 +3692,23 @@
3682 3692
       "dev": true,
3683 3693
       "license": "MIT"
3684 3694
     },
3695
+    "node_modules/deep-equal": {
3696
+      "version": "1.1.1",
3697
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
3698
+      "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
3699
+      "dev": true,
3700
+      "dependencies": {
3701
+        "is-arguments": "^1.0.4",
3702
+        "is-date-object": "^1.0.1",
3703
+        "is-regex": "^1.0.4",
3704
+        "object-is": "^1.0.1",
3705
+        "object-keys": "^1.1.1",
3706
+        "regexp.prototype.flags": "^1.2.0"
3707
+      },
3708
+      "funding": {
3709
+        "url": "https://github.com/sponsors/ljharb"
3710
+      }
3711
+    },
3685 3712
     "node_modules/deepmerge": {
3686 3713
       "version": "4.2.2",
3687 3714
       "dev": true,
@@ -4118,6 +4145,12 @@
4118 4145
         "node": ">= 0.6"
4119 4146
       }
4120 4147
     },
4148
+    "node_modules/eventemitter3": {
4149
+      "version": "2.0.3",
4150
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
4151
+      "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=",
4152
+      "dev": true
4153
+    },
4121 4154
     "node_modules/events": {
4122 4155
       "version": "3.3.0",
4123 4156
       "dev": true,
@@ -4275,11 +4308,23 @@
4275 4308
         "node": ">= 0.8.0"
4276 4309
       }
4277 4310
     },
4311
+    "node_modules/extend": {
4312
+      "version": "3.0.2",
4313
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
4314
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
4315
+      "dev": true
4316
+    },
4278 4317
     "node_modules/fast-deep-equal": {
4279 4318
       "version": "3.1.3",
4280 4319
       "dev": true,
4281 4320
       "license": "MIT"
4282 4321
     },
4322
+    "node_modules/fast-diff": {
4323
+      "version": "1.1.2",
4324
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
4325
+      "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
4326
+      "dev": true
4327
+    },
4283 4328
     "node_modules/fast-glob": {
4284 4329
       "version": "3.2.11",
4285 4330
       "dev": true,
@@ -4503,6 +4548,15 @@
4503 4548
       "dev": true,
4504 4549
       "license": "MIT"
4505 4550
     },
4551
+    "node_modules/functions-have-names": {
4552
+      "version": "1.2.3",
4553
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
4554
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
4555
+      "dev": true,
4556
+      "funding": {
4557
+        "url": "https://github.com/sponsors/ljharb"
4558
+      }
4559
+    },
4506 4560
     "node_modules/gensync": {
4507 4561
       "version": "1.0.0-beta.2",
4508 4562
       "dev": true,
@@ -4665,6 +4719,21 @@
4665 4719
         "url": "https://github.com/sponsors/ljharb"
4666 4720
       }
4667 4721
     },
4722
+    "node_modules/has-tostringtag": {
4723
+      "version": "1.0.0",
4724
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
4725
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
4726
+      "dev": true,
4727
+      "dependencies": {
4728
+        "has-symbols": "^1.0.2"
4729
+      },
4730
+      "engines": {
4731
+        "node": ">= 0.4"
4732
+      },
4733
+      "funding": {
4734
+        "url": "https://github.com/sponsors/ljharb"
4735
+      }
4736
+    },
4668 4737
     "node_modules/hash-base": {
4669 4738
       "version": "3.1.0",
4670 4739
       "dev": true,
@@ -5099,6 +5168,22 @@
5099 5168
         "node": ">= 10"
5100 5169
       }
5101 5170
     },
5171
+    "node_modules/is-arguments": {
5172
+      "version": "1.1.1",
5173
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
5174
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
5175
+      "dev": true,
5176
+      "dependencies": {
5177
+        "call-bind": "^1.0.2",
5178
+        "has-tostringtag": "^1.0.0"
5179
+      },
5180
+      "engines": {
5181
+        "node": ">= 0.4"
5182
+      },
5183
+      "funding": {
5184
+        "url": "https://github.com/sponsors/ljharb"
5185
+      }
5186
+    },
5102 5187
     "node_modules/is-arrayish": {
5103 5188
       "version": "0.2.1",
5104 5189
       "dev": true,
@@ -5131,6 +5216,21 @@
5131 5216
         "url": "https://github.com/sponsors/ljharb"
5132 5217
       }
5133 5218
     },
5219
+    "node_modules/is-date-object": {
5220
+      "version": "1.0.5",
5221
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
5222
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
5223
+      "dev": true,
5224
+      "dependencies": {
5225
+        "has-tostringtag": "^1.0.0"
5226
+      },
5227
+      "engines": {
5228
+        "node": ">= 0.4"
5229
+      },
5230
+      "funding": {
5231
+        "url": "https://github.com/sponsors/ljharb"
5232
+      }
5233
+    },
5134 5234
     "node_modules/is-docker": {
5135 5235
       "version": "2.2.1",
5136 5236
       "dev": true,
@@ -5210,6 +5310,22 @@
5210 5310
         "node": ">=0.10.0"
5211 5311
       }
5212 5312
     },
5313
+    "node_modules/is-regex": {
5314
+      "version": "1.1.4",
5315
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
5316
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
5317
+      "dev": true,
5318
+      "dependencies": {
5319
+        "call-bind": "^1.0.2",
5320
+        "has-tostringtag": "^1.0.0"
5321
+      },
5322
+      "engines": {
5323
+        "node": ">= 0.4"
5324
+      },
5325
+      "funding": {
5326
+        "url": "https://github.com/sponsors/ljharb"
5327
+      }
5328
+    },
5213 5329
     "node_modules/is-stream": {
5214 5330
       "version": "2.0.1",
5215 5331
       "dev": true,
@@ -6048,6 +6164,22 @@
6048 6164
         "url": "https://github.com/sponsors/ljharb"
6049 6165
       }
6050 6166
     },
6167
+    "node_modules/object-is": {
6168
+      "version": "1.1.5",
6169
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
6170
+      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
6171
+      "dev": true,
6172
+      "dependencies": {
6173
+        "call-bind": "^1.0.2",
6174
+        "define-properties": "^1.1.3"
6175
+      },
6176
+      "engines": {
6177
+        "node": ">= 0.4"
6178
+      },
6179
+      "funding": {
6180
+        "url": "https://github.com/sponsors/ljharb"
6181
+      }
6182
+    },
6051 6183
     "node_modules/object-keys": {
6052 6184
       "version": "1.1.1",
6053 6185
       "dev": true,
@@ -6234,6 +6366,12 @@
6234 6366
         "tslib": "^2.0.3"
6235 6367
       }
6236 6368
     },
6369
+    "node_modules/parchment": {
6370
+      "version": "1.1.4",
6371
+      "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
6372
+      "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
6373
+      "dev": true
6374
+    },
6237 6375
     "node_modules/parent-module": {
6238 6376
       "version": "1.0.1",
6239 6377
       "dev": true,
@@ -7094,6 +7232,34 @@
7094 7232
       ],
7095 7233
       "license": "MIT"
7096 7234
     },
7235
+    "node_modules/quill": {
7236
+      "version": "1.3.7",
7237
+      "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
7238
+      "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
7239
+      "dev": true,
7240
+      "dependencies": {
7241
+        "clone": "^2.1.1",
7242
+        "deep-equal": "^1.0.1",
7243
+        "eventemitter3": "^2.0.3",
7244
+        "extend": "^3.0.2",
7245
+        "parchment": "^1.1.4",
7246
+        "quill-delta": "^3.6.2"
7247
+      }
7248
+    },
7249
+    "node_modules/quill-delta": {
7250
+      "version": "3.6.3",
7251
+      "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
7252
+      "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
7253
+      "dev": true,
7254
+      "dependencies": {
7255
+        "deep-equal": "^1.0.1",
7256
+        "extend": "^3.0.2",
7257
+        "fast-diff": "1.1.2"
7258
+      },
7259
+      "engines": {
7260
+        "node": ">=0.10"
7261
+      }
7262
+    },
7097 7263
     "node_modules/randombytes": {
7098 7264
       "version": "2.1.0",
7099 7265
       "dev": true,
@@ -7247,6 +7413,23 @@
7247 7413
       "dev": true,
7248 7414
       "license": "MIT"
7249 7415
     },
7416
+    "node_modules/regexp.prototype.flags": {
7417
+      "version": "1.4.3",
7418
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
7419
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
7420
+      "dev": true,
7421
+      "dependencies": {
7422
+        "call-bind": "^1.0.2",
7423
+        "define-properties": "^1.1.3",
7424
+        "functions-have-names": "^1.2.2"
7425
+      },
7426
+      "engines": {
7427
+        "node": ">= 0.4"
7428
+      },
7429
+      "funding": {
7430
+        "url": "https://github.com/sponsors/ljharb"
7431
+      }
7432
+    },
7250 7433
     "node_modules/regexpu-core": {
7251 7434
       "version": "5.0.1",
7252 7435
       "dev": true,
@@ -11206,6 +11389,12 @@
11206 11389
         "wrap-ansi": "^7.0.0"
11207 11390
       }
11208 11391
     },
11392
+    "clone": {
11393
+      "version": "2.1.2",
11394
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
11395
+      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
11396
+      "dev": true
11397
+    },
11209 11398
     "clone-deep": {
11210 11399
       "version": "4.0.1",
11211 11400
       "dev": true,
@@ -11602,6 +11791,20 @@
11602 11791
         }
11603 11792
       }
11604 11793
     },
11794
+    "deep-equal": {
11795
+      "version": "1.1.1",
11796
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
11797
+      "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
11798
+      "dev": true,
11799
+      "requires": {
11800
+        "is-arguments": "^1.0.4",
11801
+        "is-date-object": "^1.0.1",
11802
+        "is-regex": "^1.0.4",
11803
+        "object-is": "^1.0.1",
11804
+        "object-keys": "^1.1.1",
11805
+        "regexp.prototype.flags": "^1.2.0"
11806
+      }
11807
+    },
11605 11808
     "deepmerge": {
11606 11809
       "version": "4.2.2",
11607 11810
       "dev": true
@@ -11889,6 +12092,12 @@
11889 12092
       "version": "1.8.1",
11890 12093
       "dev": true
11891 12094
     },
12095
+    "eventemitter3": {
12096
+      "version": "2.0.3",
12097
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
12098
+      "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=",
12099
+      "dev": true
12100
+    },
11892 12101
     "events": {
11893 12102
       "version": "3.3.0",
11894 12103
       "dev": true
@@ -12013,10 +12222,22 @@
12013 12222
         }
12014 12223
       }
12015 12224
     },
12225
+    "extend": {
12226
+      "version": "3.0.2",
12227
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
12228
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
12229
+      "dev": true
12230
+    },
12016 12231
     "fast-deep-equal": {
12017 12232
       "version": "3.1.3",
12018 12233
       "dev": true
12019 12234
     },
12235
+    "fast-diff": {
12236
+      "version": "1.1.2",
12237
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
12238
+      "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
12239
+      "dev": true
12240
+    },
12020 12241
     "fast-glob": {
12021 12242
       "version": "3.2.11",
12022 12243
       "dev": true,
@@ -12155,6 +12376,12 @@
12155 12376
       "version": "1.1.1",
12156 12377
       "dev": true
12157 12378
     },
12379
+    "functions-have-names": {
12380
+      "version": "1.2.3",
12381
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
12382
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
12383
+      "dev": true
12384
+    },
12158 12385
     "gensync": {
12159 12386
       "version": "1.0.0-beta.2",
12160 12387
       "dev": true
@@ -12255,6 +12482,15 @@
12255 12482
       "version": "1.0.3",
12256 12483
       "dev": true
12257 12484
     },
12485
+    "has-tostringtag": {
12486
+      "version": "1.0.0",
12487
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
12488
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
12489
+      "dev": true,
12490
+      "requires": {
12491
+        "has-symbols": "^1.0.2"
12492
+      }
12493
+    },
12258 12494
     "hash-base": {
12259 12495
       "version": "3.1.0",
12260 12496
       "dev": true,
@@ -12535,6 +12771,16 @@
12535 12771
       "version": "2.0.1",
12536 12772
       "dev": true
12537 12773
     },
12774
+    "is-arguments": {
12775
+      "version": "1.1.1",
12776
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
12777
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
12778
+      "dev": true,
12779
+      "requires": {
12780
+        "call-bind": "^1.0.2",
12781
+        "has-tostringtag": "^1.0.0"
12782
+      }
12783
+    },
12538 12784
     "is-arrayish": {
12539 12785
       "version": "0.2.1",
12540 12786
       "dev": true
@@ -12557,6 +12803,15 @@
12557 12803
         "has": "^1.0.3"
12558 12804
       }
12559 12805
     },
12806
+    "is-date-object": {
12807
+      "version": "1.0.5",
12808
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
12809
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
12810
+      "dev": true,
12811
+      "requires": {
12812
+        "has-tostringtag": "^1.0.0"
12813
+      }
12814
+    },
12560 12815
     "is-docker": {
12561 12816
       "version": "2.2.1",
12562 12817
       "dev": true
@@ -12598,6 +12853,16 @@
12598 12853
         "isobject": "^3.0.1"
12599 12854
       }
12600 12855
     },
12856
+    "is-regex": {
12857
+      "version": "1.1.4",
12858
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
12859
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
12860
+      "dev": true,
12861
+      "requires": {
12862
+        "call-bind": "^1.0.2",
12863
+        "has-tostringtag": "^1.0.0"
12864
+      }
12865
+    },
12601 12866
     "is-stream": {
12602 12867
       "version": "2.0.1",
12603 12868
       "dev": true
@@ -13154,6 +13419,16 @@
13154 13419
       "version": "1.12.0",
13155 13420
       "dev": true
13156 13421
     },
13422
+    "object-is": {
13423
+      "version": "1.1.5",
13424
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
13425
+      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
13426
+      "dev": true,
13427
+      "requires": {
13428
+        "call-bind": "^1.0.2",
13429
+        "define-properties": "^1.1.3"
13430
+      }
13431
+    },
13157 13432
     "object-keys": {
13158 13433
       "version": "1.1.1",
13159 13434
       "dev": true
@@ -13269,6 +13544,12 @@
13269 13544
         "tslib": "^2.0.3"
13270 13545
       }
13271 13546
     },
13547
+    "parchment": {
13548
+      "version": "1.1.4",
13549
+      "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
13550
+      "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
13551
+      "dev": true
13552
+    },
13272 13553
     "parent-module": {
13273 13554
       "version": "1.0.1",
13274 13555
       "dev": true,
@@ -13750,6 +14031,31 @@
13750 14031
       "version": "1.2.3",
13751 14032
       "dev": true
13752 14033
     },
14034
+    "quill": {
14035
+      "version": "1.3.7",
14036
+      "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
14037
+      "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
14038
+      "dev": true,
14039
+      "requires": {
14040
+        "clone": "^2.1.1",
14041
+        "deep-equal": "^1.0.1",
14042
+        "eventemitter3": "^2.0.3",
14043
+        "extend": "^3.0.2",
14044
+        "parchment": "^1.1.4",
14045
+        "quill-delta": "^3.6.2"
14046
+      }
14047
+    },
14048
+    "quill-delta": {
14049
+      "version": "3.6.3",
14050
+      "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
14051
+      "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
14052
+      "dev": true,
14053
+      "requires": {
14054
+        "deep-equal": "^1.0.1",
14055
+        "extend": "^3.0.2",
14056
+        "fast-diff": "1.1.2"
14057
+      }
14058
+    },
13753 14059
     "randombytes": {
13754 14060
       "version": "2.1.0",
13755 14061
       "dev": true,
@@ -13866,6 +14172,17 @@
13866 14172
       "version": "2.2.11",
13867 14173
       "dev": true
13868 14174
     },
14175
+    "regexp.prototype.flags": {
14176
+      "version": "1.4.3",
14177
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
14178
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
14179
+      "dev": true,
14180
+      "requires": {
14181
+        "call-bind": "^1.0.2",
14182
+        "define-properties": "^1.1.3",
14183
+        "functions-have-names": "^1.2.2"
14184
+      }
14185
+    },
13869 14186
     "regexpu-core": {
13870 14187
       "version": "5.0.1",
13871 14188
       "dev": true,

+ 1
- 0
package.json View File

@@ -25,6 +25,7 @@
25 25
     "primeflex": "^3.1.3",
26 26
     "primeicons": "^5.0.0",
27 27
     "primevue": "^3.12.1",
28
+    "quill": "^1.3.7",
28 29
     "resolve-url-loader": "^5.0.0",
29 30
     "sass": "^1.49.8",
30 31
     "sass-loader": "^12.1.0",

+ 1931
- 0
public/js/resources_js_pages_expense_Create_vue.js
File diff suppressed because it is too large
View File


+ 6495
- 0
public/js/resources_js_pages_expense_Index_vue.js
File diff suppressed because it is too large
View File


+ 1483
- 0
public/js/resources_js_pages_expense_Show_vue.js
File diff suppressed because it is too large
View File


+ 27
- 0
public/js/resources_js_pages_expense_TableHeader_js.js View File

@@ -0,0 +1,27 @@
1
+"use strict";
2
+(self["webpackChunk"] = self["webpackChunk"] || []).push([["resources_js_pages_expense_TableHeader_js"],{
3
+
4
+/***/ "./resources/js/pages/expense/TableHeader.js":
5
+/*!***************************************************!*\
6
+  !*** ./resources/js/pages/expense/TableHeader.js ***!
7
+  \***************************************************/
8
+/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9
+
10
+__webpack_require__.r(__webpack_exports__);
11
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
12
+/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
13
+/* harmony export */ });
14
+/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ([{
15
+  field: 'createdAt',
16
+  header: 'Tanggal'
17
+}, {
18
+  field: 'amount',
19
+  header: 'Pengeluaran'
20
+}, {
21
+  field: 'user',
22
+  header: 'User'
23
+}]);
24
+
25
+/***/ })
26
+
27
+}]);

+ 16
- 0
public/js/resources_js_pages_home_Index_vue.js View File

@@ -901,6 +901,14 @@ __webpack_require__.r(__webpack_exports__);
901 901
       to: '/dashboards',
902 902
       component: 'home/Index'
903 903
     }]
904
+  }, {
905
+    label: 'Menu',
906
+    items: [{
907
+      label: 'Pengeluaran',
908
+      icon: 'pi pi-wallet',
909
+      to: '/expenses',
910
+      component: 'expense/Index'
911
+    }]
904 912
   }, {
905 913
     label: 'Master',
906 914
     items: [{
@@ -919,6 +927,14 @@ __webpack_require__.r(__webpack_exports__);
919 927
       to: '/dashboards',
920 928
       component: 'home/Index'
921 929
     }]
930
+  }, {
931
+    label: 'Menu',
932
+    items: [{
933
+      label: 'Pengeluaran',
934
+      icon: 'pi pi-wallet',
935
+      to: '/expenses',
936
+      component: 'expense/Index'
937
+    }]
922 938
   }],
923 939
   // Operator
924 940
   3: [{

+ 31
- 9
public/js/resources_js_pages_user_Create_vue.js View File

@@ -1203,11 +1203,11 @@ var _hoisted_1 = {
1203 1203
 var _hoisted_2 = {
1204 1204
   "class": "col-12 lg:col-8"
1205 1205
 };
1206
-var _hoisted_3 = {
1207
-  "class": "grid"
1208
-};
1206
+
1207
+var _hoisted_3 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)(" Tambah User ");
1208
+
1209 1209
 var _hoisted_4 = {
1210
-  "class": "col-12 md:col-6"
1210
+  "class": "grid"
1211 1211
 };
1212 1212
 var _hoisted_5 = {
1213 1213
   "class": "col-12 md:col-6"
@@ -1219,6 +1219,9 @@ var _hoisted_7 = {
1219 1219
   "class": "col-12 md:col-6"
1220 1220
 };
1221 1221
 var _hoisted_8 = {
1222
+  "class": "col-12 md:col-6"
1223
+};
1224
+var _hoisted_9 = {
1222 1225
   "class": "flex flex-column md:flex-row justify-content-end"
1223 1226
 };
1224 1227
 function render(_ctx, _cache, $props, $setup, $data, $options) {
@@ -1231,8 +1234,11 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1231 1234
   }), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppLayout"], null, {
1232 1235
     "default": (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1233 1236
       return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_1, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_2, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_Card, null, {
1237
+        title: (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1238
+          return [_hoisted_3];
1239
+        }),
1234 1240
         content: (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1235
-          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_3, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_4, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1241
+          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_4, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_5, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1236 1242
             label: "Nama",
1237 1243
             placeholder: "nama",
1238 1244
             error: $setup.form.errors.name,
@@ -1242,7 +1248,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1242 1248
             })
1243 1249
           }, null, 8
1244 1250
           /* PROPS */
1245
-          , ["error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_5, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1251
+          , ["error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_6, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1246 1252
             label: "Nomor HP",
1247 1253
             placeholder: "nomor hp",
1248 1254
             error: $setup.form.errors.phone,
@@ -1252,7 +1258,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1252 1258
             })
1253 1259
           }, null, 8
1254 1260
           /* PROPS */
1255
-          , ["error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_6, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1261
+          , ["error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_7, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1256 1262
             label: "Email",
1257 1263
             placeholder: "email",
1258 1264
             error: $setup.form.errors.email,
@@ -1262,7 +1268,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1262 1268
             })
1263 1269
           }, null, 8
1264 1270
           /* PROPS */
1265
-          , ["error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_7, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppDropdown"], {
1271
+          , ["error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_8, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppDropdown"], {
1266 1272
             label: "Hak Akses",
1267 1273
             placeholder: "pilih satu",
1268 1274
             modelValue: $setup.form.role_id,
@@ -1276,7 +1282,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1276 1282
           , ["modelValue", "options", "error"])])])];
1277 1283
         }),
1278 1284
         footer: (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1279
-          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_8, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_Button, {
1285
+          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_9, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_Button, {
1280 1286
             label: "Simpan",
1281 1287
             icon: "pi pi-check",
1282 1288
             "class": "p-button-outlined",
@@ -1321,6 +1327,14 @@ __webpack_require__.r(__webpack_exports__);
1321 1327
       to: '/dashboards',
1322 1328
       component: 'home/Index'
1323 1329
     }]
1330
+  }, {
1331
+    label: 'Menu',
1332
+    items: [{
1333
+      label: 'Pengeluaran',
1334
+      icon: 'pi pi-wallet',
1335
+      to: '/expenses',
1336
+      component: 'expense/Index'
1337
+    }]
1324 1338
   }, {
1325 1339
     label: 'Master',
1326 1340
     items: [{
@@ -1339,6 +1353,14 @@ __webpack_require__.r(__webpack_exports__);
1339 1353
       to: '/dashboards',
1340 1354
       component: 'home/Index'
1341 1355
     }]
1356
+  }, {
1357
+    label: 'Menu',
1358
+    items: [{
1359
+      label: 'Pengeluaran',
1360
+      icon: 'pi pi-wallet',
1361
+      to: '/expenses',
1362
+      component: 'expense/Index'
1363
+    }]
1342 1364
   }],
1343 1365
   // Operator
1344 1366
   3: [{

+ 35
- 13
public/js/resources_js_pages_user_Edit_vue.js View File

@@ -1459,11 +1459,11 @@ var _hoisted_1 = {
1459 1459
 var _hoisted_2 = {
1460 1460
   "class": "col-12 lg:col-8"
1461 1461
 };
1462
-var _hoisted_3 = {
1463
-  "class": "grid"
1464
-};
1462
+
1463
+var _hoisted_3 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)(" Edit User ");
1464
+
1465 1465
 var _hoisted_4 = {
1466
-  "class": "col-12 md:col-6"
1466
+  "class": "grid"
1467 1467
 };
1468 1468
 var _hoisted_5 = {
1469 1469
   "class": "col-12 md:col-6"
@@ -1472,16 +1472,19 @@ var _hoisted_6 = {
1472 1472
   "class": "col-12 md:col-6"
1473 1473
 };
1474 1474
 var _hoisted_7 = {
1475
-  key: 0,
1476 1475
   "class": "col-12 md:col-6"
1477 1476
 };
1478 1477
 var _hoisted_8 = {
1479
-  "class": "grid"
1478
+  key: 0,
1479
+  "class": "col-12 md:col-6"
1480 1480
 };
1481 1481
 var _hoisted_9 = {
1482
-  "class": "col-12 md:col-6 flex flex-column md:flex-row justify-content-center md:justify-content-start"
1482
+  "class": "grid"
1483 1483
 };
1484 1484
 var _hoisted_10 = {
1485
+  "class": "col-12 md:col-6 flex flex-column md:flex-row justify-content-center md:justify-content-start"
1486
+};
1487
+var _hoisted_11 = {
1485 1488
   "class": "col-12 md:col-6 flex flex-column md:flex-row justify-content-center md:justify-content-end"
1486 1489
 };
1487 1490
 function render(_ctx, _cache, $props, $setup, $data, $options) {
@@ -1494,8 +1497,11 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1494 1497
   }), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppLayout"], null, {
1495 1498
     "default": (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1496 1499
       return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_1, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_2, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_Card, null, {
1500
+        title: (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1501
+          return [_hoisted_3];
1502
+        }),
1497 1503
         content: (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1498
-          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_3, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_4, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1504
+          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_4, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_5, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1499 1505
             label: "Nama",
1500 1506
             placeholder: "nama",
1501 1507
             disabled: $props.user.role_id !== 1,
@@ -1506,7 +1512,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1506 1512
             })
1507 1513
           }, null, 8
1508 1514
           /* PROPS */
1509
-          , ["disabled", "error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_5, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1515
+          , ["disabled", "error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_6, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1510 1516
             label: "Nomor HP",
1511 1517
             placeholder: "nomor hp",
1512 1518
             disabled: $props.user.role_id !== 1,
@@ -1517,7 +1523,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1517 1523
             })
1518 1524
           }, null, 8
1519 1525
           /* PROPS */
1520
-          , ["disabled", "error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_6, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1526
+          , ["disabled", "error", "modelValue"])]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_7, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppInputText"], {
1521 1527
             label: "Email",
1522 1528
             placeholder: "email",
1523 1529
             disabled: $props.user.role_id !== 1,
@@ -1528,7 +1534,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1528 1534
             })
1529 1535
           }, null, 8
1530 1536
           /* PROPS */
1531
-          , ["disabled", "error", "modelValue"])]), $props.user.role_id !== 1 ? ((0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)("div", _hoisted_7, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppDropdown"], {
1537
+          , ["disabled", "error", "modelValue"])]), $props.user.role_id !== 1 ? ((0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)("div", _hoisted_8, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppDropdown"], {
1532 1538
             label: "Hak Akses",
1533 1539
             placeholder: "Pilih satu",
1534 1540
             modelValue: $setup.form.role_id,
@@ -1542,7 +1548,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1542 1548
           , ["modelValue", "options", "error"])])) : (0,vue__WEBPACK_IMPORTED_MODULE_0__.createCommentVNode)("v-if", true)])];
1543 1549
         }),
1544 1550
         footer: (0,vue__WEBPACK_IMPORTED_MODULE_0__.withCtx)(function () {
1545
-          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_8, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_9, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppDialog"], {
1551
+          return [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_9, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_10, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppDialog"], {
1546 1552
             message: "Yakin akan menghapus data ini?",
1547 1553
             visible: $setup.visibleDialog,
1548 1554
             "onUpdate:visible": _cache[4] || (_cache[4] = function ($event) {
@@ -1560,7 +1566,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
1560 1566
             icon: "pi pi-trash",
1561 1567
             "class": "p-button-outlined p-button-danger",
1562 1568
             onClick: $setup.confirmDialog
1563
-          })) : (0,vue__WEBPACK_IMPORTED_MODULE_0__.createCommentVNode)("v-if", true)]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_10, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppButton"], {
1569
+          })) : (0,vue__WEBPACK_IMPORTED_MODULE_0__.createCommentVNode)("v-if", true)]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("div", _hoisted_11, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)($setup["AppButton"], {
1564 1570
             label: "Blokir",
1565 1571
             icon: "pi pi-ban",
1566 1572
             method: "delete",
@@ -1613,6 +1619,14 @@ __webpack_require__.r(__webpack_exports__);
1613 1619
       to: '/dashboards',
1614 1620
       component: 'home/Index'
1615 1621
     }]
1622
+  }, {
1623
+    label: 'Menu',
1624
+    items: [{
1625
+      label: 'Pengeluaran',
1626
+      icon: 'pi pi-wallet',
1627
+      to: '/expenses',
1628
+      component: 'expense/Index'
1629
+    }]
1616 1630
   }, {
1617 1631
     label: 'Master',
1618 1632
     items: [{
@@ -1631,6 +1645,14 @@ __webpack_require__.r(__webpack_exports__);
1631 1645
       to: '/dashboards',
1632 1646
       component: 'home/Index'
1633 1647
     }]
1648
+  }, {
1649
+    label: 'Menu',
1650
+    items: [{
1651
+      label: 'Pengeluaran',
1652
+      icon: 'pi pi-wallet',
1653
+      to: '/expenses',
1654
+      component: 'expense/Index'
1655
+    }]
1634 1656
   }],
1635 1657
   // Operator
1636 1658
   3: [{

+ 16
- 0
public/js/resources_js_pages_user_Index_vue.js View File

@@ -1263,6 +1263,14 @@ __webpack_require__.r(__webpack_exports__);
1263 1263
       to: '/dashboards',
1264 1264
       component: 'home/Index'
1265 1265
     }]
1266
+  }, {
1267
+    label: 'Menu',
1268
+    items: [{
1269
+      label: 'Pengeluaran',
1270
+      icon: 'pi pi-wallet',
1271
+      to: '/expenses',
1272
+      component: 'expense/Index'
1273
+    }]
1266 1274
   }, {
1267 1275
     label: 'Master',
1268 1276
     items: [{
@@ -1281,6 +1289,14 @@ __webpack_require__.r(__webpack_exports__);
1281 1289
       to: '/dashboards',
1282 1290
       component: 'home/Index'
1283 1291
     }]
1292
+  }, {
1293
+    label: 'Menu',
1294
+    items: [{
1295
+      label: 'Pengeluaran',
1296
+      icon: 'pi pi-wallet',
1297
+      to: '/expenses',
1298
+      component: 'expense/Index'
1299
+    }]
1284 1300
   }],
1285 1301
   // Operator
1286 1302
   3: [{

+ 16
- 0
public/js/resources_js_pages_user_Show_vue.js View File

@@ -1347,6 +1347,14 @@ __webpack_require__.r(__webpack_exports__);
1347 1347
       to: '/dashboards',
1348 1348
       component: 'home/Index'
1349 1349
     }]
1350
+  }, {
1351
+    label: 'Menu',
1352
+    items: [{
1353
+      label: 'Pengeluaran',
1354
+      icon: 'pi pi-wallet',
1355
+      to: '/expenses',
1356
+      component: 'expense/Index'
1357
+    }]
1350 1358
   }, {
1351 1359
     label: 'Master',
1352 1360
     items: [{
@@ -1365,6 +1373,14 @@ __webpack_require__.r(__webpack_exports__);
1365 1373
       to: '/dashboards',
1366 1374
       component: 'home/Index'
1367 1375
     }]
1376
+  }, {
1377
+    label: 'Menu',
1378
+    items: [{
1379
+      label: 'Pengeluaran',
1380
+      icon: 'pi pi-wallet',
1381
+      to: '/expenses',
1382
+      component: 'expense/Index'
1383
+    }]
1368 1384
   }],
1369 1385
   // Operator
1370 1386
   3: [{

+ 33675
- 19825
public/js/vue.js
File diff suppressed because it is too large
View File


+ 138
- 0
resources/js/components/AppInputNumber.vue View File

@@ -0,0 +1,138 @@
1
+<script setup>
2
+import { computed } from 'vue'
3
+
4
+const props = defineProps({
5
+  label: {
6
+    type: String,
7
+    required: true,
8
+  },
9
+  disabled: {
10
+    type: Boolean,
11
+    default: false,
12
+  },
13
+  type: {
14
+    type: String,
15
+    default: 'text',
16
+  },
17
+  mode: {
18
+    type: String,
19
+    default: 'decimal',
20
+  },
21
+  incrementButtonClass: {
22
+    type: String,
23
+    default: null,
24
+  },
25
+  decrementButtonClass: {
26
+    type: String,
27
+    default: null,
28
+  },
29
+  incrementButtonIcon: {
30
+    type: String,
31
+    default: 'pi pi-angle-up',
32
+  },
33
+  decrementButtonIcon: {
34
+    type: String,
35
+    default: 'pi pi-angle-down',
36
+  },
37
+  showButtons: {
38
+    type: Boolean,
39
+    default: false,
40
+  },
41
+  buttonLayout: {
42
+    type: String,
43
+    default: 'stacked',
44
+  },
45
+  min: {
46
+    type: Number,
47
+    default: null,
48
+  },
49
+  max: {
50
+    type: Number,
51
+    default: null,
52
+  },
53
+  step: {
54
+    type: Number,
55
+    default: 1,
56
+  },
57
+  prefix: {
58
+    type: String,
59
+    default: null,
60
+  },
61
+  suffix: {
62
+    type: String,
63
+    default: null,
64
+  },
65
+  placeholder: {
66
+    type: String,
67
+    required: true,
68
+  },
69
+  useGrouping: {
70
+    type: Boolean,
71
+    default: true,
72
+  },
73
+  currency: {
74
+    type: String,
75
+    default: undefined,
76
+  },
77
+  locale: {
78
+    type: String,
79
+    default: undefined,
80
+  },
81
+  error: {
82
+    type: String,
83
+    default: null,
84
+  },
85
+  currencyDisplay: {
86
+    type: String,
87
+    default: undefined,
88
+  },
89
+  modelValue: null,
90
+})
91
+
92
+defineEmits(['update:modelValue'])
93
+
94
+const isError = computed(() => (props.error ? true : false))
95
+
96
+const forLabel = computed(() => props.label.toLowerCase().replace(/\s+/g, '-'))
97
+
98
+const ariaDescribedbyLabel = computed(() => props.label.toLowerCase().replace(/\s+/g, '-') + '-help')
99
+</script>
100
+
101
+<template>
102
+  <div class="field">
103
+    <label :for="forLabel">{{ label }}</label>
104
+
105
+    <InputNumber
106
+      class="w-full"
107
+      input-class="w-full"
108
+      :currency="currency"
109
+      :currency-display="currencyDisplay"
110
+      :locale="locale"
111
+      :class="{ 'p-invalid': isError }"
112
+      :id="forLabel"
113
+      :aria-describedby="ariaDescribedbyLabel"
114
+      :type="type"
115
+      :placeholder="placeholder"
116
+      :model-value="modelValue"
117
+      :disabled="disabled"
118
+      :prefix="prefix"
119
+      :suffix="suffix"
120
+      :step="step"
121
+      :min="min"
122
+      :max="max"
123
+      :mode="mode"
124
+      :use-grouping="useGrouping"
125
+      :show-buttons="showButtons"
126
+      :button-layout="buttonLayout"
127
+      :increment-button-class="incrementButtonClass"
128
+      :decrement-button-class="decrementButtonClass"
129
+      :increment-button-icon="incrementButtonIcon"
130
+      :decrement-button-icon="decrementButtonIcon"
131
+      @input="$emit('update:modelValue', $event.value)"
132
+    />
133
+
134
+    <small v-if="error" :id="ariaDescribedbyLabel" :class="{ 'p-error': isError }">
135
+      {{ error }}
136
+    </small>
137
+  </div>
138
+</template>

+ 75
- 0
resources/js/pages/expense/Create.vue View File

@@ -0,0 +1,75 @@
1
+<script setup>
2
+import { computed, watch } from 'vue'
3
+import { useForm, usePage } from '@inertiajs/inertia-vue3'
4
+import AppLayout from '@/layouts/AppLayout.vue'
5
+import AppInputNumber from '@/components/AppInputNumber.vue'
6
+import AppEditor from '@/components/AppEditor.vue'
7
+
8
+const errors = computed(() => usePage().props.value.errors)
9
+
10
+watch(errors, () => {
11
+  form.clearErrors()
12
+})
13
+
14
+const form = useForm({
15
+  description: null,
16
+  amount: null,
17
+})
18
+
19
+const submit = () => {
20
+  form.post(route('expenses.store'), { onSuccess: () => form.reset() })
21
+}
22
+</script>
23
+
24
+<template>
25
+  <AppLayout>
26
+    <div class="grid">
27
+      <div class="col-12 md:col-8">
28
+        <Card>
29
+          <template #title> Tambah Pengeluaran </template>
30
+          <template #content>
31
+            <AppInputNumber
32
+              v-model="form.amount"
33
+              class="md:w-16rem"
34
+              label="Pengeluaran"
35
+              placeholder="jumlah pengeluaran"
36
+              :error="form.errors.amount"
37
+            />
38
+
39
+            <AppEditor
40
+              label="Keterangan"
41
+              v-model="form.description"
42
+              editor-style="height: 320px"
43
+              placeholder="tulis keterangan disini"
44
+              :error="form.errors.description"
45
+            >
46
+              <template #toolbar>
47
+                <span class="q-formats">
48
+                  <button class="ql-bold" v-tooltip.bottom="'Bold'"></button>
49
+                  <button class="ql-italic" v-tooltip.bottom="'Italic'"></button>
50
+                  <button class="ql-underline" v-tooltip.bottom="'Underline'"></button>
51
+                </span>
52
+                <span class="ql-formats">
53
+                  <button class="ql-list" value="ordered" v-tooltip.bottom="'Ordered'"></button>
54
+                  <button class="ql-list" value="bullet" v-tooltip.bottom="'Bullet'"></button>
55
+                </span>
56
+              </template>
57
+            </AppEditor>
58
+          </template>
59
+
60
+          <template #footer>
61
+            <div class="flex flex-column md:flex-row justify-content-end">
62
+              <Button
63
+                label="Simpan"
64
+                icon="pi pi-check"
65
+                class="p-button-outlined"
66
+                :disabled="form.processing"
67
+                @click="submit"
68
+              />
69
+            </div>
70
+          </template>
71
+        </Card>
72
+      </div>
73
+    </div>
74
+  </AppLayout>
75
+</template>

+ 128
- 0
resources/js/pages/expense/Index.vue View File

@@ -0,0 +1,128 @@
1
+<script setup>
2
+import { Inertia } from '@inertiajs/inertia'
3
+import { watch, onMounted } from 'vue'
4
+import { Head, useForm } from '@inertiajs/inertia-vue3'
5
+import dayjs from 'dayjs'
6
+import pickBy from 'lodash/pickBy'
7
+import AppLayout from '@/layouts/AppLayout.vue'
8
+import AppPagination from '@/components/AppPagination.vue'
9
+import AppButton from '@/components/AppButton.vue'
10
+
11
+import TableHeader from './TableHeader'
12
+
13
+const props = defineProps({
14
+  expenses: Object,
15
+  filters: Object,
16
+})
17
+
18
+const filterForm = useForm({
19
+  dates: null,
20
+  startDate: props.filters.startDate,
21
+  endDate: props.filters.endDate,
22
+})
23
+
24
+onMounted(() => {
25
+  if (props.filters.startDate || props.filters.endDate) {
26
+    if (props.filters.endDate) {
27
+      filterForm.dates = [new Date(props.filters.startDate), new Date(props.filters.endDate)]
28
+    } else {
29
+      filterForm.dates = [new Date(props.filters.startDate), null]
30
+    }
31
+  }
32
+})
33
+
34
+watch(filterForm, () => {
35
+  if (filterForm.dates) {
36
+    if (filterForm.dates[1]) {
37
+      filterForm.startDate = dayjs(filterForm.dates[0]).format('YYYY-MM-DD')
38
+      filterForm.endDate = dayjs(filterForm.dates[1]).format('YYYY-MM-DD')
39
+    } else {
40
+      filterForm.startDate = dayjs(filterForm.dates[0]).format('YYYY-MM-DD')
41
+      filterForm.endDate = null
42
+    }
43
+  } else {
44
+    filterForm.endDate = null
45
+    filterForm.startDate = null
46
+  }
47
+
48
+  Inertia.get(
49
+    '/expenses',
50
+    pickBy({
51
+      startDate: filterForm.startDate,
52
+      endDate: filterForm.endDate,
53
+    }),
54
+    {
55
+      preserveState: true,
56
+    }
57
+  )
58
+})
59
+
60
+const filterReset = () => {
61
+  Inertia.get('/expenses')
62
+}
63
+</script>
64
+
65
+<template>
66
+  <Head title="Pengeluaran" />
67
+
68
+  <AppLayout>
69
+    <DataTable
70
+      responsive-layout="scroll"
71
+      column-resize-mode="expand"
72
+      :value="expenses.data"
73
+      :row-hover="true"
74
+      :striped-rows="true"
75
+    >
76
+      <template #header>
77
+        <h1>Pengeluaran</h1>
78
+
79
+        <div class="grid">
80
+          <div class="col-12 md:col-8">
81
+            <div class="grid">
82
+              <div class="col-12 md:col-4">
83
+                <Calendar
84
+                  class="w-full"
85
+                  v-model="filterForm.dates"
86
+                  selection-mode="range"
87
+                  placeholder="filter waktu..."
88
+                  date-format="dd/mm/yy"
89
+                  :manual-input="false"
90
+                />
91
+              </div>
92
+              <div class="col-auto mt-2 ml-2">
93
+                <Button label="reset" class="p-button-link" @click="filterReset" />
94
+              </div>
95
+            </div>
96
+          </div>
97
+          <div class="col-12 md:col-4 flex flex-column md:flex-row justify-content-end">
98
+            <AppButton
99
+              label="Tambah Pengeluaran"
100
+              class="p-button-outlined"
101
+              icon="pi pi-pencil"
102
+              :href="route('expenses.create')"
103
+            />
104
+          </div>
105
+        </div>
106
+      </template>
107
+
108
+      <Column
109
+        v-for="tableHeader in TableHeader"
110
+        :field="tableHeader.field"
111
+        :header="tableHeader.header"
112
+        :key="tableHeader.field"
113
+      />
114
+
115
+      <Column>
116
+        <template #body="{ data }">
117
+          <AppButton
118
+            icon="pi pi-angle-double-right"
119
+            class="p-button-text p-button-icon-only p-button-rounded p-button-text"
120
+            :href="route('expenses.show', data.id)"
121
+          />
122
+        </template>
123
+      </Column>
124
+    </DataTable>
125
+
126
+    <AppPagination :links="expenses.links" />
127
+  </AppLayout>
128
+</template>

+ 71
- 0
resources/js/pages/expense/Show.vue View File

@@ -0,0 +1,71 @@
1
+<script setup>
2
+import { Head } from '@inertiajs/inertia-vue3'
3
+import AppLayout from '@/layouts/AppLayout.vue'
4
+
5
+defineProps({
6
+  expense: Object,
7
+})
8
+</script>
9
+
10
+<template>
11
+  <Head title="Detail Pengeluaran" />
12
+  <AppLayout>
13
+    <h1 class="text-2xl font-bold">Detail Pengeluaran</h1>
14
+
15
+    <div class="grid px-2">
16
+      <div class="col-auto mr-7">
17
+        <h2>
18
+          <span class="text-base"> <i class="pi pi-user" /> User</span>
19
+
20
+          <br />
21
+
22
+          <span class="text-lg">{{ expense.user.name }}</span>
23
+        </h2>
24
+      </div>
25
+
26
+      <div class="col-auto mr-7">
27
+        <h2>
28
+          <span class="text-base"> <i class="pi pi-mobile" /> No HP</span>
29
+
30
+          <br />
31
+
32
+          <span class="text-lg">{{ expense.user.phone }}</span>
33
+        </h2>
34
+      </div>
35
+
36
+      <div class="col-auto mr-7">
37
+        <h2>
38
+          <span class="text-base"> <i class="pi pi-at" /> Email</span>
39
+
40
+          <br />
41
+
42
+          <span class="text-lg">{{ expense.user.email }}</span>
43
+        </h2>
44
+      </div>
45
+
46
+      <div class="col-auto">
47
+        <h2 class="mb-4">
48
+          <span class="text-base">
49
+            <i class="pi pi-wallet red-700" />
50
+            Pengeluaran
51
+          </span>
52
+
53
+          <br />
54
+
55
+          <span class="text-lg">{{ expense.amount }}</span>
56
+        </h2>
57
+      </div>
58
+    </div>
59
+
60
+    <div class="grid">
61
+      <div class="col-12 md:col-8">
62
+        <Card>
63
+          <template #content>
64
+            <h5>Keterangan</h5>
65
+            <p v-html="expense.description"></p>
66
+          </template>
67
+        </Card>
68
+      </div>
69
+    </div>
70
+  </AppLayout>
71
+</template>

+ 5
- 0
resources/js/pages/expense/TableHeader.js View File

@@ -0,0 +1,5 @@
1
+export default [
2
+  { field: 'createdAt', header: 'Tanggal' },
3
+  { field: 'amount', header: 'Pengeluaran' },
4
+  { field: 'user', header: 'User' },
5
+]

+ 1
- 0
resources/js/pages/user/Create.vue View File

@@ -34,6 +34,7 @@ const submit = () => {
34 34
     <div class="grid">
35 35
       <div class="col-12 lg:col-8">
36 36
         <Card>
37
+          <template #title> Tambah User </template>
37 38
           <template #content>
38 39
             <div class="grid">
39 40
               <div class="col-12 md:col-6">

+ 1
- 0
resources/js/pages/user/Edit.vue View File

@@ -48,6 +48,7 @@ watch(errors, () => {
48 48
     <div class="grid">
49 49
       <div class="col-12 lg:col-8">
50 50
         <Card>
51
+          <template #title> Edit User </template>
51 52
           <template #content>
52 53
             <div class="grid">
53 54
               <div class="col-12 md:col-6">

+ 8
- 0
resources/js/utils/menu.js View File

@@ -5,6 +5,10 @@ export default {
5 5
       label: 'Home',
6 6
       items: [{ label: 'Dashboard', icon: 'pi pi-home', to: '/dashboards', component: 'home/Index' }],
7 7
     },
8
+    {
9
+      label: 'Menu',
10
+      items: [{ label: 'Pengeluaran', icon: 'pi pi-wallet', to: '/expenses', component: 'expense/Index' }],
11
+    },
8 12
     {
9 13
       label: 'Master',
10 14
       items: [{ label: 'User', icon: 'pi pi-user', to: '/users', component: 'user/Index' }],
@@ -17,6 +21,10 @@ export default {
17 21
       label: 'Home',
18 22
       items: [{ label: 'Dashboard', icon: 'pi pi-home', to: '/dashboards', component: 'home/Index' }],
19 23
     },
24
+    {
25
+      label: 'Menu',
26
+      items: [{ label: 'Pengeluaran', icon: 'pi pi-wallet', to: '/expenses', component: 'expense/Index' }],
27
+    },
20 28
   ],
21 29
 
22 30
   // Operator

+ 2
- 0
resources/js/vue.js View File

@@ -20,6 +20,7 @@ import DataTable from 'primevue/datatable'
20 20
 import Dialog from 'primevue/dialog'
21 21
 import Divider from 'primevue/divider'
22 22
 import Dropdown from 'primevue/dropdown'
23
+import Editor from 'primevue/editor'
23 24
 import InputNumber from 'primevue/inputnumber'
24 25
 import InputText from 'primevue/inputtext'
25 26
 import Message from 'primevue/message'
@@ -53,6 +54,7 @@ createInertiaApp({
53 54
       .component('Dialog', Dialog)
54 55
       .component('Divider', Divider)
55 56
       .component('Dropdown', Dropdown)
57
+      .component('Editor', Editor)
56 58
       .component('InputNumber', InputNumber)
57 59
       .component('InputText', InputText)
58 60
       .component('Message', Message)

+ 3
- 0
routes/web.php View File

@@ -1,6 +1,7 @@
1 1
 <?php
2 2
 
3 3
 use App\Http\Controllers\DashboardController;
4
+use App\Http\Controllers\ExpenseController;
4 5
 use App\Http\Controllers\UserController;
5 6
 use Illuminate\Support\Facades\Route;
6 7
 
@@ -23,6 +24,8 @@ Route::middleware(['auth', 'verified'])->group(function () {
23 24
     Route::delete('/users/block/{user}', [UserController::class, 'block'])->name('users.block');
24 25
     Route::post('/users/change-password/{user}', [UserController::class, 'changePassword'])->name('users.change-password');
25 26
     Route::resource('/users', UserController::class);
27
+
28
+    Route::resource('/expenses', ExpenseController::class);
26 29
 });
27 30
 
28 31
 require __DIR__ . '/auth.php';