'date', 'end_date' => 'date', 'created_by' => 'integer', 'updated_by' => 'integer', 'deleted_by' => 'integer', ]; /** * 상태 상수 */ public const STATUS_ACTIVE = 'active'; public const STATUS_COMPLETED = 'completed'; public const STATUS_ON_HOLD = 'on_hold'; /** * 상태 목록 */ public static function getStatuses(): array { return [ self::STATUS_ACTIVE => '진행중', self::STATUS_COMPLETED => '완료', self::STATUS_ON_HOLD => '보류', ]; } /** * 활성 프로젝트만 조회 */ public function scopeActive($query) { return $query->where('status', self::STATUS_ACTIVE); } /** * 관계: 작업 목록 */ public function tasks(): HasMany { return $this->hasMany(AdminPmTask::class, 'project_id'); } /** * 관계: 이슈 목록 */ public function issues(): HasMany { return $this->hasMany(AdminPmIssue::class, 'project_id'); } /** * 관계: 일일 로그 */ public function dailyLogs(): HasMany { return $this->hasMany(AdminPmDailyLog::class, 'project_id'); } /** * 오늘의 스크럼 항목 */ public function getTodayScrumAttribute(): ?AdminPmDailyLog { return $this->dailyLogs() ->where('log_date', now()->format('Y-m-d')) ->with('entries') ->first(); } /** * 관계: 생성자 */ public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } /** * 관계: 수정자 */ public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); } /** * 진행률 계산 (완료 작업 / 전체 작업) */ public function getProgressAttribute(): float { $total = $this->tasks()->count(); if ($total === 0) { return 0; } $done = $this->tasks()->where('status', AdminPmTask::STATUS_DONE)->count(); return round(($done / $total) * 100, 1); } /** * 작업 통계 */ public function getTaskStatsAttribute(): array { return [ 'total' => $this->tasks()->count(), 'todo' => $this->tasks()->where('status', AdminPmTask::STATUS_TODO)->count(), 'in_progress' => $this->tasks()->where('status', AdminPmTask::STATUS_IN_PROGRESS)->count(), 'done' => $this->tasks()->where('status', AdminPmTask::STATUS_DONE)->count(), ]; } /** * 이슈 통계 */ public function getIssueStatsAttribute(): array { return [ 'total' => $this->issues()->count(), 'open' => $this->issues()->where('status', AdminPmIssue::STATUS_OPEN)->count(), 'in_progress' => $this->issues()->where('status', AdminPmIssue::STATUS_IN_PROGRESS)->count(), 'resolved' => $this->issues()->where('status', AdminPmIssue::STATUS_RESOLVED)->count(), 'closed' => $this->issues()->where('status', AdminPmIssue::STATUS_CLOSED)->count(), ]; } /** * 상태 라벨 (한글) */ public function getStatusLabelAttribute(): string { return self::getStatuses()[$this->status] ?? $this->status; } /** * 상태별 색상 클래스 */ public function getStatusColorAttribute(): string { return match ($this->status) { self::STATUS_ACTIVE => 'bg-green-100 text-green-800', self::STATUS_COMPLETED => 'bg-blue-100 text-blue-800', self::STATUS_ON_HOLD => 'bg-yellow-100 text-yellow-800', default => 'bg-gray-100 text-gray-800', }; } }