diff --git a/app/Http/Controllers/Settings/ProjectSettingController.php b/app/Http/Controllers/Settings/ProjectSettingController.php index 57cd94c..0f23598 100644 --- a/app/Http/Controllers/Settings/ProjectSettingController.php +++ b/app/Http/Controllers/Settings/ProjectSettingController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Settings; use App\Http\Controllers\Controller; +use App\Models\Settings\Department; use App\Models\Settings\Location; use App\Models\Settings\ProjectSetting; use Illuminate\Http\Request; @@ -11,15 +12,17 @@ class ProjectSettingController extends Controller { public function index() { - $projects = ProjectSetting::with(['locations' => function ($q) { - $q->orderBy('name'); - }])->orderBy('name')->get(); + $projects = ProjectSetting::with([ + 'locations' => fn ($q) => $q->orderBy('name'), + 'departments' => fn ($q) => $q->orderBy('name'), + ])->orderBy('name')->get(); $stats = [ - 'total_projects' => $projects->count(), - 'active_projects' => $projects->where('is_active', true)->count(), - 'total_locations' => $projects->sum(fn ($p) => $p->locations->count()), - 'active_locations' => $projects->sum(fn ($p) => $p->locations->where('is_active', true)->count()), + 'total_projects' => $projects->count(), + 'active_projects' => $projects->where('is_active', true)->count(), + 'total_locations' => $projects->sum(fn ($p) => $p->locations->count()), + 'active_locations' => $projects->sum(fn ($p) => $p->locations->where('is_active', true)->count()), + 'total_departments' => $projects->sum(fn ($p) => $p->departments->count()), ]; return view('settings.projects.index', compact('projects', 'stats')); @@ -113,4 +116,24 @@ class ProjectSettingController extends Controller $location->delete(); return response()->json(['ok' => true]); } + + public function storeDepartment(Request $request, ProjectSetting $project) + { + $validated = $request->validate(['name' => 'required|string|max:255']); + $dept = $project->departments()->create(['name' => $validated['name'], 'is_active' => true]); + return response()->json(['department' => ['id' => $dept->id, 'name' => $dept->name, 'is_active' => $dept->is_active]]); + } + + public function updateDepartment(Request $request, ProjectSetting $project, Department $department) + { + $validated = $request->validate(['name' => 'required|string|max:255']); + $department->update(['name' => $validated['name'], 'is_active' => $request->boolean('is_active', true)]); + return response()->json(['department' => ['id' => $department->id, 'name' => $department->name, 'is_active' => $department->is_active]]); + } + + public function destroyDepartment(ProjectSetting $project, Department $department) + { + $department->delete(); + return response()->json(['ok' => true]); + } } diff --git a/app/Models/Settings/Department.php b/app/Models/Settings/Department.php new file mode 100644 index 0000000..f13bf48 --- /dev/null +++ b/app/Models/Settings/Department.php @@ -0,0 +1,19 @@ + 'boolean']; + + public function project(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(ProjectSetting::class, 'project_id'); + } +} diff --git a/app/Models/Settings/ProjectSetting.php b/app/Models/Settings/ProjectSetting.php index 070bc70..86ac28e 100644 --- a/app/Models/Settings/ProjectSetting.php +++ b/app/Models/Settings/ProjectSetting.php @@ -21,4 +21,9 @@ class ProjectSetting extends Model { return $this->hasMany(\App\Models\Settings\Location::class, 'project_id'); } + + public function departments(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(\App\Models\Settings\Department::class, 'project_id'); + } } diff --git a/database/migrations/2026_05_26_143758_create_settings_departments_table.php b/database/migrations/2026_05_26_143758_create_settings_departments_table.php new file mode 100644 index 0000000..816c88f --- /dev/null +++ b/database/migrations/2026_05_26_143758_create_settings_departments_table.php @@ -0,0 +1,30 @@ +id(); + $table->foreignId('project_id')->constrained('settings_projects')->cascadeOnDelete(); + $table->string('name', 255); + $table->boolean('is_active')->default(true); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('settings_departments'); + } +}; diff --git a/resources/views/settings/projects/index.blade.php b/resources/views/settings/projects/index.blade.php index af8716b..fa5b708 100644 --- a/resources/views/settings/projects/index.blade.php +++ b/resources/views/settings/projects/index.blade.php @@ -8,7 +8,15 @@ .proj-card { border:1px solid #e2e8f0; border-radius:0.875rem; overflow:hidden; margin-bottom:0.75rem; background:white; } .proj-header { display:flex; align-items:center; justify-content:space-between; padding:0.875rem 1.25rem; background:white; } .proj-body { display:block; border-top:1px solid #e2e8f0; } - .loc-row { display:flex; align-items:flex-start; justify-content:space-between; padding:0.75rem 1.25rem 0.75rem 2.5rem; border-bottom:1px solid #f1f5f9; } + .proj-body-inner { display:grid; grid-template-columns:1fr 1fr; } + .proj-section { border-right:1px solid #e2e8f0; } + .proj-section:last-child { border-right:none; } + .proj-section-header { display:flex; align-items:center; justify-content:space-between; padding:0.6rem 1.25rem; background:#f8fafc; border-bottom:1px solid #e2e8f0; } + .proj-section-title { font-size:11px; font-weight:700; color:#64748b; text-transform:uppercase; letter-spacing:.05em; } + .loc-row { display:flex; align-items:flex-start; justify-content:space-between; padding:0.65rem 1rem 0.65rem 1.25rem; border-bottom:1px solid #f1f5f9; } + .dept-row { display:flex; align-items:center; justify-content:space-between; padding:0.6rem 1rem 0.6rem 1.25rem; border-bottom:1px solid #f1f5f9; } + .dept-edit-row { display:none; padding:0.5rem 1rem; background:#f0f9ff; border-bottom:1px solid #bae6fd; gap:6px; align-items:center; } + .dept-edit-row.open { display:flex; } .proj-edit-wrap { display:none; padding:0.75rem 1.25rem; background:#f0f9ff; border-bottom:1px solid #bae6fd; align-items:center; gap:8px; } .proj-edit-wrap.open { display:flex; } .badge-inactive { display:inline-block; padding:1px 7px; border-radius:9px; font-size:11px; font-weight:600; background:#fee2e2; color:#dc2626; margin-left:6px; } @@ -25,13 +33,13 @@ {{-- Page header --}}
Manage projects and their physical sub-locations with addresses and GPS coordinates.
+Manage projects with their sub-locations and departments.