fix bagian 2

perbaikan lagi ke dua kalinya
This commit is contained in:
Ryan Maulana 2026-05-21 18:25:13 +07:00
parent 766eaa3234
commit 992c21bc3e
20 changed files with 4537 additions and 80 deletions

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=reyfandi3008@gmail.com
MAIL_PASSWORD=hhre xxfh fcyj xlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=reyfandi3008@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=reyfandi3008@gmail.com
MAIL_PASSWORD=hhre xxfh fcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=reyfandi3008@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=reyfandi3008@gmail.com
MAIL_PASSWORD=hhre xxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=reyfandi3008@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=rtgreenview@gmail.com@gmail.com
MAIL_PASSWORD=hhre xxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=reyfandi3008@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=rtgreenview@gmail.com@gmail.com
MAIL_PASSWORD=hhre xxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=rtgreenview@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=rtgreenview@gmail.com@gmail.com
MAIL_PASSWORD=hhrexxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=rtgreenview@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=rtgreenview@gmail.com@gmail.com
MAIL_PASSWORD=hhrexxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=rtgreenview@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=rtgreenview@gmail.com@gmail.com
MAIL_PASSWORD=hhrexxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=rtgreenview@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:iHPDeCnviINzk4hSncvHa8qFuz+iPW99xH+dHwxMhxQ=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=greenview
DB_USERNAME=root
DB_PASSWORD=
FONNTE_TOKEN=CEAJquyLniASpRf76eT9
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=rtgreenview@gmail.com
MAIL_PASSWORD=hhrexxfhfcyjxlck
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=rtgreenview@gmail.com
MAIL_FROM_NAME="Green View"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -0,0 +1,490 @@
@extends('layouts.app')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', sans-serif; }
/* ── PAGE HEADER ── */
.page-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 28px;
}
.page-header-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-header h1 {
font-size: 22px;
font-weight: 800;
color: #1a2e1a;
margin: 0;
line-height: 1.2;
}
.page-header p {
font-size: 13px;
color: #8a9e8a;
margin: 0;
}
/* ── ALERT ── */
.alert-success-custom {
background: #f0faf4;
border: 1px solid #a8e6be;
border-left: 4px solid #1a6e3a;
border-radius: 12px;
padding: 12px 16px;
color: #1a4e2a;
font-size: 13.5px;
font-weight: 500;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
/* ── CARD ── */
.premium-card {
background: #ffffff;
border-radius: 16px;
border: 1px solid #e8f0e8;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
margin-bottom: 24px;
overflow: hidden;
}
.premium-card-header {
padding: 16px 22px;
background: #ffffff;
border-bottom: 1px solid #f0f5f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.premium-card-header .header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-icon {
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.header-icon.green { background: #d1fae5; color: #064e3b; }
.premium-card-header h6 {
font-size: 14px;
font-weight: 700;
color: #1a2e1a;
margin: 0;
}
/* ── TABLE ── */
.premium-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.premium-table thead tr th {
background: #f6fbf7;
color: #3a5a3a;
font-weight: 700;
font-size: 11.5px;
letter-spacing: 0.5px;
text-transform: uppercase;
padding: 11px 14px;
border-bottom: 2px solid #e0ede0;
white-space: nowrap;
}
.premium-table thead tr th:first-child { border-radius: 10px 0 0 0; }
.premium-table thead tr th:last-child { border-radius: 0 10px 0 0; }
.premium-table tbody tr td {
padding: 11px 14px;
border-bottom: 1px solid #f2f7f2;
color: #2a3a2a;
vertical-align: middle;
}
.premium-table tbody tr:hover td { background: #f8fdf8; }
.premium-table tbody tr:last-child td { border-bottom: none; }
/* ── BADGES ── */
.badge-pill-custom {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 99px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.badge-green { background: #d1fae5; color: #064e3b; }
.badge-red { background: #fdf0f0; color: #c53030; }
.badge-yellow { background: #fdfbea; color: #7a6010; }
.badge-blue { background: #eaf0fb; color: #1a4e8e; }
.badge-gray { background: #f3f4f6; color: #4b5563; }
/* ── ACTION BUTTONS ── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 11px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
text-decoration: none;
transition: opacity 0.15s, transform 0.1s;
}
.btn-action:hover { opacity: 0.85; transform: translateY(-1px); text-decoration: none; }
.btn-action:active { transform: translateY(0); }
.btn-action.info { background: #eaf0fb; color: #1a4e8e; }
.btn-action.warn { background: #fdf8ea; color: #8a6000; }
.btn-action.danger { background: #fdf0f0; color: #c53030; }
.btn-action.success { background: #d1fae5; color: #064e3b; }
/* ── TANGGAPAN FORM ── */
.tanggapan-form {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 180px;
}
.tanggapan-form textarea {
border-radius: 9px !important;
border: 1.5px solid #e0ede0 !important;
font-size: 12px !important;
padding: 7px 10px !important;
resize: none;
font-family: 'Plus Jakarta Sans', sans-serif;
outline: none;
transition: border-color 0.15s;
}
.tanggapan-form textarea:focus {
border-color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.1);
}
/* ── FOTO ── */
.bukti-img {
width: 44px;
height: 44px;
border-radius: 8px;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
transition: transform 0.15s;
}
.bukti-img:hover { transform: scale(1.1); }
/* ── DESKRIPSI TRUNCATE ── */
.desc-text {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
.dataTables_wrapper .dataTables_filter label {
font-size: 13px;
color: #5a6e5a;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.dataTables_wrapper .dataTables_filter input {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 6px 12px;
font-size: 13px;
outline: none;
margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus {
border-color: #1a6e3a;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12);
}
.dataTables_wrapper .dataTables_length select {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 5px 10px;
font-size: 13px;
margin: 0 6px;
}
.dataTables_wrapper .dataTables_info {
font-size: 12.5px;
color: #8a9e8a;
font-weight: 500;
padding: 0;
line-height: 36px;
}
div.dataTables_wrapper div.dataTables_paginate {
display: flex !important;
align-items: center;
gap: 3px;
margin: 0;
padding: 0;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button {
box-sizing: border-box !important;
width: 34px !important;
height: 34px !important;
min-width: 34px !important;
border-radius: 50% !important;
padding: 0 !important;
margin: 0 2px !important;
font-size: 13px !important;
font-weight: 600 !important;
border: 1.5px solid #e0ede0 !important;
color: #5a6e5a !important;
background: #ffffff !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.15s ease !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button:hover:not(.disabled):not(.current) {
background: #eaf7f0 !important;
border-color: #2e9e58 !important;
color: #1a6e3a !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current:hover {
background: #ffffff !important;
border: 2px solid #1a6e3a !important;
color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12) !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled:hover {
color: #c8d8c8 !important;
border-color: #edf5ed !important;
background: #fafafa !important;
cursor: default !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid">
{{-- PAGE HEADER --}}
<div class="page-header">
<div class="page-header-icon">
<i class="fas fa-headset"></i>
</div>
<div>
<h1>Layanan Pengaduan</h1>
<p>Manajemen pengaduan dan tanggapan penghuni</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i>
{{ session('success') }}
</div>
@endif
{{-- TABEL PENGADUAN --}}
<div class="premium-card">
<div class="premium-card-header">
<div class="header-left">
<div class="header-icon green"><i class="fas fa-clipboard-list"></i></div>
<h6>Daftar Pengaduan</h6>
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
<th>Nama</th>
<th>Tanggal</th>
<th>Kategori</th>
<th>Deskripsi</th>
<th>Foto</th>
<th>Status</th>
<th>Tanggapan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($layanan as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td><strong>{{ $item->penghuni->nama ?? '-' }}</strong></td>
<td style="white-space:nowrap;">
{{ \Carbon\Carbon::parse($item->tanggal_pengaduan)->translatedFormat('d M Y') }}
</td>
<td>
<span class="badge-pill-custom badge-blue">
<i class="fas fa-tag" style="font-size:9px"></i>
{{ ucfirst($item->kategori_masalah) }}
</span>
</td>
<td>
<span class="desc-text" title="{{ $item->deskripsi }}">
{{ \Illuminate\Support\Str::limit($item->deskripsi, 45) }}
</span>
</td>
<td>
@if($item->foto)
<img src="{{ asset('storage/' . $item->foto) }}" class="bukti-img">
@else
<span style="color:#ccc; font-size:12px;"></span>
@endif
</td>
<td>
@if($item->status == 'diajukan')
<span class="badge-pill-custom badge-yellow"><i class="fas fa-paper-plane" style="font-size:9px"></i> Diajukan</span>
@elseif($item->status == 'menunggu')
<span class="badge-pill-custom badge-blue"><i class="fas fa-hourglass-half" style="font-size:9px"></i> Menunggu</span>
@elseif($item->status == 'selesai')
<span class="badge-pill-custom badge-green"><i class="fas fa-check-circle" style="font-size:9px"></i> Selesai</span>
@else
<span class="badge-pill-custom badge-gray"><i class="fas fa-question-circle" style="font-size:9px"></i> Unknown</span>
@endif
</td>
<td style="max-width: 180px;">
@if($item->tanggapan_admin)
<span class="desc-text" title="{{ $item->tanggapan_admin }}">
{{ \Illuminate\Support\Str::limit($item->tanggapan_admin, 40) }}
</span>
@else
<span style="color:#ccc; font-size:12px; font-style:italic;">Belum ada tanggapan</span>
@endif
</td>
<td style="white-space:nowrap; min-width:160px;">
{{-- RT: Tanggapi --}}
@if(auth()->user()->role == 'rt' && $item->status == 'diajukan')
<form action="{{ route('layanan.tanggapi.rt', $item->id) }}" method="POST">
@csrf
<div class="tanggapan-form">
<textarea name="tanggapan_admin" rows="2" placeholder="Tulis tanggapan..." required></textarea>
<button type="submit" class="btn-action success">
<i class="fas fa-reply" style="font-size:10px"></i> Tanggapi
</button>
</div>
</form>
@endif
{{-- ADMIN: Approve --}}
@if(auth()->user()->role == 'admin' && $item->status == 'menunggu')
<form action="{{ route('layanan.approve.admin', $item->id) }}" method="POST" style="display:inline;">
@csrf
<button class="btn-action success">
<i class="fas fa-check-double" style="font-size:10px"></i> Approve
</button>
</form>
@endif
{{-- HAPUS --}}
<form action="{{ route('layanan.delete', $item->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn-action danger" onclick="return confirm('Yakin hapus pengaduan ini?')">
<i class="fas fa-trash" style="font-size:10px"></i> Hapus
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" style="text-align:center; padding: 32px; color: #aaa;">
<i class="fas fa-inbox" style="font-size:28px; display:block; margin-bottom:8px; opacity:0.4;"></i>
Belum ada data pengaduan
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
if ($.fn.DataTable) {
$('#dataTable').DataTable({
pageLength: 10,
lengthMenu: [5, 10, 25, 50],
dom: '<"d-flex justify-content-between align-items-center px-3 pt-3"lf>t<"d-flex justify-content-between align-items-center px-3 pb-3"ip>',
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START__END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data",
paginate: {
first: "«",
last: "»",
next: "",
previous: ""
},
emptyTable: "Belum ada data pengaduan"
}
});
}
});
</script>
@endpush

View File

@ -0,0 +1,491 @@
@extends('layouts.app')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', sans-serif; }
/* ── PAGE HEADER ── */
.page-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 28px;
}
.page-header-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-header h1 {
font-size: 22px;
font-weight: 800;
color: #1a2e1a;
margin: 0;
line-height: 1.2;
}
.page-header p {
font-size: 13px;
color: #8a9e8a;
margin: 0;
}
/* ── ALERT ── */
.alert-success-custom {
background: #f0faf4;
border: 1px solid #a8e6be;
border-left: 4px solid #1a6e3a;
border-radius: 12px;
padding: 12px 16px;
color: #1a4e2a;
font-size: 13.5px;
font-weight: 500;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
/* ── CARD ── */
.premium-card {
background: #ffffff;
border-radius: 16px;
border: 1px solid #e8f0e8;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
margin-bottom: 24px;
overflow: hidden;
}
.premium-card-header {
padding: 16px 22px;
background: #ffffff;
border-bottom: 1px solid #f0f5f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.premium-card-header .header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-icon {
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.header-icon.green { background: #d1fae5; color: #064e3b; }
.premium-card-header h6 {
font-size: 14px;
font-weight: 700;
color: #1a2e1a;
margin: 0;
}
/* ── TABLE ── */
.premium-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.premium-table thead tr th {
background: #f6fbf7;
color: #3a5a3a;
font-weight: 700;
font-size: 11.5px;
letter-spacing: 0.5px;
text-transform: uppercase;
padding: 11px 14px;
border-bottom: 2px solid #e0ede0;
white-space: nowrap;
}
.premium-table thead tr th:first-child { border-radius: 10px 0 0 0; }
.premium-table thead tr th:last-child { border-radius: 0 10px 0 0; }
.premium-table tbody tr td {
padding: 11px 14px;
border-bottom: 1px solid #f2f7f2;
color: #2a3a2a;
vertical-align: middle;
}
.premium-table tbody tr:hover td { background: #f8fdf8; }
.premium-table tbody tr:last-child td { border-bottom: none; }
/* ── BADGES ── */
.badge-pill-custom {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 99px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.badge-green { background: #d1fae5; color: #064e3b; }
.badge-red { background: #fdf0f0; color: #c53030; }
.badge-yellow { background: #fdfbea; color: #7a6010; }
.badge-blue { background: #eaf0fb; color: #1a4e8e; }
.badge-gray { background: #f3f4f6; color: #4b5563; }
/* ── ACTION BUTTONS ── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 11px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
text-decoration: none;
transition: opacity 0.15s, transform 0.1s;
}
.btn-action:hover { opacity: 0.85; transform: translateY(-1px); text-decoration: none; }
.btn-action:active { transform: translateY(0); }
.btn-action.info { background: #eaf0fb; color: #1a4e8e; }
.btn-action.warn { background: #fdf8ea; color: #8a6000; }
.btn-action.danger { background: #fdf0f0; color: #c53030; }
.btn-action.success { background: #d1fae5; color: #064e3b; }
/* ── TANGGAPAN FORM ── */
.tanggapan-form {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 180px;
}
.tanggapan-form textarea {
border-radius: 9px !important;
border: 1.5px solid #e0ede0 !important;
font-size: 12px !important;
padding: 7px 10px !important;
resize: none;
font-family: 'Plus Jakarta Sans', sans-serif;
outline: none;
transition: border-color 0.15s;
}
.tanggapan-form textarea:focus {
border-color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.1);
}
/* ── FOTO ── */
.bukti-img {
width: 44px;
height: 44px;
border-radius: 8px;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
transition: transform 0.15s;
}
.bukti-img:hover { transform: scale(1.1); }
/* ── DESKRIPSI TRUNCATE ── */
.desc-text {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
.dataTables_wrapper .dataTables_filter label {
font-size: 13px;
color: #5a6e5a;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.dataTables_wrapper .dataTables_filter input {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 6px 12px;
font-size: 13px;
outline: none;
margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus {
border-color: #1a6e3a;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12);
}
.dataTables_wrapper .dataTables_length select {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 5px 10px;
font-size: 13px;
margin: 0 6px;
}
.dataTables_wrapper .dataTables_info {
font-size: 12.5px;
color: #8a9e8a;
font-weight: 500;
padding: 0;
line-height: 36px;
}
div.dataTables_wrapper div.dataTables_paginate {
display: flex !important;
align-items: center;
gap: 3px;
margin: 0;
padding: 0;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button {
box-sizing: border-box !important;
width: 34px !important;
height: 34px !important;
min-width: 34px !important;
border-radius: 50% !important;
padding: 0 !important;
margin: 0 2px !important;
font-size: 13px !important;
font-weight: 600 !important;
border: 1.5px solid #e0ede0 !important;
color: #5a6e5a !important;
background: #ffffff !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.15s ease !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button:hover:not(.disabled):not(.current) {
background: #eaf7f0 !important;
border-color: #2e9e58 !important;
color: #1a6e3a !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current:hover {
background: #ffffff !important;
border: 2px solid #1a6e3a !important;
color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12) !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled:hover {
color: #c8d8c8 !important;
border-color: #edf5ed !important;
background: #fafafa !important;
cursor: default !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid">
{{-- PAGE HEADER --}}
<div class="page-header">
<div class="page-header-icon">
<i class="fas fa-headset"></i>
</div>
<div>
<h1>Layanan Pengaduan</h1>
<p>Manajemen pengaduan dan tanggapan penghuni</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i>
{{ session('success') }}
</div>
@endif
{{-- TABEL PENGADUAN --}}
<div class="premium-card">
<div class="premium-card-header">
<div class="header-left">
<div class="header-icon green"><i class="fas fa-clipboard-list"></i></div>
<h6>Daftar Pengaduan</h6>
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<div class="table-responsive-wrapper">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
<th>Nama</th>
<th>Tanggal</th>
<th>Kategori</th>
<th>Deskripsi</th>
<th>Foto</th>
<th>Status</th>
<th>Tanggapan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($layanan as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td><strong>{{ $item->penghuni->nama ?? '-' }}</strong></td>
<td style="white-space:nowrap;">
{{ \Carbon\Carbon::parse($item->tanggal_pengaduan)->translatedFormat('d M Y') }}
</td>
<td>
<span class="badge-pill-custom badge-blue">
<i class="fas fa-tag" style="font-size:9px"></i>
{{ ucfirst($item->kategori_masalah) }}
</span>
</td>
<td>
<span class="desc-text" title="{{ $item->deskripsi }}">
{{ \Illuminate\Support\Str::limit($item->deskripsi, 45) }}
</span>
</td>
<td>
@if($item->foto)
<img src="{{ asset('storage/' . $item->foto) }}" class="bukti-img">
@else
<span style="color:#ccc; font-size:12px;"></span>
@endif
</td>
<td>
@if($item->status == 'diajukan')
<span class="badge-pill-custom badge-yellow"><i class="fas fa-paper-plane" style="font-size:9px"></i> Diajukan</span>
@elseif($item->status == 'menunggu')
<span class="badge-pill-custom badge-blue"><i class="fas fa-hourglass-half" style="font-size:9px"></i> Menunggu</span>
@elseif($item->status == 'selesai')
<span class="badge-pill-custom badge-green"><i class="fas fa-check-circle" style="font-size:9px"></i> Selesai</span>
@else
<span class="badge-pill-custom badge-gray"><i class="fas fa-question-circle" style="font-size:9px"></i> Unknown</span>
@endif
</td>
<td style="max-width: 180px;">
@if($item->tanggapan_admin)
<span class="desc-text" title="{{ $item->tanggapan_admin }}">
{{ \Illuminate\Support\Str::limit($item->tanggapan_admin, 40) }}
</span>
@else
<span style="color:#ccc; font-size:12px; font-style:italic;">Belum ada tanggapan</span>
@endif
</td>
<td style="white-space:nowrap; min-width:160px;">
{{-- RT: Tanggapi --}}
@if(auth()->user()->role == 'rt' && $item->status == 'diajukan')
<form action="{{ route('layanan.tanggapi.rt', $item->id) }}" method="POST">
@csrf
<div class="tanggapan-form">
<textarea name="tanggapan_admin" rows="2" placeholder="Tulis tanggapan..." required></textarea>
<button type="submit" class="btn-action success">
<i class="fas fa-reply" style="font-size:10px"></i> Tanggapi
</button>
</div>
</form>
@endif
{{-- ADMIN: Approve --}}
@if(auth()->user()->role == 'admin' && $item->status == 'menunggu')
<form action="{{ route('layanan.approve.admin', $item->id) }}" method="POST" style="display:inline;">
@csrf
<button class="btn-action success">
<i class="fas fa-check-double" style="font-size:10px"></i> Approve
</button>
</form>
@endif
{{-- HAPUS --}}
<form action="{{ route('layanan.delete', $item->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn-action danger" onclick="return confirm('Yakin hapus pengaduan ini?')">
<i class="fas fa-trash" style="font-size:10px"></i> Hapus
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" style="text-align:center; padding: 32px; color: #aaa;">
<i class="fas fa-inbox" style="font-size:28px; display:block; margin-bottom:8px; opacity:0.4;"></i>
Belum ada data pengaduan
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
if ($.fn.DataTable) {
$('#dataTable').DataTable({
pageLength: 10,
lengthMenu: [5, 10, 25, 50],
dom: '<"d-flex justify-content-between align-items-center px-3 pt-3"lf>t<"d-flex justify-content-between align-items-center px-3 pb-3"ip>',
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START__END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data",
paginate: {
first: "«",
last: "»",
next: "",
previous: ""
},
emptyTable: "Belum ada data pengaduan"
}
});
}
});
</script>
@endpush

View File

@ -0,0 +1,491 @@
@extends('layouts.app')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', sans-serif; }
/* ── PAGE HEADER ── */
.page-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 28px;
}
.page-header-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-header h1 {
font-size: 22px;
font-weight: 800;
color: #1a2e1a;
margin: 0;
line-height: 1.2;
}
.page-header p {
font-size: 13px;
color: #8a9e8a;
margin: 0;
}
/* ── ALERT ── */
.alert-success-custom {
background: #f0faf4;
border: 1px solid #a8e6be;
border-left: 4px solid #1a6e3a;
border-radius: 12px;
padding: 12px 16px;
color: #1a4e2a;
font-size: 13.5px;
font-weight: 500;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
/* ── CARD ── */
.premium-card {
background: #ffffff;
border-radius: 16px;
border: 1px solid #e8f0e8;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
margin-bottom: 24px;
overflow: hidden;
}
.premium-card-header {
padding: 16px 22px;
background: #ffffff;
border-bottom: 1px solid #f0f5f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.premium-card-header .header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-icon {
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.header-icon.green { background: #d1fae5; color: #064e3b; }
.premium-card-header h6 {
font-size: 14px;
font-weight: 700;
color: #1a2e1a;
margin: 0;
}
/* ── TABLE ── */
.premium-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.premium-table thead tr th {
background: #f6fbf7;
color: #3a5a3a;
font-weight: 700;
font-size: 11.5px;
letter-spacing: 0.5px;
text-transform: uppercase;
padding: 11px 14px;
border-bottom: 2px solid #e0ede0;
white-space: nowrap;
}
.premium-table thead tr th:first-child { border-radius: 10px 0 0 0; }
.premium-table thead tr th:last-child { border-radius: 0 10px 0 0; }
.premium-table tbody tr td {
padding: 11px 14px;
border-bottom: 1px solid #f2f7f2;
color: #2a3a2a;
vertical-align: middle;
}
.premium-table tbody tr:hover td { background: #f8fdf8; }
.premium-table tbody tr:last-child td { border-bottom: none; }
/* ── BADGES ── */
.badge-pill-custom {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 99px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.badge-green { background: #d1fae5; color: #064e3b; }
.badge-red { background: #fdf0f0; color: #c53030; }
.badge-yellow { background: #fdfbea; color: #7a6010; }
.badge-blue { background: #eaf0fb; color: #1a4e8e; }
.badge-gray { background: #f3f4f6; color: #4b5563; }
/* ── ACTION BUTTONS ── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 11px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
text-decoration: none;
transition: opacity 0.15s, transform 0.1s;
}
.btn-action:hover { opacity: 0.85; transform: translateY(-1px); text-decoration: none; }
.btn-action:active { transform: translateY(0); }
.btn-action.info { background: #eaf0fb; color: #1a4e8e; }
.btn-action.warn { background: #fdf8ea; color: #8a6000; }
.btn-action.danger { background: #fdf0f0; color: #c53030; }
.btn-action.success { background: #d1fae5; color: #064e3b; }
/* ── TANGGAPAN FORM ── */
.tanggapan-form {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 180px;
}
.tanggapan-form textarea {
border-radius: 9px !important;
border: 1.5px solid #e0ede0 !important;
font-size: 12px !important;
padding: 7px 10px !important;
resize: none;
font-family: 'Plus Jakarta Sans', sans-serif;
outline: none;
transition: border-color 0.15s;
}
.tanggapan-form textarea:focus {
border-color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.1);
}
/* ── FOTO ── */
.bukti-img {
width: 44px;
height: 44px;
border-radius: 8px;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
transition: transform 0.15s;
}
.bukti-img:hover { transform: scale(1.1); }
/* ── DESKRIPSI TRUNCATE ── */
.desc-text {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
.dataTables_wrapper .dataTables_filter label {
font-size: 13px;
color: #5a6e5a;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.dataTables_wrapper .dataTables_filter input {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 6px 12px;
font-size: 13px;
outline: none;
margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus {
border-color: #1a6e3a;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12);
}
.dataTables_wrapper .dataTables_length select {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 5px 10px;
font-size: 13px;
margin: 0 6px;
}
.dataTables_wrapper .dataTables_info {
font-size: 12.5px;
color: #8a9e8a;
font-weight: 500;
padding: 0;
line-height: 36px;
}
div.dataTables_wrapper div.dataTables_paginate {
display: flex !important;
align-items: center;
gap: 3px;
margin: 0;
padding: 0;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button {
box-sizing: border-box !important;
width: 34px !important;
height: 34px !important;
min-width: 34px !important;
border-radius: 50% !important;
padding: 0 !important;
margin: 0 2px !important;
font-size: 13px !important;
font-weight: 600 !important;
border: 1.5px solid #e0ede0 !important;
color: #5a6e5a !important;
background: #ffffff !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.15s ease !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button:hover:not(.disabled):not(.current) {
background: #eaf7f0 !important;
border-color: #2e9e58 !important;
color: #1a6e3a !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current:hover {
background: #ffffff !important;
border: 2px solid #1a6e3a !important;
color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12) !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled:hover {
color: #c8d8c8 !important;
border-color: #edf5ed !important;
background: #fafafa !important;
cursor: default !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid">
{{-- PAGE HEADER --}}
<div class="page-header">
<div class="page-header-icon">
<i class="fas fa-headset"></i>
</div>
<div>
<h1>Layanan Pengaduan</h1>
<p>Manajemen pengaduan dan tanggapan penghuni</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i>
{{ session('success') }}
</div>
@endif
{{-- TABEL PENGADUAN --}}
<div class="premium-card">
<div class="premium-card-header">
<div class="header-left">
<div class="header-icon green"><i class="fas fa-clipboard-list"></i></div>
<h6>Daftar Pengaduan</h6>
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<div class="table-responsive-wrapper">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
<th>Nama</th>
<th>Tanggal</th>
<th>Kategori</th>
<th>Deskripsi</th>
<th>Foto</th>
<th>Status</th>
<th>Tanggapan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($layanan as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td><strong>{{ $item->penghuni->nama ?? '-' }}</strong></td>
<td style="white-space:nowrap;">
{{ \Carbon\Carbon::parse($item->tanggal_pengaduan)->translatedFormat('d M Y') }}
</td>
<td>
<span class="badge-pill-custom badge-blue">
<i class="fas fa-tag" style="font-size:9px"></i>
{{ ucfirst($item->kategori_masalah) }}
</span>
</td>
<td>
<span class="desc-text" title="{{ $item->deskripsi }}">
{{ \Illuminate\Support\Str::limit($item->deskripsi, 45) }}
</span>
</td>
<td>
@if($item->foto)
<img src="{{ asset('storage/' . $item->foto) }}" class="bukti-img">
@else
<span style="color:#ccc; font-size:12px;"></span>
@endif
</td>
<td>
@if($item->status == 'diajukan')
<span class="badge-pill-custom badge-yellow"><i class="fas fa-paper-plane" style="font-size:9px"></i> Diajukan</span>
@elseif($item->status == 'menunggu')
<span class="badge-pill-custom badge-blue"><i class="fas fa-hourglass-half" style="font-size:9px"></i> Menunggu</span>
@elseif($item->status == 'selesai')
<span class="badge-pill-custom badge-green"><i class="fas fa-check-circle" style="font-size:9px"></i> Selesai</span>
@else
<span class="badge-pill-custom badge-gray"><i class="fas fa-question-circle" style="font-size:9px"></i> Unknown</span>
@endif
</td>
<td style="max-width: 180px;">
@if($item->tanggapan_admin)
<span class="desc-text" title="{{ $item->tanggapan_admin }}">
{{ \Illuminate\Support\Str::limit($item->tanggapan_admin, 40) }}
</span>
@else
<span style="color:#ccc; font-size:12px; font-style:italic;">Belum ada tanggapan</span>
@endif
</td>
<td style="white-space:nowrap; min-width:160px;">
{{-- RT: Tanggapi --}}
@if(auth()->user()->role == 'rt' && $item->status == 'diajukan')
<form action="{{ route('layanan.tanggapi.rt', $item->id) }}" method="POST">
@csrf
<div class="tanggapan-form">
<textarea name="tanggapan_admin" rows="2" placeholder="Tulis tanggapan..." required></textarea>
<button type="submit" class="btn-action success">
<i class="fas fa-reply" style="font-size:10px"></i> Tanggapi
</button>
</div>
</form>
@endif
{{-- ADMIN: Approve --}}
@if(auth()->user()->role == 'admin' && $item->status == 'menunggu')
<form action="{{ route('layanan.approve.admin', $item->id) }}" method="POST" style="display:inline;">
@csrf
<button class="btn-action success">
<i class="fas fa-check-double" style="font-size:10px"></i> Approve
</button>
</form>
@endif
{{-- HAPUS --}}
<form action="{{ route('layanan.delete', $item->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn-action danger" onclick="return confirm('Yakin hapus pengaduan ini?')">
<i class="fas fa-trash" style="font-size:10px"></i> Hapus
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" style="text-align:center; padding: 32px; color: #aaa;">
<i class="fas fa-inbox" style="font-size:28px; display:block; margin-bottom:8px; opacity:0.4;"></i>
Belum ada data pengaduan
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
if ($.fn.DataTable) {
$('#dataTable').DataTable({
pageLength: 10,
lengthMenu: [5, 10, 25, 50],
dom: '<"d-flex justify-content-between align-items-center px-3 pt-3"lf>t<"d-flex justify-content-between align-items-center px-3 pb-3"ip>',
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START__END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data",
paginate: {
first: "«",
last: "»",
next: "",
previous: ""
},
emptyTable: "Belum ada data pengaduan"
}
});
}
});
</script>
@endpush

View File

@ -0,0 +1,492 @@
@extends('layouts.app')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', sans-serif; }
/* ── PAGE HEADER ── */
.page-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 28px;
}
.page-header-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-header h1 {
font-size: 22px;
font-weight: 800;
color: #1a2e1a;
margin: 0;
line-height: 1.2;
}
.page-header p {
font-size: 13px;
color: #8a9e8a;
margin: 0;
}
/* ── ALERT ── */
.alert-success-custom {
background: #f0faf4;
border: 1px solid #a8e6be;
border-left: 4px solid #1a6e3a;
border-radius: 12px;
padding: 12px 16px;
color: #1a4e2a;
font-size: 13.5px;
font-weight: 500;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
/* ── CARD ── */
.premium-card {
background: #ffffff;
border-radius: 16px;
border: 1px solid #e8f0e8;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
margin-bottom: 24px;
overflow: hidden;
}
.premium-card-header {
padding: 16px 22px;
background: #ffffff;
border-bottom: 1px solid #f0f5f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.premium-card-header .header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-icon {
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.header-icon.green { background: #d1fae5; color: #064e3b; }
.premium-card-header h6 {
font-size: 14px;
font-weight: 700;
color: #1a2e1a;
margin: 0;
}
/* ── TABLE ── */
.premium-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.premium-table thead tr th {
background: #f6fbf7;
color: #3a5a3a;
font-weight: 700;
font-size: 11.5px;
letter-spacing: 0.5px;
text-transform: uppercase;
padding: 11px 14px;
border-bottom: 2px solid #e0ede0;
white-space: nowrap;
}
.premium-table thead tr th:first-child { border-radius: 10px 0 0 0; }
.premium-table thead tr th:last-child { border-radius: 0 10px 0 0; }
.premium-table tbody tr td {
padding: 11px 14px;
border-bottom: 1px solid #f2f7f2;
color: #2a3a2a;
vertical-align: middle;
}
.premium-table tbody tr:hover td { background: #f8fdf8; }
.premium-table tbody tr:last-child td { border-bottom: none; }
/* ── BADGES ── */
.badge-pill-custom {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 99px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.badge-green { background: #d1fae5; color: #064e3b; }
.badge-red { background: #fdf0f0; color: #c53030; }
.badge-yellow { background: #fdfbea; color: #7a6010; }
.badge-blue { background: #eaf0fb; color: #1a4e8e; }
.badge-gray { background: #f3f4f6; color: #4b5563; }
/* ── ACTION BUTTONS ── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 11px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
text-decoration: none;
transition: opacity 0.15s, transform 0.1s;
}
.btn-action:hover { opacity: 0.85; transform: translateY(-1px); text-decoration: none; }
.btn-action:active { transform: translateY(0); }
.btn-action.info { background: #eaf0fb; color: #1a4e8e; }
.btn-action.warn { background: #fdf8ea; color: #8a6000; }
.btn-action.danger { background: #fdf0f0; color: #c53030; }
.btn-action.success { background: #d1fae5; color: #064e3b; }
/* ── TANGGAPAN FORM ── */
.tanggapan-form {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 180px;
}
.tanggapan-form textarea {
border-radius: 9px !important;
border: 1.5px solid #e0ede0 !important;
font-size: 12px !important;
padding: 7px 10px !important;
resize: none;
font-family: 'Plus Jakarta Sans', sans-serif;
outline: none;
transition: border-color 0.15s;
}
.tanggapan-form textarea:focus {
border-color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.1);
}
/* ── FOTO ── */
.bukti-img {
width: 44px;
height: 44px;
border-radius: 8px;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
transition: transform 0.15s;
}
.bukti-img:hover { transform: scale(1.1); }
/* ── DESKRIPSI TRUNCATE ── */
.desc-text {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
.dataTables_wrapper .dataTables_filter label {
font-size: 13px;
color: #5a6e5a;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.dataTables_wrapper .dataTables_filter input {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 6px 12px;
font-size: 13px;
outline: none;
margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus {
border-color: #1a6e3a;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12);
}
.dataTables_wrapper .dataTables_length select {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 5px 10px;
font-size: 13px;
margin: 0 6px;
}
.dataTables_wrapper .dataTables_info {
font-size: 12.5px;
color: #8a9e8a;
font-weight: 500;
padding: 0;
line-height: 36px;
}
div.dataTables_wrapper div.dataTables_paginate {
display: flex !important;
align-items: center;
gap: 3px;
margin: 0;
padding: 0;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button {
box-sizing: border-box !important;
width: 34px !important;
height: 34px !important;
min-width: 34px !important;
border-radius: 50% !important;
padding: 0 !important;
margin: 0 2px !important;
font-size: 13px !important;
font-weight: 600 !important;
border: 1.5px solid #e0ede0 !important;
color: #5a6e5a !important;
background: #ffffff !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.15s ease !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button:hover:not(.disabled):not(.current) {
background: #eaf7f0 !important;
border-color: #2e9e58 !important;
color: #1a6e3a !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current:hover {
background: #ffffff !important;
border: 2px solid #1a6e3a !important;
color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12) !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled:hover {
color: #c8d8c8 !important;
border-color: #edf5ed !important;
background: #fafafa !important;
cursor: default !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid">
{{-- PAGE HEADER --}}
<div class="page-header">
<div class="page-header-icon">
<i class="fas fa-headset"></i>
</div>
<div>
<h1>Layanan Pengaduan</h1>
<p>Manajemen pengaduan dan tanggapan penghuni</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i>
{{ session('success') }}
</div>
@endif
{{-- TABEL PENGADUAN --}}
<div class="premium-card">
<div class="premium-card-header">
<div class="header-left">
<div class="header-icon green"><i class="fas fa-clipboard-list"></i></div>
<h6>Daftar Pengaduan</h6>
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<div class="table-responsive-wrapper">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
<th>Nama</th>
<th>Tanggal</th>
<th>Kategori</th>
<th>Deskripsi</th>
<th>Foto</th>
<th>Status</th>
<th>Tanggapan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($layanan as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td><strong>{{ $item->penghuni->nama ?? '-' }}</strong></td>
<td style="white-space:nowrap;">
{{ \Carbon\Carbon::parse($item->tanggal_pengaduan)->translatedFormat('d M Y') }}
</td>
<td>
<span class="badge-pill-custom badge-blue">
<i class="fas fa-tag" style="font-size:9px"></i>
{{ ucfirst($item->kategori_masalah) }}
</span>
</td>
<td>
<span class="desc-text" title="{{ $item->deskripsi }}">
{{ \Illuminate\Support\Str::limit($item->deskripsi, 45) }}
</span>
</td>
<td>
@if($item->foto)
<img src="{{ asset('storage/' . $item->foto) }}" class="bukti-img">
@else
<span style="color:#ccc; font-size:12px;"></span>
@endif
</td>
<td>
@if($item->status == 'diajukan')
<span class="badge-pill-custom badge-yellow"><i class="fas fa-paper-plane" style="font-size:9px"></i> Diajukan</span>
@elseif($item->status == 'menunggu')
<span class="badge-pill-custom badge-blue"><i class="fas fa-hourglass-half" style="font-size:9px"></i> Menunggu</span>
@elseif($item->status == 'selesai')
<span class="badge-pill-custom badge-green"><i class="fas fa-check-circle" style="font-size:9px"></i> Selesai</span>
@else
<span class="badge-pill-custom badge-gray"><i class="fas fa-question-circle" style="font-size:9px"></i> Unknown</span>
@endif
</td>
<td style="max-width: 180px;">
@if($item->tanggapan_admin)
<span class="desc-text" title="{{ $item->tanggapan_admin }}">
{{ \Illuminate\Support\Str::limit($item->tanggapan_admin, 40) }}
</span>
@else
<span style="color:#ccc; font-size:12px; font-style:italic;">Belum ada tanggapan</span>
@endif
</td>
<td style="white-space:nowrap; min-width:160px;">
{{-- RT: Tanggapi --}}
@if(auth()->user()->role == 'rt' && $item->status == 'diajukan')
<form action="{{ route('layanan.tanggapi.rt', $item->id) }}" method="POST">
@csrf
<div class="tanggapan-form">
<textarea name="tanggapan_admin" rows="2" placeholder="Tulis tanggapan..." required></textarea>
<button type="submit" class="btn-action success">
<i class="fas fa-reply" style="font-size:10px"></i> Tanggapi
</button>
</div>
</form>
@endif
{{-- ADMIN: Approve --}}
@if(auth()->user()->role == 'admin' && $item->status == 'menunggu')
<form action="{{ route('layanan.approve.admin', $item->id) }}" method="POST" style="display:inline;">
@csrf
<button class="btn-action success">
<i class="fas fa-check-double" style="font-size:10px"></i> Approve
</button>
</form>
@endif
{{-- HAPUS --}}
<form action="{{ route('layanan.delete', $item->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn-action danger" onclick="return confirm('Yakin hapus pengaduan ini?')">
<i class="fas fa-trash" style="font-size:10px"></i> Hapus
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" style="text-align:center; padding: 32px; color: #aaa;">
<i class="fas fa-inbox" style="font-size:28px; display:block; margin-bottom:8px; opacity:0.4;"></i>
Belum ada data pengaduan
</td>
</tr>
@endforelse
</tbody>
</table>
</div> {{-- tutup table-responsive-wrapper --}}
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
if ($.fn.DataTable) {
$('#dataTable').DataTable({
pageLength: 10,
lengthMenu: [5, 10, 25, 50],
dom: '<"d-flex justify-content-between align-items-center px-3 pt-3"lf>t<"d-flex justify-content-between align-items-center px-3 pb-3"ip>',
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START__END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data",
paginate: {
first: "«",
last: "»",
next: "",
previous: ""
},
emptyTable: "Belum ada data pengaduan"
}
});
}
});
</script>
@endpush

View File

@ -0,0 +1,492 @@
@extends('layouts.app')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', sans-serif; }
/* ── PAGE HEADER ── */
.page-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 28px;
}
.page-header-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-header h1 {
font-size: 22px;
font-weight: 800;
color: #1a2e1a;
margin: 0;
line-height: 1.2;
}
.page-header p {
font-size: 13px;
color: #8a9e8a;
margin: 0;
}
/* ── ALERT ── */
.alert-success-custom {
background: #f0faf4;
border: 1px solid #a8e6be;
border-left: 4px solid #1a6e3a;
border-radius: 12px;
padding: 12px 16px;
color: #1a4e2a;
font-size: 13.5px;
font-weight: 500;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
/* ── CARD ── */
.premium-card {
background: #ffffff;
border-radius: 16px;
border: 1px solid #e8f0e8;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
margin-bottom: 24px;
overflow: hidden;
}
.premium-card-header {
padding: 16px 22px;
background: #ffffff;
border-bottom: 1px solid #f0f5f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.premium-card-header .header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-icon {
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.header-icon.green { background: #d1fae5; color: #064e3b; }
.premium-card-header h6 {
font-size: 14px;
font-weight: 700;
color: #1a2e1a;
margin: 0;
}
/* ── TABLE ── */
.premium-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.premium-table thead tr th {
background: #f6fbf7;
color: #3a5a3a;
font-weight: 700;
font-size: 11.5px;
letter-spacing: 0.5px;
text-transform: uppercase;
padding: 11px 14px;
border-bottom: 2px solid #e0ede0;
white-space: nowrap;
}
.premium-table thead tr th:first-child { border-radius: 10px 0 0 0; }
.premium-table thead tr th:last-child { border-radius: 0 10px 0 0; }
.premium-table tbody tr td {
padding: 11px 14px;
border-bottom: 1px solid #f2f7f2;
color: #2a3a2a;
vertical-align: middle;
}
.premium-table tbody tr:hover td { background: #f8fdf8; }
.premium-table tbody tr:last-child td { border-bottom: none; }
/* ── BADGES ── */
.badge-pill-custom {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 99px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.badge-green { background: #d1fae5; color: #064e3b; }
.badge-red { background: #fdf0f0; color: #c53030; }
.badge-yellow { background: #fdfbea; color: #7a6010; }
.badge-blue { background: #eaf0fb; color: #1a4e8e; }
.badge-gray { background: #f3f4f6; color: #4b5563; }
/* ── ACTION BUTTONS ── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 11px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
text-decoration: none;
transition: opacity 0.15s, transform 0.1s;
}
.btn-action:hover { opacity: 0.85; transform: translateY(-1px); text-decoration: none; }
.btn-action:active { transform: translateY(0); }
.btn-action.info { background: #eaf0fb; color: #1a4e8e; }
.btn-action.warn { background: #fdf8ea; color: #8a6000; }
.btn-action.danger { background: #fdf0f0; color: #c53030; }
.btn-action.success { background: #d1fae5; color: #064e3b; }
/* ── TANGGAPAN FORM ── */
.tanggapan-form {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 180px;
}
.tanggapan-form textarea {
border-radius: 9px !important;
border: 1.5px solid #e0ede0 !important;
font-size: 12px !important;
padding: 7px 10px !important;
resize: none;
font-family: 'Plus Jakarta Sans', sans-serif;
outline: none;
transition: border-color 0.15s;
}
.tanggapan-form textarea:focus {
border-color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.1);
}
/* ── FOTO ── */
.bukti-img {
width: 44px;
height: 44px;
border-radius: 8px;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
transition: transform 0.15s;
}
.bukti-img:hover { transform: scale(1.1); }
/* ── DESKRIPSI TRUNCATE ── */
.desc-text {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
.dataTables_wrapper .dataTables_filter label {
font-size: 13px;
color: #5a6e5a;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.dataTables_wrapper .dataTables_filter input {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 6px 12px;
font-size: 13px;
outline: none;
margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus {
border-color: #1a6e3a;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12);
}
.dataTables_wrapper .dataTables_length select {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 5px 10px;
font-size: 13px;
margin: 0 6px;
}
.dataTables_wrapper .dataTables_info {
font-size: 12.5px;
color: #8a9e8a;
font-weight: 500;
padding: 0;
line-height: 36px;
}
div.dataTables_wrapper div.dataTables_paginate {
display: flex !important;
align-items: center;
gap: 3px;
margin: 0;
padding: 0;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button {
box-sizing: border-box !important;
width: 34px !important;
height: 34px !important;
min-width: 34px !important;
border-radius: 50% !important;
padding: 0 !important;
margin: 0 2px !important;
font-size: 13px !important;
font-weight: 600 !important;
border: 1.5px solid #e0ede0 !important;
color: #5a6e5a !important;
background: #ffffff !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.15s ease !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button:hover:not(.disabled):not(.current) {
background: #eaf7f0 !important;
border-color: #2e9e58 !important;
color: #1a6e3a !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current:hover {
background: #ffffff !important;
border: 2px solid #1a6e3a !important;
color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12) !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled:hover {
color: #c8d8c8 !important;
border-color: #edf5ed !important;
background: #fafafa !important;
cursor: default !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid">
{{-- PAGE HEADER --}}
<div class="page-header">
<div class="page-header-icon">
<i class="fas fa-headset"></i>
</div>
<div>
<h1>Layanan Pengaduan</h1>
<p>Manajemen pengaduan dan tanggapan penghuni</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i>
{{ session('success') }}
</div>
@endif
{{-- TABEL PENGADUAN --}}
<div class="premium-card">
<div class="premium-card-header">
<div class="header-left">
<div class="header-icon green"><i class="fas fa-clipboard-list"></i></div>
<h6>Daftar Pengaduan</h6>
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<div class="table-responsive-wrapper">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
<th>Nama</th>
<th>Tanggal</th>
<th>Kategori</th>
<th>Deskripsi</th>
<th>Foto</th>
<th>Status</th>
<th>Tanggapan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($layanan as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td><strong>{{ $item->penghuni->nama ?? '-' }}</strong></td>
<td style="white-space:nowrap;">
{{ \Carbon\Carbon::parse($item->tanggal_pengaduan)->translatedFormat('d M Y') }}
</td>
<td>
<span class="badge-pill-custom badge-blue">
<i class="fas fa-tag" style="font-size:9px"></i>
{{ ucfirst($item->kategori_masalah) }}
</span>
</td>
<td>
<span class="desc-text" title="{{ $item->deskripsi }}">
{{ \Illuminate\Support\Str::limit($item->deskripsi, 45) }}
</span>
</td>
<td>
@if($item->foto)
<img src="{{ asset('storage/' . $item->foto) }}" class="bukti-img">
@else
<span style="color:#ccc; font-size:12px;"></span>
@endif
</td>
<td>
@if($item->status == 'diajukan')
<span class="badge-pill-custom badge-yellow"><i class="fas fa-paper-plane" style="font-size:9px"></i> Diajukan</span>
@elseif($item->status == 'menunggu')
<span class="badge-pill-custom badge-blue"><i class="fas fa-hourglass-half" style="font-size:9px"></i> Menunggu</span>
@elseif($item->status == 'selesai')
<span class="badge-pill-custom badge-green"><i class="fas fa-check-circle" style="font-size:9px"></i> Selesai</span>
@else
<span class="badge-pill-custom badge-gray"><i class="fas fa-question-circle" style="font-size:9px"></i> Unknown</span>
@endif
</td>
<td style="max-width: 180px;">
@if($item->tanggapan_admin)
<span class="desc-text" title="{{ $item->tanggapan_admin }}">
{{ \Illuminate\Support\Str::limit($item->tanggapan_admin, 40) }}
</span>
@else
<span style="color:#ccc; font-size:12px; font-style:italic;">Belum ada tanggapan</span>
@endif
</td>
<td style="white-space:nowrap; min-width:160px;">
{{-- RT: Tanggapi --}}
@if(auth()->user()->role == 'rt' && $item->status == 'diajukan')
<form action="{{ route('layanan.tanggapi.rt', $item->id) }}" method="POST">
@csrf
<div class="tanggapan-form">
<textarea name="tanggapan_admin" rows="2" placeholder="Tulis tanggapan..." required></textarea>
<button type="submit" class="btn-action success">
<i class="fas fa-reply" style="font-size:10px"></i> Tanggapi
</button>
</div>
</form>
@endif
{{-- ADMIN: Approve --}}
@if(auth()->user()->role == 'admin' && $item->status == 'menunggu')
<form action="{{ route('layanan.approve.admin', $item->id) }}" method="POST" style="display:inline;">
@csrf
<button class="btn-action success">
<i class="fas fa-check-double" style="font-size:10px"></i> Approve
</button>
</form>
@endif
{{-- HAPUS --}}
<form action="{{ route('layanan.delete', $item->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn-action danger" onclick="return confirm('Yakin hapus pengaduan ini?')">
<i class="fas fa-trash" style="font-size:10px"></i> Hapus
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" style="text-align:center; padding: 32px; color: #aaa;">
<i class="fas fa-inbox" style="font-size:28px; display:block; margin-bottom:8px; opacity:0.4;"></i>
Belum ada data pengaduan
</td>
</tr>
@endforelse
</tbody>
</table>
</div> {{-- tutup table-responsive-wrapper --}}
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
if ($.fn.DataTable) {
$('#dataTable').DataTable({
pageLength: 10,
lengthMenu: [5, 10, 25, 50],
dom: '<"d-flex justify-content-between align-items-center px-3 pt-3"lf>t<"d-flex justify-content-between align-items-center px-3 pb-3"ip>',
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START__END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data",
paginate: {
first: "«",
last: "»",
next: "",
previous: ""
},
emptyTable: "Belum ada data pengaduan"
}
});
}
});
</script>
@endpush

View File

@ -0,0 +1,492 @@
@extends('layouts.app')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: 'Plus Jakarta Sans', sans-serif; }
/* ── PAGE HEADER ── */
.page-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 28px;
}
.page-header-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-header h1 {
font-size: 22px;
font-weight: 800;
color: #1a2e1a;
margin: 0;
line-height: 1.2;
}
.page-header p {
font-size: 13px;
color: #8a9e8a;
margin: 0;
}
/* ── ALERT ── */
.alert-success-custom {
background: #f0faf4;
border: 1px solid #a8e6be;
border-left: 4px solid #1a6e3a;
border-radius: 12px;
padding: 12px 16px;
color: #1a4e2a;
font-size: 13.5px;
font-weight: 500;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
/* ── CARD ── */
.premium-card {
background: #ffffff;
border-radius: 16px;
border: 1px solid #e8f0e8;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
margin-bottom: 24px;
overflow: hidden;
}
.premium-card-header {
padding: 16px 22px;
background: #ffffff;
border-bottom: 1px solid #f0f5f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.premium-card-header .header-left {
display: flex;
align-items: center;
gap: 10px;
}
.header-icon {
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.header-icon.green { background: #d1fae5; color: #064e3b; }
.premium-card-header h6 {
font-size: 14px;
font-weight: 700;
color: #1a2e1a;
margin: 0;
}
/* ── TABLE ── */
.premium-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.premium-table thead tr th {
background: #f6fbf7;
color: #3a5a3a;
font-weight: 700;
font-size: 11.5px;
letter-spacing: 0.5px;
text-transform: uppercase;
padding: 11px 14px;
border-bottom: 2px solid #e0ede0;
white-space: nowrap;
}
.premium-table thead tr th:first-child { border-radius: 10px 0 0 0; }
.premium-table thead tr th:last-child { border-radius: 0 10px 0 0; }
.premium-table tbody tr td {
padding: 11px 14px;
border-bottom: 1px solid #f2f7f2;
color: #2a3a2a;
vertical-align: middle;
}
.premium-table tbody tr:hover td { background: #f8fdf8; }
.premium-table tbody tr:last-child td { border-bottom: none; }
/* ── BADGES ── */
.badge-pill-custom {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 99px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.badge-green { background: #d1fae5; color: #064e3b; }
.badge-red { background: #fdf0f0; color: #c53030; }
.badge-yellow { background: #fdfbea; color: #7a6010; }
.badge-blue { background: #eaf0fb; color: #1a4e8e; }
.badge-gray { background: #f3f4f6; color: #4b5563; }
/* ── ACTION BUTTONS ── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 11px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
text-decoration: none;
transition: opacity 0.15s, transform 0.1s;
}
.btn-action:hover { opacity: 0.85; transform: translateY(-1px); text-decoration: none; }
.btn-action:active { transform: translateY(0); }
.btn-action.info { background: #eaf0fb; color: #1a4e8e; }
.btn-action.warn { background: #fdf8ea; color: #8a6000; }
.btn-action.danger { background: #fdf0f0; color: #c53030; }
.btn-action.success { background: #d1fae5; color: #064e3b; }
/* ── TANGGAPAN FORM ── */
.tanggapan-form {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 180px;
}
.tanggapan-form textarea {
border-radius: 9px !important;
border: 1.5px solid #e0ede0 !important;
font-size: 12px !important;
padding: 7px 10px !important;
resize: none;
font-family: 'Plus Jakarta Sans', sans-serif;
outline: none;
transition: border-color 0.15s;
}
.tanggapan-form textarea:focus {
border-color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.1);
}
/* ── FOTO ── */
.bukti-img {
width: 44px;
height: 44px;
border-radius: 8px;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
transition: transform 0.15s;
}
.bukti-img:hover { transform: scale(1.1); }
/* ── DESKRIPSI TRUNCATE ── */
.desc-text {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
.dataTables_wrapper .dataTables_filter label {
font-size: 13px;
color: #5a6e5a;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.dataTables_wrapper .dataTables_filter input {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 6px 12px;
font-size: 13px;
outline: none;
margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus {
border-color: #1a6e3a;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12);
}
.dataTables_wrapper .dataTables_length select {
border-radius: 9px;
border: 1.5px solid #e0ede0;
padding: 5px 10px;
font-size: 13px;
margin: 0 6px;
}
.dataTables_wrapper .dataTables_info {
font-size: 12.5px;
color: #8a9e8a;
font-weight: 500;
padding: 0;
line-height: 36px;
}
div.dataTables_wrapper div.dataTables_paginate {
display: flex !important;
align-items: center;
gap: 3px;
margin: 0;
padding: 0;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button {
box-sizing: border-box !important;
width: 34px !important;
height: 34px !important;
min-width: 34px !important;
border-radius: 50% !important;
padding: 0 !important;
margin: 0 2px !important;
font-size: 13px !important;
font-weight: 600 !important;
border: 1.5px solid #e0ede0 !important;
color: #5a6e5a !important;
background: #ffffff !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.15s ease !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button:hover:not(.disabled):not(.current) {
background: #eaf7f0 !important;
border-color: #2e9e58 !important;
color: #1a6e3a !important;
box-shadow: none !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.current:hover {
background: #ffffff !important;
border: 2px solid #1a6e3a !important;
color: #1a6e3a !important;
box-shadow: 0 0 0 3px rgba(26,110,58,0.12) !important;
}
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled,
div.dataTables_wrapper div.dataTables_paginate .paginate_button.disabled:hover {
color: #c8d8c8 !important;
border-color: #edf5ed !important;
background: #fafafa !important;
cursor: default !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid">
{{-- PAGE HEADER --}}
<div class="page-header">
<div class="page-header-icon">
<i class="fas fa-headset"></i>
</div>
<div>
<h1>Layanan Pengaduan</h1>
<p>Manajemen pengaduan dan tanggapan penghuni</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i>
{{ session('success') }}
</div>
@endif
{{-- TABEL PENGADUAN --}}
<div class="premium-card">
<div class="premium-card-header">
<div class="header-left">
<div class="header-icon green"><i class="fas fa-clipboard-list"></i></div>
<h6>Daftar Pengaduan</h6>
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<div class="table-responsive-wrapper">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
<th>Nama</th>
<th>Tanggal</th>
<th>Kategori</th>
<th>Deskripsi</th>
<th>Foto</th>
<th>Status</th>
<th>Tanggapan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($layanan as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td><strong>{{ $item->penghuni->nama ?? '-' }}</strong></td>
<td style="white-space:nowrap;">
{{ \Carbon\Carbon::parse($item->tanggal_pengaduan)->translatedFormat('d M Y') }}
</td>
<td>
<span class="badge-pill-custom badge-blue">
<i class="fas fa-tag" style="font-size:9px"></i>
{{ ucfirst($item->kategori_masalah) }}
</span>
</td>
<td>
<span class="desc-text" title="{{ $item->deskripsi }}">
{{ \Illuminate\Support\Str::limit($item->deskripsi, 45) }}
</span>
</td>
<td>
@if($item->foto)
<img src="{{ asset('storage/' . $item->foto) }}" class="bukti-img">
@else
<span style="color:#ccc; font-size:12px;"></span>
@endif
</td>
<td>
@if($item->status == 'diajukan')
<span class="badge-pill-custom badge-yellow"><i class="fas fa-paper-plane" style="font-size:9px"></i> Diajukan</span>
@elseif($item->status == 'menunggu')
<span class="badge-pill-custom badge-blue"><i class="fas fa-hourglass-half" style="font-size:9px"></i> Menunggu</span>
@elseif($item->status == 'selesai')
<span class="badge-pill-custom badge-green"><i class="fas fa-check-circle" style="font-size:9px"></i> Selesai</span>
@else
<span class="badge-pill-custom badge-gray"><i class="fas fa-question-circle" style="font-size:9px"></i> Unknown</span>
@endif
</td>
<td style="max-width: 180px;">
@if($item->tanggapan_admin)
<span class="desc-text" title="{{ $item->tanggapan_admin }}">
{{ \Illuminate\Support\Str::limit($item->tanggapan_admin, 40) }}
</span>
@else
<span style="color:#ccc; font-size:12px; font-style:italic;">Belum ada tanggapan</span>
@endif
</td>
<td style="white-space:nowrap; min-width:160px;">
{{-- RT: Tanggapi --}}
@if(auth()->user()->role == 'rt' && $item->status == 'diajukan')
<form action="{{ route('layanan.tanggapi.rt', $item->id) }}" method="POST">
@csrf
<div class="tanggapan-form">
<textarea name="tanggapan_admin" rows="2" placeholder="Tulis tanggapan..." required></textarea>
<button type="submit" class="btn-action success">
<i class="fas fa-reply" style="font-size:10px"></i> Tanggapi
</button>
</div>
</form>
@endif
{{-- ADMIN: Approve --}}
@if(auth()->user()->role == 'admin' && $item->status == 'menunggu')
<form action="{{ route('layanan.approve.admin', $item->id) }}" method="POST" style="display:inline;">
@csrf
<button class="btn-action success">
<i class="fas fa-check-double" style="font-size:10px"></i> Approve
</button>
</form>
@endif
{{-- HAPUS --}}
<form action="{{ route('layanan.delete', $item->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn-action danger" onclick="return confirm('Yakin hapus pengaduan ini?')">
<i class="fas fa-trash" style="font-size:10px"></i> Hapus
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" style="text-align:center; padding: 32px; color: #aaa;">
<i class="fas fa-inbox" style="font-size:28px; display:block; margin-bottom:8px; opacity:0.4;"></i>
Belum ada data pengaduan
</td>
</tr>
@endforelse
</tbody>
</table>
</div> {{-- tutup table-responsive-wrapper --}}
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function () {
if ($.fn.DataTable) {
$('#dataTable').DataTable({
pageLength: 10,
lengthMenu: [5, 10, 25, 50],
dom: '<"d-flex justify-content-between align-items-center px-3 pt-3"lf>t<"d-flex justify-content-between align-items-center px-3 pb-3"ip>',
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START__END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data",
paginate: {
first: "«",
last: "»",
next: "",
previous: ""
},
emptyTable: "Belum ada data pengaduan"
}
});
}
});
</script>
@endpush

View File

@ -0,0 +1,508 @@
@extends('layouts.app')
@section('content')
{{-- ============================================================
FILE: resources/views/admin/user/index.blade.php
============================================================ --}}
<style>
body { background: #f0f4f8 !important; }
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
}
.page-header-left {
display: flex;
align-items: center;
gap: 14px;
}
.page-icon {
width: 46px;
height: 46px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
flex-shrink: 0;
color: #fff;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.page-title {
font-size: 22px;
font-weight: 800;
color: #111827;
margin: 0;
line-height: 1.2;
}
.page-subtitle {
font-size: 13px;
color: #6b7280;
margin: 2px 0 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 20px;
}
.stat-card {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 16px;
padding: 20px 22px;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
animation: fadeUp 0.35s ease;
transition: all 0.15s ease;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0,0,0,0.08);
}
.stat-value {
font-size: 28px;
font-weight: 800;
line-height: 1.1;
margin-bottom: 4px;
}
.stat-label {
font-size: 12.5px;
color: #6b7280;
font-weight: 500;
margin: 0;
}
.stat-card:nth-child(1) .stat-value { color: #111827; }
.stat-card:nth-child(2) .stat-value { color: #064e3b; }
.stat-card:nth-child(3) .stat-value { color: #f59e0b; }
/* ── FILTER TABS ── */
.filter-tabs {
display: flex;
gap: 8px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.filter-tab {
padding: 7px 18px;
border-radius: 999px;
font-size: 12.5px;
font-weight: 700;
cursor: pointer;
border: 1.5px solid transparent;
transition: all 0.15s ease;
display: inline-flex;
align-items: center;
gap: 6px;
background: #ffffff;
color: #6b7280;
border-color: #e5e7eb;
user-select: none;
}
.filter-tab .tab-count {
background: #f3f4f6;
color: #6b7280;
border-radius: 999px;
padding: 1px 7px;
font-size: 11px;
font-weight: 700;
transition: all 0.15s ease;
}
.filter-tab:hover {
border-color: #d1d5db;
background: #f9fafb;
}
/* All tab */
.filter-tab[data-filter="all"].active {
background: #111827;
color: #ffffff;
border-color: #111827;
}
.filter-tab[data-filter="all"].active .tab-count {
background: rgba(255,255,255,0.2);
color: #fff;
}
/* RT tab */
.filter-tab[data-filter="rt"].active {
background: #fffbeb;
color: #d97706;
border-color: #fcd34d;
}
.filter-tab[data-filter="rt"].active .tab-count {
background: #fde68a;
color: #d97706;
}
/* User tab */
.filter-tab[data-filter="user"].active {
background: #f0fdf4;
color: #064e3b;
border-color: #6ee7b7;
}
.filter-tab[data-filter="user"].active .tab-count {
background: #bbf7d0;
color: #064e3b;
}
.card {
border: 1px solid #e5e7eb !important;
border-radius: 16px !important;
background: #ffffff;
box-shadow: 0 2px 10px rgba(0,0,0,0.05) !important;
overflow: hidden;
animation: fadeUp 0.35s ease;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.table { margin-bottom: 0; }
.table thead th {
background: #f9fafb !important;
color: #6b7280 !important;
font-size: 11px !important;
font-weight: 700 !important;
text-transform: uppercase;
letter-spacing: 0.06em;
border-bottom: 1px solid #e5e7eb !important;
border-top: none !important;
padding: 11px 18px;
white-space: nowrap;
}
.table tbody td {
color: #111827;
font-size: 13.5px;
padding: 15px 18px;
border-bottom: 1px solid #f3f4f6 !important;
vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: none !important; }
.table tbody tr:hover td { background: #fafafa; }
.nama-cell { font-weight: 700; color: #111827; }
.email-cell { color: #6b7280; font-size: 13px; }
.badge-role {
display: inline-flex;
align-items: center;
padding: 4px 12px;
border-radius: 999px;
font-size: 11.5px;
font-weight: 700;
text-transform: lowercase;
}
.badge-user { background: #f0fdf4; color: #064e3b; border: 1px solid #bbf7d0; }
.badge-rt { background: #fffbeb; color: #d97706; border: 1px solid #fde68a; }
.btn {
border-radius: 8px !important;
font-size: 12px;
padding: 6px 14px;
font-weight: 600;
transition: all 0.15s ease;
display: inline-flex;
align-items: center;
gap: 5px;
cursor: pointer;
}
.btn-tambah {
background: linear-gradient(135deg, #064e3b, #0d6e4f) !important;
border: none !important;
color: #ffffff !important;
border-radius: 10px !important;
padding: 9px 18px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.15s ease;
box-shadow: 0 3px 10px rgba(6,78,59,0.3);
}
.btn-tambah:hover { opacity: 0.9; color: #fff !important; transform: translateY(-1px); }
.btn-edit { background: #fff7ed !important; border: 1px solid #fed7aa !important; color: #ea580c !important; }
.btn-edit:hover { background: #ffedd5 !important; }
.btn-hapus { background: #fef2f2 !important; border: 1px solid #fecaca !important; color: #dc2626 !important; }
.btn-hapus:hover { background: #fee2e2 !important; }
.action-gap {
display: flex;
align-items: center;
gap: 10px;
}
.pagination-wrap {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
border-top: 1px solid #f3f4f6;
}
.pagination-info { font-size: 12.5px; color: #6b7280; }
.pagination {
display: flex;
gap: 4px;
margin: 0;
list-style: none;
padding: 0;
}
.pagination .page-item .page-link {
width: 34px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px !important;
font-size: 13px;
font-weight: 600;
color: #374151;
background: #ffffff;
border: 1px solid #e5e7eb !important;
text-decoration: none;
transition: all 0.15s ease;
}
.pagination .page-item .page-link:hover { background: #f0fdf4; border-color: #064e3b !important; color: #064e3b; }
.pagination .page-item.active .page-link { background: #064e3b !important; border-color: #064e3b !important; color: #ffffff !important; }
.pagination .page-item.disabled .page-link { background: #f9fafb; color: #d1d5db; cursor: not-allowed; }
.alert-success-custom {
background: #f0fdf4;
border: 1px solid #bbf7d0;
color: #15803d;
border-radius: 10px;
font-size: 13px;
padding: 10px 16px;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.user-row.hidden-by-filter { display: none !important; }
</style>
<div class="container-fluid" style="padding: 28px 28px;">
<div class="page-header">
<div class="page-header-left">
<div class="page-icon">
<i class="fas fa-users-cog"></i>
</div>
<div>
<h1 class="page-title">Kelola Data User</h1>
<p class="page-subtitle">Manajemen akun dan hak akses pengguna</p>
</div>
</div>
<a href="{{ route('admin.user.create') }}" class="btn-tambah">
<i class="fas fa-plus" style="font-size:11px;"></i> Tambah User RT
</a>
</div>
{{-- STATS: hanya Total (rt+user), User, RT admin disembunyikan --}}
<div class="stats-grid">
<div class="stat-card">
<p class="stat-value">{{ $user->whereIn('role', ['rt','user'])->count() }}</p>
<p class="stat-label">Total User</p>
</div>
<div class="stat-card">
<p class="stat-value">{{ $user->where('role','user')->count() }}</p>
<p class="stat-label">User</p>
</div>
<div class="stat-card">
<p class="stat-value">{{ $user->where('role','rt')->count() }}</p>
<p class="stat-label">RT</p>
</div>
</div>
@if(session('success'))
<div class="alert-success-custom">
<i class="fas fa-check-circle"></i> {{ session('success') }}
</div>
@endif
{{-- Filter Tabs: admin dihapus --}}
<div class="filter-tabs">
<button class="filter-tab active" data-filter="all">
<i class="fas fa-users" style="font-size:11px;"></i> Semua
<span class="tab-count" id="count-all">{{ $user->whereIn('role', ['rt','user'])->count() }}</span>
</button>
<button class="filter-tab" data-filter="rt">
<i class="fas fa-home" style="font-size:11px;"></i> RT
<span class="tab-count" id="count-rt">{{ $user->where('role','rt')->count() }}</span>
</button>
<button class="filter-tab" data-filter="user">
<i class="fas fa-user" style="font-size:11px;"></i> User
<span class="tab-count" id="count-user">{{ $user->where('role','user')->count() }}</span>
</button>
</div>
<div class="card">
<div class="table-responsive">
<table class="table align-middle">
<thead>
<tr>
<th style="width:40px;">#</th>
<th>Nama</th>
<th>Email</th>
<th>Role</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@php $no = 1; @endphp
@foreach($user as $u)
@if($u->role != 'admin')
<tr class="user-row" data-role="{{ $u->role }}">
<td style="color:#9ca3af; font-weight:600;">{{ $no++ }}</td>
<td class="nama-cell">{{ $u->name }}</td>
<td class="email-cell">{{ $u->email }}</td>
<td>
@if($u->role == 'rt')
<span class="badge-role badge-rt">rt</span>
@else
<span class="badge-role badge-user">user</span>
@endif
</td>
<td>
<div class="action-gap">
<a href="{{ route('admin.user.edit', $u->id) }}" class="btn btn-edit">
<i class="fas fa-edit" style="font-size:11px;"></i> Edit
</a>
<form action="{{ route('admin.user.delete', $u->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-hapus" onclick="return confirm('Yakin hapus user ini?')">
<i class="fas fa-trash" style="font-size:11px;"></i> Hapus
</button>
</form>
</div>
</td>
</tr>
@endif
@endforeach
</tbody>
</table>
</div>
<div class="pagination-wrap">
<p class="pagination-info" id="paginationInfo"></p>
<ul class="pagination" id="paginationControls"></ul>
</div>
</div>
</div>
<script>
const PER_PAGE = 10;
let currentPage = 1;
let activeFilter = 'all';
const allRows = Array.from(document.querySelectorAll('.user-row'));
function getVisibleRows() {
return allRows.filter(row => {
if (activeFilter === 'all') return true;
return row.dataset.role === activeFilter;
});
}
function showPage(page) {
currentPage = page;
const visibleRows = getVisibleRows();
const totalRows = visibleRows.length;
const totalPages = Math.ceil(totalRows / PER_PAGE) || 1;
if (currentPage > totalPages) currentPage = totalPages;
const start = (currentPage - 1) * PER_PAGE;
const end = start + PER_PAGE;
allRows.forEach(row => row.style.display = 'none');
visibleRows.forEach((row, i) => {
row.style.display = (i >= start && i < end) ? '' : 'none';
});
const from = totalRows === 0 ? 0 : start + 1;
const to = Math.min(end, totalRows);
document.getElementById('paginationInfo').textContent =
totalRows === 0
? 'Tidak ada data'
: `Menampilkan ${from}${to} dari ${totalRows} data`;
renderPagination(totalPages);
}
function renderPagination(totalPages) {
const ul = document.getElementById('paginationControls');
ul.innerHTML = '';
const prevLi = document.createElement('li');
prevLi.className = 'page-item' + (currentPage === 1 ? ' disabled' : '');
prevLi.innerHTML = `<a class="page-link" href="#"></a>`;
prevLi.addEventListener('click', e => { e.preventDefault(); if (currentPage > 1) showPage(currentPage - 1); });
ul.appendChild(prevLi);
for (let i = 1; i <= totalPages; i++) {
const li = document.createElement('li');
li.className = 'page-item' + (i === currentPage ? ' active' : '');
li.innerHTML = `<a class="page-link" href="#">${i}</a>`;
li.addEventListener('click', e => { e.preventDefault(); showPage(i); });
ul.appendChild(li);
}
const nextLi = document.createElement('li');
nextLi.className = 'page-item' + (currentPage === totalPages ? ' disabled' : '');
nextLi.innerHTML = `<a class="page-link" href="#"></a>`;
nextLi.addEventListener('click', e => { e.preventDefault(); if (currentPage < totalPages) showPage(currentPage + 1); });
ul.appendChild(nextLi);
}
document.querySelectorAll('.filter-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
activeFilter = tab.dataset.filter;
currentPage = 1;
showPage(1);
});
});
showPage(1);
</script>
@endsection

View File

@ -0,0 +1,391 @@
@extends('layouts.app')
@section('content')
{{-- HERO BANNER --}}
<div class="profile-hero">
<div class="profile-hero-icon">
<i class="fas fa-user-cog"></i>
</div>
<div>
<div class="profile-hero-title">Pengaturan Akun</div>
<div class="profile-hero-sub">Kelola informasi profil dan keamanan akun Anda</div>
</div>
</div>
<div class="profile-wrap">
{{-- NOTIFIKASI --}}
@if(session('success'))
<div style="background:#f0fdf4; border:1px solid #bbf7d0; border-left:4px solid #064e3b;
color:#065f46; border-radius:10px; padding:12px 16px; font-size:13px;
font-weight:600; display:flex; align-items:center; gap:8px; margin-bottom:18px;">
<i class="fas fa-check-circle"></i> {{ session('success') }}
</div>
@endif
@if($errors->any())
<div style="background:#fef2f2; border:1px solid #fecaca; border-left:4px solid #dc2626;
color:#dc2626; border-radius:10px; padding:12px 16px; font-size:13px;
font-weight:600; margin-bottom:18px;">
<i class="fas fa-exclamation-circle"></i>
@foreach($errors->all() as $error)
<div>{{ $error }}</div>
@endforeach
</div>
@endif
<div class="profile-grid">
{{-- KOLOM KIRI --}}
<div class="profile-left">
{{-- FOTO CARD --}}
<div class="pcard">
<div class="pcard-avatar-wrap">
<img id="previewImg"
src="{{ Auth::user()->photo
? asset('profile/' . Auth::user()->photo) . '?' . time()
: 'https://i.pravatar.cc/150?img=12' }}"
class="pcard-avatar" alt="avatar">
</div>
<div class="pcard-name">{{ $user->name }}</div>
<div class="pcard-email">{{ $user->email }}</div>
<form action="{{ route('rt.upload.photo') }}" method="POST"
enctype="multipart/form-data" class="pcard-upload-form">
@csrf
<label class="btn-pilih-foto" for="fotoInput">
<i class="fas fa-image me-1"></i> Pilih Foto
</label>
<input type="file" name="photo" id="fotoInput" accept="image/*" hidden>
<button type="submit" class="btn-upload-foto">
<i class="fas fa-cloud-upload-alt me-1"></i> Upload Foto
</button>
<div class="pcard-hint">JPG, PNG maks. 2MB</div>
</form>
</div>
{{-- INFO CARD --}}
<div class="pcard pcard-info">
<div class="pinfo-row">
<div class="pinfo-icon"><i class="fas fa-user"></i></div>
<div>
<div class="pinfo-label">USERNAME</div>
<div class="pinfo-value">{{ $user->username ?? $user->name }}</div>
</div>
</div>
<div class="pinfo-row">
<div class="pinfo-icon pinfo-icon-blue"><i class="fas fa-envelope"></i></div>
<div>
<div class="pinfo-label">EMAIL</div>
<div class="pinfo-value">{{ $user->email }}</div>
</div>
</div>
</div>
</div>
{{-- KOLOM KANAN --}}
<div class="profile-right">
<div class="pcard">
<div class="pform-header">
<div class="pform-header-icon"><i class="fas fa-edit"></i></div>
<div>
<div class="pform-title">Edit Profil</div>
<div class="pform-sub">Perbarui data diri Anda</div>
</div>
</div>
<form action="{{ route('rt.updateprofile') }}" method="POST" id="profileForm">
@csrf
<div class="pform-section-label">
<i class="fas fa-user-circle me-1"></i> INFORMASI DASAR
</div>
<div class="pform-row-2">
<div class="pform-group">
<label class="pform-label"><i class="fas fa-id-card me-1"></i> Nama Lengkap</label>
<input type="text" name="name" class="pform-input" value="{{ $user->name }}">
</div>
<div class="pform-group">
<label class="pform-label"><i class="fas fa-at me-1"></i> Username</label>
<input type="text" name="username" class="pform-input" value="{{ $user->username ?? '' }}">
</div>
</div>
<div class="pform-group">
<label class="pform-label"><i class="fas fa-envelope me-1"></i> Alamat Email</label>
<input type="email" name="email" class="pform-input" value="{{ $user->email }}">
</div>
<div class="pform-section-label mt-2">
<i class="fas fa-lock me-1"></i> KEAMANAN AKUN
<span class="pform-section-hint">Kosongkan jika tidak ingin mengubah password</span>
</div>
<div class="pform-row-2">
<div class="pform-group">
<label class="pform-label">Password Baru</label>
<div class="pform-input-wrap">
<input type="password" name="password" id="pw1" class="pform-input pform-input-icon" placeholder="Min. 8 karakter">
<span class="pform-eye" onclick="togglePw('pw1', this)"><i class="fas fa-eye"></i></span>
</div>
</div>
<div class="pform-group">
<label class="pform-label">Konfirmasi Password</label>
<div class="pform-input-wrap">
<input type="password" name="password_confirmation" id="pw2" class="pform-input pform-input-icon" placeholder="Ulangi password baru">
<span class="pform-eye" onclick="togglePw('pw2', this)"><i class="fas fa-eye"></i></span>
</div>
</div>
</div>
<div class="pform-actions">
<button type="reset" class="btn-reset">
<i class="fas fa-undo me-1"></i> Reset
</button>
<button type="submit" class="btn-simpan">
<i class="fas fa-check me-1"></i> Simpan Perubahan
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<style>
/* =================================================
WARNA UTAMA: #064e3b (hijau tua) — sama dgn Edit User
================================================= */
.profile-hero {
display: flex;
align-items: center;
gap: 18px;
background: linear-gradient(135deg, #064e3b 0%, #0a6b52 100%);
border-radius: 16px;
padding: 28px 32px;
margin: 0 0 28px 0;
box-shadow: 0 4px 20px rgba(6,78,59,0.2);
}
.profile-hero-icon {
width: 52px; height: 52px;
background: rgba(255,255,255,0.12);
border-radius: 14px;
display: flex; align-items: center; justify-content: center;
font-size: 22px; color: #fff; flex-shrink: 0;
}
.profile-hero-title { font-size: 20px; font-weight: 800; color: #fff; line-height: 1.2; }
.profile-hero-sub { font-size: 13px; color: rgba(255,255,255,0.65); margin-top: 3px; }
.profile-wrap { padding: 0; }
.profile-grid {
display: grid;
grid-template-columns: 300px 1fr;
gap: 24px;
align-items: start;
}
.pcard {
background: #fff;
border-radius: 16px;
padding: 28px 24px;
box-shadow: 0 2px 16px rgba(0,0,0,0.07);
border: 1px solid #e5e7eb;
}
.profile-left { display: flex; flex-direction: column; gap: 16px; }
.pcard-avatar-wrap { display: flex; justify-content: center; margin-bottom: 14px; }
.pcard-avatar {
width: 110px; height: 110px;
border-radius: 50%; object-fit: cover;
border: 4px solid #a7f3d0;
box-shadow: 0 4px 16px rgba(6,78,59,0.15);
}
.pcard-name { text-align: center; font-size: 16px; font-weight: 700; color: #111827; }
.pcard-email { text-align: center; font-size: 12.5px; color: #6b7280; margin-top: 3px; margin-bottom: 18px; }
.pcard-upload-form { display: flex; flex-direction: column; gap: 10px; }
.btn-pilih-foto {
display: block; text-align: center; padding: 10px;
border-radius: 10px; border: 2px dashed #6ee7b7;
color: #064e3b; font-size: 13.5px; font-weight: 600;
cursor: pointer; transition: all 0.2s; background: #f0fdf4;
}
.btn-pilih-foto:hover { background: #d1fae5; }
.btn-upload-foto {
display: block; width: 100%; padding: 11px;
border-radius: 10px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
color: #fff; font-size: 13.5px; font-weight: 700; border: none;
cursor: pointer; box-shadow: 0 4px 12px rgba(6,78,59,0.25); transition: opacity 0.2s;
}
.btn-upload-foto:hover { opacity: 0.88; }
.pcard-hint { text-align: center; font-size: 11.5px; color: #9ca3af; }
.pcard-info { padding: 20px 24px; }
.pinfo-row {
display: flex; align-items: center; gap: 14px;
padding: 10px 0; border-bottom: 1px solid #f3f4f6;
}
.pinfo-row:last-child { border-bottom: none; }
.pinfo-icon {
width: 36px; height: 36px; border-radius: 10px;
background: #f0fdf4; display: flex; align-items: center;
justify-content: center; color: #064e3b; font-size: 14px; flex-shrink: 0;
}
.pinfo-icon-blue { background: #eff6ff; color: #2563eb; }
.pinfo-label { font-size: 10px; font-weight: 700; color: #9ca3af; letter-spacing: 1px; text-transform: uppercase; }
.pinfo-value { font-size: 13.5px; font-weight: 600; color: #111827; margin-top: 2px; }
.pform-header {
display: flex; align-items: center; gap: 14px;
margin-bottom: 24px; padding-bottom: 18px; border-bottom: 1px solid #f3f4f6;
}
.pform-header-icon {
width: 40px; height: 40px; background: #f0fdf4; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
color: #064e3b; font-size: 16px; flex-shrink: 0;
}
.pform-title { font-size: 16px; font-weight: 700; color: #111827; }
.pform-sub { font-size: 12.5px; color: #6b7280; margin-top: 2px; }
.pform-section-label {
font-size: 11px; font-weight: 800; color: #064e3b;
letter-spacing: 1px; text-transform: uppercase;
margin: 0 0 14px; display: flex; align-items: center; gap: 4px;
}
.pform-section-hint {
font-size: 11px; font-weight: 400; color: #9ca3af;
letter-spacing: 0; text-transform: none; margin-left: auto;
}
.pform-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
.pform-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.pform-row-2 .pform-group { margin-bottom: 0; }
.pform-label { font-size: 12.5px; font-weight: 600; color: #374151; }
.pform-input {
padding: 11px 14px; border: 1.5px solid #e5e7eb; border-radius: 10px;
font-size: 13.5px; color: #111827; background: #fafafa;
transition: border 0.2s, box-shadow 0.2s; outline: none; width: 100%; box-sizing: border-box;
}
.pform-input:focus {
border-color: #064e3b;
box-shadow: 0 0 0 3px rgba(6,78,59,0.1);
background: #fff;
}
.pform-input-wrap { position: relative; }
.pform-input-icon { padding-right: 42px; }
.pform-eye {
position: absolute; right: 13px; top: 50%; transform: translateY(-50%);
color: #9ca3af; cursor: pointer; font-size: 14px; transition: color 0.15s;
}
.pform-eye:hover { color: #064e3b; }
.pform-actions {
display: flex; justify-content: flex-end; gap: 12px;
margin-top: 8px; padding-top: 20px; border-top: 1px solid #f3f4f6;
}
.btn-reset {
padding: 11px 22px; border-radius: 10px; border: 1.5px solid #e5e7eb;
background: #fff; color: #6b7280; font-size: 13.5px; font-weight: 600;
cursor: pointer; transition: all 0.2s;
}
.btn-reset:hover { background: #f9fafb; border-color: #d1d5db; color: #374151; }
.btn-simpan {
padding: 11px 28px; border-radius: 10px;
background: linear-gradient(135deg, #064e3b, #0d6e4f);
color: #fff; font-size: 13.5px; font-weight: 700; border: none;
cursor: pointer; transition: opacity 0.2s;
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.btn-simpan:hover { opacity: 0.88; }
/* ── MOBILE RESPONSIVE ── */
@media (max-width: 768px) {
.profile-hero {
padding: 20px 18px;
gap: 14px;
border-radius: 12px;
margin-bottom: 20px;
}
.profile-hero-title { font-size: 17px; }
.profile-hero-sub { font-size: 12px; }
.profile-grid {
grid-template-columns: 1fr; /* ← kolom jadi 1, stack ke bawah */
}
.pform-row-2 {
grid-template-columns: 1fr; /* ← 2 kolom form jadi 1 kolom */
gap: 0;
}
.pform-row-2 .pform-group { margin-bottom: 16px; }
.pform-actions {
flex-direction: column;
}
.btn-reset,
.btn-simpan {
width: 100%;
text-align: center;
}
.pcard { padding: 20px 16px; }
.pcard-info { padding: 16px; }
.pform-section-hint {
display: block;
margin-left: 0;
margin-top: 4px;
}
.pform-section-label {
flex-wrap: wrap;
}
}
</style>
<script>
document.getElementById('fotoInput').addEventListener('change', function(e) {
if (e.target.files[0]) {
document.getElementById('previewImg').src = URL.createObjectURL(e.target.files[0]);
}
});
function togglePw(id, el) {
const input = document.getElementById(id);
const isHidden = input.type === 'password';
input.type = isHidden ? 'text' : 'password';
el.innerHTML = isHidden ? '<i class="fas fa-eye-slash"></i>' : '<i class="fas fa-eye"></i>';
}
</script>
@endsection

View File

@ -207,6 +207,21 @@
display: block;
}
/* ── MOBILE RESPONSIVE ── */
.table-responsive-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 768px) {
.premium-table {
min-width: 750px;
}
.page-header h1 { font-size: 18px; }
.desc-text { max-width: 140px; }
}
/* ── DATATABLES ── */
.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length label,
@ -326,8 +341,9 @@
</div>
</div>
<div class="premium-card-body" style="padding: 0;">
<table class="premium-table" id="dataTable" style="width:100%">
<div class="premium-card-body" style="padding: 0;">
<div class="table-responsive-wrapper">
<table class="premium-table" id="dataTable" style="width:100%">
<thead>
<tr>
<th>#</th>
@ -439,7 +455,8 @@
</tr>
@endforelse
</tbody>
</table>
</table>
</div> {{-- tutup table-responsive-wrapper --}}
</div>
</div>

View File

@ -52,7 +52,7 @@
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 20px;
}
@ -87,9 +87,8 @@
}
.stat-card:nth-child(1) .stat-value { color: #111827; }
.stat-card:nth-child(2) .stat-value { color: #6366f1; }
.stat-card:nth-child(3) .stat-value { color: #064e3b; }
.stat-card:nth-child(4) .stat-value { color: #f59e0b; }
.stat-card:nth-child(2) .stat-value { color: #064e3b; }
.stat-card:nth-child(3) .stat-value { color: #f59e0b; }
/* ── FILTER TABS ── */
.filter-tabs {
@ -142,17 +141,6 @@
color: #fff;
}
/* Admin tab */
.filter-tab[data-filter="admin"].active {
background: #eef2ff;
color: #4f46e5;
border-color: #a5b4fc;
}
.filter-tab[data-filter="admin"].active .tab-count {
background: #c7d2fe;
color: #4f46e5;
}
/* RT tab */
.filter-tab[data-filter="rt"].active {
background: #fffbeb;
@ -228,7 +216,6 @@
text-transform: lowercase;
}
.badge-admin { background: #eef2ff; color: #4f46e5; border: 1px solid #c7d2fe; }
.badge-user { background: #f0fdf4; color: #064e3b; border: 1px solid #bbf7d0; }
.badge-rt { background: #fffbeb; color: #d97706; border: 1px solid #fde68a; }
@ -268,22 +255,12 @@
.btn-hapus { background: #fef2f2 !important; border: 1px solid #fecaca !important; color: #dc2626 !important; }
.btn-hapus:hover { background: #fee2e2 !important; }
/* spacer between action buttons */
.action-gap {
display: flex;
align-items: center;
gap: 10px;
}
.btn-disabled {
background: #f3f4f6 !important;
border: 1px solid #e5e7eb !important;
color: #d1d5db !important;
cursor: not-allowed !important;
opacity: 0.6;
pointer-events: none;
}
.pagination-wrap {
display: flex;
align-items: center;
@ -335,7 +312,6 @@
gap: 8px;
}
/* hide rows that don't match filter */
.user-row.hidden-by-filter { display: none !important; }
</style>
@ -356,15 +332,12 @@
</a>
</div>
{{-- STATS: hanya Total (rt+user), User, RT admin disembunyikan --}}
<div class="stats-grid">
<div class="stat-card">
<p class="stat-value">{{ $user->count() }}</p>
<p class="stat-value">{{ $user->whereIn('role', ['rt','user'])->count() }}</p>
<p class="stat-label">Total User</p>
</div>
<div class="stat-card">
<p class="stat-value">{{ $user->where('role','admin')->count() }}</p>
<p class="stat-label">Admin</p>
</div>
<div class="stat-card">
<p class="stat-value">{{ $user->where('role','user')->count() }}</p>
<p class="stat-label">User</p>
@ -381,15 +354,11 @@
</div>
@endif
{{-- Filter Tabs --}}
{{-- Filter Tabs: admin dihapus --}}
<div class="filter-tabs">
<button class="filter-tab active" data-filter="all">
<i class="fas fa-users" style="font-size:11px;"></i> Semua
<span class="tab-count" id="count-all">{{ $user->count() }}</span>
</button>
<button class="filter-tab" data-filter="admin">
<i class="fas fa-user-shield" style="font-size:11px;"></i> Admin
<span class="tab-count" id="count-admin">{{ $user->where('role','admin')->count() }}</span>
<span class="tab-count" id="count-all">{{ $user->whereIn('role', ['rt','user'])->count() }}</span>
</button>
<button class="filter-tab" data-filter="rt">
<i class="fas fa-home" style="font-size:11px;"></i> RT
@ -414,44 +383,36 @@
</tr>
</thead>
<tbody>
@foreach($user as $index => $u)
<tr class="user-row" data-role="{{ $u->role }}">
<td style="color:#9ca3af; font-weight:600;">{{ $index + 1 }}</td>
<td class="nama-cell">{{ $u->name }}</td>
<td class="email-cell">{{ $u->email }}</td>
<td>
@if($u->role == 'admin')
<span class="badge-role badge-admin">admin</span>
@elseif($u->role == 'rt')
<span class="badge-role badge-rt">rt</span>
@else
<span class="badge-role badge-user">user</span>
@endif
</td>
<td>
<div class="action-gap">
{{-- Edit: disabled for admin --}}
@if($u->role == 'admin')
<button class="btn btn-edit btn-disabled" disabled title="Admin tidak dapat diedit">
<i class="fas fa-edit" style="font-size:11px;"></i> Edit
</button>
@php $no = 1; @endphp
@foreach($user as $u)
@if($u->role != 'admin')
<tr class="user-row" data-role="{{ $u->role }}">
<td style="color:#9ca3af; font-weight:600;">{{ $no++ }}</td>
<td class="nama-cell">{{ $u->name }}</td>
<td class="email-cell">{{ $u->email }}</td>
<td>
@if($u->role == 'rt')
<span class="badge-role badge-rt">rt</span>
@else
<span class="badge-role badge-user">user</span>
@endif
</td>
<td>
<div class="action-gap">
<a href="{{ route('admin.user.edit', $u->id) }}" class="btn btn-edit">
<i class="fas fa-edit" style="font-size:11px;"></i> Edit
</a>
@endif
{{-- Hapus --}}
<form action="{{ route('admin.user.delete', $u->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-hapus" onclick="return confirm('Yakin hapus user ini?')">
<i class="fas fa-trash" style="font-size:11px;"></i> Hapus
</button>
</form>
</div>
</td>
</tr>
<form action="{{ route('admin.user.delete', $u->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-hapus" onclick="return confirm('Yakin hapus user ini?')">
<i class="fas fa-trash" style="font-size:11px;"></i> Hapus
</button>
</form>
</div>
</td>
</tr>
@endif
@endforeach
</tbody>
</table>
@ -485,16 +446,13 @@ function showPage(page) {
const totalRows = visibleRows.length;
const totalPages = Math.ceil(totalRows / PER_PAGE) || 1;
// Clamp page
if (currentPage > totalPages) currentPage = totalPages;
const start = (currentPage - 1) * PER_PAGE;
const end = start + PER_PAGE;
// Hide all first
allRows.forEach(row => row.style.display = 'none');
// Show only current page of visible rows
visibleRows.forEach((row, i) => {
row.style.display = (i >= start && i < end) ? '' : 'none';
});
@ -534,7 +492,6 @@ function renderPagination(totalPages) {
ul.appendChild(nextLi);
}
// Filter tab logic
document.querySelectorAll('.filter-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
@ -545,7 +502,6 @@ function renderPagination(totalPages) {
});
});
// Init
showPage(1);
</script>

View File

@ -328,6 +328,49 @@ class="pcard-avatar" alt="avatar">
box-shadow: 0 4px 14px rgba(6,78,59,0.3);
}
.btn-simpan:hover { opacity: 0.88; }
/* ── MOBILE RESPONSIVE ── */
@media (max-width: 768px) {
.profile-hero {
padding: 20px 18px;
gap: 14px;
border-radius: 12px;
margin-bottom: 20px;
}
.profile-hero-title { font-size: 17px; }
.profile-hero-sub { font-size: 12px; }
.profile-grid {
grid-template-columns: 1fr; /* ← kolom jadi 1, stack ke bawah */
}
.pform-row-2 {
grid-template-columns: 1fr; /* ← 2 kolom form jadi 1 kolom */
gap: 0;
}
.pform-row-2 .pform-group { margin-bottom: 16px; }
.pform-actions {
flex-direction: column;
}
.btn-reset,
.btn-simpan {
width: 100%;
text-align: center;
}
.pcard { padding: 20px 16px; }
.pcard-info { padding: 16px; }
.pform-section-hint {
display: block;
margin-left: 0;
margin-top: 4px;
}
.pform-section-label {
flex-wrap: wrap;
}
}
</style>
<script>