feat: move departments from projects to companies
Departments now belong to a Company, not a Project.
- New migration: recreates settings_departments with company_id (migrates existing data)
- Department model: company_id FK, company() relation
- Company model: departments() hasMany relation
- ProjectSetting model: removes departments() relation
- Controller: dept methods now take Company instead of ProjectSetting
- Routes: department CRUD moves to companies/{company}/departments
- View: departments section appears on each company card (purple theme);
project body is now locations-only (single column)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9a268dc79b
commit
12e07480a0
@ -14,8 +14,8 @@ class ProjectSettingController extends Controller
|
|||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$companies = Company::with([
|
$companies = Company::with([
|
||||||
'projects.locations' => fn ($q) => $q->orderBy('name'),
|
'projects.locations' => fn ($q) => $q->orderBy('name'),
|
||||||
'projects.departments' => fn ($q) => $q->orderBy('name'),
|
'departments' => fn ($q) => $q->orderBy('name'),
|
||||||
])->orderBy('name')->get();
|
])->orderBy('name')->get();
|
||||||
|
|
||||||
$allProjects = $companies->flatMap(fn ($c) => $c->projects);
|
$allProjects = $companies->flatMap(fn ($c) => $c->projects);
|
||||||
@ -25,7 +25,7 @@ class ProjectSettingController extends Controller
|
|||||||
'total_projects' => $allProjects->count(),
|
'total_projects' => $allProjects->count(),
|
||||||
'active_projects' => $allProjects->where('is_active', true)->count(),
|
'active_projects' => $allProjects->where('is_active', true)->count(),
|
||||||
'total_locations' => $allProjects->sum(fn ($p) => $p->locations->count()),
|
'total_locations' => $allProjects->sum(fn ($p) => $p->locations->count()),
|
||||||
'total_departments' => $allProjects->sum(fn ($p) => $p->departments->count()),
|
'total_departments' => $companies->sum(fn ($c) => $c->departments->count()),
|
||||||
];
|
];
|
||||||
|
|
||||||
return view('settings.projects.index', compact('companies', 'stats'));
|
return view('settings.projects.index', compact('companies', 'stats'));
|
||||||
@ -148,21 +148,21 @@ class ProjectSettingController extends Controller
|
|||||||
return response()->json(['ok' => true]);
|
return response()->json(['ok' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function storeDepartment(Request $request, ProjectSetting $project)
|
public function storeDepartment(Request $request, Company $company)
|
||||||
{
|
{
|
||||||
$validated = $request->validate(['name' => 'required|string|max:255']);
|
$validated = $request->validate(['name' => 'required|string|max:255']);
|
||||||
$dept = $project->departments()->create(['name' => $validated['name'], 'is_active' => true]);
|
$dept = $company->departments()->create(['name' => $validated['name'], 'is_active' => true]);
|
||||||
return response()->json(['department' => ['id' => $dept->id, 'name' => $dept->name, 'is_active' => $dept->is_active]]);
|
return response()->json(['department' => ['id' => $dept->id, 'name' => $dept->name, 'is_active' => $dept->is_active]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateDepartment(Request $request, ProjectSetting $project, Department $department)
|
public function updateDepartment(Request $request, Company $company, Department $department)
|
||||||
{
|
{
|
||||||
$validated = $request->validate(['name' => 'required|string|max:255']);
|
$validated = $request->validate(['name' => 'required|string|max:255']);
|
||||||
$department->update(['name' => $validated['name'], 'is_active' => $request->boolean('is_active', true)]);
|
$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]]);
|
return response()->json(['department' => ['id' => $department->id, 'name' => $department->name, 'is_active' => $department->is_active]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroyDepartment(ProjectSetting $project, Department $department)
|
public function destroyDepartment(Company $company, Department $department)
|
||||||
{
|
{
|
||||||
$department->delete();
|
$department->delete();
|
||||||
return response()->json(['ok' => true]);
|
return response()->json(['ok' => true]);
|
||||||
|
|||||||
@ -16,4 +16,9 @@ class Company extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ProjectSetting::class, 'company_id');
|
return $this->hasMany(ProjectSetting::class, 'company_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function departments(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Department::class, 'company_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,12 @@ class Department extends Model
|
|||||||
{
|
{
|
||||||
protected $table = 'settings_departments';
|
protected $table = 'settings_departments';
|
||||||
|
|
||||||
protected $fillable = ['name', 'project_id', 'is_active'];
|
protected $fillable = ['name', 'company_id', 'is_active'];
|
||||||
|
|
||||||
protected $casts = ['is_active' => 'boolean'];
|
protected $casts = ['is_active' => 'boolean'];
|
||||||
|
|
||||||
public function project(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(ProjectSetting::class, 'project_id');
|
return $this->belongsTo(Company::class, 'company_id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,8 +27,4 @@ class ProjectSetting extends Model
|
|||||||
return $this->hasMany(\App\Models\Settings\Location::class, 'project_id');
|
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Recreate table with company_id instead of project_id (SQLite-safe)
|
||||||
|
Schema::create('settings_departments_new', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('company_id')->constrained('settings_companies')->onDelete('cascade');
|
||||||
|
$table->string('name');
|
||||||
|
$table->boolean('is_active')->default(true);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Migrate: map each department's project_id → that project's company_id
|
||||||
|
DB::statement('
|
||||||
|
INSERT INTO settings_departments_new (id, company_id, name, is_active, created_at, updated_at)
|
||||||
|
SELECT d.id, p.company_id, d.name, d.is_active, d.created_at, d.updated_at
|
||||||
|
FROM settings_departments d
|
||||||
|
LEFT JOIN settings_projects p ON p.id = d.project_id
|
||||||
|
WHERE p.company_id IS NOT NULL
|
||||||
|
');
|
||||||
|
|
||||||
|
Schema::drop('settings_departments');
|
||||||
|
Schema::rename('settings_departments_new', 'settings_departments');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::create('settings_departments_old', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('project_id')->constrained('settings_projects')->onDelete('cascade');
|
||||||
|
$table->string('name');
|
||||||
|
$table->boolean('is_active')->default(true);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::drop('settings_departments');
|
||||||
|
Schema::rename('settings_departments_old', 'settings_departments');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -117,6 +117,14 @@
|
|||||||
$allLocsData = [];
|
$allLocsData = [];
|
||||||
$allDeptsData = [];
|
$allDeptsData = [];
|
||||||
foreach ($companies as $company) {
|
foreach ($companies as $company) {
|
||||||
|
foreach ($company->departments as $dept) {
|
||||||
|
$allDeptsData[$dept->id] = [
|
||||||
|
'id' => $dept->id,
|
||||||
|
'name' => $dept->name,
|
||||||
|
'is_active' => $dept->is_active,
|
||||||
|
'company_id' => $dept->company_id,
|
||||||
|
];
|
||||||
|
}
|
||||||
foreach ($company->projects as $proj) {
|
foreach ($company->projects as $proj) {
|
||||||
foreach ($proj->locations as $loc) {
|
foreach ($proj->locations as $loc) {
|
||||||
$allLocsData[$loc->id] = [
|
$allLocsData[$loc->id] = [
|
||||||
@ -128,13 +136,6 @@ foreach ($companies as $company) {
|
|||||||
'is_active' => $loc->is_active,
|
'is_active' => $loc->is_active,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
foreach ($proj->departments as $dept) {
|
|
||||||
$allDeptsData[$dept->id] = [
|
|
||||||
'id' => $dept->id,
|
|
||||||
'name' => $dept->name,
|
|
||||||
'is_active' => $dept->is_active,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$allLocsJson = json_encode($allLocsData);
|
$allLocsJson = json_encode($allLocsData);
|
||||||
@ -154,10 +155,13 @@ $allDeptsJson = json_encode($allDeptsData);
|
|||||||
<span id="company-name-{{ $company->id }}" style="font-size:15px;font-weight:700;color:#3730a3;">{{ $company->name }}</span>
|
<span id="company-name-{{ $company->id }}" style="font-size:15px;font-weight:700;color:#3730a3;">{{ $company->name }}</span>
|
||||||
<span id="company-inactive-{{ $company->id }}" class="badge-inactive" style="{{ $company->is_active ? 'display:none' : '' }}">Inactive</span>
|
<span id="company-inactive-{{ $company->id }}" class="badge-inactive" style="{{ $company->is_active ? 'display:none' : '' }}">Inactive</span>
|
||||||
<span id="company-proj-count-{{ $company->id }}" style="font-size:12px;color:#6366f1;opacity:.7;">{{ $company->projects->count() }} {{ Str::plural('project', $company->projects->count()) }}</span>
|
<span id="company-proj-count-{{ $company->id }}" style="font-size:12px;color:#6366f1;opacity:.7;">{{ $company->projects->count() }} {{ Str::plural('project', $company->projects->count()) }}</span>
|
||||||
|
<span id="company-dept-count-{{ $company->id }}" style="font-size:12px;color:#7c3aed;opacity:.7;">· {{ $company->departments->count() }} {{ Str::plural('dept', $company->departments->count()) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:6px;align-items:center;">
|
<div style="display:flex;gap:6px;align-items:center;">
|
||||||
|
<button type="button" onclick="openAddCoDept({{ $company->id }})"
|
||||||
|
class="btn-secondary btn-sm" style="border-color:#a78bfa;color:#6d28d9;">+ Dept</button>
|
||||||
<button type="button" onclick="openAddProject({{ $company->id }})"
|
<button type="button" onclick="openAddProject({{ $company->id }})"
|
||||||
class="btn-primary" style="padding:4px 12px;font-size:12px;">+ Add Project</button>
|
class="btn-primary" style="padding:4px 12px;font-size:12px;">+ Project</button>
|
||||||
<button type="button" onclick="openEditCompany({{ $company->id }}, '{{ addslashes($company->name) }}', {{ $company->is_active ? 'true' : 'false' }})"
|
<button type="button" onclick="openEditCompany({{ $company->id }}, '{{ addslashes($company->name) }}', {{ $company->is_active ? 'true' : 'false' }})"
|
||||||
class="btn-secondary btn-sm">Edit</button>
|
class="btn-secondary btn-sm">Edit</button>
|
||||||
<button type="button" onclick="deleteCompany({{ $company->id }}, '{{ addslashes($company->name) }}')"
|
<button type="button" onclick="deleteCompany({{ $company->id }}, '{{ addslashes($company->name) }}')"
|
||||||
@ -186,6 +190,49 @@ $allDeptsJson = json_encode($allDeptsData);
|
|||||||
<p id="add-proj-error-{{ $company->id }}" class="field-error" style="margin:0;"></p>
|
<p id="add-proj-error-{{ $company->id }}" class="field-error" style="margin:0;"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Departments section --}}
|
||||||
|
<div style="border:1px solid #c7d2fe;border-top:none;">
|
||||||
|
<div style="display:flex;align-items:center;justify-content:space-between;padding:0.5rem 1.25rem;background:#f5f3ff;border-bottom:1px solid #ede9fe;">
|
||||||
|
<span style="font-size:11px;font-weight:700;color:#6d28d9;text-transform:uppercase;letter-spacing:.05em;">Departments</span>
|
||||||
|
</div>
|
||||||
|
{{-- Add dept inline row --}}
|
||||||
|
<div class="dept-edit-row" id="co-dept-add-row-{{ $company->id }}">
|
||||||
|
<input id="co-dept-add-name-{{ $company->id }}" type="text" class="form-input" style="flex:1;font-size:12px;" placeholder="Department name…"
|
||||||
|
onkeydown="if(event.key==='Enter') saveCoDeptAdd({{ $company->id }}); if(event.key==='Escape') closeAddCoDept({{ $company->id }})">
|
||||||
|
<button type="button" onclick="saveCoDeptAdd({{ $company->id }})" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>
|
||||||
|
<button type="button" onclick="closeAddCoDept({{ $company->id }})" class="btn-secondary btn-sm">✕</button>
|
||||||
|
</div>
|
||||||
|
<div id="co-dept-list-{{ $company->id }}">
|
||||||
|
@forelse($company->departments as $dept)
|
||||||
|
<div id="co-dept-wrap-{{ $dept->id }}">
|
||||||
|
<div class="dept-row" id="co-dept-row-{{ $dept->id }}">
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;flex:1;min-width:0;">
|
||||||
|
<svg width="13" height="13" fill="none" stroke="{{ $dept->is_active ? '#7c3aed' : '#9ca3af' }}" viewBox="0 0 24 24" id="co-dept-icon-{{ $dept->id }}" style="flex-shrink:0;"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
||||||
|
<span id="co-dept-name-{{ $dept->id }}" style="font-size:13px;font-weight:600;color:#1e293b;">{{ $dept->name }}</span>
|
||||||
|
<span id="co-dept-inactive-{{ $dept->id }}" class="badge-inactive" style="{{ $dept->is_active ? 'display:none' : '' }}">Inactive</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:4px;flex-shrink:0;">
|
||||||
|
<button type="button" onclick="openEditCoDept({{ $dept->id }}, {{ $company->id }}, '{{ addslashes($dept->name) }}', {{ $dept->is_active ? 'true' : 'false' }})" class="btn-secondary btn-sm" style="padding:3px 8px;font-size:12px;">Edit</button>
|
||||||
|
<button type="button" onclick="deleteCoDept({{ $company->id }}, {{ $dept->id }}, '{{ addslashes($dept->name) }}')" class="btn-danger btn-sm" style="padding:3px 8px;font-size:12px;">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dept-edit-row" id="co-dept-edit-row-{{ $dept->id }}">
|
||||||
|
<input id="co-dept-edit-name-{{ $dept->id }}" type="text" class="form-input" style="flex:1;font-size:12px;"
|
||||||
|
onkeydown="if(event.key==='Enter') saveEditCoDept({{ $dept->id }}, {{ $company->id }}); if(event.key==='Escape') closeEditCoDept({{ $dept->id }})">
|
||||||
|
<label style="display:flex;align-items:center;gap:4px;font-size:12px;color:#374151;white-space:nowrap;cursor:pointer;">
|
||||||
|
<input type="checkbox" id="co-dept-edit-active-{{ $dept->id }}" {{ $dept->is_active ? 'checked' : '' }} style="width:13px;height:13px;">
|
||||||
|
Active
|
||||||
|
</label>
|
||||||
|
<button type="button" onclick="saveEditCoDept({{ $dept->id }}, {{ $company->id }})" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>
|
||||||
|
<button type="button" onclick="closeEditCoDept({{ $dept->id }})" class="btn-secondary btn-sm">✕</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div id="co-dept-empty-{{ $company->id }}" style="padding:10px 1.25rem;color:#9ca3af;font-size:13px;">No departments yet.</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{-- Projects container --}}
|
{{-- Projects container --}}
|
||||||
<div id="proj-list-{{ $company->id }}" style="border:1px solid #c7d2fe;border-top:none;border-radius:0 0 0.875rem 0.875rem;overflow:hidden;">
|
<div id="proj-list-{{ $company->id }}" style="border:1px solid #c7d2fe;border-top:none;border-radius:0 0 0.875rem 0.875rem;overflow:hidden;">
|
||||||
|
|
||||||
@ -227,11 +274,9 @@ $allDeptsJson = json_encode($allDeptsData);
|
|||||||
<p id="edit-proj-error-{{ $project->id }}" class="field-error" style="margin:0;"></p>
|
<p id="edit-proj-error-{{ $project->id }}" class="field-error" style="margin:0;"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Project body: Locations + Departments --}}
|
{{-- Project body: Locations --}}
|
||||||
<div class="proj-body" id="proj-body-{{ $project->id }}">
|
<div class="proj-body" id="proj-body-{{ $project->id }}">
|
||||||
<div class="proj-body-inner">
|
<div class="proj-section" style="border-right:none;">
|
||||||
{{-- Locations --}}
|
|
||||||
<div class="proj-section">
|
|
||||||
<div class="proj-section-header">
|
<div class="proj-section-header">
|
||||||
<span class="proj-section-title">Locations</span>
|
<span class="proj-section-title">Locations</span>
|
||||||
<button type="button" onclick="openLocModal(null, {{ $project->id }})" class="btn-primary" style="padding:3px 10px;font-size:11px;">+ Add</button>
|
<button type="button" onclick="openLocModal(null, {{ $project->id }})" class="btn-primary" style="padding:3px 10px;font-size:11px;">+ Add</button>
|
||||||
@ -264,49 +309,6 @@ $allDeptsJson = json_encode($allDeptsData);
|
|||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{-- Departments --}}
|
|
||||||
<div class="proj-section">
|
|
||||||
<div class="proj-section-header">
|
|
||||||
<span class="proj-section-title">Departments</span>
|
|
||||||
<button type="button" onclick="openAddDept({{ $project->id }})" class="btn-primary" style="padding:3px 10px;font-size:11px;">+ Add</button>
|
|
||||||
</div>
|
|
||||||
<div class="dept-edit-row" id="dept-add-row-{{ $project->id }}">
|
|
||||||
<input id="dept-add-name-{{ $project->id }}" type="text" class="form-input" style="flex:1;font-size:12px;" placeholder="Department name…"
|
|
||||||
onkeydown="if(event.key==='Enter') saveDeptAdd({{ $project->id }}); if(event.key==='Escape') closeAddDept({{ $project->id }})">
|
|
||||||
<button type="button" onclick="saveDeptAdd({{ $project->id }})" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>
|
|
||||||
<button type="button" onclick="closeAddDept({{ $project->id }})" class="btn-secondary btn-sm">✕</button>
|
|
||||||
</div>
|
|
||||||
<div id="dept-list-{{ $project->id }}">
|
|
||||||
@forelse($project->departments as $dept)
|
|
||||||
<div id="dept-wrap-{{ $dept->id }}">
|
|
||||||
<div class="dept-row" id="dept-row-{{ $dept->id }}">
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;flex:1;min-width:0;">
|
|
||||||
<svg width="13" height="13" fill="none" stroke="{{ $dept->is_active ? '#06b6d4' : '#9ca3af' }}" viewBox="0 0 24 24" id="dept-icon-{{ $dept->id }}" style="flex-shrink:0;"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
|
||||||
<span id="dept-name-{{ $dept->id }}" style="font-size:13px;font-weight:600;color:#1e293b;">{{ $dept->name }}</span>
|
|
||||||
<span id="dept-inactive-{{ $dept->id }}" class="badge-inactive" style="{{ $dept->is_active ? 'display:none' : '' }}">Inactive</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:4px;flex-shrink:0;">
|
|
||||||
<button type="button" onclick="openEditDept({{ $dept->id }}, {{ $project->id }}, '{{ addslashes($dept->name) }}', {{ $dept->is_active ? 'true' : 'false' }})" class="btn-secondary btn-sm" style="padding:3px 8px;font-size:12px;">Edit</button>
|
|
||||||
<button type="button" onclick="deleteDept({{ $project->id }}, {{ $dept->id }}, '{{ addslashes($dept->name) }}')" class="btn-danger btn-sm" style="padding:3px 8px;font-size:12px;">Delete</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="dept-edit-row" id="dept-edit-row-{{ $dept->id }}">
|
|
||||||
<input id="dept-edit-name-{{ $dept->id }}" type="text" class="form-input" style="flex:1;font-size:12px;"
|
|
||||||
onkeydown="if(event.key==='Enter') saveDeptEdit({{ $dept->id }}, {{ $project->id }}); if(event.key==='Escape') closeEditDept({{ $dept->id }})">
|
|
||||||
<label style="display:flex;align-items:center;gap:4px;font-size:12px;color:#374151;white-space:nowrap;cursor:pointer;">
|
|
||||||
<input type="checkbox" id="dept-edit-active-{{ $dept->id }}" {{ $dept->is_active ? 'checked' : '' }} style="width:13px;height:13px;">
|
|
||||||
Active
|
|
||||||
</label>
|
|
||||||
<button type="button" onclick="saveDeptEdit({{ $dept->id }}, {{ $project->id }})" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>
|
|
||||||
<button type="button" onclick="closeEditDept({{ $dept->id }})" class="btn-secondary btn-sm">✕</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@empty
|
|
||||||
<div id="dept-empty-{{ $project->id }}" style="padding:12px 1.25rem;color:#9ca3af;font-size:13px;">No departments yet.</div>
|
|
||||||
@endforelse
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>{{-- end proj-body-inner --}}
|
|
||||||
</div>{{-- end proj-body --}}
|
</div>{{-- end proj-body --}}
|
||||||
</div>{{-- end proj-card --}}
|
</div>{{-- end proj-card --}}
|
||||||
@empty
|
@empty
|
||||||
@ -832,9 +834,11 @@ function buildCompanyCard(c) {
|
|||||||
+ '<span id="company-name-' + c.id + '" style="font-size:15px;font-weight:700;color:#3730a3;">' + cName + '</span>'
|
+ '<span id="company-name-' + c.id + '" style="font-size:15px;font-weight:700;color:#3730a3;">' + cName + '</span>'
|
||||||
+ '<span id="company-inactive-' + c.id + '" class="badge-inactive" style="display:none;">Inactive</span>'
|
+ '<span id="company-inactive-' + c.id + '" class="badge-inactive" style="display:none;">Inactive</span>'
|
||||||
+ '<span id="company-proj-count-' + c.id + '" style="font-size:12px;color:#6366f1;opacity:.7;">0 projects</span>'
|
+ '<span id="company-proj-count-' + c.id + '" style="font-size:12px;color:#6366f1;opacity:.7;">0 projects</span>'
|
||||||
|
+ '<span id="company-dept-count-' + c.id + '" style="font-size:12px;color:#7c3aed;opacity:.7;">· 0 depts</span>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div style="display:flex;gap:6px;align-items:center;">'
|
+ '<div style="display:flex;gap:6px;align-items:center;">'
|
||||||
+ '<button type="button" onclick="openAddProject(' + c.id + ')" class="btn-primary" style="padding:4px 12px;font-size:12px;">+ Add Project</button>'
|
+ '<button type="button" onclick="openAddCoDept(' + c.id + ')" class="btn-secondary btn-sm" style="border-color:#a78bfa;color:#6d28d9;">+ Dept</button>'
|
||||||
|
+ '<button type="button" onclick="openAddProject(' + c.id + ')" class="btn-primary" style="padding:4px 12px;font-size:12px;">+ Project</button>'
|
||||||
+ '<button type="button" onclick="openEditCompany(' + c.id + ', \'' + rawName + '\', true)" class="btn-secondary btn-sm">Edit</button>'
|
+ '<button type="button" onclick="openEditCompany(' + c.id + ', \'' + rawName + '\', true)" class="btn-secondary btn-sm">Edit</button>'
|
||||||
+ '<button type="button" onclick="deleteCompany(' + c.id + ', \'' + rawName + '\')" class="btn-danger btn-sm">Delete</button>'
|
+ '<button type="button" onclick="deleteCompany(' + c.id + ', \'' + rawName + '\')" class="btn-danger btn-sm">Delete</button>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
@ -846,6 +850,17 @@ function buildCompanyCard(c) {
|
|||||||
+ '<button type="button" onclick="closeEditCompany(' + c.id + ')" class="btn-secondary btn-sm">Cancel</button>'
|
+ '<button type="button" onclick="closeEditCompany(' + c.id + ')" class="btn-secondary btn-sm">Cancel</button>'
|
||||||
+ '<p id="edit-company-error-' + c.id + '" class="field-error" style="margin:0;"></p>'
|
+ '<p id="edit-company-error-' + c.id + '" class="field-error" style="margin:0;"></p>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
|
+ '<div style="border:1px solid #c7d2fe;border-top:none;">'
|
||||||
|
+ '<div style="display:flex;align-items:center;justify-content:space-between;padding:0.5rem 1.25rem;background:#f5f3ff;border-bottom:1px solid #ede9fe;">'
|
||||||
|
+ '<span style="font-size:11px;font-weight:700;color:#6d28d9;text-transform:uppercase;letter-spacing:.05em;">Departments</span>'
|
||||||
|
+ '</div>'
|
||||||
|
+ '<div class="dept-edit-row" id="co-dept-add-row-' + c.id + '">'
|
||||||
|
+ '<input id="co-dept-add-name-' + c.id + '" type="text" class="form-input" style="flex:1;font-size:12px;" placeholder="Department name…" onkeydown="if(event.key===\'Enter\') saveCoDeptAdd(' + c.id + '); if(event.key===\'Escape\') closeAddCoDept(' + c.id + ')">'
|
||||||
|
+ '<button type="button" onclick="saveCoDeptAdd(' + c.id + ')" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>'
|
||||||
|
+ '<button type="button" onclick="closeAddCoDept(' + c.id + ')" class="btn-secondary btn-sm">✕</button>'
|
||||||
|
+ '</div>'
|
||||||
|
+ '<div id="co-dept-list-' + c.id + '"><div id="co-dept-empty-' + c.id + '" style="padding:10px 1.25rem;color:#9ca3af;font-size:13px;">No departments yet.</div></div>'
|
||||||
|
+ '</div>'
|
||||||
+ '<div class="proj-edit-wrap" id="add-proj-strip-' + c.id + '" style="border-radius:0;border-left:1px solid #c7d2fe;border-right:1px solid #c7d2fe;background:#f0fdf4;border-color:#bbf7d0;">'
|
+ '<div class="proj-edit-wrap" id="add-proj-strip-' + c.id + '" style="border-radius:0;border-left:1px solid #c7d2fe;border-right:1px solid #c7d2fe;background:#f0fdf4;border-color:#bbf7d0;">'
|
||||||
+ '<input id="add-proj-name-' + c.id + '" type="text" class="form-input" style="flex:1;font-size:13px;" placeholder="New project name…" onkeydown="if(event.key===\'Enter\') saveAddProject(' + c.id + '); if(event.key===\'Escape\') closeAddProject(' + c.id + ')">'
|
+ '<input id="add-proj-name-' + c.id + '" type="text" class="form-input" style="flex:1;font-size:13px;" placeholder="New project name…" onkeydown="if(event.key===\'Enter\') saveAddProject(' + c.id + '); if(event.key===\'Escape\') closeAddProject(' + c.id + ')">'
|
||||||
+ '<button type="button" onclick="saveAddProject(' + c.id + ')" class="btn-primary" style="padding:5px 14px;font-size:12px;white-space:nowrap;">Save</button>'
|
+ '<button type="button" onclick="saveAddProject(' + c.id + ')" class="btn-primary" style="padding:5px 14px;font-size:12px;white-space:nowrap;">Save</button>'
|
||||||
@ -885,22 +900,10 @@ function buildProjectCard(p, companyId) {
|
|||||||
+ '<p id="edit-proj-error-' + p.id + '" class="field-error" style="margin:0;"></p>'
|
+ '<p id="edit-proj-error-' + p.id + '" class="field-error" style="margin:0;"></p>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="proj-body" id="proj-body-' + p.id + '">'
|
+ '<div class="proj-body" id="proj-body-' + p.id + '">'
|
||||||
+ '<div class="proj-body-inner">'
|
+ '<div class="proj-section" style="border-right:none;">'
|
||||||
+ '<div class="proj-section">'
|
+ '<div class="proj-section-header"><span class="proj-section-title">Locations</span>'
|
||||||
+ '<div class="proj-section-header"><span class="proj-section-title">Locations</span>'
|
+ '<button type="button" onclick="openLocModal(null,' + p.id + ')" class="btn-primary" style="padding:3px 10px;font-size:11px;">+ Add</button></div>'
|
||||||
+ '<button type="button" onclick="openLocModal(null,' + p.id + ')" class="btn-primary" style="padding:3px 10px;font-size:11px;">+ Add</button></div>'
|
+ '<div id="loc-list-' + p.id + '"><div id="loc-empty-' + p.id + '" style="padding:12px 1.25rem;color:#9ca3af;font-size:13px;">No locations yet.</div></div>'
|
||||||
+ '<div id="loc-list-' + p.id + '"><div id="loc-empty-' + p.id + '" style="padding:12px 1.25rem;color:#9ca3af;font-size:13px;">No locations yet.</div></div>'
|
|
||||||
+ '</div>'
|
|
||||||
+ '<div class="proj-section">'
|
|
||||||
+ '<div class="proj-section-header"><span class="proj-section-title">Departments</span>'
|
|
||||||
+ '<button type="button" onclick="openAddDept(' + p.id + ')" class="btn-primary" style="padding:3px 10px;font-size:11px;">+ Add</button></div>'
|
|
||||||
+ '<div class="dept-edit-row" id="dept-add-row-' + p.id + '">'
|
|
||||||
+ '<input id="dept-add-name-' + p.id + '" type="text" class="form-input" style="flex:1;font-size:12px;" placeholder="Department name…" onkeydown="if(event.key===\'Enter\') saveDeptAdd(' + p.id + '); if(event.key===\'Escape\') closeAddDept(' + p.id + ')">'
|
|
||||||
+ '<button type="button" onclick="saveDeptAdd(' + p.id + ')" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>'
|
|
||||||
+ '<button type="button" onclick="closeAddDept(' + p.id + ')" class="btn-secondary btn-sm">✕</button>'
|
|
||||||
+ '</div>'
|
|
||||||
+ '<div id="dept-list-' + p.id + '"><div id="dept-empty-' + p.id + '" style="padding:12px 1.25rem;color:#9ca3af;font-size:13px;">No departments yet.</div></div>'
|
|
||||||
+ '</div>'
|
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>';
|
+ '</div>';
|
||||||
@ -949,78 +952,80 @@ function insertLocInOrder(projectId, newWrapEl) {
|
|||||||
if (!inserted) list.appendChild(newWrapEl);
|
if (!inserted) list.appendChild(newWrapEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Department CRUD ───────────────────────────────────────────────────────────
|
// ── Company Department CRUD ───────────────────────────────────────────────────
|
||||||
function openAddDept(projectId) {
|
var CO_DEPT_BASE = BASE + '/companies';
|
||||||
var row = document.getElementById('dept-add-row-' + projectId);
|
|
||||||
|
function openAddCoDept(companyId) {
|
||||||
|
var row = document.getElementById('co-dept-add-row-' + companyId);
|
||||||
row.classList.add('open');
|
row.classList.add('open');
|
||||||
document.getElementById('dept-add-name-' + projectId).value = '';
|
document.getElementById('co-dept-add-name-' + companyId).value = '';
|
||||||
document.getElementById('dept-add-name-' + projectId).focus();
|
document.getElementById('co-dept-add-name-' + companyId).focus();
|
||||||
}
|
}
|
||||||
function closeAddDept(projectId) {
|
function closeAddCoDept(companyId) {
|
||||||
document.getElementById('dept-add-row-' + projectId).classList.remove('open');
|
document.getElementById('co-dept-add-row-' + companyId).classList.remove('open');
|
||||||
}
|
}
|
||||||
function saveDeptAdd(projectId) {
|
function saveCoDeptAdd(companyId) {
|
||||||
var input = document.getElementById('dept-add-name-' + projectId);
|
var input = document.getElementById('co-dept-add-name-' + companyId);
|
||||||
var name = input.value.trim();
|
var name = input.value.trim();
|
||||||
if (!name) { showToast('Department name is required.', 'warn'); return; }
|
if (!name) { showToast('Department name is required.', 'warn'); return; }
|
||||||
api(BASE + '/' + projectId + '/departments', 'POST', { name: name })
|
api(CO_DEPT_BASE + '/' + companyId + '/departments', 'POST', { name: name })
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
var dept = data.department;
|
var dept = data.department;
|
||||||
DEPT_DATA[dept.id] = dept;
|
DEPT_DATA[dept.id] = dept;
|
||||||
closeAddDept(projectId);
|
closeAddCoDept(companyId);
|
||||||
var emptyEl = document.getElementById('dept-empty-' + projectId);
|
var emptyEl = document.getElementById('co-dept-empty-' + companyId);
|
||||||
if (emptyEl) emptyEl.remove();
|
if (emptyEl) emptyEl.remove();
|
||||||
var list = document.getElementById('dept-list-' + projectId);
|
var list = document.getElementById('co-dept-list-' + companyId);
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.innerHTML = buildDeptRow(projectId, dept);
|
div.innerHTML = buildCoDeptRow(companyId, dept);
|
||||||
insertDeptInOrder(projectId, div.firstElementChild);
|
insertCoDeptInOrder(companyId, div.firstElementChild);
|
||||||
updateDeptCount(projectId);
|
updateCoDeptCount(companyId);
|
||||||
showToast('Department "' + esc(dept.name) + '" added.', 'success');
|
showToast('Department "' + esc(dept.name) + '" added.', 'success');
|
||||||
})
|
})
|
||||||
.catch(function(e) { showToast(firstError(e), 'error'); });
|
.catch(function(e) { showToast(firstError(e), 'error'); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEditDept(deptId, projectId, name, isActive) {
|
function openEditCoDept(deptId, companyId, name, isActive) {
|
||||||
document.getElementById('dept-row-' + deptId).style.display = 'none';
|
document.getElementById('co-dept-row-' + deptId).style.display = 'none';
|
||||||
var row = document.getElementById('dept-edit-row-' + deptId);
|
var row = document.getElementById('co-dept-edit-row-' + deptId);
|
||||||
row.classList.add('open');
|
row.classList.add('open');
|
||||||
document.getElementById('dept-edit-name-' + deptId).value = name;
|
document.getElementById('co-dept-edit-name-' + deptId).value = name;
|
||||||
document.getElementById('dept-edit-active-' + deptId).checked = isActive;
|
document.getElementById('co-dept-edit-active-' + deptId).checked = isActive;
|
||||||
document.getElementById('dept-edit-name-' + deptId).focus();
|
document.getElementById('co-dept-edit-name-' + deptId).focus();
|
||||||
}
|
}
|
||||||
function closeEditDept(deptId) {
|
function closeEditCoDept(deptId) {
|
||||||
document.getElementById('dept-edit-row-' + deptId).classList.remove('open');
|
document.getElementById('co-dept-edit-row-' + deptId).classList.remove('open');
|
||||||
document.getElementById('dept-row-' + deptId).style.display = '';
|
document.getElementById('co-dept-row-' + deptId).style.display = '';
|
||||||
}
|
}
|
||||||
function saveDeptEdit(deptId, projectId) {
|
function saveEditCoDept(deptId, companyId) {
|
||||||
var name = document.getElementById('dept-edit-name-' + deptId).value.trim();
|
var name = document.getElementById('co-dept-edit-name-' + deptId).value.trim();
|
||||||
var isActive = document.getElementById('dept-edit-active-' + deptId).checked;
|
var isActive = document.getElementById('co-dept-edit-active-' + deptId).checked;
|
||||||
if (!name) { showToast('Department name is required.', 'warn'); return; }
|
if (!name) { showToast('Department name is required.', 'warn'); return; }
|
||||||
api(BASE + '/' + projectId + '/departments/' + deptId, 'PATCH', { name: name, is_active: isActive ? 1 : 0 })
|
api(CO_DEPT_BASE + '/' + companyId + '/departments/' + deptId, 'PATCH', { name: name, is_active: isActive ? 1 : 0 })
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
var dept = data.department;
|
var dept = data.department;
|
||||||
DEPT_DATA[dept.id] = dept;
|
DEPT_DATA[dept.id] = dept;
|
||||||
closeEditDept(deptId);
|
closeEditCoDept(deptId);
|
||||||
document.getElementById('dept-name-' + deptId).textContent = dept.name;
|
document.getElementById('co-dept-name-' + deptId).textContent = dept.name;
|
||||||
var badge = document.getElementById('dept-inactive-' + deptId);
|
var badge = document.getElementById('co-dept-inactive-' + deptId);
|
||||||
if (badge) badge.style.display = dept.is_active ? 'none' : '';
|
if (badge) badge.style.display = dept.is_active ? 'none' : '';
|
||||||
var icon = document.getElementById('dept-icon-' + deptId);
|
var icon = document.getElementById('co-dept-icon-' + deptId);
|
||||||
if (icon) icon.setAttribute('stroke', dept.is_active ? '#06b6d4' : '#9ca3af');
|
if (icon) icon.setAttribute('stroke', dept.is_active ? '#7c3aed' : '#9ca3af');
|
||||||
showToast('Department updated.', 'success');
|
showToast('Department updated.', 'success');
|
||||||
})
|
})
|
||||||
.catch(function(e) { showToast(firstError(e), 'error'); });
|
.catch(function(e) { showToast(firstError(e), 'error'); });
|
||||||
}
|
}
|
||||||
function deleteDept(projectId, deptId, name) {
|
function deleteCoDept(companyId, deptId, name) {
|
||||||
confirmAction('Delete Department', 'Delete "' + name + '"?', function() {
|
confirmAction('Delete Department', 'Delete "' + name + '"?', function() {
|
||||||
api(BASE + '/' + projectId + '/departments/' + deptId, 'DELETE')
|
api(CO_DEPT_BASE + '/' + companyId + '/departments/' + deptId, 'DELETE')
|
||||||
.then(function() {
|
.then(function() {
|
||||||
delete DEPT_DATA[deptId];
|
delete DEPT_DATA[deptId];
|
||||||
var wrap = document.getElementById('dept-wrap-' + deptId);
|
var wrap = document.getElementById('co-dept-wrap-' + deptId);
|
||||||
if (wrap) wrap.remove();
|
if (wrap) wrap.remove();
|
||||||
updateDeptCount(projectId);
|
updateCoDeptCount(companyId);
|
||||||
var list = document.getElementById('dept-list-' + projectId);
|
var list = document.getElementById('co-dept-list-' + companyId);
|
||||||
if (list && !list.querySelector('[id^="dept-wrap-"]')) {
|
if (list && !list.querySelector('[id^="co-dept-wrap-"]')) {
|
||||||
list.innerHTML = '<div id="dept-empty-' + projectId + '" style="padding:14px 1.25rem;color:#9ca3af;font-size:13px;">No departments yet.</div>';
|
list.innerHTML = '<div id="co-dept-empty-' + companyId + '" style="padding:10px 1.25rem;color:#9ca3af;font-size:13px;">No departments yet.</div>';
|
||||||
}
|
}
|
||||||
showToast('Department "' + esc(name) + '" deleted.', 'success');
|
showToast('Department "' + esc(name) + '" deleted.', 'success');
|
||||||
})
|
})
|
||||||
@ -1028,20 +1033,20 @@ function deleteDept(projectId, deptId, name) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDeptCount(projectId) {
|
function updateCoDeptCount(companyId) {
|
||||||
var list = document.getElementById('dept-list-' + projectId);
|
var list = document.getElementById('co-dept-list-' + companyId);
|
||||||
var count = list ? list.querySelectorAll('[id^="dept-wrap-"]').length : 0;
|
var count = list ? list.querySelectorAll('[id^="co-dept-wrap-"]').length : 0;
|
||||||
var el = document.getElementById('proj-dept-count-' + projectId);
|
var el = document.getElementById('company-dept-count-' + companyId);
|
||||||
if (el) el.textContent = count + ' ' + (count === 1 ? 'dept' : 'depts');
|
if (el) el.textContent = '· ' + count + ' ' + (count === 1 ? 'dept' : 'depts');
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertDeptInOrder(projectId, newWrapEl) {
|
function insertCoDeptInOrder(companyId, newWrapEl) {
|
||||||
var list = document.getElementById('dept-list-' + projectId);
|
var list = document.getElementById('co-dept-list-' + companyId);
|
||||||
var newName = (newWrapEl.querySelector('[id^="dept-name-"]').textContent || '').trim().toLowerCase();
|
var newName = (newWrapEl.querySelector('[id^="co-dept-name-"]').textContent || '').trim().toLowerCase();
|
||||||
var wraps = list.querySelectorAll('[id^="dept-wrap-"]');
|
var wraps = list.querySelectorAll('[id^="co-dept-wrap-"]');
|
||||||
var inserted = false;
|
var inserted = false;
|
||||||
for (var i = 0; i < wraps.length; i++) {
|
for (var i = 0; i < wraps.length; i++) {
|
||||||
var nameEl = wraps[i].querySelector('[id^="dept-name-"]');
|
var nameEl = wraps[i].querySelector('[id^="co-dept-name-"]');
|
||||||
if (nameEl && newName < nameEl.textContent.trim().toLowerCase()) {
|
if (nameEl && newName < nameEl.textContent.trim().toLowerCase()) {
|
||||||
list.insertBefore(newWrapEl, wraps[i]);
|
list.insertBefore(newWrapEl, wraps[i]);
|
||||||
inserted = true; break;
|
inserted = true; break;
|
||||||
@ -1050,25 +1055,25 @@ function insertDeptInOrder(projectId, newWrapEl) {
|
|||||||
if (!inserted) list.appendChild(newWrapEl);
|
if (!inserted) list.appendChild(newWrapEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildDeptRow(projectId, dept) {
|
function buildCoDeptRow(companyId, dept) {
|
||||||
var safeName = (dept.name || '').replace(/\\/g,'\\\\').replace(/'/g,"\\'");
|
var safeName = (dept.name || '').replace(/\\/g,'\\\\').replace(/'/g,"\\'");
|
||||||
return '<div id="dept-wrap-' + dept.id + '">'
|
return '<div id="co-dept-wrap-' + dept.id + '">'
|
||||||
+ '<div class="dept-row" id="dept-row-' + dept.id + '">'
|
+ '<div class="dept-row" id="co-dept-row-' + dept.id + '">'
|
||||||
+ '<div style="display:flex;align-items:center;gap:8px;flex:1;min-width:0;">'
|
+ '<div style="display:flex;align-items:center;gap:8px;flex:1;min-width:0;">'
|
||||||
+ '<svg width="13" height="13" fill="none" stroke="#06b6d4" viewBox="0 0 24 24" id="dept-icon-' + dept.id + '" style="flex-shrink:0;"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>'
|
+ '<svg width="13" height="13" fill="none" stroke="#7c3aed" viewBox="0 0 24 24" id="co-dept-icon-' + dept.id + '" style="flex-shrink:0;"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>'
|
||||||
+ '<span id="dept-name-' + dept.id + '" style="font-size:13px;font-weight:600;color:#1e293b;">' + esc(dept.name) + '</span>'
|
+ '<span id="co-dept-name-' + dept.id + '" style="font-size:13px;font-weight:600;color:#1e293b;">' + esc(dept.name) + '</span>'
|
||||||
+ '<span id="dept-inactive-' + dept.id + '" class="badge-inactive" style="display:none;">Inactive</span>'
|
+ '<span id="co-dept-inactive-' + dept.id + '" class="badge-inactive" style="display:none;">Inactive</span>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div style="display:flex;gap:4px;flex-shrink:0;">'
|
+ '<div style="display:flex;gap:4px;flex-shrink:0;">'
|
||||||
+ '<button type="button" onclick="openEditDept(' + dept.id + ',' + projectId + ',\'' + safeName + '\',true)" class="btn-secondary btn-sm" style="padding:3px 8px;font-size:12px;">Edit</button>'
|
+ '<button type="button" onclick="openEditCoDept(' + dept.id + ',' + companyId + ',\'' + safeName + '\',true)" class="btn-secondary btn-sm" style="padding:3px 8px;font-size:12px;">Edit</button>'
|
||||||
+ '<button type="button" onclick="deleteDept(' + projectId + ',' + dept.id + ',\'' + safeName + '\')" class="btn-danger btn-sm" style="padding:3px 8px;font-size:12px;">Delete</button>'
|
+ '<button type="button" onclick="deleteCoDept(' + companyId + ',' + dept.id + ',\'' + safeName + '\')" class="btn-danger btn-sm" style="padding:3px 8px;font-size:12px;">Delete</button>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="dept-edit-row" id="dept-edit-row-' + dept.id + '">'
|
+ '<div class="dept-edit-row" id="co-dept-edit-row-' + dept.id + '">'
|
||||||
+ '<input id="dept-edit-name-' + dept.id + '" type="text" class="form-input" style="flex:1;font-size:12px;" onkeydown="if(event.key===\'Enter\') saveDeptEdit(' + dept.id + ',' + projectId + '); if(event.key===\'Escape\') closeEditDept(' + dept.id + ')">'
|
+ '<input id="co-dept-edit-name-' + dept.id + '" type="text" class="form-input" style="flex:1;font-size:12px;" onkeydown="if(event.key===\'Enter\') saveEditCoDept(' + dept.id + ',' + companyId + '); if(event.key===\'Escape\') closeEditCoDept(' + dept.id + ')">'
|
||||||
+ '<label style="display:flex;align-items:center;gap:4px;font-size:12px;color:#374151;white-space:nowrap;cursor:pointer;"><input type="checkbox" id="dept-edit-active-' + dept.id + '" checked style="width:13px;height:13px;"> Active</label>'
|
+ '<label style="display:flex;align-items:center;gap:4px;font-size:12px;color:#374151;white-space:nowrap;cursor:pointer;"><input type="checkbox" id="co-dept-edit-active-' + dept.id + '" checked style="width:13px;height:13px;"> Active</label>'
|
||||||
+ '<button type="button" onclick="saveDeptEdit(' + dept.id + ',' + projectId + ')" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>'
|
+ '<button type="button" onclick="saveEditCoDept(' + dept.id + ',' + companyId + ')" class="btn-primary" style="padding:4px 12px;font-size:12px;white-space:nowrap;">Save</button>'
|
||||||
+ '<button type="button" onclick="closeEditDept(' + dept.id + ')" class="btn-secondary btn-sm">✕</button>'
|
+ '<button type="button" onclick="closeEditCoDept(' + dept.id + ')" class="btn-secondary btn-sm">✕</button>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>';
|
+ '</div>';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,9 +144,9 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
Route::post('settings/projects/{project}/locations', [ProjectSettingController::class, 'storeLocation'])->name('settings.projects.locations.store');
|
Route::post('settings/projects/{project}/locations', [ProjectSettingController::class, 'storeLocation'])->name('settings.projects.locations.store');
|
||||||
Route::patch('settings/projects/{project}/locations/{location}', [ProjectSettingController::class, 'updateLocation'])->name('settings.projects.locations.update');
|
Route::patch('settings/projects/{project}/locations/{location}', [ProjectSettingController::class, 'updateLocation'])->name('settings.projects.locations.update');
|
||||||
Route::delete('settings/projects/{project}/locations/{location}', [ProjectSettingController::class, 'destroyLocation'])->name('settings.projects.locations.destroy');
|
Route::delete('settings/projects/{project}/locations/{location}', [ProjectSettingController::class, 'destroyLocation'])->name('settings.projects.locations.destroy');
|
||||||
Route::post('settings/projects/{project}/departments', [ProjectSettingController::class, 'storeDepartment'])->name('settings.projects.departments.store');
|
Route::post('settings/projects/companies/{company}/departments', [ProjectSettingController::class, 'storeDepartment'])->name('settings.projects.companies.departments.store');
|
||||||
Route::patch('settings/projects/{project}/departments/{department}', [ProjectSettingController::class, 'updateDepartment'])->name('settings.projects.departments.update');
|
Route::patch('settings/projects/companies/{company}/departments/{department}', [ProjectSettingController::class, 'updateDepartment'])->name('settings.projects.companies.departments.update');
|
||||||
Route::delete('settings/projects/{project}/departments/{department}', [ProjectSettingController::class, 'destroyDepartment'])->name('settings.projects.departments.destroy');
|
Route::delete('settings/projects/companies/{company}/departments/{department}', [ProjectSettingController::class, 'destroyDepartment'])->name('settings.projects.companies.departments.destroy');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user