diff --git a/app/Http/Controllers/Admin/CustomerController.php b/app/Http/Controllers/Admin/CustomerController.php index 1b6461f..a5e2826 100644 --- a/app/Http/Controllers/Admin/CustomerController.php +++ b/app/Http/Controllers/Admin/CustomerController.php @@ -62,6 +62,7 @@ class CustomerController extends Controller public function store(Request $request) { $request->validate([ + 'email' => 'nullable|email|unique:customers,email', 'username' => 'required|string|min:5|alpha_dash|unique:customers,username', 'password' => 'required|string|min:8', 'name' => 'required|string', @@ -73,6 +74,7 @@ class CustomerController extends Controller ]); $customer = Customer::make([ + 'email' => $request->email, 'username' => $request->username, 'password' => bcrypt($request->password), 'name' => $request->name, @@ -106,6 +108,7 @@ class CustomerController extends Controller public function update(Request $request, Customer $customer) { $request->validate([ + 'email' => 'nullable|email|unique:customers,email,' . $customer->id, 'username' => 'required|string|min:5|alpha_dash|unique:customers,username,' . $customer->id, 'password' => 'nullable|string|min:8', 'name' => 'required|string', @@ -127,6 +130,7 @@ class CustomerController extends Controller } $customer->update([ + 'email' => $request->email, 'username' => $request->username, 'password' => $customer->password, 'name' => $request->name, @@ -154,6 +158,7 @@ class CustomerController extends Controller $request->validate([ 'level' => 'required|exists:customer_levels,key', 'paylater_limit' => 'required|numeric', + 'day_deadline' => 'required|numeric', ]); $level = CustomerLevel::where('key', $request->level)->first(); @@ -164,6 +169,7 @@ class CustomerController extends Controller 'customer_id' => $customer->id, ], [ 'limit' => $request->paylater_limit, + 'day_deadline' => $request->day_deadline ]); return redirect()->route('customer.index') @@ -173,7 +179,8 @@ class CustomerController extends Controller public function update_partner(Request $request, Customer $customer) { $request->validate([ - 'job' => 'required|string', + 'id_number' => 'nullable|string', + 'job' => 'nullable|string', 'image_selfie' => 'nullable|file', 'file_statement' => 'nullable|file', 'file_agreement' => 'nullable|file', @@ -187,6 +194,7 @@ class CustomerController extends Controller $partner = CustomerAsDataPartner::updateOrCreate([ 'customer_id' => $customer->id, ], [ + 'id_number' => $request->id_number, 'job' => $request->job, 'additional_json' => json_encode($request->items), ]); diff --git a/app/Http/Controllers/Admin/CustomerMitraController.php b/app/Http/Controllers/Admin/CustomerMitraController.php new file mode 100644 index 0000000..25038ae --- /dev/null +++ b/app/Http/Controllers/Admin/CustomerMitraController.php @@ -0,0 +1,298 @@ + Customer::whereHas('level', function ($q) { + return $q->where('key', CustomerLevel::GOLD) + ->orWhere('key', CustomerLevel::PLATINUM); + })->count(), + 'blocked_mitra' => Customer::whereHas('level', function ($q) { + return $q->where('key', CustomerLevel::GOLD) + ->orWhere('key', CustomerLevel::PLATINUM); + }) + ->where('status', Customer::STATUS_SUSPEND) + ->count(), + 'active_mitra' => Customer::whereHas('level', function ($q) { + return $q->where('key', CustomerLevel::GOLD) + ->orWhere('key', CustomerLevel::PLATINUM); + }) + ->where('status', '!=', Customer::STATUS_SUSPEND) + ->count(), + 'sum_paylater_limit' => PaylaterCustomer::sum('limit'), + 'sum_paylater_usage' => PaylaterCustomer::sum('usage'), + 'sum_paylater_remain' => PaylaterCustomer::selectRaw('(SUM("limit") - SUM(usage)) as remain')->value('remain'), + ]; + + $query = Customer::query()->with(['level', 'paylater', 'locationFavorites']) + ->whereHas('level', function ($q) { + $q->where('key', CustomerLevel::GOLD) + ->orWhere('key', CustomerLevel::PLATINUM); + }); + + if ($request->q != '') { + $query->where(function ($query) use ($request) { + $query->where('name', 'like', "%$request->q%") + ->orWhere('fullname', 'like', "%$request->q%") + ->orWhere('email', 'like', "%$request->q%") + ->orWhere('phone', 'like', "%$request->q%"); + }); + } + + if ($request->location_id != '') { + $query->whereHas('locationFavorites', fn ($q) => $q->where('id', $request->location_id)); + } + + if ($request->level_id != '') { + $query->where('customer_level_id', $request->level_id); + } + + if ($request->sortBy != '' && $request->sortRule != '') { + $query->orderBy($request->sortBy, $request->sortRule); + } else { + $query->orderBy('updated_at', 'desc'); + } + + return inertia('CustomerMitra/Index', [ + 'query' => $query->paginate(), + 'stats' => $stats, + ]); + } + + public function create() + { + $levels = CustomerLevel::where('key', CustomerLevel::GOLD) + ->orWhere('key', CustomerLevel::PLATINUM) + ->get(); + + return inertia('CustomerMitra/Form', [ + 'levels' => $levels, + 'statuses' => Customer::STATUS + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'email' => 'nullable|email|unique:customers,email', + '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', + 'status' => 'required|numeric', + 'image' => 'nullable|image', + 'identity_image' => 'nullable|image', + // + 'level' => 'required|exists:customer_levels,key', + 'paylater_limit' => 'required|numeric', + 'day_deadline' => 'required|numeric', + // + 'id_number' => 'nullable|string', + 'job' => 'nullable|string', + 'image_selfie' => 'nullable|image', + 'file_statement' => 'nullable|file', + 'file_agreement' => 'nullable|file', + 'items' => 'nullable|array', + 'items.*.name' => 'nullable|string', + 'items.*.type' => 'required|in:text,file', + 'items.*.value' => 'nullable|string', + ]); + + $level = CustomerLevel::where('key', $request->level)->first(); + + DB::beginTransaction(); + $customer = Customer::make([ + 'email' => $request->email, + 'username' => $request->username, + 'password' => bcrypt($request->password), + 'name' => $request->name, + 'fullname' => $request->fullname, + 'address' => $request->address, + 'phone' => $request->phone, + 'status' => $request->status, + 'customer_level_id' => $level->id, + 'identity_verified' => $request->hasFile('identity_image') ? Customer::VERIFIED : Customer::NOT_VERIFIED + ]); + + if ($request->hasFile('image')) { + $file = $request->file('image'); + $file->store('uploads', 'public'); + $customer->image = $file->hashName('uploads'); + } + + if ($request->hasFile('identity_image')) { + $file = $request->file('identity_image'); + $file->store('uploads', 'public'); + $customer->identity_image = $file->hashName('uploads'); + } + + $customer->save(); + + $customer->paylater()->create([ + 'limit' => $request->paylater_limit, + 'day_deadline' => $request->day_deadline + ]); + + $partner = $customer->partner()->create([ + 'id_number' => $request->id_number, + 'job' => $request->job, + 'additional_json' => json_encode($request->items), + ]); + + if ($request->hasFile('image_selfie')) { + $file = $request->file('image_selfie'); + $file->store('uploads', 'public'); + $partner->update(['image_selfie' => $file->hashName('uploads')]); + } + + if ($request->hasFile('file_statement')) { + $file = $request->file('file_statement'); + $file->store('uploads', 'public'); + $partner->update(['file_statement' => $file->hashName('uploads')]); + } + + if ($request->hasFile('file_agreement')) { + $file = $request->file('file_agreement'); + $file->store('uploads', 'public'); + $partner->update(['file_agreement' => $file->hashName('uploads')]); + } + DB::commit(); + + return redirect()->route('mitra.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed created']); + } + + public function edit(Customer $customer) + { + $levels = CustomerLevel::where('key', CustomerLevel::GOLD) + ->orWhere('key', CustomerLevel::PLATINUM) + ->get(); + + return inertia('CustomerMitra/Form', [ + 'customer' => $customer->load(['paylater', 'partner']), + 'levels' => $levels, + 'statuses' => Customer::STATUS + ]); + } + + public function update(Request $request, Customer $customer) + { + $request->validate([ + 'email' => 'nullable|email|unique:customers,email,' . $customer->id, + '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', + 'status' => 'required|numeric', + 'image' => 'nullable|image', + 'identity_image' => 'nullable|image', + // + 'level' => 'required|exists:customer_levels,key', + 'paylater_limit' => 'required|numeric', + 'day_deadline' => 'required|numeric', + // + 'id_number' => 'nullable|string', + 'job' => 'nullable|string', + 'image_selfie' => 'nullable|image', + 'file_statement' => 'nullable|file', + 'file_agreement' => 'nullable|file', + 'items' => 'nullable|array', + 'items.*.name' => 'nullable|string', + 'items.*.type' => 'required|in:text,file', + 'items.*.value' => 'nullable|string', + ]); + + $level = CustomerLevel::where('key', $request->level)->first(); + + DB::beginTransaction(); + $customer->fill([ + 'email' => $request->email, + 'username' => $request->username, + 'name' => $request->name, + 'fullname' => $request->fullname, + 'address' => $request->address, + 'phone' => $request->phone, + 'status' => $request->status, + 'customer_level_id' => $level->id, + 'identity_verified' => $request->hasFile('identity_image') ? Customer::VERIFIED : Customer::NOT_VERIFIED + ]); + + 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'); + } + + if ($request->hasFile('identity_image')) { + $file = $request->file('identity_image'); + $file->store('uploads', 'public'); + $customer->identity_image = $file->hashName('uploads'); + } + + $customer->save(); + + $customer->paylater()->updateOrCreate([ + 'customer_id' => $customer->id, + ], [ + 'limit' => $request->paylater_limit, + 'day_deadline' => $request->day_deadline + ]); + + $partner = $customer->partner()->updateOrCreate([ + 'customer_id' => $customer->id, + ], [ + 'id_number' => $request->id_number, + 'job' => $request->job, + 'additional_json' => json_encode($request->items), + ]); + + if ($request->hasFile('image_selfie')) { + $file = $request->file('image_selfie'); + $file->store('uploads', 'public'); + $partner->update(['image_selfie' => $file->hashName('uploads')]); + } + + if ($request->hasFile('file_statement')) { + $file = $request->file('file_statement'); + $file->store('uploads', 'public'); + $partner->update(['file_statement' => $file->hashName('uploads')]); + } + + if ($request->hasFile('file_agreement')) { + $file = $request->file('file_agreement'); + $file->store('uploads', 'public'); + $partner->update(['file_agreement' => $file->hashName('uploads')]); + } + DB::commit(); + + return redirect()->route('mitra.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed updated']); + } + + public function destroy(Customer $customer) + { + $customer->delete(); + + return redirect()->route('mitra.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed deleted']); + } +} diff --git a/app/Http/Controllers/Api/CustomerLevelController.php b/app/Http/Controllers/Api/CustomerLevelController.php index eb8e136..9685ed7 100644 --- a/app/Http/Controllers/Api/CustomerLevelController.php +++ b/app/Http/Controllers/Api/CustomerLevelController.php @@ -10,6 +10,12 @@ class CustomerLevelController extends Controller { public function index(Request $request) { - return CustomerLevel::all(); + $query = CustomerLevel::query(); + + if ($request->get('only') != '') { + $query->whereIn('key', $request->get('only')); + } + + return $query->get(); } } diff --git a/app/Http/Controllers/Customer/AuthController.php b/app/Http/Controllers/Customer/AuthController.php index 6876ed8..993ec13 100644 --- a/app/Http/Controllers/Customer/AuthController.php +++ b/app/Http/Controllers/Customer/AuthController.php @@ -48,6 +48,11 @@ class AuthController extends Controller 'username' => $request->username, ])->first(); + if ($user === null) { + return redirect()->route('customer.login') + ->with('message', ['type' => 'error', 'message' => 'Invalid credentials']); + } + $password = Hash::check($request->password, $user->password); if (!$password) { return redirect()->route('customer.login') diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 0f43125..4e3040e 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -253,7 +253,7 @@ class Customer extends Authenticatable public function paylater() { - return $this->hasOne(PaylaterCustomer::class); + return $this->hasOne(PaylaterCustomer::class, 'customer_id', 'id'); } public function paylaterHistories() diff --git a/app/Models/CustomerAsDataPartner.php b/app/Models/CustomerAsDataPartner.php index 3589d36..8102a14 100644 --- a/app/Models/CustomerAsDataPartner.php +++ b/app/Models/CustomerAsDataPartner.php @@ -8,6 +8,7 @@ class CustomerAsDataPartner extends Model { protected $fillable = [ 'customer_id', + 'id_number', 'job', 'image_selfie', 'file_statement', diff --git a/app/Models/PaylaterCustomer.php b/app/Models/PaylaterCustomer.php index 6503542..924535c 100644 --- a/app/Models/PaylaterCustomer.php +++ b/app/Models/PaylaterCustomer.php @@ -12,4 +12,9 @@ class PaylaterCustomer extends Model 'day_deadline', 'day_deadline_at' ]; + + public function customer() + { + return $this->hasOne(Customer::class, 'customer_id', 'id'); + } } diff --git a/database/migrations/2023_06_28_142741_create_customer_as_data_partners_table.php b/database/migrations/2023_06_28_142741_create_customer_as_data_partners_table.php index 36cfbc6..7e9287f 100644 --- a/database/migrations/2023_06_28_142741_create_customer_as_data_partners_table.php +++ b/database/migrations/2023_06_28_142741_create_customer_as_data_partners_table.php @@ -15,6 +15,7 @@ return new class extends Migration $table->ulid('id')->primary(); $table->ulid('customer_id')->nullable(); + $table->string('id_number')->nullable(); $table->string('job')->nullable(); $table->string('image_selfie')->nullable(); $table->string('file_statement')->nullable(); diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index f68faba..c26c5e4 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -55,6 +55,18 @@ class PermissionSeeder extends Seeder ['id' => Str::ulid(), 'label' => 'Update Customer Level', 'name' => 'update-customer-level'], ['id' => Str::ulid(), 'label' => 'View Customer Level', 'name' => 'view-customer-level'], + ['id' => Str::ulid(), 'label' => 'Create Mitra', 'name' => 'create-mitra'], + ['id' => Str::ulid(), 'label' => 'Update Mitra', 'name' => 'update-mitra'], + ['id' => Str::ulid(), 'label' => 'View Mitra', 'name' => 'view-mitra'], + ['id' => Str::ulid(), 'label' => 'Delete Mitra', 'name' => 'delete-mitra'], + + ['id' => Str::ulid(), 'label' => 'Update Limit Mitra', 'name' => 'update-limit-mitra'], + ['id' => Str::ulid(), 'label' => 'Update Limit Tenor', 'name' => 'update-limit-tenor'], + + ['id' => Str::ulid(), 'label' => 'Create Pembayaran Hutang', 'name' => 'create-paylater-repayment'], + ['id' => Str::ulid(), 'label' => 'Update Pembayaran Hutang', 'name' => 'update-paylater-repayment'], + ['id' => Str::ulid(), 'label' => 'View Pembayaran Hutang', 'name' => 'view-paylater-repayment'], + ['id' => Str::ulid(), 'label' => 'View Customer Verification', 'name' => 'view-customer-verification'], ['id' => Str::ulid(), 'label' => 'View Setting', 'name' => 'view-setting'], diff --git a/resources/js/Components/FormInputTime.jsx b/resources/js/Components/FormInputTime.jsx index f5888ec..5c93703 100644 --- a/resources/js/Components/FormInputTime.jsx +++ b/resources/js/Components/FormInputTime.jsx @@ -42,7 +42,7 @@ export default function FormInputTime({ selectTime(e.target.value, 'm')} - className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" > {minutes.map((m) => (