feat: [daily-logs] 일일 스크럼 기능 구현
주요 기능:
- 일일 로그 CRUD (생성, 조회, 수정, 삭제, 복원, 영구삭제)
- 로그 항목(Entry) 관리 (추가, 상태변경, 삭제, 순서변경)
- 주간 타임라인 (최근 7일 진행률 표시)
- 테이블 리스트 아코디언 상세보기
- 담당자 자동완성 (일반 사용자는 슈퍼관리자 목록 제외)
- HTMX 기반 동적 테이블 로딩 및 필터링
- Soft Delete 지원
파일 추가:
- Models: AdminPmDailyLog, AdminPmDailyLogEntry
- Controllers: DailyLogController (Web, API)
- Service: DailyLogService
- Requests: StoreDailyLogRequest, UpdateDailyLogRequest
- Views: index, show, table partial, modal-form partial
라우트 추가:
- Web: /daily-logs, /daily-logs/today, /daily-logs/{id}
- API: /api/admin/daily-logs/* (CRUD + 항목관리)
This commit is contained in:
261
app/Http/Controllers/Api/Admin/DailyLogController.php
Normal file
261
app/Http/Controllers/Api/Admin/DailyLogController.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\DailyLog\StoreDailyLogRequest;
|
||||
use App\Http\Requests\DailyLog\UpdateDailyLogRequest;
|
||||
use App\Services\ProjectManagement\DailyLogService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class DailyLogController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DailyLogService $dailyLogService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 일일 로그 목록 (HTMX용)
|
||||
*/
|
||||
public function index(Request $request): View|JsonResponse
|
||||
{
|
||||
$tenantId = session('current_tenant_id', 1);
|
||||
|
||||
$filters = $request->only([
|
||||
'search',
|
||||
'project_id',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'trashed',
|
||||
'sort_by',
|
||||
'sort_direction',
|
||||
]);
|
||||
|
||||
$logs = $this->dailyLogService->getDailyLogs($tenantId, $filters, 15);
|
||||
|
||||
// HTMX 요청이면 HTML 파셜 반환
|
||||
if ($request->header('HX-Request')) {
|
||||
return view('daily-logs.partials.table', compact('logs'));
|
||||
}
|
||||
|
||||
// 일반 요청이면 JSON
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $logs,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 통계
|
||||
*/
|
||||
public function stats(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('current_tenant_id', 1);
|
||||
$projectId = $request->input('project_id');
|
||||
|
||||
$stats = $this->dailyLogService->getStats($tenantId, $projectId);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $stats,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 상세 조회
|
||||
*/
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
$log = $this->dailyLogService->getDailyLogById($id, true);
|
||||
|
||||
if (! $log) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '일일 로그를 찾을 수 없습니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $log,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 생성
|
||||
*/
|
||||
public function store(StoreDailyLogRequest $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('current_tenant_id', 1);
|
||||
|
||||
$log = $this->dailyLogService->createDailyLog($tenantId, $request->validated());
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '일일 로그가 생성되었습니다.',
|
||||
'data' => $log,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 수정
|
||||
*/
|
||||
public function update(UpdateDailyLogRequest $request, int $id): JsonResponse
|
||||
{
|
||||
$log = $this->dailyLogService->updateDailyLog($id, $request->validated());
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '일일 로그가 수정되었습니다.',
|
||||
'data' => $log,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 삭제 (Soft Delete)
|
||||
*/
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$this->dailyLogService->deleteDailyLog($id);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '일일 로그가 삭제되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 복원
|
||||
*/
|
||||
public function restore(int $id): JsonResponse
|
||||
{
|
||||
$this->dailyLogService->restoreDailyLog($id);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '일일 로그가 복원되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일일 로그 영구 삭제
|
||||
*/
|
||||
public function forceDestroy(int $id): JsonResponse
|
||||
{
|
||||
$this->dailyLogService->forceDeleteDailyLog($id);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '일일 로그가 영구 삭제되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 항목(Entry) 관리 API
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 항목 추가
|
||||
*/
|
||||
public function addEntry(Request $request, int $logId): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'assignee_type' => 'required|in:team,user',
|
||||
'assignee_id' => 'nullable|integer',
|
||||
'assignee_name' => 'required|string|max:100',
|
||||
'content' => 'required|string|max:2000',
|
||||
'status' => 'nullable|in:todo,in_progress,done',
|
||||
]);
|
||||
|
||||
$entry = $this->dailyLogService->addEntry($logId, $validated);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '항목이 추가되었습니다.',
|
||||
'data' => $entry,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 항목 상태 변경
|
||||
*/
|
||||
public function updateEntryStatus(Request $request, int $entryId): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|in:todo,in_progress,done',
|
||||
]);
|
||||
|
||||
$entry = $this->dailyLogService->updateEntryStatus($entryId, $validated['status']);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '상태가 변경되었습니다.',
|
||||
'data' => $entry,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 항목 삭제
|
||||
*/
|
||||
public function deleteEntry(int $entryId): JsonResponse
|
||||
{
|
||||
$this->dailyLogService->deleteEntry($entryId);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '항목이 삭제되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 항목 순서 변경
|
||||
*/
|
||||
public function reorderEntries(Request $request, int $logId): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'entry_ids' => 'required|array',
|
||||
'entry_ids.*' => 'integer',
|
||||
]);
|
||||
|
||||
$this->dailyLogService->reorderEntries($logId, $validated['entry_ids']);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '순서가 변경되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 담당자 목록 조회 (자동완성용)
|
||||
*/
|
||||
public function assignees(): JsonResponse
|
||||
{
|
||||
$tenantId = session('current_tenant_id', 1);
|
||||
|
||||
$assignees = $this->dailyLogService->getAssigneeList($tenantId);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $assignees,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 오늘 날짜 로그 조회 또는 생성
|
||||
*/
|
||||
public function today(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('current_tenant_id', 1);
|
||||
$projectId = $request->input('project_id');
|
||||
$today = now()->format('Y-m-d');
|
||||
|
||||
$log = $this->dailyLogService->getOrCreateByDate($tenantId, $today, $projectId);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $log,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user