command?->warn("No assessment packs directory at {$dir}"); return; } $files = collect(File::files($dir)) ->filter(fn ($file) => str_ends_with(strtolower($file->getFilename()), '.json')) ->sortBy(fn ($file) => $file->getFilename()) ->values(); if ($files->isEmpty()) { $this->command?->warn('No assessment JSON packs found.'); return; } foreach ($files as $file) { $this->seedPack($file->getPathname()); } } public function seedPack(string $path): AssessmentTemplate { $raw = File::get($path); $data = json_decode($raw, true); if (! is_array($data) || ! isset($data['template'], $data['questions'])) { throw new RuntimeException("Invalid assessment pack: {$path}"); } $t = $data['template']; foreach (['code', 'name', 'category', 'version'] as $required) { if (! array_key_exists($required, $t)) { throw new RuntimeException("Assessment pack missing template.{$required}: {$path}"); } } return DB::transaction(function () use ($t, $data, $path) { $template = AssessmentTemplate::withTrashed() ->whereNull('organization_id') ->where('code', $t['code']) ->where('version', (int) $t['version']) ->first(); $attributes = [ 'organization_id' => null, 'code' => $t['code'], 'name' => $t['name'], 'category' => $t['category'], 'description' => $t['description'] ?? null, 'version' => (int) $t['version'], 'is_current' => (bool) ($t['is_current'] ?? true), 'is_active' => (bool) ($t['is_active'] ?? true), 'scoring_strategy' => $t['scoring_strategy'] ?? null, 'meta' => $t['meta'] ?? null, 'deleted_at' => null, ]; if ($template) { if ($template->trashed()) { $template->restore(); } $template->fill(collect($attributes)->except('deleted_at')->all())->save(); } else { $template = AssessmentTemplate::create(collect($attributes)->except('deleted_at')->all()); } if ($template->is_current) { AssessmentTemplate::query() ->whereNull('organization_id') ->where('code', $template->code) ->where('id', '!=', $template->id) ->where('is_current', true) ->update(['is_current' => false]); } // Replace questions for this template version (stable codes within version). AssessmentQuestion::query()->where('template_id', $template->id)->delete(); $sort = 0; foreach ($data['questions'] as $q) { if (empty($q['code']) || empty($q['label']) || empty($q['answer_type'])) { throw new RuntimeException("Invalid question in pack {$path}"); } $sort++; AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => $q['code'], 'section' => $q['section'] ?? null, 'label' => $q['label'], 'help_text' => $q['help_text'] ?? null, 'answer_type' => $q['answer_type'], 'options' => $q['options'] ?? null, 'is_required' => (bool) ($q['is_required'] ?? false), 'sort_order' => (int) ($q['sort_order'] ?? $sort), 'score_key' => $q['score_key'] ?? null, 'validation' => $q['validation'] ?? null, ]); } $this->command?->info(sprintf( 'Seeded assessment pack %s v%d (%d questions)', $template->code, $template->version, count($data['questions']), )); return $template->fresh('questions'); }); } }