coin referral and history

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

@ -33,7 +33,7 @@
- [x] Customer Deposit Payment Gateway - [x] Customer Deposit Payment Gateway
- [x] Customer Purchase Voucher - [x] Customer Purchase Voucher
- [x] Customer Share Buyed Voucher, via WA dll - [x] Customer Share Buyed Voucher, via WA dll
- [ ] Register Refferal - [x] Register Refferal
- [ ] Customer View Coin History - [x] Customer View Coin History
- [ ] Verified Akun - [ ] Verified Akun
- [ ] Notification (purchase success, deposit success) - [ ] Notification (purchase success, deposit success)

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Customer; use App\Models\Customer;
use App\Models\Setting;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -96,6 +97,7 @@ class AuthController extends Controller
'phone' => 'required|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',
'referral_code' => 'nullable|exists:customers,referral_code'
]); ]);
DB::beginTransaction(); DB::beginTransaction();
@ -107,6 +109,26 @@ class AuthController extends Controller
'username' => $request->username, 'username' => $request->username,
'password' => bcrypt($request->password), 'password' => bcrypt($request->password),
]); ]);
if ($request->referral_code != '') {
$refferal = Customer::where('referral_code', $request->referral_code)->first();
$refferal->customerRefferals()->create([
'refferal_id' => $customer->id,
'customer_code' => $refferal->referral_code
]);
$affilateEnabled = Setting::getByKey('AFFILATE_ENABLED');
if ($affilateEnabled == 1) {
$bonusCoin = Setting::getByKey('AFFILATE_COIN_AMOUNT');
$coin = $refferal->coins()->create([
'debit' => $bonusCoin,
'description' => 'Bonus Refferal #' . Str::random(5),
]);
$coin->update_customer_balance();
}
}
DB::commit(); DB::commit();
Auth::guard('customer')->loginUsingId($customer->id); Auth::guard('customer')->loginUsingId($customer->id);

@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\CoinHistory;
use Illuminate\Http\Request;
class CoinController extends Controller
{
public function index()
{
$coins = CoinHistory::where('customer_id', auth()->id())
->orderBy('updated_at', 'desc');
return inertia('Home/Coin/Index', [
'coins' => $coins->paginate(20),
]);
}
public function show(CoinHistory $coin)
{
return inertia('Home/Coin/Detail', [
'coin' => $coin,
]);
}
}

@ -2,6 +2,9 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Carbon;
class CoinHistory extends Model class CoinHistory extends Model
{ {
protected $fillable = [ protected $fillable = [
@ -13,8 +16,46 @@ class CoinHistory extends Model
'related_id', 'related_id',
]; ];
protected $appends = [
'amount',
'format_human_created_at',
'format_created_at',
];
public function formatHumanCreatedAt(): Attribute
{
return Attribute::make(get: function () {
return Carbon::parse($this->created_at)->locale('id')->translatedFormat('d F Y');
});
}
public function formatCreatedAt(): Attribute
{
return Attribute::make(get: function () {
return Carbon::parse($this->created_at)->locale('id')->translatedFormat('d F Y H:i:s');
});
}
public function amount(): Attribute
{
return Attribute::make(get: function () {
if ($this->credit == 0) {
return 'Rp' . number_format($this->debit, 0, ',', '.');
}
return '-Rp' . number_format($this->credit, 0, ',', '.');
});
}
public function customer() public function customer()
{ {
return $this->belongsTo(Customer::class); return $this->belongsTo(Customer::class);
} }
public function update_customer_balance()
{
$customer = Customer::find($this->customer_id);
$customer->update(['coin_balance' => $customer->coin_balance + $this->debit - $this->credit]);
}
} }

@ -59,7 +59,7 @@ class Customer extends Authenticatable
$basic = CustomerLevel::where('key', CustomerLevel::BASIC)->first(); $basic = CustomerLevel::where('key', CustomerLevel::BASIC)->first();
$customer->customer_level_id = $basic->id; $customer->customer_level_id = $basic->id;
$customer->referral_code = Str::random(6); $customer->referral_code = Str::upper(Str::random(6));
CustomerLevelHistory::create([ CustomerLevelHistory::create([
'customer_id' => $customer->id, 'customer_id' => $customer->id,
@ -142,4 +142,14 @@ class Customer extends Authenticatable
{ {
return $this->hasMany(DepositHistory::class); return $this->hasMany(DepositHistory::class);
} }
public function coins()
{
return $this->hasMany(CoinHistory::class);
}
public function customerRefferals()
{
return $this->hasMany(CustomerRefferal::class);
}
} }

@ -5,8 +5,8 @@ namespace App\Models;
class CustomerRefferal extends Model class CustomerRefferal extends Model
{ {
protected $fillable = [ protected $fillable = [
'customer_id', 'customer_id', //customer has code
'refferal_id', 'refferal_id', //customer use the code
'customer_code', 'customer_code', //code of referal
]; ];
} }

@ -107,6 +107,12 @@ export default function Customer(props) {
> >
Coin Coin
</th> </th>
<th
scope="col"
className="py-3 px-6"
>
Referal Code
</th>
<th <th
scope="col" scope="col"
className="py-3 px-6 w-1/8" className="py-3 px-6 w-1/8"
@ -143,6 +149,12 @@ export default function Customer(props) {
> >
{customer.display_coin} {customer.display_coin}
</td> </td>
<td
scope="row"
className="py-4 px-6"
>
{customer.referral_code}
</td>
<td className="py-4 px-6 flex justify-end"> <td className="py-4 px-6 flex justify-end">
<Dropdown <Dropdown
label={'Opsi'} label={'Opsi'}

@ -18,6 +18,7 @@ export default function Index({ app_name, flash }) {
name: '', name: '',
address: '', address: '',
phone: '', phone: '',
referral_code: '',
}) })
const handleOnChange = (event) => { const handleOnChange = (event) => {
@ -129,6 +130,16 @@ export default function Index({ app_name, flash }) {
onKeyDownCapture={(e) => handleKeyDown(e)} onKeyDownCapture={(e) => handleKeyDown(e)}
/> />
</div> </div>
<div className="w-full my-4">
<FormInput
placeholder="referral code (optional)"
name="referral_code"
value={data.referral_code}
onChange={handleOnChange}
error={errors.referral_code}
onKeyDownCapture={(e) => handleKeyDown(e)}
/>
</div>
<div className="w-full flex flex-row justify-between"> <div className="w-full flex flex-row justify-between">
<Button processing={processing} onClick={handleSubmit}> <Button processing={processing} onClick={handleSubmit}>
Register Register

@ -0,0 +1,36 @@
import React from 'react'
import { Head, router } from '@inertiajs/react'
import { HiChevronLeft } from 'react-icons/hi2'
import CustomerLayout from '@/Layouts/CustomerLayout'
export default function Detail({ coin }) {
return (
<CustomerLayout>
<Head title="Top Up" />
<div className="flex flex-col min-h-[calc(95dvh)]">
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('customer.coin.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />
</div>
{/* detail */}
<div className="flex flex-row justify-between items-center pb-5 border-b px-5">
<div>
<div className="font-semibold text-xl text-gray-400">
{coin.description}
</div>
<div className="font-bold text-3xl">{coin.amount}</div>
<div className="text-gray-400">
{coin.format_created_at}
</div>
</div>
</div>
</div>
</CustomerLayout>
)
}

@ -0,0 +1,83 @@
import React, { useState } from 'react'
import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout'
export default function Index({
auth: { user },
coins: { data, next_page_url },
}) {
const [_coins, setCoins] = useState(data)
const handleNextPage = () => {
router.get(
next_page_url,
{},
{
replace: true,
preserveState: true,
only: ['coins'],
onSuccess: (res) => {
setCoins(_coins.concat(res.props.coins.data))
},
}
)
}
return (
<CustomerLayout>
<Head title="Top Up" />
<div className="flex flex-col w-full min-h-[calc(90dvh)]">
<div className="w-full pt-10 px-5">
<div className="text-base">{user.fullname}</div>
</div>
<div className="flex flex-row justify-between items-center pb-10 border-b px-5">
<div>
<div className="font-semibold text-xl text-gray-400">
Coin
</div>
<div className="font-bold text-3xl">
Rp {user.display_coin}
</div>
</div>
</div>
<div className="w-full">
<div className="flex flex-col py-10 space-y-5 px-5">
{_coins.map((coin) => (
<div
key={coin.id}
className="flex flex-row pb-2 items-center justify-between border-b"
onClick={() =>
router.get(
route('customer.coin.show', coin.id)
)
}
>
<div className="flex flex-col">
<div className="font-bold">
{coin.format_human_created_at}
</div>
<div className="font-thin">
{coin.description}
</div>
</div>
<div className="flex flex-col items-end">
<div className="font-bold text-lg">
{coin.amount}
</div>
</div>
</div>
))}
{next_page_url !== null && (
<div
onClick={handleNextPage}
className="w-full text-center px-2 py-1 border mt-5 hover:bg-blue-600 hover:text-white"
>
Load more
</div>
)}
</div>
</div>
</div>
</CustomerLayout>
)
}

@ -1,13 +1,14 @@
import React from 'react' import React from 'react'
import { Head, Link, router } from '@inertiajs/react' import { Head, router } from '@inertiajs/react'
import CustomerLayout from '@/Layouts/CustomerLayout' import { toast } from 'react-toastify'
import UserBanner from '../Index/UserBanner' import { HiOutlineBell } from 'react-icons/hi'
import { import {
HiChevronRight, HiChevronRight,
HiOutlineUser, HiClipboardDocumentList,
HiOutlineUserCircle, HiOutlineUserCircle,
} from 'react-icons/hi2' } from 'react-icons/hi2'
import { HiOutlineBell, HiOutlineCash } from 'react-icons/hi'
import CustomerLayout from '@/Layouts/CustomerLayout'
import { useModalState } from '@/hooks' import { useModalState } from '@/hooks'
import ModalConfirm from '@/Components/ModalConfirm' import ModalConfirm from '@/Components/ModalConfirm'
import BalanceBanner from '../Index/BalanceBanner' import BalanceBanner from '../Index/BalanceBanner'
@ -23,6 +24,11 @@ export default function Index({ auth: { user } }) {
router.post(route('customer.logout')) router.post(route('customer.logout'))
} }
const handleCopyToClipboard = (text) => {
toast.info('copied to clipboard')
navigator.clipboard.writeText(text)
}
return ( return (
<CustomerLayout> <CustomerLayout>
<Head title="Profile" /> <Head title="Profile" />
@ -62,6 +68,21 @@ export default function Index({ auth: { user } }) {
{/* saldo */} {/* saldo */}
<BalanceBanner user={user} /> <BalanceBanner user={user} />
</div> </div>
<div className="p-4 pb-0">
<div
className="p-4 text-blue-800 rounded-lg bg-blue-50 flex flex-row space-x-2 w-full items-center"
role="alert"
onClick={() =>
handleCopyToClipboard(user.referral_code)
}
>
<div>Referral Code: </div>
<div className="font-bold">{user.referral_code}</div>
<div>
<HiClipboardDocumentList className="text-blue-600" />
</div>
</div>
</div>
<div className="p-4 flex flex-col"> <div className="p-4 flex flex-col">
<div className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100"> <div className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100">
<div>Upgrade Member</div> <div>Upgrade Member</div>
@ -76,7 +97,10 @@ export default function Index({ auth: { user } }) {
<div>Deposit Saldo</div> <div>Deposit Saldo</div>
<HiChevronRight className="h-5 w-5" /> <HiChevronRight className="h-5 w-5" />
</div> </div>
<div className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100"> <div
className="flex flex-row justify-between items-center px-2 py-4 w-full border-b border-gray-400 hover:bg-gray-100"
onClick={() => router.get(route('customer.coin.index'))}
>
<div>Coin</div> <div>Coin</div>
<HiChevronRight className="h-5 w-5" /> <HiChevronRight className="h-5 w-5" />
</div> </div>

@ -2,6 +2,7 @@
use App\Http\Controllers\Customer\AuthController; use App\Http\Controllers\Customer\AuthController;
use App\Http\Controllers\Customer\CartController; use App\Http\Controllers\Customer\CartController;
use App\Http\Controllers\Customer\CoinController;
use App\Http\Controllers\Customer\DepositController; use App\Http\Controllers\Customer\DepositController;
use App\Http\Controllers\Customer\HomeController; use App\Http\Controllers\Customer\HomeController;
use App\Http\Controllers\Customer\ProfileController; use App\Http\Controllers\Customer\ProfileController;
@ -39,6 +40,10 @@ Route::middleware(['http_secure_aware', 'guard_should_customer', 'inertia.custom
Route::get('deposit/trx/{deposit}', [DepositController::class, 'show'])->name('customer.deposit.show'); Route::get('deposit/trx/{deposit}', [DepositController::class, 'show'])->name('customer.deposit.show');
Route::post('deposit/trx/{deposit}', [DepositController::class, 'update'])->name('customer.deposit.update'); Route::post('deposit/trx/{deposit}', [DepositController::class, 'update'])->name('customer.deposit.update');
// coin
Route::get('coin', [CoinController::class, 'index'])->name('customer.coin.index');
Route::get('coin/{coin}', [CoinController::class, 'show'])->name('customer.coin.show');
// cart // cart
Route::get('cart', [CartController::class, 'index'])->name('cart.index'); Route::get('cart', [CartController::class, 'index'])->name('cart.index');
Route::post('cart/process', [CartController::class, 'purchase'])->name('cart.purchase'); Route::post('cart/process', [CartController::class, 'purchase'])->name('cart.purchase');

Loading…
Cancel
Save