Skip to content

Commit

Permalink
feat: student guardian
Browse files Browse the repository at this point in the history
  • Loading branch information
iqbaleff214 committed Jun 3, 2024
1 parent d25c0d1 commit 6200a76
Show file tree
Hide file tree
Showing 10 changed files with 529 additions and 0 deletions.
148 changes: 148 additions & 0 deletions app/Http/Controllers/StudentGuardianController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php

namespace App\Http\Controllers;

use App\Enum\Role;
use App\Http\Requests\StoreStudentGuardianRequest;
use App\Http\Requests\UpdateStudentGuardianRequest;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Yajra\DataTables\Facades\DataTables;

class StudentGuardianController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): View|JsonResponse
{
if ($request->ajax()) {
return DataTables::eloquent(User::role(Role::STUDENT_GUARDIAN))
->addColumn('action', function ($row) {
return '<div class="dropdown">
<button type="button"
class="btn p-0 dropdown-toggle hide-arrow"
data-bs-toggle="dropdown">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="'.route('student-guardian.edit', $row).'"><i class="bx bx-edit-alt me-1"></i>'.__('label.edit').'</a>
<form action="'.route('student-guardian.destroy', $row).'" method="post" onsubmit="confirmSubmit(event, this)">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="'.csrf_token().'" />
<button class="dropdown-item" type="submit"><i class="bx bx-trash me-1"></i>'.__('label.delete').'</button>
</form>
</div>
</div>';
})
->editColumn('image', fn ($row) => '<a data-fslightbox href="'.$row->image_url.'"><img src="'.$row->image_url.'" alt="user-avatar" class="d-block rounded" height="30" width="30"></a>')
->editColumn('gender', fn ($row) => __('label.'.$row->gender))
->filterColumn('gender', fn ($query, $keyword) => $query->where('gender', $keyword))
->rawColumns(['action', 'image'])
->toJson();
}

return view('pages.student-guardian.index');
}

/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('pages.student-guardian.create');
}

/**
* Store a newly created resource in storage.
*/
public function store(StoreStudentGuardianRequest $request): RedirectResponse
{
try {
$data = $request->validated();
$data['role'] = Role::STUDENT_GUARDIAN;
$data['password'] = bcrypt('password');

if ($request->hasFile('image')) {
$data['image'] = time().random_int(0, PHP_INT_MAX).'.'.$request->file('image')->extension();
Storage::putFileAs('public', $request->file('image'), $data['image']);
}

User::create($data);

return redirect()->route('student-guardian.index')->with('notification', $this->successNotification('notification.success_create', 'menu.student_guardian'));
} catch (\Throwable $throwable) {
Log::error($throwable->getMessage());

return back()->with('notification', $this->successNotification('notification.fail_create', 'menu.student_guardian'));
}
}

/**
* Display the specified resource.
*/
public function show(User $student_guardian): View
{
return view('pages.student-guardian.show', [
'guardian' => $student_guardian,
]);
}

/**
* Show the form for editing the specified resource.
*/
public function edit(User $student_guardian): View
{
return view('pages.student-guardian.edit', [
'guardian' => $student_guardian,
]);
}

/**
* Update the specified resource in storage.
*/
public function update(UpdateStudentGuardianRequest $request, User $student_guardian): RedirectResponse
{
try {
$data = $request->validated();

if ($request->hasFile('image')) {
if ($student_guardian->image) {
Storage::delete("public/{$student_guardian->image}");
}

$data['image'] = time().random_int(0, PHP_INT_MAX).'.'.$request->file('image')->extension();
Storage::putFileAs('public', $request->file('image'), $data['image']);
}

$student_guardian->update($data);

return back()->with('notification', $this->successNotification('notification.success_update', 'menu.student_guardian'));
} catch (\Throwable $throwable) {
Log::error($throwable->getMessage());

return back()->with('notification', $this->successNotification('notification.fail_update', 'menu.student_guardian'));
}
}

/**
* Remove the specified resource from storage.
*/
public function destroy(User $student_guardian): RedirectResponse
{
try {
$student_guardian->delete();

return back()->with('notification', $this->successNotification('notification.success_delete', 'menu.student_guardian'));
} catch (\Throwable $throwable) {
Log::error($throwable->getMessage());

return back()->with('notification', $this->successNotification('notification.fail_delete', 'menu.student_guardian'));
}
}
}
50 changes: 50 additions & 0 deletions app/Http/Requests/StoreStudentGuardianRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace App\Http\Requests;

use App\Enum\Role;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreStudentGuardianRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->user()->isRole(Role::OWNER) || auth()->user()->isRole(Role::HEADMASTER);
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
'name' => ['required'],
'email' => ['required', 'email', Rule::unique('users', 'email')],
'image' => ['nullable', 'image', 'max:2048'],
'phone' => ['required'],
'address' => ['nullable'],
'marital_status' => ['nullable'],
'gender' => ['required'],
];
}

public function attributes(): array
{
return [
'name' => __('field.name'),
'email' => __('field.email'),
'image' => __('field.image'),
'phone' => __('field.phone'),
'address' => __('field.address'),
'marital_status' => __('field.marital_status'),
'gender' => __('field.gender'),
];
}
}
50 changes: 50 additions & 0 deletions app/Http/Requests/UpdateStudentGuardianRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace App\Http\Requests;

use App\Enum\Role;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateStudentGuardianRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->user()->isRole(Role::OWNER) || auth()->user()->isRole(Role::HEADMASTER);
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
'name' => ['required'],
'email' => ['required', 'email', Rule::unique('users', 'email')->ignore($this->id)],
'image' => ['nullable', 'image', 'max:2048'],
'phone' => ['required'],
'address' => ['nullable'],
'marital_status' => ['nullable'],
'gender' => ['required'],
];
}

public function attributes(): array
{
return [
'name' => __('field.name'),
'email' => __('field.email'),
'image' => __('field.image'),
'phone' => __('field.phone'),
'address' => __('field.address'),
'marital_status' => __('field.marital_status'),
'gender' => __('field.gender'),
];
}
}
1 change: 1 addition & 0 deletions lang/en/menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@
'user_management' => 'User Management',
'setting' => 'Setting',
'administrator' => 'Administrator',
'student_guardian' => 'Student Guardian',
];
1 change: 1 addition & 0 deletions lang/id/menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@
'user_management' => 'Pengelolaan Pengguna',
'setting' => 'Pengaturan',
'administrator' => 'Admin',
'student_guardian' => 'Wali Santri',
];
11 changes: 11 additions & 0 deletions resources/menu/verticalMenu.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@
"administrator"
]
},
{
"url": "student-guardian.index",
"name": "menu.student_guardian",
"icon": "menu-icon tf-icons bx bx-group",
"slug": "student-guardian.*",
"role": [
"owner",
"headmaster",
"administrator"
]
},
{
"menuHeader": "menu.setting",
"role": [
Expand Down
100 changes: 100 additions & 0 deletions resources/views/pages/student-guardian/create.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@extends('layouts.dashboard')

@section('content')
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="py-3 mb-0">
<a class="text-muted fw-light" href="{{ route('student-guardian.index') }}">{{ __('menu.student_guardian') }} /</a>
{{ __('label.new') }}
</h4>
<div>
<a href="{{ route('student-guardian.index') }}" class="btn btn-secondary text-white fw-medium">
{{ __('button.back') }}
</a>
</div>
</div>

<div class="card mb-4">
<form action="{{ route('student-guardian.store') }}" method="post" enctype="multipart/form-data">
@csrf
<div class="card-header">
<div class="d-flex align-items-start align-items-sm-center gap-4">
<a data-fslightbox href="{{ asset('404_Black.jpg') }}">
<img src="{{ asset('404_Black.jpg') }}"
alt="user-avatar" class="d-block rounded" height="100" width="100" id="uploadImage">
</a>
<div class="button-wrapper">
<label for="upload" class="btn btn-primary me-2 mb-4" tabindex="0">
<span class="d-none d-sm-block">{{ __('button.upload') }}</span>
<i class="bx bx-upload d-block d-sm-none"></i>
<input type="file" id="upload" class="account-file-input" hidden="" name="image"
accept="image/png,image/jpeg">
</label>
<button type="button" class="btn btn-outline-secondary account-image-reset mb-4">
<i class="bx bx-reset d-block d-sm-none"></i>
<span class="d-none d-sm-block">{{ __('button.reset') }}</span>
</button>

<small class="text-muted mb-0 d-block">{{ __('label.allowed_image_upload') }}</small>
</div>
</div>
</div>
<hr class="m-0">
<div class="card-body">
<div class="mb-3">
<x-forms.input name="name" required/>
</div>
<div class="mb-3">
<x-forms.input name="email" type="email" required/>
</div>
<div class="mb-3">
<x-forms.input-select2 name="gender" required
:options="[ ['male', __('label.male')], ['female', __('label.female')] ]"/>
</div>
<div class="mb-3">
<x-forms.input name="phone" type="phone" required/>
</div>
<div class="mb-3">
<x-forms.input-textarea name="address"/>
</div>
<div class="mb-3">
<x-forms.input-select2 name="marital_status"
:options="[ ['single', __('label.single')], ['married', __('label.married')], ['divorced', __('label.divorced')], ['widowed', __('label.widowed')] ]"/>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-primary me-2">{{ __('button.submit') }}</button>
<button type="reset" class="btn btn-outline-secondary">{{ __('button.reset') }}</button>
</div>
</div>
</form>
</div>

@endsection

@push('script')
<script>
document.addEventListener("DOMContentLoaded", (function (e) {
const imageElement = document.getElementById("uploadImage");
const imageInputElement = document.querySelector(".account-file-input");
const imageResetEl = document.querySelector(".account-image-reset");
if (imageElement) {
const originalImage = imageElement.src;
imageInputElement.onchange = () => {
imageInputElement.files[0] && (imageElement.src = window.URL.createObjectURL(imageInputElement.files[0]));
imageElement.closest('a').setAttribute('href', imageElement.src);
refreshFsLightbox();
}
imageResetEl.onclick = () => {
imageInputElement.value = "";
imageElement.src = originalImage;
imageElement.closest('a').setAttribute('href', imageElement.src);
refreshFsLightbox();
}
}
}));
</script>
@endpush
Loading

0 comments on commit 6200a76

Please sign in to comment.