mejoras en la gestión de proyectos y documentos: se añaden nuevos campos y validaciones para optimizar la organización y el seguimiento de los mismos.
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled

This commit is contained in:
2025-10-25 11:29:20 +02:00
parent 28c225687a
commit d8ae8c8894
29 changed files with 2054 additions and 856 deletions

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Company;
use App\Models\User;
class CompanyContactController extends Controller
{
public function store(Request $request, Company $company)
{
$request->validate([
'user_id' => 'required|exists:users,id',
'position' => 'nullable|string|max:100'
]);
// Evitar duplicados
if (!$company->contacts()->where('user_id', $request->user_id)->exists()) {
$company->contacts()->attach($request->user_id, [
'position' => $request->position
]);
return redirect()->back()
->with('success', 'Contacto agregado exitosamente');
}
return redirect()->back()
->with('error', 'Este contacto ya está asociado a la empresa');
}
public function update(Request $request, Company $company, User $contact)
{
$request->validate([
'position' => 'nullable|string|max:100'
]);
$company->contacts()->updateExistingPivot($contact->id, [
'position' => $request->position
]);
return redirect()->back()
->with('success', 'Cargo del contacto actualizado');
}
public function destroy(Company $company, User $contact)
{
$company->contacts()->detach($contact->id);
return redirect()->back()
->with('success', 'Contacto eliminado de la empresa');
}
}

View File

@@ -0,0 +1,143 @@
<?php
namespace App\Http\Controllers;
use App\Models\Company;
use App\Models\User;
use Illuminate\Http\Request;
class CompanyController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
$companies = Company::orderBy('name')->paginate(10);
return view('companies.index', compact('companies'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return view('companies.form');
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'commercial_name' => 'required|string|max:255',
'status' => 'required|in:active,closed',
'address' => 'nullable|string|max:255',
'postal_code' => 'nullable|string|max:10',
'city' => 'nullable|string|max:100',
'country' => 'nullable|string|max:100',
'phone' => 'nullable|string|max:20',
'email' => 'nullable|email|max:255',
'cif' => 'nullable|string|max:20',
'logo' => 'nullable|image|max:2048|mimes:jpg,jpeg,png,gif',
]);
// Manejar la carga del logo
if ($request->hasFile('logo')) {
$validated['logo'] = $request->file('logo')->store('companies/logos', 'public');
}
Company::create($validated);
return redirect()->route('companies.index')
->with('success', 'Empresa creada exitosamente.');
}
/**
* Display the specified resource.
*/
public function show(Company $company)
{
$company->load(['contacts' => function($query) {
$query->withPivot('position');
}]);
$contacts = $company->contacts()->paginate(5);
$projects = $company->contacts()->paginate(5);//$company->projects()->paginate(5);
$availableUsers = User::whereDoesntHave('companies', function($query) use ($company) {
$query->where('company_id', $company->id);
})->get();
return view('companies.show', compact('company', 'contacts', 'projects', 'availableUsers'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Company $company)
{
return view('companies.form', compact('company'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Company $company)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'commercial_name' => 'required|string|max:255',
'status' => 'required|in:active,closed',
'address' => 'nullable|string|max:255',
'postal_code' => 'nullable|string|max:10',
'city' => 'nullable|string|max:100',
'country' => 'nullable|string|max:100',
'phone' => 'nullable|string|max:20',
'email' => 'nullable|email|max:255',
'cif' => 'nullable|string|max:20',
'logo' => 'nullable|image|max:2048|mimes:jpg,jpeg,png,gif',
]);
// Manejar la actualización del logo
if ($request->hasFile('logo')) {
// Eliminar el logo anterior si existe
if ($company->logo && Storage::disk('public')->exists($company->logo)) {
Storage::disk('public')->delete($company->logo);
}
$validated['logo'] = $request->file('logo')->store('companies/logos', 'public');
} elseif ($request->has('remove_logo')) {
// Eliminar el logo si se seleccionó la opción de eliminar
if ($company->logo && Storage::disk('public')->exists($company->logo)) {
Storage::disk('public')->delete($company->logo);
}
$validated['logo'] = null;
} else {
// Mantener el logo existente
$validated['logo'] = $company->logo;
}
$company->update($validated);
return redirect()->route('companies.index')
->with('success', 'Empresa actualizada exitosamente.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Company $company)
{
// Eliminar el logo si existe
if ($company->logo && Storage::disk('public')->exists($company->logo)) {
Storage::disk('public')->delete($company->logo);
}
$company->delete();
return redirect()->route('companies.index')
->with('success', 'Empresa eliminada exitosamente.');
}
}

View File

@@ -19,8 +19,8 @@ class ProjectController extends Controller
public function index()
{
$projects = auth()->user()->hasRole('admin')
? Project::get() // Todos los proyectos para admin
: auth()->user()->projects()->latest()->get(); // Solo proyectos asignados
? Project::get() // Todos los proyectos para admin
: auth()->user()->projects()->latest()->get(); // Solo proyectos asignados
/*
$projects = Project::whereHas('users', function($query) {
@@ -43,6 +43,7 @@ class ProjectController extends Controller
'project' => $project,
'categories' => Category::orderBy('name')->get(),
'users' => User::where('id', '!=', auth()->id())->get(),
'companies' => \App\Models\Company::all(), // Pass companies to the view
]);
}
@@ -65,9 +66,8 @@ class ProjectController extends Controller
'start_date' => 'nullable|date',
'deadline' => 'nullable|date|after:start_date',
'categories' => 'nullable|array|exists:categories,id',
//'categories' => 'required|array',
//'categories.*' => 'exists:categories,id',
'project_image_path' => 'nullable|string',
'company_id' => 'required|exists:companies,id', // Validate company_id
]);
@@ -82,8 +82,8 @@ class ProjectController extends Controller
$tempPath = $request->project_image_path;
$newPath = 'images/projects/' . basename($tempPath);
Storage::move($tempPath, $newPath); // Mover el archivo
$validated['project_image_path'] = $newPath; // Actualizar path
Storage::move($tempPath, $newPath); // Mover el archivo
$validated['project_image_path'] = $newPath; // Actualizar path
}
// Crear el proyecto con todos los datos validados
@@ -111,6 +111,7 @@ class ProjectController extends Controller
'project' => $project,
'categories' => Category::orderBy('name')->get(),
'users' => User::where('id', '!=', auth()->id())->get(),
'companies' => \App\Models\Company::all(), // Pass companies to the view
]);
}
@@ -135,8 +136,31 @@ class ProjectController extends Controller
public function update(Request $request, Project $project)
{
$this->authorize('update', $project);
// Lógica de actualización
$project->update($request->all());
$validated = $request->validate([
'reference' => 'required|string|max:255',
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'status' => 'required|in:Activo,Inactivo',
'address' => 'nullable|string|max:255',
'province' => 'nullable|string|max:100',
'country' => 'nullable|string|size:2',
'postal_code' => 'nullable|string|max:10',
'latitude' => 'required|numeric|between:-90,90',
'longitude' => 'required|numeric|between:-180,180',
'start_date' => 'nullable|date',
'deadline' => 'nullable|date|after:start_date',
'categories' => 'nullable|array|exists:categories,id',
'project_image_path' => 'nullable|string',
'company_id' => 'required|exists:companies,id', // Validate company_id
]);
$project->update($validated);
if ($request->has('categories')) {
$project->categories()->sync($request->categories);
}
return redirect()->route('projects.show', $project)->with('success', 'Project updated successfully.');
}

View File

@@ -65,6 +65,8 @@ class UserController extends Controller
'email' => 'required|email|unique:users',
'phone' => 'nullable|string|max:20',
'address' => 'nullable|string|max:255',
'user_type' => 'required|integer|in:0,1,2',
'company_id' => 'nullable|exists:companies,id', // Si se usa una relación con empresas
'profile_photo_path' => 'nullable|string' // Ruta de la imagen subida por Livewire
]);
@@ -80,7 +82,9 @@ class UserController extends Controller
'address' => $validated['address'],
'access_start' => $validated['start_date'],
'access_end' => $validated['end_date'],
'is_active' => true,
'is_active' => $validated['is_active'] ?? false, // Por defecto, inactivo
'user_type' => $validated['user_type'] ?? 0, // 0: Usuario, 1: Administrador, 2: Super Admin
'company_id' => $validated['company_id'] ?? null, // Si se usa una relación con empresas
'profile_photo_path' => $validated['profile_photo_path'] ?? null
]);

65
app/Models/Company.php Normal file
View File

@@ -0,0 +1,65 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Company extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'commercial_name',
'status',
'address',
'postal_code',
'city',
'country',
'phone',
'email',
'cif',
'logo'
];
protected $casts = [
'created_at' => 'datetime:d/m/Y H:i',
'updated_at' => 'datetime:d/m/Y H:i',
];
public function getStatusColorAttribute()
{
return [
'active' => 'bg-green-100 text-green-800',
'closed' => 'bg-red-100 text-red-800',
][$this->status] ?? 'bg-gray-100 text-gray-800';
}
public function getStatusTextAttribute()
{
return [
'active' => 'Activo',
'closed' => 'Cerrado',
][$this->status] ?? 'Desconocido';
}
public function contacts(): BelongsToMany
{
return $this->belongsToMany(User::class, 'company_contacts')
->withPivot('position')
->withTimestamps();
}
public function users()
{
return $this->hasMany(User::class);
}
public function projects()
{
return $this->hasMany(Project::class);
}
}

View File

@@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
protected $fillable = [
'reference',
'name',
'description',
'creator_id',
@@ -22,6 +23,7 @@ class Project extends Model
'icon',
'start_date',
'deadline',
'company_id',
// Agrega cualquier otro campo nuevo aquí
];
@@ -81,5 +83,8 @@ class Project extends Model
return $this->belongsToMany(Category::class);
}
public function company()
{
return $this->belongsTo(Company::class);
}
}

View File

@@ -10,6 +10,7 @@ use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Spatie\Permission\Traits\HasRoles;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class User extends Authenticatable
{
@@ -109,5 +110,17 @@ class User extends Authenticatable
{
return $this->profile_photo ? Storage::url($this->profile_photo) : asset('images/default-user.png');
}
public function companies(): BelongsToMany
{
return $this->belongsToMany(Company::class, 'company_contacts')
->withPivot('position')
->withTimestamps();
}
public function company()
{
return $this->belongsTo(Company::class);
}
}

View File

@@ -21,6 +21,10 @@ class Accordion extends Component
*/
public function render(): View|Closure|string
{
return view('components.accordion');
return <<<'blade'
<div>
<!-- Simplicity is the essence of happiness. - Cedric Bledsoe -->
</div>
blade;
}
}