fileStorage = $fileStorage; } public function createDisease(array $data, $userId): array { try { DB::beginTransaction(); $image_url = null; if (isset($data['cover_page']) && $data['cover_page']) { $disease = Disease::create([ 'name' => $data['name'], 'deskripsi' => $data['deskripsi'], 'visibilitas' => $data['visibilitas'], 'schema' => $data['schema'], 'cover_page' => null, ]); $image_url = $this->fileStorage->storeCoverImage($data['cover_page'], $disease->id); $disease->update(['cover_page' => $image_url]); } else { $disease = Disease::create([ 'name' => $data['name'], 'deskripsi' => $data['deskripsi'], 'visibilitas' => $data['visibilitas'], 'schema' => $data['schema'], 'cover_page' => null, ]); } DB::commit(); LogActionService::logAction( $userId, 'create', 'Disease', $disease->id, $disease->toArray(), "Created disease: {$disease->name}", true ); return [true, 'Disease created successfully.', $disease->toArray()]; } catch (\Throwable $exception) { DB::rollBack(); LogActionService::logAction( $userId, 'create', 'Disease', null, [ 'name' => $data['name'] ?? null, 'error' => $exception->getMessage() ], "Failed to create disease: {$exception->getMessage()}", false ); return [false, 'Disease creation failed: ' . $exception->getMessage(), []]; } } public function editDisease($id, array $data, $userId): array { try { // var_dump($data); DB::beginTransaction(); $disease = Disease::find($id); if (!$disease) { DB::rollBack(); LogActionService::logAction( $userId, 'edit', 'Disease', $id, ['error' => 'Disease not found'], "Failed to edit disease: Disease not found", false ); return [false, 'Disease not found.', []]; } $oldData = $disease->toArray(); if (isset($data['cover_page']) && $data['cover_page']) { if ($disease->cover_page) { $this->fileStorage->deleteFile($disease->cover_page, true); } $data['cover_page'] = $this->fileStorage->storeCoverImage($data['cover_page'], $disease->id); } else { unset($data['cover_page']); } $changedFields = array_keys(array_diff_assoc($data, $oldData)); // Get only the values that are actually changing $relevantOldData = array_intersect_key($oldData, $data); $relevantNewData = array_intersect_key($data, $oldData); $disease->update($data); DB::commit(); LogActionService::logAction( $userId, 'edit', 'Disease', $disease->id, [ 'old' => $relevantOldData, 'new' => $relevantNewData, 'changed_fields' => $changedFields ], "Updated disease: {$disease->name}", true ); return [true, 'Disease updated successfully.', $disease]; } catch (\Throwable $exception) { DB::rollBack(); LogActionService::logAction( $userId, 'edit', 'Disease', $id, [ 'attempted_changes' => array_keys($data), 'error' => $exception->getMessage() ], "Failed to update disease: {$exception->getMessage()}", false ); return [false, 'Disease update failed: ' . $exception->getMessage(), []]; } } public function deleteDisease($id, $userId): array { try { DB::beginTransaction(); $disease = Disease::find($id); if (!$disease) { DB::rollBack(); LogActionService::logAction( $userId, 'delete', 'Disease', $id, ['error' => 'Disease not found'], "Failed to delete disease: Disease not found", false ); return [false, 'Disease not found.', []]; } $diseaseInfo = [ 'name' => $disease->name, 'had_cover_page' => !is_null($disease->cover_page), 'schema_columns_count' => count($disease->schema['columns'] ?? []) ]; if ($disease->cover_page) { $this->fileStorage->deleteFile($disease->cover_page, true); } $directoryPath = "diseases/records/$id"; $this->fileStorage->deleteDirectory($directoryPath, true); $disease->delete(); DB::commit(); LogActionService::logAction( $userId, 'delete', 'Disease', $id, $diseaseInfo, "Deleted disease: {$disease->name}", true ); return [true, 'Disease deleted successfully.', []]; } catch (\Throwable $exception) { DB::rollBack(); LogActionService::logAction( $userId, 'delete', 'Disease', $id, ['error' => $exception->getMessage()], "Failed to delete disease: {$exception->getMessage()}", false ); return [false, 'Disease deletion failed: ' . $exception->getMessage(), []]; } } public function getDisease(array $filters, $user): array { try { $query = Disease::query(); $query->withCount('diseaseRecords'); if ($user->role === 'operator') { $query->where('id', $user->disease_id); } elseif ($user->role === 'peneliti') { $query->where('visibilitas', 'publik'); } $this->applyDiseaseFilters($query, $filters); $paginatedData = $this->paginateResults($query, $filters, 'diseases'); return [true, 'Diseases retrieved successfully.', $paginatedData]; } catch (\Throwable $exception) { return [false, 'Failed to retrieve diseases: ' . $exception->getMessage(), []]; } } public function getDiseaseDetails($id): array { try { $user = auth()->user(); $disease = Disease::withCount('diseaseRecords')->findOrFail($id); if (!$disease) { return [false, 'Disease not found.', []]; } // Admins can access all diseases if ($user->role === 'admin') { return [true, 'Disease details retrieved successfully.', $disease]; } // Operators can only access their assigned diseases if ($user->role === 'operator') { $managesDisease = $user->managed_diseases['disease_id']; if ($managesDisease != $id) { return [false, 'Access denied. You are not assigned to this disease.', []]; } } // Researchers can only access public diseases if ($user->role === 'peneliti') { if ($user->approval_status !== 'approved') { return [false, 'Access denied. Your account is not approved.', []]; } if ($disease->visibilitas !== 'publik') { return [false, 'Access denied. This disease is private.', []]; } } return [true, 'Disease details retrieved successfully.', $disease]; } catch (\Throwable $exception) { return [false, 'Failed to retrieve disease details: ' . $exception->getMessage(), []]; } } public function getStatistics(array $filters): array { try { $query = Disease::query(); $query->withCount('diseaseRecords'); $this->applyDiseaseFilters($query, $filters); $diseases = $query->get(['id', 'name', 'disease_records_count']); $totalDiseaseRecords = $diseases->sum('disease_records_count'); $stats = [ 'total_disease_records' => $totalDiseaseRecords, 'diseases' => $diseases->map(function ($disease) { return [ 'id' => $disease->id, 'name' => $disease->name, 'disease_records_count' => $disease->disease_records_count, ]; }), ]; return [true, 'Disease statistics retrieved successfully.', $stats]; } catch (\Throwable $exception) { return [false, 'Failed to retrieve statistics: ' . $exception->getMessage(), []]; } } public function getSchemaField(int $diseaseId): array { try { [$success, $message, $disease] = $this->getDiseaseDetails($diseaseId); if (!$success) { return [[], $message]; } $schema = $disease->schema['columns'] ?? null; if (!$schema) { return [[], $message]; } $formattedSchema = []; foreach ($schema as $column) { $field = [ 'name' => $column['name'] ?? null, 'type' => $column['type'] ?? null, ]; if ($field['type'] === 'file') { $field['format'] = $column['format']; $field['multiple'] = isset($column['multiple']) ? (bool) $column['multiple'] : false; // Ensure multiple is set with default false } $field['is_visible'] = filter_var($column['is_visible'] ?? false, FILTER_VALIDATE_BOOLEAN); $formattedSchema[] = $field; } return [$formattedSchema, 'Schema Retrieved Successfully']; } catch (\Throwable $exception) { return []; } } private function applyDiseaseFilters(Builder $query, array $filters): void { if (!empty($filters['visibilitas'])) { $query->where('visibilitas', $filters['visibilitas']); } if (!empty($filters['name'])) { $query->where('name', 'ILIKE', '%' . trim($filters['name']) . '%'); } if (!empty($filters['column_type'])) { $query->whereJsonContains('schema->columns', ['type' => $filters['column_type']]); } if (!empty($filters['column_name'])) { $query->whereJsonContains('schema->columns', ['name' => $filters['column_name']]); } //Sorting $query->orderBy('updated_at', 'desc'); } private function paginateResults(Builder $query, array $filters, string $itemsKey): array { $perPage = isset($filters['per_page']) && is_numeric($filters['per_page']) && $filters['per_page'] > 0 && $filters['per_page'] <= self::MAX_PER_PAGE ? (int) $filters['per_page'] : self::DEFAULT_PER_PAGE; $page = isset($filters['page']) && is_numeric($filters['page']) && $filters['page'] > 0 ? (int) $filters['page'] : 1; $results = $query->paginate($perPage, ['*'], 'page', $page); if ($results->isEmpty() && $results->currentPage() > 1) { $results = $query->paginate($perPage, ['*'], 'page', $results->lastPage()); } return [ $itemsKey => $results->items(), 'pagination' => [ 'current_page' => $results->currentPage(), 'last_page' => $results->lastPage(), 'per_page' => (int) $results->perPage(), 'total' => $results->total(), 'from' => $results->firstItem(), 'to' => $results->lastItem(), 'has_more_pages' => $results->hasMorePages(), ] ]; } }