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:
2025-12-01 09:56:01 +08:00
parent 83ce1f7fc8
commit 642b879dd4
207 changed files with 19487 additions and 3048 deletions

View File

@@ -1,7 +1,7 @@
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
{{ __('Create Task') }} (建立任務)
新增任務 (建立任務)
</h2>
</x-slot>
@@ -14,22 +14,22 @@
<!-- Title -->
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Title') }} <span class="text-red-500">*</span>
標題 <span class="text-red-500">*</span>
</label>
<input type="text" name="title" id="title" value="{{ old('title') }}" required maxlength="255"
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 @error('title') border-red-300 @enderror"
placeholder="{{ __('Brief summary of the task') }}">
placeholder="任務簡述">
@error('title')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
<!-- Description -->
<div>
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Description') }}
描述
</label>
<textarea name="description" id="description" rows="5"
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"
placeholder="{{ __('Detailed description of the task...') }}">{{ old('description') }}</textarea>
placeholder="詳細描述此任務...">{{ old('description') }}</textarea>
@error('description')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
@@ -37,30 +37,30 @@
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="issue_type" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Task Type') }} <span class="text-red-500">*</span>
任務類型 <span class="text-red-500">*</span>
</label>
<select name="issue_type" id="issue_type" required
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 @error('issue_type') border-red-300 @enderror">
<option value="">{{ __('Select type...') }}</option>
<option value="work_item" @selected(old('issue_type') === 'work_item')>{{ __('Work Item') }}</option>
<option value="project_task" @selected(old('issue_type') === 'project_task')>{{ __('Project Task') }}</option>
<option value="maintenance" @selected(old('issue_type') === 'maintenance')>{{ __('Maintenance') }}</option>
<option value="member_request" @selected(old('issue_type') === 'member_request')>{{ __('Member Request') }}</option>
<option value="">選擇類型...</option>
<option value="work_item" @selected(old('issue_type') === 'work_item')>工作項目</option>
<option value="project_task" @selected(old('issue_type') === 'project_task')>專案任務</option>
<option value="maintenance" @selected(old('issue_type') === 'maintenance')>維護</option>
<option value="member_request" @selected(old('issue_type') === 'member_request')>會員請求</option>
</select>
@error('issue_type')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
<div>
<label for="priority" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Priority') }} <span class="text-red-500">*</span>
優先級 <span class="text-red-500">*</span>
</label>
<select name="priority" id="priority" required
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 @error('priority') border-red-300 @enderror">
<option value="">{{ __('Select priority...') }}</option>
<option value="low" @selected(old('priority') === 'low')>{{ __('Low') }} </option>
<option value="medium" @selected(old('priority', 'medium') === 'medium')>{{ __('Medium') }} </option>
<option value="high" @selected(old('priority') === 'high')>{{ __('High') }} </option>
<option value="urgent" @selected(old('priority') === 'urgent')>{{ __('Urgent') }} </option>
<option value="">選擇優先級...</option>
<option value="low" @selected(old('priority') === 'low')> </option>
<option value="medium" @selected(old('priority', 'medium') === 'medium')> </option>
<option value="high" @selected(old('priority') === 'high')> </option>
<option value="urgent" @selected(old('priority') === 'urgent')>緊急 </option>
</select>
@error('priority')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
@@ -70,21 +70,21 @@
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="assigned_to_user_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Assign To') }}
指派給
</label>
<select name="assigned_to_user_id" id="assigned_to_user_id"
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">
<option value="">{{ __('Unassigned') }}</option>
<option value="">未指派</option>
@foreach($users as $user)
<option value="{{ $user->id }}" @selected(old('assigned_to_user_id') == $user->id)>{{ $user->name }}</option>
@endforeach
</select>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{{ __('Optional: Assign to a team member') }}</p>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">選填:指派給團隊成員</p>
</div>
<div>
<label for="due_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Due Date') }}
截止日期
</label>
<input type="date" name="due_date" id="due_date" value="{{ old('due_date') }}"
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">
@@ -95,50 +95,50 @@
<!-- Estimated Hours -->
<div>
<label for="estimated_hours" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Estimated Hours') }}
預估時數
</label>
<input type="number" name="estimated_hours" id="estimated_hours" value="{{ old('estimated_hours') }}" step="0.5" min="0"
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"
placeholder="0.0">
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{{ __('Estimated time to complete this task') }}</p>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">完成此任務的預估時間</p>
</div>
<!-- Member (for member requests) -->
<div>
<label for="member_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Related Member') }}
相關會員
</label>
<select name="member_id" id="member_id"
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">
<option value="">{{ __('None') }}</option>
<option value=""></option>
@foreach($members as $member)
<option value="{{ $member->id }}" @selected(old('member_id') == $member->id)>{{ $member->full_name }}</option>
@endforeach
</select>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{{ __('Link to a member for member requests') }}</p>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">連結至會員(用於會員請求)</p>
</div>
<!-- Parent Task (for sub-tasks) -->
<div>
<label for="parent_issue_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Parent Task') }}
父任務
</label>
<select name="parent_issue_id" id="parent_issue_id"
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">
<option value="">{{ __('None (top-level task)') }}</option>
<option value="">無(頂層任務)</option>
@foreach($openIssues as $parentIssue)
<option value="{{ $parentIssue->id }}" @selected(old('parent_issue_id') == $parentIssue->id)>
{{ $parentIssue->issue_number }} - {{ $parentIssue->title }}
</option>
@endforeach
</select>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{{ __('Make this a sub-task of another task') }}</p>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">將此設為另一個任務的子任務</p>
</div>
<!-- Labels -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{{ __('Labels') }}
標籤
</label>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
@foreach($labels as $label)
@@ -153,18 +153,18 @@
</label>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">{{ __('Select one or more labels to categorize this task') }}</p>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">選擇一個或多個標籤來分類此任務</p>
</div>
<!-- Actions -->
<div class="flex items-center justify-end gap-x-4 border-t border-gray-200 pt-6 dark:border-gray-700">
<a href="{{ route('admin.issues.index') }}"
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600 dark:hover:bg-gray-600">
{{ __('Cancel') }}
取消
</a>
<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 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-indigo-500 dark:hover:bg-indigo-400 dark:focus:ring-offset-gray-800">
{{ __('Create Task') }}
新增任務
</button>
</div>
</form>
@@ -179,14 +179,14 @@
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-blue-800 dark:text-blue-200">{{ __('Creating Tasks') }}</h3>
<h3 class="text-sm font-medium text-blue-800 dark:text-blue-200">建立任務</h3>
<div class="mt-2 text-sm text-blue-700 dark:text-blue-300">
<ul class="list-disc pl-5 space-y-1">
<li>{{ __('Use work items for general tasks and todos') }}</li>
<li>{{ __('Project tasks are for specific project milestones') }}</li>
<li>{{ __('Member requests track inquiries or requests from members') }}</li>
<li>{{ __('Assign tasks to team members to track responsibility') }}</li>
<li>{{ __('Use labels to categorize and filter tasks easily') }}</li>
<li>使用工作項目處理一般任務和待辦事項</li>
<li>專案任務用於特定的專案里程碑</li>
<li>會員請求追蹤來自會員的查詢或請求</li>
<li>將任務指派給團隊成員以追蹤責任歸屬</li>
<li>使用標籤輕鬆分類和篩選任務</li>
</ul>
</div>
</div>

View File

@@ -1,7 +1,7 @@
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
{{ __('Edit Issue') }} - {{ $issue->issue_number }}
編輯任務 - {{ $issue->issue_number }}
</h2>
</x-slot>
@@ -15,22 +15,22 @@
<!-- Title -->
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Title') }} <span class="text-red-500">*</span>
標題 <span class="text-red-500">*</span>
</label>
<input type="text" name="title" id="title" value="{{ old('title', $issue->title) }}" required maxlength="255"
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 @error('title') border-red-300 @enderror"
placeholder="{{ __('Brief summary of the issue') }}">
placeholder="任務簡述">
@error('title')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
<!-- Description -->
<div>
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Description') }}
描述
</label>
<textarea name="description" id="description" rows="5"
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"
placeholder="{{ __('Detailed description of the issue...') }}">{{ old('description', $issue->description) }}</textarea>
placeholder="詳細描述此任務...">{{ old('description', $issue->description) }}</textarea>
@error('description')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
@@ -38,30 +38,30 @@
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="issue_type" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Issue Type') }} <span class="text-red-500">*</span>
任務類型 <span class="text-red-500">*</span>
</label>
<select name="issue_type" id="issue_type" required
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 @error('issue_type') border-red-300 @enderror">
<option value="">{{ __('Select type...') }}</option>
<option value="work_item" @selected(old('issue_type', $issue->issue_type) === 'work_item')>{{ __('Work Item') }}</option>
<option value="project_task" @selected(old('issue_type', $issue->issue_type) === 'project_task')>{{ __('Project Task') }}</option>
<option value="maintenance" @selected(old('issue_type', $issue->issue_type) === 'maintenance')>{{ __('Maintenance') }}</option>
<option value="member_request" @selected(old('issue_type', $issue->issue_type) === 'member_request')>{{ __('Member Request') }}</option>
<option value="">選擇類型...</option>
<option value="work_item" @selected(old('issue_type', $issue->issue_type) === 'work_item')>工作項目</option>
<option value="project_task" @selected(old('issue_type', $issue->issue_type) === 'project_task')>專案任務</option>
<option value="maintenance" @selected(old('issue_type', $issue->issue_type) === 'maintenance')>維護</option>
<option value="member_request" @selected(old('issue_type', $issue->issue_type) === 'member_request')>會員請求</option>
</select>
@error('issue_type')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
<div>
<label for="priority" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Priority') }} <span class="text-red-500">*</span>
優先級 <span class="text-red-500">*</span>
</label>
<select name="priority" id="priority" required
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 @error('priority') border-red-300 @enderror">
<option value="">{{ __('Select priority...') }}</option>
<option value="low" @selected(old('priority', $issue->priority) === 'low')>{{ __('Low') }} </option>
<option value="medium" @selected(old('priority', $issue->priority) === 'medium')>{{ __('Medium') }} </option>
<option value="high" @selected(old('priority', $issue->priority) === 'high')>{{ __('High') }} </option>
<option value="urgent" @selected(old('priority', $issue->priority) === 'urgent')>{{ __('Urgent') }} </option>
<option value="">選擇優先級...</option>
<option value="low" @selected(old('priority', $issue->priority) === 'low')> </option>
<option value="medium" @selected(old('priority', $issue->priority) === 'medium')> </option>
<option value="high" @selected(old('priority', $issue->priority) === 'high')> </option>
<option value="urgent" @selected(old('priority', $issue->priority) === 'urgent')>緊急 </option>
</select>
@error('priority')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
@@ -71,11 +71,11 @@
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="assigned_to_user_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Assign To') }}
指派給
</label>
<select name="assigned_to_user_id" id="assigned_to_user_id"
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">
<option value="">{{ __('Unassigned') }}</option>
<option value="">未指派</option>
@foreach($users as $user)
<option value="{{ $user->id }}" @selected(old('assigned_to_user_id', $issue->assigned_to_user_id) == $user->id)>{{ $user->name }}</option>
@endforeach
@@ -84,11 +84,11 @@
<div>
<label for="reviewer_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Reviewer') }}
審查者
</label>
<select name="reviewer_id" id="reviewer_id"
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">
<option value="">{{ __('None') }}</option>
<option value=""></option>
@foreach($users as $user)
<option value="{{ $user->id }}" @selected(old('reviewer_id', $issue->reviewer_id) == $user->id)>{{ $user->name }}</option>
@endforeach
@@ -100,7 +100,7 @@
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="due_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Due Date') }}
截止日期
</label>
<input type="date" name="due_date" id="due_date" value="{{ old('due_date', $issue->due_date?->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">
@@ -109,7 +109,7 @@
<div>
<label for="estimated_hours" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Estimated Hours') }}
預估時數
</label>
<input type="number" name="estimated_hours" id="estimated_hours" value="{{ old('estimated_hours', $issue->estimated_hours) }}" step="0.5" min="0"
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">
@@ -119,11 +119,11 @@
<!-- Member (for member requests) -->
<div>
<label for="member_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Related Member') }}
相關會員
</label>
<select name="member_id" id="member_id"
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">
<option value="">{{ __('None') }}</option>
<option value=""></option>
@foreach($members as $member)
<option value="{{ $member->id }}" @selected(old('member_id', $issue->member_id) == $member->id)>{{ $member->full_name }}</option>
@endforeach
@@ -133,11 +133,11 @@
<!-- Parent Issue (for sub-tasks) -->
<div>
<label for="parent_issue_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Parent Issue') }}
父任務
</label>
<select name="parent_issue_id" id="parent_issue_id"
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">
<option value="">{{ __('None (top-level issue)') }}</option>
<option value="">無(頂層任務)</option>
@foreach($openIssues as $parentIssue)
<option value="{{ $parentIssue->id }}" @selected(old('parent_issue_id', $issue->parent_issue_id) == $parentIssue->id)>
{{ $parentIssue->issue_number }} - {{ $parentIssue->title }}
@@ -149,7 +149,7 @@
<!-- Labels -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{{ __('Labels') }}
標籤
</label>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
@foreach($labels as $label)
@@ -170,11 +170,11 @@
<div class="flex items-center justify-end gap-x-4 border-t border-gray-200 pt-6 dark:border-gray-700">
<a href="{{ route('admin.issues.show', $issue) }}"
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600 dark:hover:bg-gray-600">
{{ __('Cancel') }}
取消
</a>
<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 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-indigo-500 dark:hover:bg-indigo-400 dark:focus:ring-offset-gray-800">
{{ __('Update Issue') }}
更新任務
</button>
</div>
</form>

View File

@@ -1,7 +1,7 @@
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
{{ __('Tasks') }} (任務追蹤)
任務管理 (任務追蹤)
</h2>
</x-slot>
@@ -23,13 +23,13 @@
<!-- Header -->
<div class="sm:flex sm:items-center sm:justify-between mb-6">
<div>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('Task Tracker') }}</h3>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">{{ __('Manage work items, tasks, and member requests') }}</p>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">任務追蹤器</h3>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">管理工作項目、任務和會員請求</p>
</div>
<div class="mt-4 sm:mt-0">
<a href="{{ route('admin.issues.create') }}" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-indigo-500 dark:hover:bg-indigo-400 dark:focus:ring-offset-gray-800">
<svg class="-ml-0.5 mr-1.5 h-5 w-5" fill="currentColor" viewBox="0 0 20 20"><path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"/></svg>
{{ __('Create Task') }}
新增任務
</a>
</div>
</div>
@@ -37,19 +37,19 @@
<!-- Summary Stats -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-4 mb-6">
<div class="bg-blue-50 dark:bg-blue-900/30 rounded-lg p-4 border-l-4 border-blue-400">
<dt class="text-sm font-medium text-blue-800 dark:text-blue-200">{{ __('Total Open') }}</dt>
<dt class="text-sm font-medium text-blue-800 dark:text-blue-200">總開啟數</dt>
<dd class="mt-1 text-2xl font-semibold text-blue-900 dark:text-blue-100">{{ $stats['total_open'] }}</dd>
</div>
<div class="bg-purple-50 dark:bg-purple-900/30 rounded-lg p-4 border-l-4 border-purple-400">
<dt class="text-sm font-medium text-purple-800 dark:text-purple-200">{{ __('Assigned to Me') }}</dt>
<dt class="text-sm font-medium text-purple-800 dark:text-purple-200">指派給我</dt>
<dd class="mt-1 text-2xl font-semibold text-purple-900 dark:text-purple-100">{{ $stats['assigned_to_me'] }}</dd>
</div>
<div class="bg-red-50 dark:bg-red-900/30 rounded-lg p-4 border-l-4 border-red-400">
<dt class="text-sm font-medium text-red-800 dark:text-red-200">{{ __('Overdue') }}</dt>
<dt class="text-sm font-medium text-red-800 dark:text-red-200">逾期</dt>
<dd class="mt-1 text-2xl font-semibold text-red-900 dark:text-red-100">{{ $stats['overdue'] }}</dd>
</div>
<div class="bg-orange-50 dark:bg-orange-900/30 rounded-lg p-4 border-l-4 border-orange-400">
<dt class="text-sm font-medium text-orange-800 dark:text-orange-200">{{ __('High Priority') }}</dt>
<dt class="text-sm font-medium text-orange-800 dark:text-orange-200">高優先級</dt>
<dd class="mt-1 text-2xl font-semibold text-orange-900 dark:text-orange-100">{{ $stats['high_priority'] }}</dd>
</div>
</div>
@@ -58,40 +58,40 @@
<form method="GET" class="mb-6 space-y-4" role="search">
<div class="grid grid-cols-1 gap-4 sm:grid-cols-4">
<div>
<label for="issue_type" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Type') }}</label>
<label for="issue_type" class="block text-sm font-medium text-gray-700 dark:text-gray-300">類型</label>
<select name="issue_type" id="issue_type" 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">
<option value="">{{ __('All Types') }}</option>
<option value="work_item" @selected(request('issue_type') === 'work_item')>{{ __('Work Item') }}</option>
<option value="project_task" @selected(request('issue_type') === 'project_task')>{{ __('Project Task') }}</option>
<option value="maintenance" @selected(request('issue_type') === 'maintenance')>{{ __('Maintenance') }}</option>
<option value="member_request" @selected(request('issue_type') === 'member_request')>{{ __('Member Request') }}</option>
<option value="">所有類型</option>
<option value="work_item" @selected(request('issue_type') === 'work_item')>工作項目</option>
<option value="project_task" @selected(request('issue_type') === 'project_task')>專案任務</option>
<option value="maintenance" @selected(request('issue_type') === 'maintenance')>維護</option>
<option value="member_request" @selected(request('issue_type') === 'member_request')>會員請求</option>
</select>
</div>
<div>
<label for="status" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Status') }}</label>
<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 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">
<option value="">{{ __('All Statuses') }}</option>
<option value="new" @selected(request('status') === 'new')>{{ __('New') }}</option>
<option value="assigned" @selected(request('status') === 'assigned')>{{ __('Assigned') }}</option>
<option value="in_progress" @selected(request('status') === 'in_progress')>{{ __('In Progress') }}</option>
<option value="review" @selected(request('status') === 'review')>{{ __('Review') }}</option>
<option value="closed" @selected(request('status') === 'closed')>{{ __('Closed') }}</option>
<option value="">所有狀態</option>
<option value="new" @selected(request('status') === 'new')></option>
<option value="assigned" @selected(request('status') === 'assigned')>已指派</option>
<option value="in_progress" @selected(request('status') === 'in_progress')>進行中</option>
<option value="review" @selected(request('status') === 'review')>審查</option>
<option value="closed" @selected(request('status') === 'closed')>已結案</option>
</select>
</div>
<div>
<label for="priority" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Priority') }}</label>
<label for="priority" class="block text-sm font-medium text-gray-700 dark:text-gray-300">優先級</label>
<select name="priority" id="priority" 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">
<option value="">{{ __('All Priorities') }}</option>
<option value="low" @selected(request('priority') === 'low')>{{ __('Low') }}</option>
<option value="medium" @selected(request('priority') === 'medium')>{{ __('Medium') }}</option>
<option value="high" @selected(request('priority') === 'high')>{{ __('High') }}</option>
<option value="urgent" @selected(request('priority') === 'urgent')>{{ __('Urgent') }}</option>
<option value="">所有優先級</option>
<option value="low" @selected(request('priority') === 'low')></option>
<option value="medium" @selected(request('priority') === 'medium')></option>
<option value="high" @selected(request('priority') === 'high')></option>
<option value="urgent" @selected(request('priority') === 'urgent')>緊急</option>
</select>
</div>
<div>
<label for="assigned_to" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Assignee') }}</label>
<label for="assigned_to" class="block text-sm font-medium text-gray-700 dark:text-gray-300">指派對象</label>
<select name="assigned_to" id="assigned_to" 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">
<option value="">{{ __('All Assignees') }}</option>
<option value="">所有指派對象</option>
@foreach($users as $user)
<option value="{{ $user->id }}" @selected(request('assigned_to') == $user->id)>{{ $user->name }}</option>
@endforeach
@@ -100,14 +100,14 @@
</div>
<div class="flex gap-4">
<div class="flex-1">
<label for="search" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Search') }}</label>
<input type="text" name="search" id="search" value="{{ request('search') }}" placeholder="{{ __('Task number, title, or description...') }}" 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">
<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 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 items-end gap-2">
<button type="submit" class="inline-flex justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600 dark:hover:bg-gray-600">{{ __('Filter') }}</button>
<button type="submit" class="inline-flex justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600 dark:hover:bg-gray-600">篩選</button>
<label class="inline-flex items-center">
<input type="checkbox" name="show_closed" value="1" @checked(request('show_closed') === '1') class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600">
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">{{ __('Show closed') }}</span>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">顯示已結案</span>
</label>
</div>
</div>
@@ -116,16 +116,16 @@
<!-- Issues Table -->
<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">
<caption class="sr-only">{{ __('List of tasks with their current status and assignment') }}</caption>
<caption class="sr-only">任務列表及其目前狀態與指派</caption>
<thead class="bg-gray-50 dark:bg-gray-700">
<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">{{ __('Task') }}</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">{{ __('Type') }}</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">{{ __('Status') }}</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">{{ __('Priority') }}</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">{{ __('Assignee') }}</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">{{ __('Due Date') }}</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4"><span class="sr-only">{{ __('Actions') }}</span></th>
<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>
<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="relative py-3.5 pl-3 pr-4"><span class="sr-only">操作</span></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-800">
@@ -163,7 +163,7 @@
<span class="{{ $issue->is_overdue ? 'text-red-600 dark:text-red-400 font-semibold' : '' }}">
{{ $issue->due_date->format('Y-m-d') }}
@if($issue->is_overdue)
<span class="text-xs">({{ __('Overdue') }})</span>
<span class="text-xs">(逾期)</span>
@endif
</span>
@else
@@ -171,16 +171,16 @@
@endif
</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium">
<a href="{{ route('admin.issues.show', $issue) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">{{ __('View') }}</a>
<a href="{{ route('admin.issues.show', $issue) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">檢視</a>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-3 py-8 text-center text-sm text-gray-500 dark:text-gray-400">
<p>{{ __('No tasks found') }}</p>
<p>找不到任務</p>
<div class="mt-4">
<a href="{{ route('admin.issues.create') }}" class="inline-flex items-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">
+ {{ __('Create First Task') }}
+ 新增第一個任務
</a>
</div>
</td>

View File

@@ -31,23 +31,23 @@
<div class="flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400">
<span>{{ $issue->issue_type_label }}</span>
<span>"</span>
<span>{{ __('Created by') }} {{ $issue->creator->name }}</span>
<span>建立者 {{ $issue->creator->name }}</span>
<span>"</span>
<span>{{ $issue->created_at->diffForHumans() }}</span>
</div>
</div>
<div class="mt-4 sm:mt-0 flex gap-2">
@if(!$issue->isClosed() || Auth::user()->is_admin)
@if(!$issue->isClosed() || Auth::user()->hasRole('admin'))
<a href="{{ route('admin.issues.edit', $issue) }}" class="inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600">
{{ __('Edit') }}
編輯
</a>
@endif
@if(Auth::user()->is_admin)
<form method="POST" action="{{ route('admin.issues.destroy', $issue) }}" onsubmit="return confirm('{{ __('Are you sure?') }}')">
@if(Auth::user()->hasRole('admin'))
<form method="POST" action="{{ route('admin.issues.destroy', $issue) }}" onsubmit="return confirm('您確定嗎?')">
@csrf
@method('DELETE')
<button type="submit" class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 dark:bg-red-500 dark:hover:bg-red-400">
{{ __('Delete') }}
刪除
</button>
</form>
@endif
@@ -68,55 +68,55 @@
<!-- Description -->
<div class="prose dark:prose-invert max-w-none mb-6">
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">{{ __('Description') }}</h4>
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">描述</h4>
<div class="text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{ $issue->description ?: __('No description provided') }}</div>
</div>
<!-- Details Grid -->
<dl class="grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-2 border-t border-gray-200 dark:border-gray-700 pt-6">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ __('Assigned To') }}</dt>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">指派對象</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $issue->assignee?->name ?? __('Unassigned') }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ __('Reviewer') }}</dt>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">審查者</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $issue->reviewer?->name ?? __('None') }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ __('Due Date') }}</dt>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">截止日期</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
@if($issue->due_date)
<span class="{{ $issue->is_overdue ? 'text-red-600 dark:text-red-400 font-semibold' : '' }}">
{{ $issue->due_date->format('Y-m-d') }}
@if($issue->is_overdue)
({{ __('Overdue by :days days', ['days' => abs($issue->days_until_due)]) }})
(逾期 {{ abs($issue->days_until_due) }} )
@elseif($issue->days_until_due !== null && $issue->days_until_due >= 0)
({{ __(':days days left', ['days' => $issue->days_until_due]) }})
({{ $issue->days_until_due }} 天剩餘)
@endif
</span>
@else
<span class="text-gray-400">{{ __('No due date') }}</span>
<span class="text-gray-400">無截止日期</span>
@endif
</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ __('Time Tracking') }}</dt>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">時間追蹤</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ number_format($issue->actual_hours, 1) }}h
@if($issue->estimated_hours)
/ {{ number_format($issue->estimated_hours, 1) }}h {{ __('estimated') }}
/ {{ number_format($issue->estimated_hours, 1) }}h 預估
@endif
</dd>
</div>
@if($issue->member)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ __('Related Member') }}</dt>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">相關會員</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $issue->member->full_name }}</dd>
</div>
@endif
@if($issue->parentIssue)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ __('Parent Issue') }}</dt>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">父任務</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
<a href="{{ route('admin.issues.show', $issue->parentIssue) }}" class="text-indigo-600 hover:underline dark:text-indigo-400">
{{ $issue->parentIssue->issue_number }} - {{ $issue->parentIssue->title }}
@@ -129,7 +129,7 @@
<!-- Sub-tasks -->
@if($issue->subTasks->count() > 0)
<div class="mt-6 border-t border-gray-200 dark:border-gray-700 pt-6">
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">{{ __('Sub-tasks') }} ({{ $issue->subTasks->count() }})</h4>
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">子任務 ({{ $issue->subTasks->count() }})</h4>
<ul class="space-y-2">
@foreach($issue->subTasks as $subTask)
<li class="flex items-center gap-2">
@@ -150,21 +150,21 @@
<!-- Workflow Actions -->
@if(!$issue->isClosed())
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">{{ __('Actions') }}</h3>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">操作</h3>
<div class="flex flex-wrap gap-2">
<!-- Update Status -->
<form method="POST" action="{{ route('admin.issues.update-status', $issue) }}" class="inline-flex gap-2">
@csrf
@method('PATCH')
<select name="status" class="rounded-md border-gray-300 text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<option value="new" @selected($issue->status === 'new')>{{ __('New') }}</option>
<option value="assigned" @selected($issue->status === 'assigned')>{{ __('Assigned') }}</option>
<option value="in_progress" @selected($issue->status === 'in_progress')>{{ __('In Progress') }}</option>
<option value="review" @selected($issue->status === 'review')>{{ __('Review') }}</option>
<option value="closed" @selected($issue->status === 'closed')>{{ __('Closed') }}</option>
<option value="new" @selected($issue->status === 'new')></option>
<option value="assigned" @selected($issue->status === 'assigned')>已指派</option>
<option value="in_progress" @selected($issue->status === 'in_progress')>進行中</option>
<option value="review" @selected($issue->status === 'review')>審查</option>
<option value="closed" @selected($issue->status === 'closed')>已結案</option>
</select>
<button type="submit" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-400">
{{ __('Update Status') }}
更新狀態
</button>
</form>
@@ -172,13 +172,13 @@
<form method="POST" action="{{ route('admin.issues.assign', $issue) }}" class="inline-flex gap-2">
@csrf
<select name="assigned_to_user_id" class="rounded-md border-gray-300 text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<option value="">{{ __('Unassigned') }}</option>
<option value="">未指派</option>
@foreach($users as $user)
<option value="{{ $user->id }}" @selected($issue->assigned_to_user_id == $user->id)>{{ $user->name }}</option>
@endforeach
</select>
<button type="submit" class="inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600">
{{ __('Assign') }}
指派
</button>
</form>
</div>
@@ -188,7 +188,7 @@
<!-- Comments -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">
{{ __('Comments') }} ({{ $issue->comments->count() }})
留言 ({{ $issue->comments->count() }})
</h3>
<!-- Comments List -->
@@ -199,13 +199,13 @@
<span class="font-medium text-sm text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</span>
<span class="text-xs text-gray-500 dark:text-gray-400">{{ $comment->created_at->diffForHumans() }}</span>
@if($comment->is_internal)
<span class="text-xs bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 px-2 py-0.5 rounded">{{ __('Internal') }}</span>
<span class="text-xs bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 px-2 py-0.5 rounded">內部</span>
@endif
</div>
<p class="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{ $comment->comment_text }}</p>
</div>
@empty
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">{{ __('No comments yet') }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">尚無留言</p>
@endforelse
</div>
@@ -214,14 +214,14 @@
@csrf
<textarea name="comment_text" rows="3" required
class="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 mb-2"
placeholder="{{ __('Add a comment...') }}"></textarea>
placeholder="新增留言..."></textarea>
<div class="flex items-center justify-between">
<label class="inline-flex items-center">
<input type="checkbox" name="is_internal" value="1" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600">
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">{{ __('Internal comment') }}</span>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">內部留言</span>
</label>
<button type="submit" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500 dark:bg-indigo-500">
{{ __('Add Comment') }}
新增留言
</button>
</div>
</form>
@@ -230,7 +230,7 @@
<!-- Attachments -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">
{{ __('Attachments') }} ({{ $issue->attachments->count() }})
附件 ({{ $issue->attachments->count() }})
</h3>
<div class="space-y-2 mb-6">
@@ -246,18 +246,18 @@
</div>
</div>
<div class="flex gap-2">
<a href="{{ route('admin.issues.attachments.download', $attachment) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 text-sm">{{ __('Download') }}</a>
@if(Auth::user()->is_admin)
<form method="POST" action="{{ route('admin.issues.attachments.destroy', $attachment) }}" onsubmit="return confirm('{{ __('Delete this attachment?') }}')">
<a href="{{ route('admin.issues.attachments.download', $attachment) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 text-sm">下載</a>
@if(Auth::user()->hasRole('admin'))
<form method="POST" action="{{ route('admin.issues.attachments.destroy', $attachment) }}" onsubmit="return confirm('刪除此附件?')">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 text-sm">{{ __('Delete') }}</button>
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 text-sm">刪除</button>
</form>
@endif
</div>
</div>
@empty
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">{{ __('No attachments') }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">無附件</p>
@endforelse
</div>
@@ -267,17 +267,17 @@
<div class="flex items-center gap-2">
<input type="file" name="file" required class="block w-full text-sm text-gray-900 dark:text-gray-100 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100 dark:file:bg-indigo-900 dark:file:text-indigo-200">
<button type="submit" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500 dark:bg-indigo-500">
{{ __('Upload') }}
上傳
</button>
</div>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ __('Max size: 10MB') }}</p>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">最大大小10MB</p>
</form>
</div>
<!-- Time Logs -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">
{{ __('Time Tracking') }} ({{ number_format($issue->total_time_logged, 1) }}h total)
時間追蹤 ({{ number_format($issue->total_time_logged, 1) }}h total)
</h3>
<div class="space-y-2 mb-6">
@@ -289,21 +289,21 @@
</div>
</div>
@empty
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">{{ __('No time logged yet') }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">尚未記錄時間</p>
@endforelse
</div>
<!-- Log Time Form -->
<form method="POST" action="{{ route('admin.issues.time-logs.store', $issue) }}" class="border-t border-gray-200 dark:border-gray-700 pt-4 grid grid-cols-2 gap-2">
@csrf
<input type="number" name="hours" step="0.25" min="0.25" placeholder="{{ __('Hours') }}" required
<input type="number" name="hours" step="0.25" min="0.25" placeholder="時數" required
class="rounded-md border-gray-300 text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<input type="date" name="logged_at" value="{{ now()->format('Y-m-d') }}" required
class="rounded-md border-gray-300 text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<input type="text" name="description" placeholder="{{ __('What did you do?') }}"
<input type="text" name="description" placeholder="您做了什麼?"
class="col-span-2 rounded-md border-gray-300 text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<button type="submit" class="col-span-2 inline-flex justify-center items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500 dark:bg-indigo-500">
{{ __('Log Time') }}
記錄時間
</button>
</form>
</div>
@@ -313,11 +313,11 @@
<div class="space-y-6">
<!-- Timeline -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">{{ __('Progress') }}</h3>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">進度</h3>
<x-issue.timeline :issue="$issue" />
<div class="mt-4">
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-700 dark:text-gray-300">{{ __('Completion') }}</span>
<span class="text-gray-700 dark:text-gray-300">完成度</span>
<span class="font-medium text-gray-900 dark:text-gray-100">{{ $issue->progress_percentage }}%</span>
</div>
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
@@ -329,7 +329,7 @@
<!-- Watchers -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">
{{ __('Watchers') }} ({{ $issue->watchers->count() }})
觀察者 ({{ $issue->watchers->count() }})
</h3>
<ul class="space-y-2 mb-4">
@@ -341,7 +341,7 @@
@csrf
@method('DELETE')
<input type="hidden" name="user_id" value="{{ $watcher->id }}">
<button type="submit" class="text-xs text-red-600 hover:text-red-900 dark:text-red-400">{{ __('Remove') }}</button>
<button type="submit" class="text-xs text-red-600 hover:text-red-900 dark:text-red-400">移除</button>
</form>
@endif
</li>
@@ -353,13 +353,13 @@
@csrf
<div class="flex gap-2">
<select name="user_id" required class="flex-1 rounded-md border-gray-300 text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<option value="">{{ __('Add watcher...') }}</option>
<option value="">新增觀察者...</option>
@foreach($users->whereNotIn('id', $issue->watchers->pluck('id')) as $user)
<option value="{{ $user->id }}">{{ $user->name }}</option>
@endforeach
</select>
<button type="submit" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500 dark:bg-indigo-500">
{{ __('Add') }}
新增
</button>
</div>
</form>