Add membership fee system with disability discount and fix document permissions
Features: - Implement two fee types: entrance fee and annual fee (both NT$1,000) - Add 50% discount for disability certificate holders - Add disability certificate upload in member profile - Integrate disability verification into cashier approval workflow - Add membership fee settings in system admin Document permissions: - Fix hard-coded role logic in Document model - Use permission-based authorization instead of role checks Additional features: - Add announcements, general ledger, and trial balance modules - Add income management and accounting entries - Add comprehensive test suite with factories - Update UI translations to Traditional Chinese 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
186
resources/views/admin/announcements/index.blade.php
Normal file
186
resources/views/admin/announcements/index.blade.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
公告管理
|
||||
</h2>
|
||||
<a href="{{ route('admin.announcements.create') }}" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 dark:bg-indigo-500 px-3 py-2 text-sm font-medium text-white hover:bg-indigo-700 dark:hover:bg-indigo-600">
|
||||
+ 建立公告
|
||||
</a>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8 space-y-6">
|
||||
@if (session('status'))
|
||||
<div class="rounded-md bg-green-50 dark:bg-green-900/50 p-4">
|
||||
<p class="text-sm font-medium text-green-800 dark:text-green-200">{{ session('status') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('error'))
|
||||
<div class="rounded-md bg-red-50 dark:bg-red-900/50 p-4">
|
||||
<p class="text-sm font-medium text-red-800 dark:text-red-200">{{ session('error') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Statistics -->
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-5">
|
||||
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg p-6">
|
||||
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">總計</div>
|
||||
<div class="mt-2 text-3xl font-bold text-gray-900 dark:text-gray-100">{{ $stats['total'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg p-6">
|
||||
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">草稿</div>
|
||||
<div class="mt-2 text-3xl font-bold text-gray-600 dark:text-gray-400">{{ $stats['draft'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg p-6">
|
||||
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">已發布</div>
|
||||
<div class="mt-2 text-3xl font-bold text-green-600 dark:text-green-400">{{ $stats['published'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg p-6">
|
||||
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">已歸檔</div>
|
||||
<div class="mt-2 text-3xl font-bold text-yellow-600 dark:text-yellow-400">{{ $stats['archived'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg p-6">
|
||||
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">置頂中</div>
|
||||
<div class="mt-2 text-3xl font-bold text-blue-600 dark:text-blue-400">{{ $stats['pinned'] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search and Filter -->
|
||||
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg p-6">
|
||||
<form method="GET" action="{{ route('admin.announcements.index') }}" class="space-y-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||
<div>
|
||||
<label for="search" class="block text-sm font-medium text-gray-700 dark:text-gray-300">搜尋</label>
|
||||
<input type="text" name="search" id="search" value="{{ request('search') }}" placeholder="標題、內容..."
|
||||
class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 sm:text-sm dark:bg-gray-900 dark:text-gray-300">
|
||||
</div>
|
||||
<div>
|
||||
<label for="status" class="block text-sm font-medium text-gray-700 dark:text-gray-300">狀態</label>
|
||||
<select name="status" id="status" class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 sm:text-sm dark:bg-gray-900 dark:text-gray-300">
|
||||
<option value="">全部</option>
|
||||
<option value="draft" {{ request('status') === 'draft' ? 'selected' : '' }}>草稿</option>
|
||||
<option value="published" {{ request('status') === 'published' ? 'selected' : '' }}>已發布</option>
|
||||
<option value="archived" {{ request('status') === 'archived' ? 'selected' : '' }}>已歸檔</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="access_level" class="block text-sm font-medium text-gray-700 dark:text-gray-300">存取權限</label>
|
||||
<select name="access_level" id="access_level" class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 sm:text-sm dark:bg-gray-900 dark:text-gray-300">
|
||||
<option value="">全部</option>
|
||||
<option value="public" {{ request('access_level') === 'public' ? 'selected' : '' }}>公開</option>
|
||||
<option value="members" {{ request('access_level') === 'members' ? 'selected' : '' }}>會員</option>
|
||||
<option value="board" {{ request('access_level') === 'board' ? 'selected' : '' }}>理事會</option>
|
||||
<option value="admin" {{ request('access_level') === 'admin' ? 'selected' : '' }}>管理員</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="pinned" class="block text-sm font-medium text-gray-700 dark:text-gray-300">置頂</label>
|
||||
<select name="pinned" id="pinned" class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 sm:text-sm dark:bg-gray-900 dark:text-gray-300">
|
||||
<option value="">全部</option>
|
||||
<option value="yes" {{ request('pinned') === 'yes' ? 'selected' : '' }}>已置頂</option>
|
||||
<option value="no" {{ request('pinned') === 'no' ? 'selected' : '' }}>未置頂</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<a href="{{ route('admin.announcements.index') }}" class="rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
清除
|
||||
</a>
|
||||
<button type="submit" class="rounded-md border border-transparent bg-indigo-600 dark:bg-indigo-500 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 dark:hover:bg-indigo-600">
|
||||
搜尋
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">共 {{ $announcements->total() }} 則公告</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Announcements Table -->
|
||||
<div class="bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-lg">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">公告</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">狀態</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">存取權限</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">建立者</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">瀏覽次數</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">建立時間</th>
|
||||
<th scope="col" class="px-6 py-3 text-right text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($announcements as $announcement)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
@if($announcement->is_pinned)
|
||||
<span class="mr-2 text-blue-500" title="置頂公告">📌</span>
|
||||
@endif
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
<a href="{{ route('admin.announcements.show', $announcement) }}" class="hover:text-indigo-600 dark:hover:text-indigo-400">
|
||||
{{ $announcement->title }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ \Illuminate\Support\Str::limit($announcement->content, 60) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="inline-flex rounded-full px-2 text-xs font-semibold leading-5
|
||||
@if($announcement->status === 'draft') bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300
|
||||
@elseif($announcement->status === 'published') bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300
|
||||
@else bg-yellow-100 dark:bg-yellow-900/50 text-yellow-800 dark:text-yellow-300
|
||||
@endif">
|
||||
{{ $announcement->getStatusLabel() }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $announcement->getAccessLevelLabel() }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $announcement->creator->name ?? 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $announcement->view_count }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $announcement->created_at->format('Y-m-d H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.announcements.show', $announcement) }}" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">查看</a>
|
||||
@if($announcement->canBeEditedBy(auth()->user()))
|
||||
<a href="{{ route('admin.announcements.edit', $announcement) }}" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300">編輯</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-12 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
沒有找到公告。<a href="{{ route('admin.announcements.create') }}" class="text-indigo-600 dark:text-indigo-400 hover:underline">建立第一則公告</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
@if($announcements->hasPages())
|
||||
<div class="bg-white dark:bg-gray-800 px-4 py-3 border-t border-gray-200 dark:border-gray-700 sm:px-6">
|
||||
{{ $announcements->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
Reference in New Issue
Block a user