Files
usher-manage-stack/resources/views/admin/issue-reports/index.blade.php
Gbanyan 642b879dd4 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>
2025-12-01 09:56:01 +08:00

280 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
任務報告與分析
</h2>
</x-slot>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8 space-y-6">
{{-- Date Range Filter --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<form method="GET" action="{{ route('admin.issue-reports.index') }}" class="flex flex-wrap gap-4 items-end">
<div class="flex-1 min-w-[200px]">
<label for="start_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
開始日期
</label>
<input type="date" name="start_date" id="start_date" value="{{ $startDate->format('Y-m-d') }}"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
</div>
<div class="flex-1 min-w-[200px]">
<label for="end_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
結束日期
</label>
<input type="date" name="end_date" id="end_date" value="{{ $endDate->format('Y-m-d') }}"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
</div>
<button type="submit" class="inline-flex justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-400">
套用篩選
</button>
</form>
</div>
{{-- Summary Statistics --}}
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 border-l-4 border-blue-400">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">總任務數</dt>
<dd class="mt-1 text-3xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($stats['total_issues']) }}</dd>
</div>
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 border-l-4 border-green-400">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">開啟的任務</dt>
<dd class="mt-1 text-3xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($stats['open_issues']) }}</dd>
</div>
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 border-l-4 border-gray-400">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">已結案任務</dt>
<dd class="mt-1 text-3xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($stats['closed_issues']) }}</dd>
</div>
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 border-l-4 border-red-400">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">逾期任務</dt>
<dd class="mt-1 text-3xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($stats['overdue_issues']) }}</dd>
</div>
</div>
{{-- Charts Section --}}
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
{{-- Tasks by Status --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">依狀態的任務</h3>
<div class="space-y-2">
@foreach(['new', 'assigned', 'in_progress', 'review', 'closed'] as $status)
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600 dark:text-gray-400">{{ ucfirst(str_replace('_', ' ', $status)) }}</span>
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $issuesByStatus[$status] ?? 0 }}</span>
</div>
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
@php
$percentage = $stats['total_issues'] > 0 ? (($issuesByStatus[$status] ?? 0) / $stats['total_issues']) * 100 : 0;
@endphp
<div class="bg-indigo-600 h-2 rounded-full" style="width: {{ $percentage }}%"></div>
</div>
@endforeach
</div>
</div>
{{-- Tasks by Priority --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">依優先級的任務</h3>
<div class="space-y-2">
@foreach(['low', 'medium', 'high', 'urgent'] as $priority)
@php
$colors = ['low' => 'green', 'medium' => 'yellow', 'high' => 'orange', 'urgent' => 'red'];
@endphp
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600 dark:text-gray-400">{{ ucfirst($priority) }}</span>
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $issuesByPriority[$priority] ?? 0 }}</span>
</div>
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
@php
$percentage = $stats['total_issues'] > 0 ? (($issuesByPriority[$priority] ?? 0) / $stats['total_issues']) * 100 : 0;
@endphp
<div class="bg-{{ $colors[$priority] }}-600 h-2 rounded-full" style="width: {{ $percentage }}%"></div>
</div>
@endforeach
</div>
</div>
{{-- Tasks by Type --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">依類型的任務</h3>
<div class="space-y-2">
@foreach(['work_item', 'project_task', 'maintenance', 'member_request'] as $type)
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600 dark:text-gray-400">{{ ucfirst(str_replace('_', ' ', $type)) }}</span>
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $issuesByType[$type] ?? 0 }}</span>
</div>
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
@php
$percentage = $stats['total_issues'] > 0 ? (($issuesByType[$type] ?? 0) / $stats['total_issues']) * 100 : 0;
@endphp
<div class="bg-blue-600 h-2 rounded-full" style="width: {{ $percentage }}%"></div>
</div>
@endforeach
</div>
</div>
</div>
{{-- Time Tracking Metrics --}}
@if($timeTrackingMetrics && $timeTrackingMetrics->total_estimated > 0)
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">時間追蹤指標</h3>
<div class="grid grid-cols-1 sm:grid-cols-4 gap-4">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">總預估時數</dt>
<dd class="mt-1 text-2xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($timeTrackingMetrics->total_estimated, 1) }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">總實際時數</dt>
<dd class="mt-1 text-2xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($timeTrackingMetrics->total_actual, 1) }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">平均預估時數</dt>
<dd class="mt-1 text-2xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($timeTrackingMetrics->avg_estimated, 1) }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">平均實際時數</dt>
<dd class="mt-1 text-2xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($timeTrackingMetrics->avg_actual, 1) }}</dd>
</div>
</div>
@php
$variance = $timeTrackingMetrics->total_actual - $timeTrackingMetrics->total_estimated;
$variancePercentage = $timeTrackingMetrics->total_estimated > 0 ? ($variance / $timeTrackingMetrics->total_estimated) * 100 : 0;
@endphp
<div class="mt-4">
<p class="text-sm text-gray-600 dark:text-gray-400">
差異:
<span class="font-semibold {{ $variance > 0 ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400' }}">
{{ $variance > 0 ? '+' : '' }}{{ number_format($variance, 1) }} hours ({{ number_format($variancePercentage, 1) }}%)
</span>
</p>
</div>
</div>
@endif
{{-- Average Resolution Time --}}
@if($avgResolutionTime)
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">平均解決時間</h3>
<p class="text-3xl font-semibold text-gray-900 dark:text-gray-100">{{ number_format($avgResolutionTime, 1) }} </p>
</div>
@endif
{{-- Assignee Performance --}}
@if($assigneePerformance->isNotEmpty())
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">指派對象表現前10名</h3>
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
<table class="min-w-full divide-y divide-gray-300 dark:divide-gray-600">
<thead class="bg-gray-50 dark:bg-gray-900">
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">指派對象</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">總指派數</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">已完成</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">逾期</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">完成率</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700 bg-white dark:bg-gray-800">
@foreach($assigneePerformance as $user)
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 dark:text-gray-100">
{{ $user->name }}
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-900 dark:text-gray-100">
{{ $user->total_assigned }}
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-900 dark:text-gray-100">
{{ $user->completed }}
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-900 dark:text-gray-100">
<span class="{{ $user->overdue > 0 ? 'text-red-600 dark:text-red-400' : '' }}">
{{ $user->overdue }}
</span>
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-900 dark:text-gray-100">
<div class="flex items-center gap-2">
<div class="flex-1 bg-gray-200 rounded-full h-2 dark:bg-gray-700 min-w-[60px]">
<div class="bg-green-600 h-2 rounded-full" style="width: {{ $user->completion_rate }}%"></div>
</div>
<span class="text-sm font-medium">{{ $user->completion_rate }}%</span>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
{{-- Top Labels Used --}}
@if($topLabels->isNotEmpty())
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">最常用標籤</h3>
<div class="space-y-3">
@foreach($topLabels as $label)
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<span class="inline-flex items-center rounded-full px-3 py-1 text-sm font-medium"
style="background-color: {{ $label->color }}; color: {{ \App\Models\IssueLabel::find($label->id)->text_color ?? '#000000' }}">
{{ $label->name }}
</span>
</div>
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $label->usage_count }} 使用</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
@php
$maxUsage = $topLabels->max('usage_count');
$percentage = $maxUsage > 0 ? ($label->usage_count / $maxUsage) * 100 : 0;
@endphp
<div class="h-2 rounded-full" style="width: {{ $percentage }}%; background-color: {{ $label->color }}"></div>
</div>
@endforeach
</div>
</div>
@endif
{{-- Recent Tasks --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">最近的任務</h3>
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg dark:ring-gray-700">
<table class="min-w-full divide-y divide-gray-300 dark:divide-gray-600">
<thead class="bg-gray-50 dark:bg-gray-900">
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">任務</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">狀態</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">優先級</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">指派對象</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">已建立</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-800">
@foreach($recentIssues as $issue)
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
<td class="py-4 pl-4 pr-3 text-sm">
<a href="{{ route('admin.issues.show', $issue) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
{{ $issue->issue_number }}: {{ Str::limit($issue->title, 50) }}
</a>
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm">
<x-issue.status-badge :status="$issue->status" />
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm">
<x-issue.priority-badge :priority="$issue->priority" />
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-900 dark:text-gray-100">
{{ $issue->assignee?->name ?? '—' }}
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ $issue->created_at->format('Y-m-d') }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</x-app-layout>