kokjan/backend/modules/administrator/views/filemanager/index.php

542 lines
20 KiB
PHP

<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\bootstrap4\ActiveForm;
use yii\bootstrap4\Modal;
// Safety check for variables passed from controller
$currentPath = isset($currentPath) ? $currentPath : '';
$dirs = isset($dirs) ? $dirs : [];
$files = isset($files) ? $files : [];
$this->title = 'คลังสื่อและเอกสาร - ' . Yii::$app->params['org_short'];
$this->params['breadcrumbs'][] = ['label' => 'Administrator', 'url' => ['/administrator/default/index']];
$this->params['breadcrumbs'][] = 'File Manager';
$baseUrl = Yii::$app->request->baseUrl . '/_uploads/';
?>
<style>
:root {
--or: #FFC068; /* Warm Orange */
--or2: #FFDBBB; /* Soft Peach */
--bd: #2c1810; /* Deep Brown */
--rd: #8e1d1d; /* Thai Red */
--light: #fdfbf9;
}
.fm-wrapper {
background: var(--light);
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0,0,0,0.05);
min-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Header Gradient */
.fm-header-main {
background: linear-gradient(135deg, var(--bd) 0%, var(--rd) 100%);
padding: 2rem;
color: white;
position: relative;
}
.fm-header-main h3 {
font-weight: 700;
letter-spacing: -0.5px;
margin-bottom: 5px;
}
.fm-stat-badge {
background: rgba(255,255,255,0.15);
backdrop-filter: blur(10px);
padding: 8px 15px;
border-radius: 30px;
font-size: 0.85rem;
border: 1px solid rgba(255,255,255,0.1);
}
/* Toolbar Area */
.fm-toolbar {
background: white;
padding: 15px 25px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.search-group {
position: relative;
flex-grow: 1;
max-width: 400px;
}
.search-group i {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: #bbb;
}
.fm-search-input {
padding-left: 40px;
height: 45px;
border-radius: 12px;
border: 1.5px solid #eee;
background: #fcfcfc;
transition: all 0.3s;
}
.fm-search-input:focus {
border-color: var(--or);
background: white;
box-shadow: 0 0 0 4px rgba(255, 192, 104, 0.1);
}
/* Breadcrumbs Modern */
.fm-nav-bar {
background: #fdfdfd;
padding: 10px 25px;
border-bottom: 1px solid #f5f5f5;
}
.fm-bc {
display: flex;
align-items: center;
margin: 0;
padding: 0;
list-style: none;
font-size: 0.9rem;
}
.fm-bc-item a {
color: #888;
padding: 5px 10px;
border-radius: 8px;
transition: all 0.2s;
}
.fm-bc-item a:hover {
background: var(--or2);
color: var(--bd);
text-decoration: none;
}
.fm-bc-item.active {
font-weight: 700;
color: var(--bd);
}
.fm-bc-sep {
color: #ddd;
margin: 0 5px;
}
/* Grid Area */
.fm-content {
padding: 25px;
flex-grow: 1;
}
.section-title {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 1.5px;
color: #aaa;
font-weight: 700;
margin-bottom: 20px;
display: flex;
align-items: center;
}
.section-title::after {
content: '';
height: 1px;
background: #eee;
flex-grow: 1;
margin-left: 15px;
}
/* Folder Card */
.folder-box {
background: white;
border: 1.5px solid #f0f0f0;
border-radius: 16px;
padding: 15px;
display: flex;
align-items: center;
transition: all 0.25s cubic-bezier(.25,.8,.25,1);
cursor: pointer;
position: relative;
margin-bottom: 20px;
}
.folder-box:hover {
border-color: var(--or);
box-shadow: 0 10px 25px rgba(255, 192, 104, 0.15);
transform: translateY(-3px);
}
.folder-icon-wrap {
width: 45px;
height: 45px;
background: var(--or2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
}
.folder-icon-wrap i {
font-size: 1.5rem;
color: #d35400;
}
.folder-name {
font-weight: 600;
color: var(--bd);
font-size: 0.95rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-grow: 1;
}
/* File Card */
.file-box {
background: white;
border: 1.5px solid #f0f0f0;
border-radius: 18px;
overflow: hidden;
transition: all 0.3s ease;
height: 100%;
position: relative;
}
.file-box:hover {
border-color: var(--or);
box-shadow: 0 15px 35px rgba(0,0,0,0.08);
transform: translateY(-5px);
}
.file-thumb {
height: 140px;
background: #fafafa;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.file-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-thumb i {
font-size: 3.5rem;
opacity: 0.8;
}
.file-info {
padding: 12px 15px;
}
.file-name {
font-size: 0.85rem;
font-weight: 600;
color: var(--bd);
margin-bottom: 4px;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-meta {
font-size: 0.75rem;
color: #999;
display: flex;
justify-content: space-between;
}
/* Action Buttons Overlay */
.fm-actions-overlay {
position: absolute;
top: 8px;
right: 8px;
display: flex;
gap: 5px;
opacity: 0;
transition: opacity 0.2s;
z-index: 5;
}
.folder-box:hover .fm-actions-overlay,
.file-box:hover .fm-actions-overlay {
opacity: 1;
}
.btn-action-sm {
width: 32px;
height: 32px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
background: rgba(255,255,255,0.9);
backdrop-filter: blur(5px);
border: 1px solid #eee;
color: #555;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn-action-sm:hover {
background: white;
color: var(--rd);
transform: scale(1.1);
}
/* Empty State */
.empty-state-wrap {
text-align: center;
padding: 80px 20px;
}
.empty-icon {
font-size: 5rem;
color: var(--or2);
margin-bottom: 20px;
}
/* Custom Buttons */
.btn-fm-primary {
background: var(--or);
color: var(--bd);
border: none;
font-weight: 700;
padding: 10px 25px;
border-radius: 12px;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(255, 192, 104, 0.3);
}
.btn-fm-primary:hover {
background: var(--bd);
color: white;
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(44, 24, 16, 0.2);
}
</style>
<div class="fm-wrapper">
<!-- Header -->
<div class="fm-header-main">
<div class="row align-items-center">
<div class="col-md-8">
<h3><i class="fas fa-th-large mr-2"></i> File Explorer</h3>
<p class="mb-0 opacity-75">จัดการสื่อ รูปภาพ และเอกสารทั้งหมดในโฟลเดอร์ _uploads</p>
</div>
<div class="col-md-4 text-right">
<div class="fm-stat-badge d-inline-block">
<i class="fas fa-database mr-2"></i>
<strong><?= count($dirs) ?></strong> โฟลเดอร์ / <strong><?= count($files) ?></strong> ไฟล์
</div>
</div>
</div>
</div>
<!-- Toolbar -->
<div class="fm-toolbar">
<div class="search-group">
<i class="fas fa-search"></i>
<input type="text" id="fileSearch" class="form-control fm-search-input" placeholder="ค้นหาตามชื่อไฟล์หรือโฟลเดอร์...">
</div>
<div class="action-group d-flex align-items-center">
<button type="button" class="btn btn-outline-secondary mr-3 rounded-pill px-3" data-toggle="modal" data-target="#newFolderModal">
<i class="fas fa-folder-plus mr-1 text-warning"></i> โฟลเดอร์ใหม่
</button>
<?php $form = ActiveForm::begin([
'action' => ['upload', 'path' => $currentPath],
'options' => ['enctype' => 'multipart/form-data', 'id' => 'upload-form', 'class' => 'd-flex align-items-center']
]); ?>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text bg-light border-0 small" style="border-radius: 12px 0 0 12px;">ย่อ:</span>
</div>
<select name="resize_width" class="form-control border-0 bg-light" style="width: 100px; font-size: 0.85rem;">
<option value="original">ต้นฉบับ</option>
<option value="1200">1200px</option>
<option value="1024" selected>1024px</option>
<option value="512">512px</option>
<option value="384">384px</option>
</select>
<div class="input-group-append">
<button type="button" class="btn btn-fm-primary" onclick="document.getElementById('file-input').click()">
<i class="fas fa-cloud-upload-alt mr-1"></i> อัปโหลด
</button>
</div>
</div>
<input type="file" name="files[]" id="file-input" multiple style="display: none;" onchange="document.getElementById('upload-form').submit()">
<?php ActiveForm::end(); ?>
</div>
</div>
<!-- Breadcrumbs -->
<div class="fm-nav-bar">
<ul class="fm-bc">
<li class="fm-bc-item">
<?= Html::a('<i class="fas fa-hdd"></i> root', ['index', 'path' => '']) ?>
</li>
<?php
$parts = array_filter(explode('/', $currentPath));
$accumulatedPath = '';
foreach ($parts as $part):
$accumulatedPath .= ($accumulatedPath ? '/' : '') . $part;
?>
<li class="fm-bc-sep">/</li>
<li class="fm-bc-item <?= ($accumulatedPath == $currentPath) ? 'active' : '' ?>">
<?= Html::a(Html::encode($part), ['index', 'path' => $accumulatedPath]) ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<!-- Main Content -->
<div class="fm-content">
<div id="fileGridArea">
<!-- Folders Section -->
<?php if (!empty($dirs)): ?>
<div class="section-title">โฟลเดอร์ย่อย</div>
<div class="row">
<?php foreach ($dirs as $dir): ?>
<div class="col-xl-3 col-lg-4 col-md-6 mb-3 card-item" data-name="<?= strtolower($dir['name']) ?>">
<div class="folder-box">
<div class="fm-actions-overlay">
<?= Html::beginForm(['delete', 'relPath' => $dir['relPath']], 'post') ?>
<button type="submit" class="btn-action-sm text-danger" title="ลบ" data-confirm="ต้องการลบโฟลเดอร์นี้หรือไม่? (ต้องเป็นโฟลเดอร์ว่าง)">
<i class="far fa-trash-alt"></i>
</button>
<?= Html::endForm() ?>
</div>
<a href="<?= Url::to(['index', 'path' => $dir['relPath']]) ?>" class="d-flex align-items-center text-decoration-none w-100">
<div class="folder-icon-wrap">
<i class="fas fa-folder"></i>
</div>
<div class="folder-name"><?= Html::encode($dir['name']) ?></div>
</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Files Section -->
<?php if (!empty($files)): ?>
<div class="section-title mt-4">ไฟล์ทั้งหมด</div>
<div class="row">
<?php foreach ($files as $file): ?>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 mb-4 card-item" data-name="<?= strtolower($file['name']) ?>">
<div class="file-box">
<!-- Actions -->
<div class="fm-actions-overlay">
<button class="btn-action-sm copy-link text-info" data-url="<?= Yii::$app->request->hostInfo . '/_uploads/' . $file['relPath'] ?>" title="คัดลอกลิงก์">
<i class="fas fa-link"></i>
</button>
<?= Html::beginForm(['delete', 'relPath' => $file['relPath']], 'post') ?>
<button type="submit" class="btn-action-sm text-danger" title="ลบ" data-confirm="ยืนยันการลบไฟล์นี้?">
<i class="far fa-trash-alt"></i>
</button>
<?= Html::endForm() ?>
</div>
<div class="file-thumb">
<?php
$ext = strtolower($file['ext']);
$isImage = in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']);
if ($isImage): ?>
<?= Html::img($baseUrl . $file['relPath'], ['alt' => $file['name']]) ?>
<?php else:
$icon = 'far fa-file';
$color = '#95a5a6';
if ($ext == 'pdf') { $icon = 'far fa-file-pdf'; $color = '#e74c3c'; }
elseif (in_array($ext, ['doc', 'docx'])) { $icon = 'far fa-file-word'; $color = '#3498db'; }
elseif (in_array($ext, ['xls', 'xlsx'])) { $icon = 'far fa-file-excel'; $color = '#27ae60'; }
elseif (in_array($ext, ['zip', 'rar', '7z'])) { $icon = 'far fa-file-archive'; $color = '#f39c12'; }
?>
<i class="<?= $icon ?>" style="color: <?= $color ?>"></i>
<?php endif; ?>
</div>
<div class="file-info">
<span class="file-name" title="<?= Html::encode($file['name']) ?>"><?= Html::encode($file['name']) ?></span>
<div class="file-meta">
<span><?= $file['size'] ?></span>
<?= Html::a('<i class="fas fa-download"></i>', ['download', 'relPath' => $file['relPath']], ['class' => 'text-primary']) ?>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Empty State -->
<?php if (empty($dirs) && empty($files)): ?>
<div class="empty-state-wrap">
<i class="fas fa-cloud-upload-alt empty-icon"></i>
<h4>ยังไม่มีไฟล์ในโฟลเดอร์นี้</h4>
<p class="text-muted">ลากไฟล์มาวางที่นี่เพื่ออัปโหลด หรือกดปุ่ม "โฟลเดอร์ใหม่" เพื่อจัดระเบียบ</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Modal: New Folder -->
<div class="modal fade" id="newFolderModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content" style="border-radius: 20px; border: none; overflow: hidden;">
<div class="modal-header" style="background: var(--bd); color: white; border: none;">
<h5 class="modal-title font-weight-bold"><i class="fas fa-folder-plus mr-2"></i> สร้างโฟลเดอร์ใหม่</h5>
<button type="button" class="close text-white" data-dismiss="modal">&times;</button>
</div>
<?php $form = ActiveForm::begin(['action' => ['create-folder', 'path' => $currentPath]]); ?>
<div class="modal-body p-4">
<div class="form-group">
<label class="font-weight-bold text-muted">ชื่อโฟลเดอร์ที่คุณต้องการ</label>
<input type="text" name="folder_name" class="form-control form-control-lg" style="border-radius: 12px; border: 2px solid #eee;" placeholder="เช่น images, news, 2567..." required autofocus>
</div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-light rounded-pill px-4" data-dismiss="modal">ยกเลิก</button>
<button type="submit" class="btn btn-fm-primary px-4">สร้างเดี๋ยวนี้</button>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>
<?php
$js = <<<JS
// Real-time Search Filter
$("#fileSearch").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#fileGridArea .card-item").filter(function() {
$(this).toggle($(this).attr('data-name').indexOf(value) > -1)
});
});
// Copy Link Feedback
$('.copy-link').click(function(e) {
e.preventDefault();
var url = $(this).data('url');
var tempInput = document.createElement("input");
tempInput.style = "position: absolute; left: -1000px; top: -1000px";
tempInput.value = url;
document.body.appendChild(tempInput);
tempInput.select();
document.execCommand("copy");
document.body.removeChild(tempInput);
var btn = $(this);
var oldHtml = btn.html();
btn.html('<i class="fas fa-check"></i>').css('color', '#27ae60');
setTimeout(function(){ btn.html(oldHtml).css('color', ''); }, 2000);
});
// Drag and Drop Effects (Optional Visual Enhancement)
var dropZone = document.querySelector('.fm-content');
window.addEventListener('dragover', function(e) { e.preventDefault(); }, false);
window.addEventListener('drop', function(e) { e.preventDefault(); }, false);
JS;
$this->registerJs($js);
?>