From c0bf6292390cb6872797d44f9389b8fc1dd03f66 Mon Sep 17 00:00:00 2001 From: myqnrrhmn Date: Mon, 23 Jun 2025 14:33:55 +0700 Subject: [PATCH] feat: contact message --- app/Helpers/CustomHelper.php | 182 +++++++++--------- .../Dashboard/ContactController.php | 64 ++++++ .../Dashboard/DatatableController.php | 88 ++++++++- .../Controllers/Dashboard/NewsController.php | 1 + app/Http/Controllers/HomepageController.php | 15 ++ app/Http/Requests/LoginRequest.php | 2 +- app/Http/Requests/NewsRequest.php | 4 +- .../Requests/ReceiveMessageContactRequest.php | 32 +++ app/Models/Contact.php | 23 +++ app/Providers/AppServiceProvider.php | 6 + ...025_06_02_142649_create_contacts_table.php | 35 ++++ database/seeders/ContactSeeder.php | 17 ++ .../views/dashboard/contacts/index.blade.php | 100 ++++++++++ .../views/dashboard/contacts/reply.blade.php | 142 ++++++++++++++ .../views/dashboard/contacts/show.blade.php | 135 +++++++++++++ .../views/emails/contact/message.blade.php | 0 .../views/homepage/contacts/index.blade.php | 7 +- .../views/partials/app/datatable.blade.php | 35 ++++ .../partials/dashboard/sidebar.blade.php | 11 ++ routes/web.php | 21 +- 20 files changed, 820 insertions(+), 100 deletions(-) create mode 100644 app/Http/Controllers/Dashboard/ContactController.php create mode 100644 app/Http/Requests/ReceiveMessageContactRequest.php create mode 100644 app/Models/Contact.php create mode 100644 database/migrations/2025_06_02_142649_create_contacts_table.php create mode 100644 database/seeders/ContactSeeder.php create mode 100644 resources/views/dashboard/contacts/index.blade.php create mode 100644 resources/views/dashboard/contacts/reply.blade.php create mode 100644 resources/views/dashboard/contacts/show.blade.php create mode 100644 resources/views/emails/contact/message.blade.php diff --git a/app/Helpers/CustomHelper.php b/app/Helpers/CustomHelper.php index e726635..53726a0 100644 --- a/app/Helpers/CustomHelper.php +++ b/app/Helpers/CustomHelper.php @@ -17,114 +17,114 @@ // } // } -if(env('FILESYSTEM_DISK') === 's3'){ - if (! function_exists('store_file')) { - function store_file($file = null, $path, $disk = null) - { - $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); +// if(env('FILESYSTEM_DISK') === 's3'){ + // if (! function_exists('store_file')) { + // function store_file($file = null, $path, $disk = null) + // { + // $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); - if (filled($file) && $file instanceof \Illuminate\Http\UploadedFile) { - $randomFilename = Str::random(35) . time() . '.' . $file->getClientOriginalExtension(); - $originalFileName = Str::slug(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME)) . '.' . $file->getClientOriginalExtension(); + // if (filled($file) && $file instanceof \Illuminate\Http\UploadedFile) { + // $randomFilename = Str::random(35) . time() . '.' . $file->getClientOriginalExtension(); + // $originalFileName = Str::slug(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME)) . '.' . $file->getClientOriginalExtension(); - $stored = $file->storeAs($path, $randomFilename, $disk); + // $stored = $file->storeAs($path, $randomFilename, $disk); - if ($stored) { - $url = Storage::disk($disk)->url($stored); + // if ($stored) { + // $url = Storage::disk($disk)->url($stored); - return [ - 'randomFileName' => $randomFilename, - 'originalFileName' => $originalFileName, - 'url' => $url, - 'isSuccess' => true, - ]; - } - } + // return [ + // 'randomFileName' => $randomFilename, + // 'originalFileName' => $originalFileName, + // 'url' => $url, + // 'isSuccess' => true, + // ]; + // } + // } - return [ - 'randomFileName' => null, - 'originalFileName' => null, - 'url' => null, - 'isSuccess' => false, - ]; - } - } + // return [ + // 'randomFileName' => null, + // 'originalFileName' => null, + // 'url' => null, + // 'isSuccess' => false, + // ]; + // } + // } - if (!function_exists('display_file')) { - function display_file($dir, $filename, $disk = null, $expirationMinutes = 60): string - { - $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); + // if (!function_exists('display_file')) { + // function display_file($dir, $filename, $disk = null, $expirationMinutes = 60): string + // { + // $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); - if (!filled($filename)) { - return '

Belum ditambahkan.

'; - } + // if (!filled($filename)) { + // return '

Belum ditambahkan.

'; + // } - $path = "uploads/{$dir}/{$filename}"; + // $path = "uploads/{$dir}/{$filename}"; - try { - $fileExists = is_file_exists($path, $disk); + // try { + // $fileExists = is_file_exists($path, $disk); - if (!$fileExists) { - return '

File tidak ditemukan.

'; - } + // if (!$fileExists) { + // return '

File tidak ditemukan.

'; + // } - if ($disk === 's3') { - $fileUrl = Storage::disk($disk)->temporaryUrl($path, now()->addMinutes($expirationMinutes)); - } else { - $fileUrl = Storage::disk($disk)->url($path); - } + // if ($disk === 's3') { + // $fileUrl = Storage::disk($disk)->temporaryUrl($path, now()->addMinutes($expirationMinutes)); + // } else { + // $fileUrl = Storage::disk($disk)->url($path); + // } - $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); - $mimeType = match ($ext) { - 'jpg', 'jpeg', 'png', 'gif', 'webp' => 'image/' . $ext, - 'pdf' => 'application/pdf', - default => 'unknown', - }; + // $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + // $mimeType = match ($ext) { + // 'jpg', 'jpeg', 'png', 'gif', 'webp' => 'image/' . $ext, + // 'pdf' => 'application/pdf', + // default => 'unknown', + // }; - if (str_contains($mimeType, 'image')) { - return "File Image"; - } elseif ($mimeType === 'application/pdf') { - return ""; - } + // if (str_contains($mimeType, 'image')) { + // return "File Image"; + // } elseif ($mimeType === 'application/pdf') { + // return ""; + // } - return '

Format file tidak didukung

'; - } catch (\Exception $e) { - return '

Terjadi kesalahan saat menampilkan file.

'; - } - } - } + // return '

Format file tidak didukung

'; + // } catch (\Exception $e) { + // return '

Terjadi kesalahan saat menampilkan file.

'; + // } + // } + // } - if (! function_exists('delete_file')) { - function delete_file($path, $disk = null): bool - { - $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); + // if (! function_exists('delete_file')) { + // function delete_file($path, $disk = null): bool + // { + // $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); - try { - if(is_file_exists($path)){ - return Storage::disk($disk)->delete($path); - }else{ - return false; - } - } catch (\Exception $e) { - return false; - } - } - } + // try { + // if(is_file_exists($path)){ + // return Storage::disk($disk)->delete($path); + // }else{ + // return false; + // } + // } catch (\Exception $e) { + // return false; + // } + // } + // } - if (!function_exists('is_file_exists')) { - function is_file_exists($path, $disk = null): bool - { - $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); + // if (!function_exists('is_file_exists')) { + // function is_file_exists($path, $disk = null): bool + // { + // $disk = $disk ?? env('FILESYSTEM_DISK', 'public'); - try { - $file = Storage::disk($disk)->get($path); - return !empty($file); - } catch (\Throwable $e) { - return false; - } - } - } -}else if(env('FILESYSTEM_DISK') === 'local'){ + // try { + // $file = Storage::disk($disk)->get($path); + // return !empty($file); + // } catch (\Throwable $e) { + // return false; + // } + // } + // } +// }else if(env('FILESYSTEM_DISK') === 'local'){ if (! function_exists('store_file')) { function store_file($file = null, $path, $disk = 'public') { @@ -196,7 +196,7 @@ function is_file_exists($path) return File::exists(public_path('storage/' . $path)); } } -} +// } if (!function_exists('get_role_name')) { function get_role_name($role = null) { diff --git a/app/Http/Controllers/Dashboard/ContactController.php b/app/Http/Controllers/Dashboard/ContactController.php new file mode 100644 index 0000000..c05a967 --- /dev/null +++ b/app/Http/Controllers/Dashboard/ContactController.php @@ -0,0 +1,64 @@ + 'Pesan Kontak', + 'status' => $request->input('status', null) + ]); + } + + public function show($id){ + $contact = Contact::findOrFail(decrypt_id($id)); + + return view('dashboard.contacts.show', [ + 'title' => 'Detail Pesan', + 'contact' => $contact + ]); + } + + public function reply($id){ + $contact = Contact::findOrFail(decrypt_id($id)); + + return view('dashboard.contacts.reply', [ + 'title' => 'Balas Pesan', + 'contact' => $contact + ]); + } + + public function replyAction(Request $request, $id){ + $validatedData = $request->validate([ + 'reply_message' => ['required'], + ], [ + 'reply_message.required' => 'Pesan balasan wajib diisi.', + ]); + + $contact = Contact::findOrFail(decrypt_id($id)); + + $contact->update([ + 'reply_message' => $validatedData['reply_message'], + 'status' => '1' + ]); + + return redirect()->route('dashboard.contact.messages.show', ['id' => encrypt_id($contact->id)])->with('success-status', 'Berhasil membalas pesan.'); + } + + public function destroy($id){ + try { + $contact = Contact::findOrFail(decrypt_id($id)); + $contact->delete(); + + return response()->json(['message' => 'Berhasil menghapus kontak pesan.']); + } catch (\Exception $e) { + return response()->json(['message' => 'Gagal menghapus kontak pesan.'], 500); + } + } +} diff --git a/app/Http/Controllers/Dashboard/DatatableController.php b/app/Http/Controllers/Dashboard/DatatableController.php index a673c84..f576f9c 100644 --- a/app/Http/Controllers/Dashboard/DatatableController.php +++ b/app/Http/Controllers/Dashboard/DatatableController.php @@ -7,6 +7,7 @@ use App\Models\Announcement; use App\Models\Classification; use App\Models\Company; +use App\Models\Contact; use App\Models\ContentRecap; use App\Models\ContentRecapClassification; use App\Models\Cooperation; @@ -1426,8 +1427,8 @@ public function cooperation(Request $request){ $mediaId = Auth::user()->company->media->id; $query->whereHas('proposals', function ($q) use ($mediaId) { - $q->where('status', 1) - ->where('media_id', $mediaId); + $q->where('status', '=', '1'); + $q ->where('media_id', $mediaId); }); } @@ -2647,6 +2648,89 @@ public function issueManagement(Request $request){ } + public function contactMessage(Request $request){ + if ($request->ajax()) { + + $status = $request->input('status', null); + + $query = Contact::query(); + + + if($status && in_array($status, ['replied','not_yet_replied'])){ + switch($status){ + case 'not_yet_replied': + $statusNum = '0'; + break; + case 'replied': + $statusNum = '1'; + break; + } + $query->where('status', '=', $statusNum)->get(); + } + + $data = $query->get(); + + return DataTables::of($data) + ->addIndexColumn() + ->addColumn('status', function ($data){ + switch($data->status){ + case '0': + return 'Belum Dibalas'; + case '1': + return 'Sudah Dibalas'; + default: + return 'Tidak Diketahui'; + + } + }) + ->addColumn('date', function ($data){ + return idn_date($data->created_at, 'H:i l, d F Y'); + }) + ->addColumn('action', function ($data) { + $showUrl = route('dashboard.contact.messages.show', ['id' => encrypt_id($data->id)]); + + $replyUrl = route('dashboard.contact.messages.reply', ['id' => encrypt_id($data->id)]); + + $deleteUrl = route('dashboard.contact.messages.destroy', ['id' => encrypt_id($data->id)]); + + $dropdown = ''; + + return $dropdown; + }) + + ->rawColumns(['status', 'action']) + ->make(true); + } + + return response()->json(['error' => 'Unauthorized'], 401); + + } + public function user(Request $request){ if ($request->ajax()) { diff --git a/app/Http/Controllers/Dashboard/NewsController.php b/app/Http/Controllers/Dashboard/NewsController.php index 6a7dd23..e90eeaf 100644 --- a/app/Http/Controllers/Dashboard/NewsController.php +++ b/app/Http/Controllers/Dashboard/NewsController.php @@ -34,6 +34,7 @@ public function create(){ } public function store(NewsRequest $request){ + // dd('hai'); $validatedData = $request->validated(); $isNewsExists = News::withTrashed()->where('slug', $validatedData['slug'])->first(); diff --git a/app/Http/Controllers/HomepageController.php b/app/Http/Controllers/HomepageController.php index a73357e..9ed8468 100644 --- a/app/Http/Controllers/HomepageController.php +++ b/app/Http/Controllers/HomepageController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers; +use App\Http\Requests\ReceiveMessageContactRequest; +use App\Models\Contact; use App\Models\News; use Carbon\Carbon; use Illuminate\Http\Request; @@ -187,6 +189,19 @@ public function contact(){ return view('homepage.contacts.index', $data); } + public function receiveMessage(ReceiveMessageContactRequest $request){ + $validatedData = $request->validated(); + $validatedData['ip_address'] = $request->ip(); + + $createdMessage = Contact::create($validatedData); + + if($createdMessage){ + return "Berhasil mengirim pesan"; + }else{ + return "Gagal mengirim pesan"; + } + } + public function customError($code){ abort($code); } diff --git a/app/Http/Requests/LoginRequest.php b/app/Http/Requests/LoginRequest.php index 763a0b1..333a652 100644 --- a/app/Http/Requests/LoginRequest.php +++ b/app/Http/Requests/LoginRequest.php @@ -29,7 +29,7 @@ public function rules(): array ->mixedCase() ->numbers() ->symbols()], - 'g-recaptcha-response' => 'recaptcha', + // 'g-recaptcha-response' => 'recaptcha', ]; } diff --git a/app/Http/Requests/NewsRequest.php b/app/Http/Requests/NewsRequest.php index 0dbb742..738ac8e 100644 --- a/app/Http/Requests/NewsRequest.php +++ b/app/Http/Requests/NewsRequest.php @@ -31,7 +31,7 @@ public function rules(): array 'category' => ['required', 'in:0,1'], 'image' => ['nullable', 'image', 'mimes:png,jpg,jpeg', 'max:2048'], 'slug' => ['required'], - 'enhancer_id' => ['required'], + 'author_id' => ['required'], ]; return $rules; @@ -58,7 +58,7 @@ protected function prepareForValidation() { $this->merge([ 'slug' => filled($this->title) ? Str::slug($this->title) : null, - 'enhancer_id' => Auth::check() ? Auth::id() : null, + 'author_id' => Auth::check() ? Auth::id() : null, ]); } } diff --git a/app/Http/Requests/ReceiveMessageContactRequest.php b/app/Http/Requests/ReceiveMessageContactRequest.php new file mode 100644 index 0000000..c38967b --- /dev/null +++ b/app/Http/Requests/ReceiveMessageContactRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => ['required', 'max:25'], + 'email' => ['required', 'max:25'], + 'phone_number' => ['required', 'max:15'], + 'message' => ['required'], + ]; + } +} diff --git a/app/Models/Contact.php b/app/Models/Contact.php new file mode 100644 index 0000000..8b61608 --- /dev/null +++ b/app/Models/Contact.php @@ -0,0 +1,23 @@ +back()->with('toManyRequest', 'Harap tunggu 1 menit untuk login ulang.'); }); }); + + RateLimiter::for('contact_message', function (Request $request) { + return Limit::perDay(2)->by($request->ip())->response(function (Request $request) { + return redirect()->back()->with('toManyRequest', 'Harap tunggu 1 hari untuk mengirim pesan ulang.'); + }); + }); } } diff --git a/database/migrations/2025_06_02_142649_create_contacts_table.php b/database/migrations/2025_06_02_142649_create_contacts_table.php new file mode 100644 index 0000000..0d6286a --- /dev/null +++ b/database/migrations/2025_06_02_142649_create_contacts_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('name'); + $table->string('email'); + $table->string('phone_number'); + $table->text('message'); + $table->text('reply_message')->nullable(); + $table->string('ip_address'); + $table->enum('status', ['0', '1'])->default('0'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('contacts'); + } +}; diff --git a/database/seeders/ContactSeeder.php b/database/seeders/ContactSeeder.php new file mode 100644 index 0000000..ebf4186 --- /dev/null +++ b/database/seeders/ContactSeeder.php @@ -0,0 +1,17 @@ + 'contact_messages']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+

{{$title}}

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ + + + + + + + + + + +
NoNamaEmailStatusTanggal
+
+
+
+
+ +
+
+
+
+ +@include('partials.dashboard.footer' ,['datatable' => 'contact_message']) diff --git a/resources/views/dashboard/contacts/reply.blade.php b/resources/views/dashboard/contacts/reply.blade.php new file mode 100644 index 0000000..6abe507 --- /dev/null +++ b/resources/views/dashboard/contacts/reply.blade.php @@ -0,0 +1,142 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'contact_messages']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+

Balas Pesan

+
+
    +
  • Balas pesan masuk.
  • +
+
+
+
+ + Kembali +
+
+
+
+
+
+
+ +
+
+ {{--
+
Personal Information
+

Basic info, like your name and address, that you use on Nio Platform.

+
--}} +
+
+
+ Nama + {{$contact->name}} +
+
+
+
+ Email + {{$contact->email}} +
+
+
+
+ Nomor Telepon + {{$contact->phone_number}} +
+
+
+
+ Tanggal + {{idn_date($contact->created_at, 'H:i l, d F Y')}} +
+
+
+
+ Status + @if (filled($contact->deleted_at)) + Dihapus + @elseif ($contact->status == '0') + Belum Dibalas + @elseif ($contact->status == '1') + Sudah Dibalas + @else + Tidak Diketahui + @endif +
+ +
+
+
+ {{--
--}} +
+
+ Pesan +
+
+
+
+ {{$contact->message}} +
+
+
+
+
+
+
+
+
+ @if ($contact->status != '1') +
+
+
+
+ +
+ {{--
--}} +
+
+ @csrf +
+
+ + @error('reply_message') + {{$message}} + @enderror +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ @endif +
+
+
+
+ +@include('partials.dashboard.footer', ['rich_editor' => false]) diff --git a/resources/views/dashboard/contacts/show.blade.php b/resources/views/dashboard/contacts/show.blade.php new file mode 100644 index 0000000..21df314 --- /dev/null +++ b/resources/views/dashboard/contacts/show.blade.php @@ -0,0 +1,135 @@ +@include('partials.dashboard.head') +@include('partials.dashboard.sidebar', ['page' => 'contact_messages']) +@include('partials.dashboard.header') + +
+
+
+
+
+
+
+

Detail Pesan

+
+
    +
  • Detail pesan dapat dilihat di sini.
  • +
+
+
+
+ + Kembali +
+
+
+
+
+
+
+ +
+
+ {{--
+
Personal Information
+

Basic info, like your name and address, that you use on Nio Platform.

+
--}} +
+
+
+ Nama + {{$contact->name}} +
+
+
+
+ Email + {{$contact->email}} +
+
+
+
+ Nomor Telepon + {{$contact->phone_number}} +
+
+
+
+ Tanggal + {{idn_date($contact->created_at, 'H:i l, d F Y')}} +
+
+
+
+ Status + @if (filled($contact->deleted_at)) + Dihapus + @elseif ($contact->status == '0') + Belum Dibalas + @elseif ($contact->status == '1') + Sudah Dibalas + @else + Tidak Diketahui + @endif +
+ +
+
+
+ {{--
--}} +
+
+ Pesan +
+
+
+
+ {{$contact->message}} +
+
+
+
+
+
+
+
+
+ @if ($contact->status === '1') +
+
+
+
+ +
+ {{--
--}} +
+
+ Pesan Balasan +
+
+
+
+ {{$contact->reply_message}} +
+
+
+
+
+
+
+
+
+ @endif +
+
+
+
+ +@include('partials.dashboard.footer', ['rich_editor' => false]) diff --git a/resources/views/emails/contact/message.blade.php b/resources/views/emails/contact/message.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/homepage/contacts/index.blade.php b/resources/views/homepage/contacts/index.blade.php index f975cff..1daf12a 100644 --- a/resources/views/homepage/contacts/index.blade.php +++ b/resources/views/homepage/contacts/index.blade.php @@ -117,7 +117,8 @@
-
+ + @csrf
Lebih mudah

Hubungi kami

@@ -126,7 +127,7 @@
- +
@@ -136,7 +137,7 @@
- +
diff --git a/resources/views/partials/app/datatable.blade.php b/resources/views/partials/app/datatable.blade.php index 02cb276..ffae7f8 100644 --- a/resources/views/partials/app/datatable.blade.php +++ b/resources/views/partials/app/datatable.blade.php @@ -1206,6 +1206,41 @@ @endif +@if ($datatable === 'contact_message') + +@endif + @if ($datatable === 'user')