customer crud

dev
Aji Kamaludin 1 year ago
parent fa0d534a8e
commit d3a470b9fc
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -4,8 +4,8 @@
- [x] CRUD Info - [x] CRUD Info
- [x] CRUD Banner - [x] CRUD Banner
- [ ] CRUD Rekening / Account - [x] CRUD Rekening / Account
- [ ] CRUD Customer - [x] CRUD Customer
- [ ] CRUD Voucher - [ ] CRUD Voucher
- [ ] Import Voucher - [ ] Import Voucher
- [ ] Deposit Menu (view daftar histori deposit) - [ ] Deposit Menu (view daftar histori deposit)

@ -62,13 +62,12 @@ class AuthController extends Controller
->user(); ->user();
} catch (\Exception $e) { } catch (\Exception $e) {
return redirect()->route('customer.login') return redirect()->route('customer.login')
->with('message', ['type' => 'error', 'message' => 'something went wrong']); ->with('message', ['type' => 'error', 'message' => 'Google authentication fail, please try again']);
} }
$customer = Customer::where('google_id', $user->id)->first(); $customer = Customer::where('google_id', $user->id)->first();
if ($customer == null) { if ($customer == null) {
DB::beginTransaction(); DB::beginTransaction();
$basic = CustomerLevel::where('key', CustomerLevel::BASIC)->first();
$customer = Customer::create([ $customer = Customer::create([
'fullname' => $user->name, 'fullname' => $user->name,
'name' => $user->nickname, 'name' => $user->nickname,
@ -76,13 +75,6 @@ class AuthController extends Controller
'username' => Str::slug($user->name . '_' . Str::random(5), '_'), 'username' => Str::slug($user->name . '_' . Str::random(5), '_'),
'google_id' => $user->id, 'google_id' => $user->id,
'google_oauth_response' => json_encode($user), 'google_oauth_response' => json_encode($user),
'customer_level_id' => $basic->id,
]);
CustomerLevelHistory::create([
'customer_id' => $customer->id,
'customer_level_id' => $basic->id,
'date_time' => now(),
]); ]);
DB::commit(); DB::commit();
} }
@ -103,13 +95,12 @@ class AuthController extends Controller
'fullname' => 'required|string', 'fullname' => 'required|string',
'name' => 'required|string', 'name' => 'required|string',
'address' => 'required|string', 'address' => 'required|string',
'phone' => 'required|string|numeric', 'phone' => 'required|numeric',
'username' => 'required|string|min:5|alpha_dash|unique:customers,username', 'username' => 'required|string|min:5|alpha_dash|unique:customers,username',
'password' => 'required|string|min:8|confirmed', 'password' => 'required|string|min:8|confirmed',
]); ]);
DB::beginTransaction(); DB::beginTransaction();
$basic = CustomerLevel::where('key', CustomerLevel::BASIC)->first();
$customer = Customer::create([ $customer = Customer::create([
'fullname' => $request->fullname, 'fullname' => $request->fullname,
'name' => $request->name, 'name' => $request->name,
@ -117,12 +108,6 @@ class AuthController extends Controller
'phone' => $request->phone, 'phone' => $request->phone,
'username' => $request->username, 'username' => $request->username,
'password' => bcrypt($request->password), 'password' => bcrypt($request->password),
'customer_level_id' => $basic->id,
]);
CustomerLevelHistory::create([
'customer_id' => $customer->id,
'customer_level_id' => $basic->id,
'date_time' => now(),
]); ]);
DB::commit(); DB::commit();

@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers;
use App\Models\Customer;
use Illuminate\Http\Request;
class CustomerController extends Controller
{
public function index(Request $request)
{
$query = Customer::query()->with(['level']);
if ($request->q != '') {
$query->where('name', 'like', "%$request->q%")
->orWhere('fullname', 'like', "%$request->q%")
->orWhere('email', 'like', "%$request->q%")
->orWhere('phone', 'like', "%$request->q%");
}
return inertia('Customer/Index', [
'query' => $query->paginate(),
]);
}
public function create()
{
return inertia('Customer/Form');
}
public function store(Request $request)
{
$request->validate([
'username' => 'required|string|min:5|alpha_dash|unique:customers,username',
'password' => 'required|string|min:8',
'name' => 'required|string',
'fullname' => 'required|string',
'address' => 'required|string',
'phone' => 'required|string',
'image' => 'nullable|string',
]);
Customer::create([
'username' => $request->username,
'password' => bcrypt($request->password),
'name' => $request->name,
'fullname' => $request->fullname,
'address' => $request->address,
'phone' => $request->phone,
'image' => $request->image,
]);
return redirect()->route('customer.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed saved']);
}
public function edit(Customer $customer)
{
return inertia('Customer/Form', [
'customer' => $customer
]);
}
public function update(Request $request, Customer $customer)
{
$request->validate([
'username' => 'required|string|min:5|alpha_dash|unique:customers,username,' . $customer->id,
'password' => 'nullable|string|min:8',
'name' => 'required|string',
'fullname' => 'required|string',
'address' => 'required|string',
'phone' => 'required|string',
'image' => 'nullable|image',
]);
if ($request->password != '') {
$customer->password = bcrypt($request->password);
}
if ($request->hasFile('image')) {
$file = $request->file('image');
$file->store('uploads', 'public');
$customer->image = $file->hashName('uploads');
}
$customer->update([
'username' => $request->username,
'password' => $customer->password,
'name' => $request->name,
'fullname' => $request->fullname,
'address' => $request->address,
'phone' => $request->phone,
'image' => $customer->image,
]);
return redirect()->route('customer.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed updated']);
}
public function destroy(Customer $customer)
{
$customer->delete();
return redirect()->route('customer.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed deleted']);
}
}

@ -44,6 +44,37 @@ class Customer extends Authenticatable
'display_phone' 'display_phone'
]; ];
protected static function booted(): void
{
static::creating(function (Customer $customer) {
if ($customer->customer_level_id == null) {
$basic = CustomerLevel::where('key', CustomerLevel::BASIC)->first();
$customer->customer_level_id = $basic->id;
CustomerLevelHistory::create([
'customer_id' => $customer->id,
'customer_level_id' => $basic->id,
'date_time' => now(),
]);
}
});
static::updating(function (Customer $customer) {
if ($customer->isDirty('customer_level_id')) {
$level = CustomerLevel::find($customer->customer_level_id);
$customer->customer_level_id = $level->id;
CustomerLevelHistory::create([
'customer_id' => $customer->id,
'customer_level_id' => $level->id,
'date_time' => now(),
]);
}
});
}
public function imageUrl(): Attribute public function imageUrl(): Attribute
{ {
return Attribute::make( return Attribute::make(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

@ -5,7 +5,12 @@ import {
HiUserGroup, HiUserGroup,
HiInformationCircle, HiInformationCircle,
} from 'react-icons/hi' } from 'react-icons/hi'
import { HiBanknotes, HiQuestionMarkCircle } from 'react-icons/hi2' import {
HiBanknotes,
HiOutlineGlobeAlt,
HiQuestionMarkCircle,
HiUserCircle,
} from 'react-icons/hi2'
export default [ export default [
{ {
@ -16,6 +21,14 @@ export default [
active: 'dashboard', active: 'dashboard',
permission: 'view-dashboard', permission: 'view-dashboard',
}, },
{
name: 'Customer',
show: true,
icon: HiUserCircle,
route: route('customer.index'),
active: 'customer.*',
permission: 'view-customer',
},
{ {
name: 'Bank Akun', name: 'Bank Akun',
show: true, show: true,
@ -25,20 +38,27 @@ export default [
permission: 'view-account', permission: 'view-account',
}, },
{ {
name: 'Banner', name: 'Front Home',
show: true, show: true,
icon: HiInformationCircle, icon: HiOutlineGlobeAlt,
route: route('banner.index'), items: [
active: 'banner.*', {
permission: 'view-banner', name: 'Banner',
}, show: true,
{ icon: HiInformationCircle,
name: 'Info', route: route('banner.index'),
show: true, active: 'banner.*',
icon: HiQuestionMarkCircle, permission: 'view-banner',
route: route('info.index'), },
active: 'info.index', {
permission: 'view-info', name: 'Info',
show: true,
icon: HiQuestionMarkCircle,
route: route('info.index'),
active: 'info.index',
permission: 'view-info',
},
],
}, },
{ {
name: 'User', name: 'User',

@ -0,0 +1,149 @@
import React, { useEffect, Suspense } from 'react'
import { isEmpty } from 'lodash'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import FormInput from '@/Components/FormInput'
import Button from '@/Components/Button'
import { Head, useForm } from '@inertiajs/react'
import FormFile from '@/Components/FormFile'
import FormInputWith from '@/Components/FormInputWith'
import TextArea from '@/Components/TextArea'
export default function Form(props) {
const { customer } = props
const { data, setData, post, processing, errors } = useForm({
username: '',
password: '',
name: '',
fullname: '',
address: '',
phone: '',
image: '',
image_url: '',
})
const handleOnChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
? 1
: 0
: event.target.value
)
}
const handleSubmit = () => {
if (isEmpty(customer) === false) {
post(route('customer.update', customer))
return
}
post(route('customer.store'))
}
useEffect(() => {
if (isEmpty(customer) === false) {
setData({
username: customer.username,
password: customer.password,
name: customer.name,
fullname: customer.fullname,
address: customer.address,
phone: customer.phone,
image: customer.image,
image_url: customer.image_url,
})
}
}, [customer])
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
page={'Customer'}
action={'Form'}
>
<Head title="Customer" />
<div>
<div className="mx-auto sm:px-6 lg:px-8">
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col ">
<div className="text-xl font-bold mb-4">Customer</div>
<FormInput
name="fullname"
value={data.fullname}
onChange={handleOnChange}
label="Nama Lengkap"
error={errors.fullname}
/>
<FormInput
name="name"
value={data.name}
onChange={handleOnChange}
label="Nama Panggilan"
error={errors.name}
/>
<FormInputWith
type="number"
leftItem={<div className="text-sm">+62</div>}
name="phone"
value={data.phone}
onChange={handleOnChange}
error={errors.phone}
formClassName={'pl-10'}
label="Whatsapp"
/>
<TextArea
name="address"
value={data.address}
onChange={handleOnChange}
label="Alamat Lengkap"
error={errors.address}
rows={4}
/>
<FormInput
name="username"
value={data.username}
onChange={handleOnChange}
label="username"
error={errors.username}
/>
<FormInput
name="password"
value={data.password}
onChange={handleOnChange}
label="password"
error={errors.password}
/>
<FormFile
label={'Image'}
onChange={(e) =>
setData('image', e.target.files[0])
}
error={errors.image}
preview={
isEmpty(data.image_url) === false && (
<img
src={data.image_url}
className="mb-1 h-24 w-24 object-cover rounded-full"
alt="preview"
/>
)
}
/>
<div className="mt-8">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
)
}

@ -0,0 +1,203 @@
import React, { useEffect, useState } from 'react'
import { Link, router } from '@inertiajs/react'
import { usePrevious } from 'react-use'
import { Head } from '@inertiajs/react'
import { Button, Dropdown } from 'flowbite-react'
import { HiPencil, HiTrash } from 'react-icons/hi'
import { useModalState } from '@/hooks'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import ModalConfirm from '@/Components/ModalConfirm'
import SearchInput from '@/Components/SearchInput'
import { hasPermission } from '@/utils'
export default function Customer(props) {
const {
query: { links, data },
auth,
} = props
const [search, setSearch] = useState('')
const preValue = usePrevious(search)
const confirmModal = useModalState()
const handleDeleteClick = (customer) => {
confirmModal.setData(customer)
confirmModal.toggle()
}
const onDelete = () => {
if (confirmModal.data !== null) {
router.delete(route('customer.destroy', confirmModal.data.id))
}
}
const params = { q: search }
useEffect(() => {
if (preValue) {
router.get(
route(route().current()),
{ q: search },
{
replace: true,
preserveState: true,
}
)
}
}, [search])
const canCreate = hasPermission(auth, 'create-customer')
const canUpdate = hasPermission(auth, 'update-customer')
const canDelete = hasPermission(auth, 'delete-customer')
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
page={'Customer'}
action={''}
>
<Head title="Customer" />
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex justify-between">
{canCreate && (
<Link href={route('customer.create')}>
<Button size="sm">Tambah</Button>
</Link>
)}
<div className="flex items-center">
<SearchInput
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
</div>
</div>
<div className="overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th
scope="col"
className="py-3 px-6"
>
Nama
</th>
<th
scope="col"
className="py-3 px-6"
>
Level
</th>
<th
scope="col"
className="py-3 px-6"
>
Deposit
</th>
<th
scope="col"
className="py-3 px-6"
>
Coin
</th>
<th
scope="col"
className="py-3 px-6 w-1/8"
/>
</tr>
</thead>
<tbody>
{data.map((customer) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={customer.id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{customer.name}
</td>
<td
scope="row"
className="py-4 px-6"
>
{customer.level.name}
</td>
<td
scope="row"
className="py-4 px-6"
>
{customer.display_deposit}
</td>
<td
scope="row"
className="py-4 px-6"
>
{customer.display_coin}
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
label={'Opsi'}
floatingArrow={true}
arrowIcon={true}
dismissOnClick={true}
size={'sm'}
>
{canUpdate && (
<Dropdown.Item>
<Link
href={route(
'customer.edit',
customer
)}
className="flex space-x-1 items-center"
>
<HiPencil />
<div>
Ubah
</div>
</Link>
</Dropdown.Item>
)}
{canDelete && (
<Dropdown.Item
onClick={() =>
handleDeleteClick(
customer
)
}
>
<div className="flex space-x-1 items-center">
<HiTrash />
<div>
Hapus
</div>
</div>
</Dropdown.Item>
)}
</Dropdown>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="w-full flex items-center justify-center">
<Pagination links={links} params={params} />
</div>
</div>
</div>
</div>
</div>
<ModalConfirm modalState={confirmModal} onConfirm={onDelete} />
</AuthenticatedLayout>
)
}

@ -3,6 +3,7 @@
use App\Http\Controllers\AccountController; use App\Http\Controllers\AccountController;
use App\Http\Controllers\Auth\AuthenticatedSessionController; use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\BannerController; use App\Http\Controllers\BannerController;
use App\Http\Controllers\CustomerController;
use App\Http\Controllers\GeneralController; use App\Http\Controllers\GeneralController;
use App\Http\Controllers\InfoController; use App\Http\Controllers\InfoController;
use App\Http\Controllers\ProfileController; use App\Http\Controllers\ProfileController;
@ -66,5 +67,13 @@ Route::middleware(['http_secure_aware', 'inertia.admin'])
Route::get('/banner/{banner}', [BannerController::class, 'edit'])->name('banner.edit'); Route::get('/banner/{banner}', [BannerController::class, 'edit'])->name('banner.edit');
Route::post('/banner/{banner}', [BannerController::class, 'update'])->name('banner.update'); Route::post('/banner/{banner}', [BannerController::class, 'update'])->name('banner.update');
Route::delete('/banner/{banner}', [BannerController::class, 'destroy'])->name('banner.destroy'); Route::delete('/banner/{banner}', [BannerController::class, 'destroy'])->name('banner.destroy');
// customer
Route::get('/customers', [CustomerController::class, 'index'])->name('customer.index');
Route::get('/customers/create', [CustomerController::class, 'create'])->name('customer.create');
Route::post('/customers', [CustomerController::class, 'store'])->name('customer.store');
Route::get('/customers/{customer}', [CustomerController::class, 'edit'])->name('customer.edit');
Route::post('/customers/{customer}', [CustomerController::class, 'update'])->name('customer.update');
Route::delete('/customers/{customer}', [CustomerController::class, 'destroy'])->name('customer.destroy');
}); });
}); });

Loading…
Cancel
Save