feat: 사용자 의견(피드백) 기능 — 화면 캡처+이미지 첨부, 관리자 검토
- 전 화면 우하단 '의견' 위젯(embed/portal/admin 레이아웃) — 로그인 사용자 - 의견 + html2canvas 화면 캡처 + 이미지 첨부(최대 5장) 저장 - 화면 URL/계정/지자체 자동 수집(계정·지자체는 서버 세션 기준) - 관리자 /admin/feedback 목록·상세·상태(접수/처리중/완료/보류)·메모, 테넌트 분리 - feedback/feedback_file 테이블, 첨부 이미지는 관리자 전용 라우트로 서빙 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
66
app/Views/admin/feedback/index.php
Normal file
66
app/Views/admin/feedback/index.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @var list<object> $list
|
||||
* @var string $status
|
||||
*/
|
||||
use App\Models\FeedbackModel;
|
||||
|
||||
$list = $list ?? [];
|
||||
$status = (string) ($status ?? '');
|
||||
$badge = static function (string $s): string {
|
||||
$map = [
|
||||
'new' => 'bg-blue-50 text-blue-700',
|
||||
'in_progress' => 'bg-amber-50 text-amber-700',
|
||||
'done' => 'bg-emerald-50 text-emerald-700',
|
||||
'hold' => 'bg-gray-100 text-gray-500',
|
||||
];
|
||||
$c = $map[$s] ?? 'bg-gray-100 text-gray-600';
|
||||
return '<span class="inline-block px-2 py-0.5 rounded-full text-[11px] font-medium ' . $c . '">' . esc(FeedbackModel::statusLabel($s)) . '</span>';
|
||||
};
|
||||
?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel flex flex-wrap items-center justify-between gap-2">
|
||||
<span class="text-sm font-bold text-gray-700">사용자 의견</span>
|
||||
<form method="get" class="flex items-center gap-2 text-sm">
|
||||
<select name="status" class="border border-gray-300 rounded px-2 py-1 text-sm" onchange="this.form.submit()">
|
||||
<option value="">전체 상태</option>
|
||||
<?php foreach (FeedbackModel::STATUSES as $s): ?>
|
||||
<option value="<?= esc($s, 'attr') ?>" <?= $status === $s ? 'selected' : '' ?>><?= esc(FeedbackModel::statusLabel($s)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<div class="border border-gray-300 rounded-lg p-4 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-16 text-left">번호</th>
|
||||
<th class="w-36 text-left">일시</th>
|
||||
<th class="w-20 text-left">상태</th>
|
||||
<th class="text-left">화면</th>
|
||||
<th class="text-left">내용</th>
|
||||
<th class="w-24 text-left">작성자</th>
|
||||
<th class="w-16 text-center">캡처</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($list)): ?>
|
||||
<tr><td colspan="7" class="text-center text-gray-400 py-6">접수된 의견이 없습니다.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($list as $row): ?>
|
||||
<tr class="cursor-pointer hover:bg-blue-50/60" onclick="window.location.href='<?= site_url('admin/feedback/' . (int) $row->fb_idx) ?>'">
|
||||
<td class="text-left text-gray-500"><?= (int) $row->fb_idx ?></td>
|
||||
<td class="text-left text-gray-500 text-[12px]"><?= esc((string) ($row->fb_regdate ?? '')) ?></td>
|
||||
<td class="text-left"><?= $badge((string) ($row->fb_status ?? 'new')) ?></td>
|
||||
<td class="text-left text-[12px] text-gray-600"><?= esc((string) ($row->fb_page_title ?: $row->fb_page_url)) ?></td>
|
||||
<td class="text-left"><?= esc(mb_substr((string) $row->fb_content, 0, 50)) ?><?= mb_strlen((string) $row->fb_content) > 50 ? '…' : '' ?></td>
|
||||
<td class="text-left text-[12px]"><?= esc((string) ($row->fb_mb_name ?? '')) ?></td>
|
||||
<td class="text-center"><?= ! empty($row->fb_screenshot) ? '<i class="fa-regular fa-image text-gray-400"></i>' : '' ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php if (isset($pager)): ?><div class="mt-3"><?= $pager->links() ?></div><?php endif; ?>
|
||||
Reference in New Issue
Block a user