diff --git a/app/Http/Controllers/CustomerController.php b/app/Http/Controllers/CustomerController.php
index db0f60a..28ff3af 100644
--- a/app/Http/Controllers/CustomerController.php
+++ b/app/Http/Controllers/CustomerController.php
@@ -18,7 +18,7 @@ class CustomerController extends Controller
->orWhere('code', 'like', "%{$request->q}%");
}
- $query->orderBy('created_at', 'desc');
+ $query->orderBy('updated_at', 'desc');
return inertia('Customer/Index', [
'query' => $query->paginate(),
@@ -47,7 +47,7 @@ class CustomerController extends Controller
public function update(Request $request, Customer $customer)
{
$request->validate([
- 'code' => 'required|string|max:255|unique:customers,code',
+ 'code' => 'required|string|max:255|unique:customers,code,' . $customer->id,
'name' => 'required|string|max:255',
'point' => 'required|numeric',
]);
diff --git a/app/Http/Controllers/CustomerPointController.php b/app/Http/Controllers/CustomerPointController.php
index 00961fd..11b6855 100644
--- a/app/Http/Controllers/CustomerPointController.php
+++ b/app/Http/Controllers/CustomerPointController.php
@@ -2,23 +2,85 @@
namespace App\Http\Controllers;
+use App\Imports\CustomerPointsImport;
+use App\Models\Customer;
+use App\Models\CustomerPoint;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use Maatwebsite\Excel\Facades\Excel;
class CustomerPointController extends Controller
{
- public function index()
- {
- }
- public function store()
+ public function index(Request $request)
{
+ $query = CustomerPoint::with(['customer'])->orderBy('created_at', 'desc');
+
+ if ($request->customer_id != '') {
+ $query->where('customer_id', $request->customer_id);
+ }
+
+ if ($request->q != '') {
+ $query->whereHas('customer', function ($query) use ($request) {
+ $query->where('name', 'like', "%{$request->q}%")
+ ->orWhere('code', 'like', "%{$request->q}%");
+ });
+ }
+
+ return inertia('CustomerPoint/Index', [
+ 'query' => $query->paginate()
+ ]);
}
- public function update()
+
+ public function create(Request $request)
{
+ $customer = Customer::query()->orderBy('updated_at', 'desc');
+
+ if ($request->q != '') {
+ $customer->where('name', 'like', "%{$request->q}%")
+ ->orWhere('code', 'like', "%{$request->q}%");
+ }
+
+ return inertia('CustomerPoint/Form', [
+ 'customers' => $customer->paginate(10)
+ ]);
}
- public function destroy()
+
+ public function store(Request $request)
{
+ $request->validate([
+ 'items' => 'required|array',
+ 'items.*.customer_id' => 'required|exists:customers,id',
+ 'items.*.point' => 'required|numeric',
+ 'items.*.description' => 'nullable|string'
+ ]);
+
+ DB::beginTransaction();
+ foreach ($request->items as $item) {
+ $customer = Customer::find($item['customer_id']);
+ $customer->update([
+ 'last_point' => $customer->last_point + $item['point']
+ ]);
+ $customer->points()->create([
+ 'point' => $item['point'],
+ 'description' => $item['description']
+ ]);
+ }
+
+ DB::commit();
+
+ return redirect()->route('customer-point.index')
+ ->with('message', ['type' => 'success', 'message' => 'Create item success']);
}
- public function import()
+
+ public function import(Request $request)
{
+ $request->validate([
+ 'file' => 'required|file'
+ ]);
+
+ Excel::import(new CustomerPointsImport, $request->file('file'));
+
+ return redirect()->route('customer-point.index')
+ ->with('message', ['type' => 'success', 'message' => 'Import Success']);
}
}
diff --git a/app/Imports/CustomerPointsImport.php b/app/Imports/CustomerPointsImport.php
new file mode 100644
index 0000000..da58e3a
--- /dev/null
+++ b/app/Imports/CustomerPointsImport.php
@@ -0,0 +1,33 @@
+first();
+
+ if ($customer != null) {
+ $customer->update([
+ 'last_point' => $customer->last_point + $row['point']
+ ]);
+
+ return new CustomerPoint([
+ 'customer_id' => $customer->id,
+ 'description' => $row['description'],
+ 'point' => $row['point'],
+ ]);
+ }
+ }
+}
diff --git a/app/Models/Customer.php b/app/Models/Customer.php
index c48a7cb..63db19f 100644
--- a/app/Models/Customer.php
+++ b/app/Models/Customer.php
@@ -11,4 +11,9 @@ class Customer extends Model
'start_point',
'last_point',
];
+
+ public function points()
+ {
+ return $this->hasMany(CustomerPoint::class);
+ }
}
diff --git a/app/Models/CustomerPoint.php b/app/Models/CustomerPoint.php
index adaeb9b..6ec81b4 100644
--- a/app/Models/CustomerPoint.php
+++ b/app/Models/CustomerPoint.php
@@ -9,4 +9,9 @@ class CustomerPoint extends Model
'description',
'point',
];
+
+ public function customer()
+ {
+ return $this->belongsTo(Customer::class);
+ }
}
diff --git a/database/migrations/2023_06_14_022155_create_customers_table.php b/database/migrations/2023_06_14_022155_create_customers_table.php
index 9916218..eb31f92 100644
--- a/database/migrations/2023_06_14_022155_create_customers_table.php
+++ b/database/migrations/2023_06_14_022155_create_customers_table.php
@@ -15,8 +15,8 @@ return new class extends Migration
$table->ulid('id')->primary();
$table->string('name');
$table->string('code')->unique();
- $table->decimal('start_point', 20, 2);
- $table->decimal('last_point', 20, 2);
+ $table->decimal('start_point', 20, 2)->default(0);
+ $table->decimal('last_point', 20, 2)->default(0);
$table->timestamps();
$table->softDeletes();
$table->ulid('created_by')->nullable();
diff --git a/database/migrations/2023_06_14_022203_create_customer_points_table.php b/database/migrations/2023_06_14_022203_create_customer_points_table.php
index 04060c7..235d211 100644
--- a/database/migrations/2023_06_14_022203_create_customer_points_table.php
+++ b/database/migrations/2023_06_14_022203_create_customer_points_table.php
@@ -14,8 +14,8 @@ return new class extends Migration
Schema::create('customer_points', function (Blueprint $table) {
$table->ulid('id')->primary();
$table->ulid('customer_id');
- $table->string('description', 1000);
- $table->decimal('point', 20, 2);
+ $table->string('description', 1000)->nullable();
+ $table->decimal('point', 20, 2)->default(0);
$table->timestamps();
$table->softDeletes();
$table->ulid('created_by')->nullable();
diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php
index 82f3e2c..dda3ed9 100644
--- a/database/seeders/PermissionSeeder.php
+++ b/database/seeders/PermissionSeeder.php
@@ -39,9 +39,7 @@ class PermissionSeeder extends Seeder
['id' => Str::ulid(), 'label' => 'Delete Customer', 'name' => 'delete-customer'],
['id' => Str::ulid(), 'label' => 'Create Customer Point', 'name' => 'create-customer-point'],
- ['id' => Str::ulid(), 'label' => 'Update Customer Point', 'name' => 'update-customer-point'],
['id' => Str::ulid(), 'label' => 'View Customer Point', 'name' => 'view-customer-point'],
- ['id' => Str::ulid(), 'label' => 'Delete Customer Point', 'name' => 'delete-customer-point'],
];
foreach ($permissions as $permission) {
diff --git a/public/point.csv b/public/point.csv
new file mode 100644
index 0000000..0e30cc1
--- /dev/null
+++ b/public/point.csv
@@ -0,0 +1,5 @@
+code,point,description
+AACRB400,1,'some note'
+AAISU221,20,
+AAISU221,-10,
+AAMMZ012,-2,
\ No newline at end of file
diff --git a/resources/js/Layouts/Partials/routes.js b/resources/js/Layouts/Partials/routes.js
index 17125f1..17408a4 100644
--- a/resources/js/Layouts/Partials/routes.js
+++ b/resources/js/Layouts/Partials/routes.js
@@ -30,7 +30,7 @@ export default [
show: true,
icon: HiTrophy,
route: route('customer-point.index'),
- active: 'customer-point.index',
+ active: 'customer-point.*',
permission: 'view-customer-point',
},
{
diff --git a/resources/js/Pages/CustomerPoint/CustomerSelectionModal.jsx b/resources/js/Pages/CustomerPoint/CustomerSelectionModal.jsx
new file mode 100644
index 0000000..1662aa0
--- /dev/null
+++ b/resources/js/Pages/CustomerPoint/CustomerSelectionModal.jsx
@@ -0,0 +1,95 @@
+import React, { useState, useEffect } from 'react'
+import { router, usePage } from '@inertiajs/react'
+import { Spinner } from 'flowbite-react'
+import { usePrevious } from 'react-use'
+
+import Modal from '@/Components/Modal'
+import SearchInput from '@/Components/SearchInput'
+import Pagination from '@/Components/Pagination'
+
+export default function CustomerSelectionModal(props) {
+ const { modalState, onItemSelected } = props
+ const [loading, setLoading] = useState(false)
+
+ const {
+ props: {
+ customers: { data, links },
+ },
+ } = usePage()
+
+ const [search, setSearch] = useState('')
+ const preValue = usePrevious(search)
+ const params = { customer_q: search }
+
+ const handleItemSelected = (item) => {
+ onItemSelected(item)
+ modalState.toggle()
+ }
+
+ useEffect(() => {
+ if (preValue) {
+ router.get(route(route().current()), params, {
+ replace: true,
+ preserveState: true,
+ })
+ }
+ }, [search])
+
+ useEffect(() => {
+ router.on('start', () => setLoading(true))
+ router.on('finish', () => setLoading(false))
+ }, [])
+
+ return (
+
+ setSearch(e.target.value)}
+ value={search}
+ />
+ {loading ? (
+
+
+
+ ) : (
+
+
+
+
+
+
+ Name
+ |
+
+
+
+ {data.map((customer) => (
+
+ handleItemSelected(customer)
+ }
+ >
+
+ {customer.name}
+ |
+
+ ))}
+
+
+
+
+
+ )}
+
+ )
+}
diff --git a/resources/js/Pages/CustomerPoint/Form.jsx b/resources/js/Pages/CustomerPoint/Form.jsx
new file mode 100644
index 0000000..b1ef037
--- /dev/null
+++ b/resources/js/Pages/CustomerPoint/Form.jsx
@@ -0,0 +1,156 @@
+import React from 'react'
+import { Head, useForm } from '@inertiajs/react'
+
+import { useModalState } from '@/hooks'
+import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
+import Button from '@/Components/Button'
+import CustomerSelectionModal from './CustomerSelectionModal'
+import FormInput from '@/Components/FormInput'
+import { HiXCircle } from 'react-icons/hi2'
+
+export default function Setting(props) {
+ const { data, setData, post, processing, errors } = useForm({
+ items: [],
+ })
+
+ const customerSelectionModal = useModalState()
+
+ const addItem = (customer) => {
+ const isExists = data.items.find((i) => i.customer.id === customer.id)
+ if (!isExists) {
+ let items = data.items.concat({
+ customer: customer,
+ customer_id: customer.id,
+ point: 0,
+ description: '',
+ })
+ setData('items', items)
+ }
+ }
+
+ const removeItem = (index) => {
+ let items = data.items.filter((_, i) => i !== index)
+ setData('items', items)
+ }
+
+ const handleChangeValue = (name, value, index) => {
+ setData(
+ 'items',
+ data.items.map((item, i) => {
+ if (i === index) {
+ item[name] = value
+ }
+ return item
+ })
+ )
+ }
+
+ const handleSubmit = () => {
+ post(route('customer-point.store'))
+ }
+
+ return (
+
+
+
+
+
+
+
Point
+
+
+
+
+
+
+ Customer
+ |
+
+ Point
+ |
+
+ Description
+ |
+ |
+
+
+
+ {data.items.map((item, index) => (
+
+
+ {item.customer.name} ({' '}
+ {item.customer.code} )
+ |
+
+
+ handleChangeValue(
+ 'point',
+ e.target.value,
+ index
+ )
+ }
+ />
+ |
+
+
+ handleChangeValue(
+ 'description',
+ e.target.value,
+ index
+ )
+ }
+ />
+ |
+
+
+ removeItem(index)
+ }
+ />
+ |
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/resources/js/Pages/CustomerPoint/ImportModal.jsx b/resources/js/Pages/CustomerPoint/ImportModal.jsx
new file mode 100644
index 0000000..b06964c
--- /dev/null
+++ b/resources/js/Pages/CustomerPoint/ImportModal.jsx
@@ -0,0 +1,102 @@
+import React, { useEffect, useRef } from 'react'
+import { useForm } from '@inertiajs/react'
+import Modal from '@/Components/Modal'
+import Button from '@/Components/Button'
+export default function ImportModal(props) {
+ const { modalState } = props
+
+ const { data, setData, post, progress, processing, errors, clearErrors } =
+ useForm({
+ file: null,
+ })
+
+ const inputFileImport = useRef()
+
+ const handleReset = () => {
+ setData({ file: null })
+ inputFileImport.current.value = ''
+ clearErrors()
+ }
+
+ const handleCancel = () => {
+ modalState.toggle()
+ handleReset()
+ }
+
+ const handleClose = () => {
+ handleReset()
+ modalState.toggle()
+ }
+
+ function handleSubmit(e) {
+ e.preventDefault()
+ post(route('customer-point.import'), {
+ forceFormData: false,
+ onSuccess: () => Promise.all([handleReset(), modalState.toggle()]),
+ })
+ return
+ }
+
+ return (
+
+ {
+ console.log(inputFileImport.current.click())
+ }}
+ >
+
+ Pilih File:{' '}
+
+
{data.file ? data.file.name : 'Pilih File'}
+
+ {errors.file}
+ setData('file', e.target.files[0])}
+ />
+ {progress && (
+
+
+ {' '}
+ {progress.percentage}%
+
+
+ )}
+
+
+ Unduh format file import{' '}
+
+ disini
+
+
+
+
+
+
+
+ )
+}
diff --git a/resources/js/Pages/CustomerPoint/Index.jsx b/resources/js/Pages/CustomerPoint/Index.jsx
new file mode 100644
index 0000000..4e9aed9
--- /dev/null
+++ b/resources/js/Pages/CustomerPoint/Index.jsx
@@ -0,0 +1,158 @@
+import React, { useEffect, useState } from 'react'
+import { router, Link } from '@inertiajs/react'
+import { usePrevious } from 'react-use'
+import { Head } from '@inertiajs/react'
+import { Button } from 'flowbite-react'
+import { useModalState } from '@/hooks'
+
+import { hasPermission } from '@/utils'
+import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
+import Pagination from '@/Components/Pagination'
+import SearchInput from '@/Components/SearchInput'
+import ImportModal from './ImportModal'
+import CustomerSelectionInput from '../Customer/SelectionInput'
+
+export default function Customer(props) {
+ const {
+ query: { links, data },
+ auth,
+ } = props
+
+ const [search, setSearch] = useState('')
+ const [customer_id, setCustomerId] = useState(null)
+ const preValue = usePrevious(`${search}-${customer_id}`)
+
+ const importModal = useModalState()
+
+ const toggleImportModal = () => {
+ importModal.toggle()
+ }
+
+ const params = { q: search, customer_id: customer_id }
+ useEffect(() => {
+ if (preValue) {
+ router.get(
+ route(route().current()),
+ { q: search, customer_id: customer_id },
+ {
+ replace: true,
+ preserveState: true,
+ }
+ )
+ }
+ }, [search, customer_id])
+
+ const canCreate = hasPermission(auth, 'create-customer-point')
+
+ return (
+
+
+
+
+
+
+
+ {canCreate && (
+
+
+
+ Create
+
+
+
+
+ )}
+
+
+
+ setCustomerId(id)
+ }
+ />
+
+
+
+ setSearch(e.target.value)
+ }
+ value={search}
+ />
+
+
+
+
+
+
+
+
+
+ Customer
+ |
+
+ Point
+ |
+
+ Description
+ |
+
+
+
+ {data.map((point) => (
+
+
+ {point.customer.name} ({' '}
+ {point.customer.code} )
+ |
+
+ {point.point}
+ |
+
+ {point.description}
+ |
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/routes/web.php b/routes/web.php
index a29c44d..69484cf 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -52,9 +52,8 @@ Route::middleware(['auth'])->group(function () {
// Customer Point
Route::get('/customers-points', [CustomerPointController::class, 'index'])->name('customer-point.index');
Route::post('/customers-points/import', [CustomerPointController::class, 'import'])->name('customer-point.import');
+ Route::get('/customers-points/create', [CustomerPointController::class, 'create'])->name('customer-point.create');
Route::post('/customers-points', [CustomerPointController::class, 'store'])->name('customer-point.store');
- Route::put('/customers-points/{customer}', [CustomerPointController::class, 'update'])->name('customer-point.update');
- Route::delete('/customers-points/{customer}', [CustomerPointController::class, 'destroy'])->name('customer-point.destroy');
});
Route::middleware('auth')->group(function () {