'encrypted', // enkripsi kandungan sensitif 'transcript_confidence' => 'float', 'processed_at' => 'datetime', 'file_size' => 'integer', 'duration_seconds' => 'integer', ]; } protected static function booted(): void { static::creating(function (self $project) { if (empty($project->uuid)) { $project->uuid = (string) Str::uuid(); } }); } // Route model binding gunakan uuid public function getRouteKeyName(): string { return 'uuid'; } // ------------------------------------------------------- // Relationships // ------------------------------------------------------- public function owner(): BelongsTo { return $this->belongsTo(User::class, 'owner_user_id'); } public function collaborators(): BelongsToMany { return $this->belongsToMany(User::class, 'project_collaborators', 'project_id', 'user_id') ->withPivot('role', 'added_by') ->withTimestamps(); } public function projectCollaborators(): HasMany { return $this->hasMany(ProjectCollaborator::class, 'project_id'); } public function transcriptVersions(): HasMany { return $this->hasMany(TranscriptVersion::class, 'project_id')->orderByDesc('version_number'); } public function comments(): HasMany { return $this->hasMany(ProjectComment::class, 'project_id')->latest(); } // ------------------------------------------------------- // Helpers // ------------------------------------------------------- public function isOwnedBy(User $user): bool { return $this->owner_user_id === $user->id; } public function hasCollaborator(User $user): bool { return $this->collaborators()->where('user_id', $user->id)->exists(); } public function isAccessibleBy(User $user): bool { return $this->isOwnedBy($user) || $this->hasCollaborator($user); } public function isPending(): bool { return $this->transcription_status === 'pending'; } public function isProcessing(): bool { return $this->transcription_status === 'processing'; } public function isCompleted(): bool { return $this->transcription_status === 'completed'; } public function isFailed(): bool { return $this->transcription_status === 'failed'; } public function fileSizeForHumans(): string { $bytes = $this->file_size; if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } if ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } return number_format($bytes / 1024, 2) . ' KB'; } public function durationForHumans(): ?string { if (! $this->duration_seconds) { return null; } $m = intdiv($this->duration_seconds, 60); $s = $this->duration_seconds % 60; return sprintf('%d:%02d', $m, $s); } }