feat: Add company association to projects with role management
- Created Company model and migration with fields: name, tax_id, address, phone, email, website, type, notes - Created company_project pivot table with role_in_project field - Added relationships: Project.companies() and Company.projects() - Created Livewire component ProjectCompanies for managing company assignments - Added 'Companies' tab to project edit interface alongside Phases and Users tabs - Implemented assign/remove company functionality with role selection - Applied same permissions logic as user assignment (assign users permission or Admin role)
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use App\Models\Project;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ProjectCompanies extends Component
|
||||
{
|
||||
public Project $project;
|
||||
public $assignedCompanies = [];
|
||||
public $allCompanies = [];
|
||||
public $selectedCompanyId = '';
|
||||
public $selectedRole = 'other';
|
||||
|
||||
public function mount(Project $project)
|
||||
{
|
||||
$this->project = $project;
|
||||
$this->loadCompanies();
|
||||
}
|
||||
|
||||
public function loadCompanies()
|
||||
{
|
||||
$this->assignedCompanies = $this->project->companies()->withPivot('role_in_project')->get();
|
||||
$assignedIds = $this->assignedCompanies->pluck('id')->toArray();
|
||||
$this->allCompanies = Company::whereNotIn('id', $assignedIds)->orderBy('name')->get();
|
||||
}
|
||||
|
||||
public function assignCompany()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (!$user->can('assign users') && !$user->hasRole('Admin')) {
|
||||
session()->flash('error', 'No tienes permisos para asignar compañías.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'selectedCompanyId' => 'required|exists:companies,id',
|
||||
'selectedRole' => 'required|in:owner,constructor,subcontractor,consultant,supplier,other',
|
||||
]);
|
||||
|
||||
$this->project->companies()->attach($this->selectedCompanyId, [
|
||||
'role_in_project' => $this->selectedRole
|
||||
]);
|
||||
|
||||
$this->reset(['selectedCompanyId', 'selectedRole']);
|
||||
$this->loadCompanies();
|
||||
$this->dispatch('notify', 'Compañía asignada al proyecto.');
|
||||
}
|
||||
|
||||
public function removeCompany($companyId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (!$user->can('assign users') && !$user->hasRole('Admin')) {
|
||||
session()->flash('error', 'Sin permisos.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->project->companies()->detach($companyId);
|
||||
$this->loadCompanies();
|
||||
$this->dispatch('notify', 'Compañía eliminada del proyecto.');
|
||||
}
|
||||
|
||||
public function changeRole($companyId, $role)
|
||||
{
|
||||
if (!in_array($role, ['owner', 'constructor', 'subcontractor', 'consultant', 'supplier', 'other'])) return;
|
||||
|
||||
$this->project->companies()->updateExistingPivot($companyId, [
|
||||
'role_in_project' => $role
|
||||
]);
|
||||
$this->loadCompanies();
|
||||
$this->dispatch('notify', 'Rol actualizado.');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project-companies');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
||||
use Rappasoft\LaravelLivewireTables\Views\Column;
|
||||
use App\Models\Project;
|
||||
|
||||
class ProjectTable extends DataTableComponent
|
||||
{
|
||||
protected $model = Project::class;
|
||||
|
||||
public function configure(): void
|
||||
{
|
||||
$this->setPrimaryKey('id')
|
||||
->setDefaultSort('created_at', 'desc')
|
||||
->setTableAttributes(['class' => 'table-auto w-full'])
|
||||
->setThAttributes(['class' => 'px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider'])
|
||||
->setTdAttributes(['class' => 'px-4 py-2 whitespace-nowrap text-sm text-gray-900']);
|
||||
|
||||
$this->addColumn('name', __('Project Name'))
|
||||
->setSortable()
|
||||
->setSearchable();
|
||||
|
||||
$this->addColumn('address', __('Address'))
|
||||
->setSortable()
|
||||
->setSearchable();
|
||||
|
||||
$this->addColumn('status', __('Status'))
|
||||
->setSortable()
|
||||
->setFilterable([
|
||||
'planning' => __('Planning'),
|
||||
'in_progress' => __('In progress'),
|
||||
'paused' => __('Paused'),
|
||||
'completed' => __('Completed'),
|
||||
])
|
||||
->setLabel(fn ($value, $row, $column, $component) =>
|
||||
match ($value) {
|
||||
'planning' => '<span class="badge badge-primary">'.__('Planning').'</span>',
|
||||
'in_progress' => '<span class="badge badge-success">'.__('In progress').'</span>',
|
||||
'paused' => '<span class="badge badge-warning">'.__('Paused').'</span>',
|
||||
'completed' => '<span class="badge badge-secondary">'.__('Completed').'</span>',
|
||||
default => $value
|
||||
}
|
||||
);
|
||||
|
||||
$this->addColumn('start_date', __('Start Date'))
|
||||
->setSortable()
|
||||
->setFormat(fn ($value, $row, $column) => $value ? $value->format('Y-m-d') : '');
|
||||
|
||||
$this->addColumn('end_date_estimated', __('Estimated End Date'))
|
||||
->setSortable()
|
||||
->setFormat(fn ($value, $row, $column) => $value ? $value->format('Y-m-d') : '');
|
||||
|
||||
$this->addColumn('actions', __('Actions'))
|
||||
->setLabel(fn ($row) => '<div class="flex space-x-2">
|
||||
<a href="'.route('projects.edit', $row->id).'" class="btn btn-sm btn-primary">'.__('Edit').'</a>
|
||||
<form action="'.route('projects.destroy', $row->id).'" method="POST" onsubmit="return confirm(''.__('Are you sure you want to delete this project?').'');">
|
||||
'.csrf_field().'
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<button type="submit" class="btn btn-sm btn-error">'.__('Delete').'</button>
|
||||
</form>
|
||||
</div>')
|
||||
->setHtmlAttribute(['class' => 'text-right']);
|
||||
}
|
||||
|
||||
public function columns(): array
|
||||
{
|
||||
return [
|
||||
Column::make(__('Project Name'), 'name')
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Column::make(__('Address'), 'address')
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Column::make(__('Status'), 'status')
|
||||
->sortable()
|
||||
->filterable([
|
||||
'planning' => __('Planning'),
|
||||
'in_progress' => __('In progress'),
|
||||
'paused' => __('Paused'),
|
||||
'completed' => __('Completed'),
|
||||
])
|
||||
->label(fn ($value, $row, $column) =>
|
||||
match ($value) {
|
||||
'planning' => '<span class="badge badge-primary">'.__('Planning').'</span>',
|
||||
'in_progress' => '<span class="badge badge-success">'.__('In progress').'</span>',
|
||||
'paused' => '<span class="badge badge-warning">'.__('Paused').'</span>',
|
||||
'completed' => '<span class="badge badge-secondary">'.__('Completed').'</span>',
|
||||
default => $value
|
||||
}
|
||||
),
|
||||
Column::make(__('Start Date'), 'start_date')
|
||||
->sortable()
|
||||
->format(fn ($value, $row, $column) => $value ? $value->format('Y-m-d') : ''),
|
||||
Column::make(__('Estimated End Date'), 'end_date_estimated')
|
||||
->sortable()
|
||||
->format(fn ($value, $row, $column) => $value ? $value->format('Y-m-d') : ''),
|
||||
Column::make(__('Actions'))
|
||||
->label(fn ($row) => '<div class="flex space-x-2">
|
||||
<a href="'.route('projects.edit', $row->id).'" class="btn btn-sm btn-primary">'.__('Edit').'</a>
|
||||
<form action="'.route('projects.destroy', $row->id).'" method="POST" onsubmit="return confirm(''.__('Are you sure you want to delete this project?').'');">
|
||||
'.csrf_field().'
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<button type="submit" class="btn btn-sm btn-error">'.__('Delete').'</button>
|
||||
</form>
|
||||
</div>')
|
||||
->htmlAttribute(['class' => 'text-right']),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Company extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'tax_id',
|
||||
'address',
|
||||
'phone',
|
||||
'email',
|
||||
'website',
|
||||
'type',
|
||||
'notes',
|
||||
];
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
// Relationships
|
||||
public function projects()
|
||||
{
|
||||
return $this->belongsToMany(Project::class, 'company_project')
|
||||
->withPivot('role_in_project')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,13 @@ class Project extends Model
|
||||
return $this->belongsToMany(User::class)->withPivot('role_in_project');
|
||||
}
|
||||
|
||||
public function companies()
|
||||
{
|
||||
return $this->belongsToMany(Company::class, 'company_project')
|
||||
->withPivot('role_in_project')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('companies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('tax_id')->nullable()->unique();
|
||||
$table->string('address')->nullable();
|
||||
$table->string('phone')->nullable();
|
||||
$table->string('email')->nullable();
|
||||
$table->string('website')->nullable();
|
||||
$table->enum('type', ['owner', 'constructor', 'subcontractor', 'consultant', 'supplier', 'other'])->default('other');
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('companies');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('company_project', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('company_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('project_id')->constrained()->onDelete('cascade');
|
||||
$table->string('role_in_project');
|
||||
$table->timestamps();
|
||||
|
||||
// Ensure a company is not linked more than once to the same project
|
||||
$table->unique(['company_id', 'project_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('company_project');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,419 @@
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
DROP TABLE IF EXISTS `cache`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `cache` (
|
||||
`key` varchar(255) NOT NULL,
|
||||
`value` mediumtext NOT NULL,
|
||||
`expiration` int(11) NOT NULL,
|
||||
PRIMARY KEY (`key`),
|
||||
KEY `cache_expiration_index` (`expiration`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `cache_locks`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `cache_locks` (
|
||||
`key` varchar(255) NOT NULL,
|
||||
`owner` varchar(255) NOT NULL,
|
||||
`expiration` int(11) NOT NULL,
|
||||
PRIMARY KEY (`key`),
|
||||
KEY `cache_locks_expiration_index` (`expiration`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `failed_jobs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `failed_jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(255) NOT NULL,
|
||||
`connection` text NOT NULL,
|
||||
`queue` text NOT NULL,
|
||||
`payload` longtext NOT NULL,
|
||||
`exception` longtext NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `features`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `features` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`layer_id` bigint(20) unsigned NOT NULL,
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`geometry` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`geometry`)),
|
||||
`properties` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`properties`)),
|
||||
`template_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`progress` int(11) NOT NULL DEFAULT 0,
|
||||
`responsible` varchar(255) DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `features_layer_id_foreign` (`layer_id`),
|
||||
KEY `features_template_id_foreign` (`template_id`),
|
||||
CONSTRAINT `features_layer_id_foreign` FOREIGN KEY (`layer_id`) REFERENCES `layers` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `features_template_id_foreign` FOREIGN KEY (`template_id`) REFERENCES `inspection_templates` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `inspection_templates`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `inspection_templates` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`project_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`fields` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`fields`)),
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`phase_id` bigint(20) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `inspection_templates_project_id_foreign` (`project_id`),
|
||||
KEY `inspection_templates_phase_id_index` (`phase_id`),
|
||||
CONSTRAINT `inspection_templates_phase_id_foreign` FOREIGN KEY (`phase_id`) REFERENCES `phases` (`id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `inspection_templates_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `inspections`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `inspections` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
`layer_id` bigint(20) unsigned NOT NULL,
|
||||
`template_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`data`)),
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`feature_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `inspections_project_id_foreign` (`project_id`),
|
||||
KEY `inspections_layer_id_foreign` (`layer_id`),
|
||||
KEY `inspections_template_id_foreign` (`template_id`),
|
||||
KEY `inspections_user_id_foreign` (`user_id`),
|
||||
KEY `inspections_feature_id_foreign` (`feature_id`),
|
||||
CONSTRAINT `inspections_feature_id_foreign` FOREIGN KEY (`feature_id`) REFERENCES `features` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `inspections_layer_id_foreign` FOREIGN KEY (`layer_id`) REFERENCES `layers` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `inspections_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `inspections_template_id_foreign` FOREIGN KEY (`template_id`) REFERENCES `inspection_templates` (`id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `inspections_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `job_batches`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `job_batches` (
|
||||
`id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`total_jobs` int(11) NOT NULL,
|
||||
`pending_jobs` int(11) NOT NULL,
|
||||
`failed_jobs` int(11) NOT NULL,
|
||||
`failed_job_ids` longtext NOT NULL,
|
||||
`options` mediumtext DEFAULT NULL,
|
||||
`cancelled_at` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`finished_at` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `jobs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`queue` varchar(255) NOT NULL,
|
||||
`payload` longtext NOT NULL,
|
||||
`attempts` tinyint(3) unsigned NOT NULL,
|
||||
`reserved_at` int(10) unsigned DEFAULT NULL,
|
||||
`available_at` int(10) unsigned NOT NULL,
|
||||
`created_at` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `jobs_queue_index` (`queue`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `layers`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `layers` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
`phase_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`color` varchar(7) NOT NULL DEFAULT '#3b82f6',
|
||||
`original_file` varchar(255) DEFAULT NULL,
|
||||
`uploaded_by` bigint(20) unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `layers_project_id_foreign` (`project_id`),
|
||||
KEY `layers_phase_id_foreign` (`phase_id`),
|
||||
KEY `layers_uploaded_by_foreign` (`uploaded_by`),
|
||||
CONSTRAINT `layers_phase_id_foreign` FOREIGN KEY (`phase_id`) REFERENCES `phases` (`id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `layers_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `layers_uploaded_by_foreign` FOREIGN KEY (`uploaded_by`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `media`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `media` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`mediable_type` varchar(255) NOT NULL,
|
||||
`mediable_id` bigint(20) unsigned NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`file_path` varchar(255) NOT NULL,
|
||||
`file_type` varchar(255) NOT NULL,
|
||||
`file_extension` varchar(10) NOT NULL,
|
||||
`file_size` int(10) unsigned NOT NULL,
|
||||
`category` enum('image','document','other') NOT NULL DEFAULT 'image',
|
||||
`description` text DEFAULT NULL,
|
||||
`metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)),
|
||||
`uploaded_by` bigint(20) unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `media_mediable_type_mediable_id_index` (`mediable_type`,`mediable_id`),
|
||||
KEY `media_uploaded_by_foreign` (`uploaded_by`),
|
||||
CONSTRAINT `media_uploaded_by_foreign` FOREIGN KEY (`uploaded_by`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `migrations`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `migrations` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`migration` varchar(255) NOT NULL,
|
||||
`batch` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `model_has_permissions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `model_has_permissions` (
|
||||
`permission_id` bigint(20) unsigned NOT NULL,
|
||||
`model_type` varchar(255) NOT NULL,
|
||||
`model_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
|
||||
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
|
||||
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `model_has_roles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `model_has_roles` (
|
||||
`role_id` bigint(20) unsigned NOT NULL,
|
||||
`model_type` varchar(255) NOT NULL,
|
||||
`model_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
|
||||
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
|
||||
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `password_reset_tokens`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `password_reset_tokens` (
|
||||
`email` varchar(255) NOT NULL,
|
||||
`token` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `pending_syncs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `pending_syncs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`action` varchar(255) NOT NULL,
|
||||
`payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`payload`)),
|
||||
`synced_at` timestamp NULL DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `pending_syncs_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `pending_syncs_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `permissions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `permissions` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`guard_name` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `phases`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `phases` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`order` int(11) NOT NULL DEFAULT 0,
|
||||
`color` varchar(7) NOT NULL DEFAULT '#3b82f6',
|
||||
`progress_percent` int(11) NOT NULL DEFAULT 0,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `phases_project_id_foreign` (`project_id`),
|
||||
CONSTRAINT `phases_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `progress_updates`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `progress_updates` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`phase_id` bigint(20) unsigned NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`progress_percent` int(11) NOT NULL,
|
||||
`comment` text DEFAULT NULL,
|
||||
`location` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`location`)),
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `progress_updates_phase_id_foreign` (`phase_id`),
|
||||
KEY `progress_updates_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `progress_updates_phase_id_foreign` FOREIGN KEY (`phase_id`) REFERENCES `phases` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `progress_updates_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `project_user`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `project_user` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`role_in_project` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `project_user_project_id_foreign` (`project_id`),
|
||||
KEY `project_user_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `project_user_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `project_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `projects`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `projects` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`address` text NOT NULL,
|
||||
`lat` decimal(10,8) NOT NULL,
|
||||
`lng` decimal(11,8) NOT NULL,
|
||||
`start_date` date NOT NULL,
|
||||
`end_date_estimated` date DEFAULT NULL,
|
||||
`status` enum('planning','in_progress','paused','completed') NOT NULL DEFAULT 'planning',
|
||||
`created_by` bigint(20) unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `projects_created_by_foreign` (`created_by`),
|
||||
CONSTRAINT `projects_created_by_foreign` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `role_has_permissions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `role_has_permissions` (
|
||||
`permission_id` bigint(20) unsigned NOT NULL,
|
||||
`role_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`permission_id`,`role_id`),
|
||||
KEY `role_has_permissions_role_id_foreign` (`role_id`),
|
||||
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `roles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `roles` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`guard_name` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `sessions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `sessions` (
|
||||
`id` varchar(255) NOT NULL,
|
||||
`user_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`user_agent` text DEFAULT NULL,
|
||||
`payload` longtext NOT NULL,
|
||||
`last_activity` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `sessions_user_id_index` (`user_id`),
|
||||
KEY `sessions_last_activity_index` (`last_activity`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`email` varchar(255) NOT NULL,
|
||||
`email_verified_at` timestamp NULL DEFAULT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`remember_token` varchar(100) DEFAULT NULL,
|
||||
`locale` varchar(5) NOT NULL DEFAULT 'en',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `users_email_unique` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'0001_01_01_000000_create_users_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'0001_01_01_000001_create_cache_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'0001_01_01_000002_create_jobs_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4,'2026_04_27_095918_create_projects_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5,'2026_04_27_095919_create_phases_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6,'2026_04_27_095920_create_layers_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7,'2026_04_27_095921_create_progress_updates_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8,'2026_04_27_095925_create_pending_syncs_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9,'2026_04_27_095925_create_project_user_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10,'2026_04_27_095926_create_permission_tables',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11,'2026_04_28_090208_create_inspection_templates_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12,'2026_04_28_090218_create_inspections_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13,'2026_05_06_122423_create_features_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14,'2026_05_06_122711_update_inspections_feature_id_foreign',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15,'2026_05_06_125429_drop_geojson_data_from_layers',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (16,'2026_05_06_172557_add_color_to_layers_table',1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (17,'2026_05_09_210000_create_media_table',2);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (18,'2026_05_09_220000_add_locale_to_users_table',3);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (19,'2026_05_11_115552_add_phase_id_to_inspection_templates',4);
|
||||
@@ -0,0 +1,76 @@
|
||||
<div>
|
||||
@if(session()->has('message'))
|
||||
<div class="alert alert-success mb-2 text-sm">{{ session('message') }}</div>
|
||||
@endif
|
||||
@if(session()->has('error'))
|
||||
<div class="alert alert-error mb-2 text-sm">{{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
{{-- Asignar compañía --}}
|
||||
@can('assign users')
|
||||
<form wire:submit.prevent="assignCompany" class="flex items-end gap-2 mb-4">
|
||||
<div class="flex-1">
|
||||
<label class="label-text text-xs">{{ __('Companies') }}</label>
|
||||
<select wire:model="selectedCompanyId" class="select select-bordered select-sm w-full">
|
||||
<option value="">{{ __('Select') }}...</option>
|
||||
@foreach($allCompanies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }} @if($company->tax_id) ({{ $company->tax_id }}) @endif</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<label class="label-text text-xs">{{ __('Role') }}</label>
|
||||
<select wire:model="selectedRole" class="select select-bordered select-sm w-full">
|
||||
<option value="owner">{{ __('Owner') }}</option>
|
||||
<option value="constructor">{{ __('Constructor') }}</option>
|
||||
<option value="subcontractor">{{ __('Subcontractor') }}</option>
|
||||
<option value="consultant">{{ __('Consultant') }}</option>
|
||||
<option value="supplier">{{ __('Supplier') }}</option>
|
||||
<option value="other">{{ __('Other') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-sm">{{ __('Assign') }}</button>
|
||||
</form>
|
||||
@endcan
|
||||
|
||||
{{-- Lista de compañías asignadas --}}
|
||||
@if($assignedCompanies->isNotEmpty())
|
||||
<div class="space-y-1">
|
||||
@foreach($assignedCompanies as $company)
|
||||
<div class="flex items-center justify-between p-2 border rounded text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-6 h-6 rounded-full bg-primary text-white flex items-center justify-center text-xs font-bold">
|
||||
{{ strtoupper(substr($company->name, 0, 1)) }}
|
||||
</span>
|
||||
<div>
|
||||
<span class="font-medium">{{ $company->name }}</span>
|
||||
@if($company->tax_id)
|
||||
<span class="text-xs text-gray-400 ml-1">{{ $company->tax_id }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
@can('assign users')
|
||||
<select wire:change="changeRole({{ $company->id }}, $event.target.value)"
|
||||
class="select select-bordered select-xs">
|
||||
<option value="owner" @selected($company->pivot->role_in_project == 'owner')>{{ __('Owner') }}</option>
|
||||
<option value="constructor" @selected($company->pivot->role_in_project == 'constructor')>{{ __('Constructor') }}</option>
|
||||
<option value="subcontractor" @selected($company->pivot->role_in_project == 'subcontractor')>{{ __('Subcontractor') }}</option>
|
||||
<option value="consultant" @selected($company->pivot->role_in_project == 'consultant')>{{ __('Consultant') }}</option>
|
||||
<option value="supplier" @selected($company->pivot->role_in_project == 'supplier')>{{ __('Supplier') }}</option>
|
||||
<option value="other" @selected($company->pivot->role_in_project == 'other')>{{ __('Other') }}</option>
|
||||
</select>
|
||||
<button wire:click="removeCompany({{ $company->id }})"
|
||||
class="btn btn-xs btn-ghost text-error"
|
||||
onclick="return confirm('{{ __('Remove') }} {{ $company->name }}?')">✕</button>
|
||||
@else
|
||||
<span class="badge badge-sm">{{ ucfirst($company->pivot->role_in_project) }}</span>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<p class="text-sm text-gray-400 text-center py-4">{{ __('No companies assigned yet') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@@ -18,6 +18,12 @@
|
||||
<label for="tab-users-{{ $project->id }}" class="tab {{ $activeTab === 'users' ? 'tab-active' : '' }}">
|
||||
{{ __('Users') }}
|
||||
</label>
|
||||
|
||||
<input type="radio" name="tabs-project-edit-{{ $project->id }}" id="tab-companies-{{ $project->id }}"
|
||||
{{ $activeTab === 'companies' ? 'checked' : '' }} class="tab-toggle" />
|
||||
<label for="tab-companies-{{ $project->id }}" class="tab {{ $activeTab === 'companies' ? 'tab-active' : '' }}">
|
||||
{{ __('Companies') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
{{-- Nothing in the world is as soft and yielding as water. --}}
|
||||
</div>
|
||||
@@ -0,0 +1,160 @@
|
||||
<div>
|
||||
<div class="bg-base-100 p-4 rounded shadow">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold">📋 Templates de inspección</h2>
|
||||
<div>
|
||||
<button wire:click="newTemplate" class="btn btn-primary btn-sm">
|
||||
Nuevo template
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(session()->has('message'))
|
||||
<div class="alert alert-success mb-4">{{ session('message') }}</div>
|
||||
@endif
|
||||
|
||||
{{-- Formulario de creación/edición con diseño de dos columnas --}}
|
||||
@if($showForm)
|
||||
<form wire:submit.prevent="saveTemplate" class="border p-4 rounded mb-6 bg-base-200">
|
||||
<table class="w-full mb-8">
|
||||
<tbody>
|
||||
{{-- Nombre del template --}}
|
||||
<tr>
|
||||
<td class="w-1/4 py-3 pr-4 align-top">
|
||||
{{__('Nombre del template')}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<input type="text" wire:model="form.name"
|
||||
class="input w-full"
|
||||
required>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{-- Descripción --}}
|
||||
<tr>
|
||||
<td class="w-1/4 py-3 pr-4 align-top">
|
||||
{{__('Descripción')}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<textarea wire:model="form.description" class="textarea textarea-bordered w-full" rows="2"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{-- Fase asociada (opcional) --}}
|
||||
<tr>
|
||||
<td class="w-1/4 py-3 pr-4 align-top">
|
||||
{{__('Fase asociada (opcional)')}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<select wire:model="form.phase_id" class="select select-bordered w-full">
|
||||
<option value="">Ninguna (global para el proyecto)</option>
|
||||
@foreach($phases as $phase)
|
||||
<option value="{{ $phase->id }}" {{ old('form.phase_id') == $phase->id ? 'selected' : '' }}>
|
||||
{{ $phase->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{-- Campos dinámicos --}}
|
||||
<div class="border-t pt-4 mt-2">
|
||||
<h3 class="font-bold mb-3">Campos del formulario</h3>
|
||||
@foreach($form['fields'] as $index => $field)
|
||||
<div class="border p-3 rounded mb-3 bg-base-100">
|
||||
{{-- Fila: nombre interno --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div class="font-medium">Nombre interno</div>
|
||||
<div><input type="text" wire:model="form.fields.{{ $index }}.name" placeholder="ej: altura_medida" class="input input-sm w-full"></div>
|
||||
</div>
|
||||
{{-- Fila: etiqueta --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div class="font-medium">Etiqueta visible</div>
|
||||
<div><input type="text" wire:model="form.fields.{{ $index }}.label" placeholder="ej: Altura medida (m)" class="input input-sm w-full"></div>
|
||||
</div>
|
||||
{{-- Fila: tipo --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div class="font-medium">Tipo de campo</div>
|
||||
<div>
|
||||
<select wire:model="form.fields.{{ $index }}.type" class="select select-sm w-full">
|
||||
@foreach($fieldTypes as $typeValue => $typeLabel)
|
||||
<option value="{{ $typeValue }}">{{ $typeLabel }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{-- Fila: requerido y botón eliminar --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div class="font-medium">Requerido</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<input type="checkbox" wire:model="form.fields.{{ $index }}.required" class="checkbox checkbox-sm">
|
||||
<button type="button" wire:click="removeField({{ $index }})" class="btn btn-xs btn-error">Eliminar campo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Campos adicionales según tipo --}}
|
||||
@if(in_array($field['type'], ['integer', 'decimal', 'percentage']))
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div class="font-medium">Mínimo / Máximo / Paso</div>
|
||||
<div class="flex gap-2">
|
||||
<input type="number" wire:model="form.fields.{{ $index }}.min" placeholder="Mín" class="input input-xs w-20">
|
||||
<input type="number" wire:model="form.fields.{{ $index }}.max" placeholder="Máx" class="input input-xs w-20">
|
||||
<input type="number" step="any" wire:model="form.fields.{{ $index }}.step" placeholder="Paso" class="input input-xs w-20">
|
||||
</div>
|
||||
</div>
|
||||
@elseif($field['type'] === 'select')
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div class="font-medium">Opciones (separadas por coma)</div>
|
||||
<div><input type="text" wire:model="form.fields.{{ $index }}.options" placeholder="ej: Bueno,Regular,Malo" class="input input-sm w-full"></div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
<button type="button" wire:click="addField" class="btn btn-sm btn-secondary mt-2">+ Agregar campo</button>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mt-4">
|
||||
<button type="submit" class="btn btn-primary">Guardar template</button>
|
||||
<button type="button" wire:click="cancelForm" class="btn">Cancelar</button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{-- Tabla de templates existentes --}}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nombre</th>
|
||||
<th>Descripción</th>
|
||||
<th>Fase</th>
|
||||
<th>Campos</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($templates as $template)
|
||||
<tr>
|
||||
<td>{{ $template->name }}</td>
|
||||
<td>{{ $template->description ?? '-' }}</td>
|
||||
<td>{{ $template->phase ? $template->phase->name : 'Global' }}</td>
|
||||
<td>{{ count($template->fields) }}</td>
|
||||
<td>
|
||||
<button wire:click="editTemplate({{ $template->id }})" class="btn btn-xs btn-warning">
|
||||
Editar
|
||||
</button>
|
||||
<button wire:click="deleteTemplate({{ $template->id }})" class="btn btn-xs btn-error" onclick="return confirm('¿Eliminar template?')">Eliminar</button>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">No hay templates creados. Presiona "Nuevo template" para comenzar.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user