añadir nuevas funcionalidades
This commit is contained in:
23
app/Helpers/FileHelper.php
Normal file
23
app/Helpers/FileHelper.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
// En App\Helpers\FileHelper.php
|
||||
namespace App\Helpers;
|
||||
|
||||
class FileHelper
|
||||
{
|
||||
public static function getFileType($filename)
|
||||
{
|
||||
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
|
||||
return match($extension) {
|
||||
'pdf' => 'pdf',
|
||||
'doc', 'docx' => 'word',
|
||||
'xls', 'xlsx' => 'excel',
|
||||
'ppt', 'pptx' => 'powerpoint',
|
||||
'jpg', 'jpeg', 'png', 'gif' => 'image',
|
||||
'zip', 'rar' => 'archive',
|
||||
'txt' => 'text',
|
||||
default => 'document'
|
||||
};
|
||||
}
|
||||
}
|
||||
82
app/Http/Controllers/DocumentCommentController.php
Normal file
82
app/Http/Controllers/DocumentCommentController.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\DocumentComment;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DocumentCommentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request, Document $document)
|
||||
{
|
||||
$request->validate([
|
||||
'content' => 'required|string|max:1000',
|
||||
'page' => 'required|integer|min:1',
|
||||
'x' => 'required|numeric|between:0,1',
|
||||
'y' => 'required|numeric|between:0,1'
|
||||
]);
|
||||
|
||||
$document->comments()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'content' => $request->content,
|
||||
'page' => $request->page,
|
||||
'x' => $request->x,
|
||||
'y' => $request->y,
|
||||
'parent_id' => $request->parent_id
|
||||
]);
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(DocumentComment $documentComment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(DocumentComment $documentComment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, DocumentComment $documentComment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(DocumentComment $documentComment)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,13 @@ use App\Jobs\ProcessDocumentOCR;
|
||||
use App\Models\Document;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DocumentController extends Controller
|
||||
{
|
||||
public $comments=[];
|
||||
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
@@ -56,7 +60,19 @@ class DocumentController extends Controller
|
||||
*/
|
||||
public function show(Document $document)
|
||||
{
|
||||
//
|
||||
$this->authorize('view', $document); // Si usas políticas
|
||||
|
||||
if (!Storage::exists($document->file_path)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$document->url = Storage::url($document->file_path);
|
||||
|
||||
return view('documents.show', [
|
||||
'document' => $document,
|
||||
'versions' => $document->versions()->latest()->get(),
|
||||
'comments' => $this->comments,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,12 +17,16 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = Project::withCount('documents')
|
||||
->whereHas('users', function($query) {
|
||||
$projects = auth()->user()->hasRole('admin')
|
||||
? Project::get() // Todos los proyectos para admin
|
||||
: auth()->user()->projects()->latest()->get(); // Solo proyectos asignados
|
||||
|
||||
/*
|
||||
$projects = Project::whereHas('users', function($query) {
|
||||
$query->where('user_id', auth()->id());
|
||||
})
|
||||
->filter(['search' => request('search')])
|
||||
->paginate(9);
|
||||
->paginate(9);*/
|
||||
|
||||
return view('projects.index', compact('projects'));
|
||||
}
|
||||
@@ -47,24 +51,24 @@ class ProjectController extends Controller
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'description' => 'required|string',
|
||||
'status' => 'required|in:active,inactive',
|
||||
'team' => 'sometimes|array',
|
||||
'team.*' => 'exists:users,id',
|
||||
'description' => 'nullable|string',
|
||||
'status' => 'required|in:Activo,Inactivo',
|
||||
//'team' => 'sometimes|array',
|
||||
//'team.*' => 'exists:users,id',
|
||||
'project_image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'province' => 'nullable|string|max:100',
|
||||
'country' => 'nullable|string|size:2',
|
||||
//'country' => 'nullable|string|size:2',
|
||||
'postal_code' => 'nullable|string|max:10',
|
||||
'latitude' => 'required|numeric|between:-90,90',
|
||||
'longitude' => 'required|numeric|between:-180,180',
|
||||
'icon' => 'nullable|in:'.implode(',', config('project.icons')),
|
||||
//'icon' => 'nullable|in:'.implode(',', config('project.icons')),
|
||||
'start_date' => 'nullable|date',
|
||||
'deadline' => 'nullable|date|after:start_date',
|
||||
'categories' => 'array|exists:categories,id',
|
||||
'categories' => 'nullable|array|exists:categories,id',
|
||||
//'categories' => 'required|array',
|
||||
'categories.*' => 'exists:categories,id',
|
||||
'documents.*' => 'file|max:5120|mimes:pdf,docx,xlsx,jpg,png'
|
||||
//'categories.*' => 'exists:categories,id',
|
||||
//'documents.*' => 'file|max:5120|mimes:pdf,docx,xlsx,jpg,png'
|
||||
]);
|
||||
|
||||
|
||||
@@ -89,6 +93,7 @@ class ProjectController extends Controller
|
||||
}
|
||||
|
||||
// Manejar documentos adjuntos
|
||||
/*
|
||||
if($request->hasFile('documents')) {
|
||||
foreach ($request->file('documents') as $file) {
|
||||
$project->documents()->create([
|
||||
@@ -96,14 +101,12 @@ class ProjectController extends Controller
|
||||
'original_name' => $file->getClientOriginalName()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
return redirect()->route('projects.show', $project)
|
||||
->with('success', 'Proyecto creado exitosamente');
|
||||
return redirect()->route('projects.show', $project)->with('success', 'Proyecto creado exitosamente');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withInput()
|
||||
->with('error', 'Error al crear el proyecto: ' . $e->getMessage());
|
||||
return back()->withInput()->with('error', 'Error al crear el proyecto: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +115,7 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function show(Project $project)
|
||||
{
|
||||
$this->authorize('view', $project); // Si usas políticas
|
||||
//$this->authorize('view', $project); // Si usas políticas
|
||||
$project->load(['categories', 'documents']);
|
||||
|
||||
return view('projects.show', [
|
||||
|
||||
@@ -5,7 +5,14 @@ namespace App\Http\Controllers;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Http\Requests\UpdateUserRequest;
|
||||
use App\Rules\PasswordRule;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class UserController extends Controller
|
||||
@@ -13,7 +20,7 @@ class UserController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('viewAny', User::class);
|
||||
$users = User::with('roles')->paginate(10);
|
||||
$users = User::paginate(10);
|
||||
return view('users.index', compact('users'));
|
||||
}
|
||||
|
||||
@@ -27,24 +34,62 @@ class UserController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$data = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|unique:users',
|
||||
'password' => 'required|min:8|confirmed',
|
||||
'roles' => 'array'
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make($data['password'])
|
||||
]);
|
||||
|
||||
$user->syncRoles($data['roles'] ?? []);
|
||||
|
||||
return redirect()->route('users.index')
|
||||
->with('success', 'Usuario creado exitosamente');
|
||||
try {
|
||||
// Validación de datos
|
||||
$validated = $request->validate([
|
||||
'title' => 'nullable|string|max:10',
|
||||
'first_name' => 'required|string|max:50',
|
||||
'last_name' => 'required|string|max:50',
|
||||
'username' => 'required|string|unique:users|max:30',
|
||||
'password' => ['required',
|
||||
new PasswordRule(
|
||||
minLength: 12,
|
||||
requireUppercase: true,
|
||||
requireNumeric: true,
|
||||
requireSpecialCharacter: true,
|
||||
//uncompromised: true, // Verificar contra Have I Been Pwned
|
||||
//requireLetters: true
|
||||
),
|
||||
Password::defaults()->mixedCase()->numbers()->symbols()
|
||||
->uncompromised(3) // Número mínimo de apariciones en brechas
|
||||
],
|
||||
'start_date' => 'nullable|date',
|
||||
'end_date' => 'nullable|date|after_or_equal:start_date',
|
||||
'email' => 'required|email|unique:users',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'profile_photo_path' => 'nullable|string' // Ruta de la imagen subida por Livewire
|
||||
]);
|
||||
|
||||
// Creación del usuario
|
||||
$user = User::create([
|
||||
'title' => $validated['title'],
|
||||
'first_name' => $validated['first_name'],
|
||||
'last_name' => $validated['last_name'],
|
||||
'username' => $validated['username'],
|
||||
'password' => Hash::make($validated['password']),
|
||||
'email' => $validated['email'],
|
||||
'phone' => $validated['phone'],
|
||||
'address' => $validated['address'],
|
||||
'access_start' => $validated['start_date'],
|
||||
'access_end' => $validated['end_date'],
|
||||
'is_active' => true,
|
||||
'profile_photo_path' => $validated['profile_photo_path'] ?? null
|
||||
]);
|
||||
|
||||
if ($request->hasFile('image_path')) {
|
||||
$path = $request->file('image_path')->store('public/photos');
|
||||
$user->profile_photo_path = basename($path);
|
||||
$user->save();
|
||||
}
|
||||
|
||||
// Asignación de roles (opcional, usando Spatie Permissions)
|
||||
// $user->assignRole('user');
|
||||
|
||||
return redirect()->route('users.index')->with('success', 'Usuario creado exitosamente.')->with('temp_password', $validated['password']);;
|
||||
} catch (\Exception $e) {
|
||||
return back()->withInput()->with('error', 'Error al crear el usuario: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function edit(User $user)
|
||||
@@ -52,17 +97,110 @@ class UserController extends Controller
|
||||
$this->authorize('update', $user);
|
||||
$roles = Role::all();
|
||||
$userRoles = $user->roles->pluck('id')->toArray();
|
||||
|
||||
return view('users.edit', compact('user', 'roles', 'userRoles'));
|
||||
return view('users.create', compact('user', 'roles', 'userRoles'));
|
||||
}
|
||||
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
public function update(Request $request, User $user)
|
||||
{
|
||||
$user->update($request->validated());
|
||||
$user->syncRoles($request->roles);
|
||||
|
||||
return redirect()->route('users.index')
|
||||
->with('success', 'Usuario actualizado correctamente');
|
||||
try {
|
||||
// Validación de datos
|
||||
$validated = $request->validate([
|
||||
'title' => 'nullable|string|max:10',
|
||||
'first_name' => 'required|string|max:50',
|
||||
'last_name' => 'required|string|max:50',
|
||||
'username' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:30',
|
||||
Rule::unique('users')->ignore($user->id)
|
||||
],
|
||||
'password' => [
|
||||
'nullable',
|
||||
Password::min(12)
|
||||
->mixedCase()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised(3)
|
||||
],
|
||||
'start_date' => 'nullable|date',
|
||||
'end_date' => 'nullable|date|after_or_equal:start_date',
|
||||
'email' => [
|
||||
'required',
|
||||
'email',
|
||||
Rule::unique('users')->ignore($user->id)
|
||||
],
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'profile_photo_path' => 'nullable|string', // Añadido para la ruta de la imagen
|
||||
//'is_active' => 'nullable|boolean' // Añadido para el estado activo
|
||||
]);
|
||||
|
||||
// Preparar datos para actualización
|
||||
$updateData = [
|
||||
'title' => $validated['title'],
|
||||
'first_name' => $validated['first_name'],
|
||||
'last_name' => $validated['last_name'],
|
||||
'username' => $validated['username'],
|
||||
'email' => $validated['email'],
|
||||
'phone' => $validated['phone'],
|
||||
'address' => $validated['address'],
|
||||
'access_start' => $validated['start_date'],
|
||||
'access_end' => $validated['end_date'],
|
||||
'is_active' => $validated['is_active'] ?? false,
|
||||
'profile_photo_path' => $validated['profile_photo_path'] ?? $user->profile_photo_path
|
||||
];
|
||||
|
||||
// Actualizar contraseña solo si se proporciona
|
||||
if (!empty($validated['password'])) {
|
||||
$updateData['password'] = Hash::make($validated['password']);
|
||||
}
|
||||
|
||||
// Eliminar imagen anterior si se está actualizando
|
||||
if (isset($validated['profile_photo_path']) && $user->profile_photo_path) {
|
||||
Storage::disk('public')->delete($user->profile_photo_path);
|
||||
}
|
||||
|
||||
// Actualizar el usuario
|
||||
$user->update($updateData);
|
||||
|
||||
// Redireccionar con mensaje de éxito
|
||||
return redirect()->route('users.show', $user)
|
||||
->with('success', 'Usuario actualizado exitosamente');
|
||||
|
||||
} catch (ValidationException $e) {
|
||||
return redirect()->back()->withErrors($e->validator)->withInput();
|
||||
|
||||
} catch (QueryException $e) {
|
||||
$errorCode = $e->errorInfo[1];
|
||||
$errorMessage = 'Error al actualizar el usuario: ';
|
||||
|
||||
if ($errorCode == 1062) {
|
||||
$errorMessage .= 'El nombre de usuario o correo electrónico ya está en uso';
|
||||
} else {
|
||||
$errorMessage .= 'Error en la base de datos';
|
||||
}
|
||||
|
||||
Log::error("Error actualizando usuario ID {$user->id}: " . $e->getMessage());
|
||||
return redirect()->back()->with('error', $errorMessage)->withInput();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error general actualizando usuario ID {$user->id}: " . $e->getMessage());
|
||||
return redirect()->back()->with('error', 'Ocurrió un error inesperado al actualizar el usuario')->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
public function show(User $user)
|
||||
{
|
||||
$previousUser = User::where('id', '<', $user->id)->latest('id')->first();
|
||||
$nextUser = User::where('id', '>', $user->id)->oldest('id')->first();
|
||||
$permissionGroups = $this->getPermissionGroups($user);
|
||||
|
||||
return view('users.show', [
|
||||
'user' => $user,
|
||||
'previousUser' => $previousUser,
|
||||
'nextUser' => $nextUser,
|
||||
'permissionGroups' => $permissionGroups,
|
||||
]);
|
||||
}
|
||||
|
||||
public function updatePassword(Request $request, User $user)
|
||||
@@ -94,4 +232,45 @@ class UserController extends Controller
|
||||
return redirect()->route('users.index')
|
||||
->with('success', 'Usuario eliminado correctamente');
|
||||
}
|
||||
|
||||
private function getPermissionGroups(User $user)
|
||||
{
|
||||
// Obtener todos los permisos disponibles
|
||||
$allPermissions = Permission::all();
|
||||
|
||||
// Agrupar permisos por tipo (asumiendo que los nombres siguen el formato "tipo.acción")
|
||||
$grouped = $allPermissions->groupBy(function ($permission) {
|
||||
return explode('.', $permission->name)[0]; // Extrae "user" de "user.create"
|
||||
});
|
||||
|
||||
// Formatear para la vista
|
||||
$groups = [];
|
||||
foreach ($grouped as $groupName => $permissions) {
|
||||
$groups[$groupName] = [
|
||||
'name' => ucfirst($groupName),
|
||||
'permissions' => $permissions->map(function ($permission) use ($user) {
|
||||
return [
|
||||
'id' => $permission->id,
|
||||
'name' => $permission->name,
|
||||
'description' => $this->getPermissionDescription($permission->name),
|
||||
'enabled' => $user->hasPermissionTo($permission)
|
||||
];
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
private function getPermissionDescription($permissionName)
|
||||
{
|
||||
$descriptions = [
|
||||
'user.create' => 'Crear nuevos usuarios',
|
||||
'user.edit' => 'Editar usuarios existentes',
|
||||
'document.view' => 'Ver documentos',
|
||||
// Agrega más descripciones según necesites
|
||||
];
|
||||
|
||||
return $descriptions[$permissionName] ?? str_replace('.', ' ', $permissionName);
|
||||
}
|
||||
}
|
||||
66
app/Livewire/CountrySelect.php
Normal file
66
app/Livewire/CountrySelect.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class CountrySelect extends Component
|
||||
{
|
||||
public $selectedCountry;
|
||||
public $countrySearch = '';
|
||||
public $search = '';
|
||||
public $isOpen = false;
|
||||
public $initialCountry;
|
||||
|
||||
protected $rules = [
|
||||
'selectedCountry' => 'required',
|
||||
];
|
||||
|
||||
public function mount($initialCountry = null)
|
||||
{
|
||||
$this->selectedCountry = $initialCountry;
|
||||
}
|
||||
|
||||
public function selectCountry($code)
|
||||
{
|
||||
$this->selectedCountry = $code;
|
||||
$this->isOpen = false;
|
||||
$this->search = ''; // Limpiar la búsqueda al seleccionar
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$countries = collect(config('countries'))
|
||||
->when($this->search, function($collection) {
|
||||
// Corregimos el filtrado aquí
|
||||
return $collection->filter(function($name, $code) {
|
||||
$searchLower = strtolower($this->search);
|
||||
$nameLower = strtolower($name);
|
||||
$codeLower = strtolower($code);
|
||||
|
||||
return str_contains($nameLower, $searchLower) ||
|
||||
str_contains($codeLower, $searchLower);
|
||||
});
|
||||
});
|
||||
|
||||
return view('livewire.country-select', compact('countries'));
|
||||
}
|
||||
|
||||
public function formattedCountry($code, $name)
|
||||
{
|
||||
return $this->getFlagEmoji($code).' '.$name.' ('.strtoupper($code).')';
|
||||
}
|
||||
|
||||
protected function getFlagEmoji($countryCode)
|
||||
{
|
||||
$countryCode = strtoupper($countryCode);
|
||||
$flagOffset = 0x1F1E6;
|
||||
$asciiOffset = 0x41;
|
||||
|
||||
$firstChar = ord($countryCode[0]) - $asciiOffset + $flagOffset;
|
||||
$secondChar = ord($countryCode[1]) - $asciiOffset + $flagOffset;
|
||||
|
||||
return mb_convert_encoding('&#'.intval($firstChar).';', 'UTF-8', 'HTML-ENTITIES')
|
||||
. mb_convert_encoding('&#'.intval($secondChar).';', 'UTF-8', 'HTML-ENTITIES');
|
||||
}
|
||||
}
|
||||
86
app/Livewire/ImageUploader.php
Normal file
86
app/Livewire/ImageUploader.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ImageUploader extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $image;
|
||||
public $imagePath;
|
||||
public $fieldName; // Nombre del campo para el formulario
|
||||
public $label = 'Subir imagen'; // Etiqueta personalizable
|
||||
public $hover = false;
|
||||
|
||||
// Recibir parámetros si es necesario
|
||||
public function mount($fieldName = 'image_path', $label = 'Subir imagen')
|
||||
{
|
||||
$this->fieldName = $fieldName;
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
public function updatedImage()
|
||||
{
|
||||
$this->validate([
|
||||
'image' => 'image|max:2048', // 2MB Max
|
||||
]);
|
||||
|
||||
// Subir automáticamente al seleccionar
|
||||
$this->uploadImage();
|
||||
}
|
||||
|
||||
public function uploadImage()
|
||||
{
|
||||
$this->validate([
|
||||
'image' => 'required|image|max:2048',
|
||||
]);
|
||||
|
||||
// Guardar la imagen
|
||||
$this->imagePath = $this->image->store('uploads', 'public');
|
||||
// Emitir evento con el nombre del campo y la ruta
|
||||
$this->dispatch('imageUploaded',
|
||||
field: $this->fieldName,
|
||||
path: $this->imagePath
|
||||
);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate([
|
||||
'image' => 'required|image|max:2048',
|
||||
]);
|
||||
|
||||
// Guardar la imagen
|
||||
$this->imagePath = $this->image->store('images', 'public');
|
||||
|
||||
// Emitir evento con el nombre del campo y la ruta
|
||||
$this->emit('imageUploaded', [
|
||||
'field' => $this->fieldName,
|
||||
'path' => $this->imagePath
|
||||
]);
|
||||
}
|
||||
|
||||
public function removeImage()
|
||||
{
|
||||
$this->reset(['image', 'imagePath']);
|
||||
|
||||
// Cambiar emit por dispatch en Livewire v3+
|
||||
$this->dispatch('imageRemoved',
|
||||
field: $this->fieldName
|
||||
);
|
||||
}
|
||||
|
||||
public function toggleHover($status)
|
||||
{
|
||||
$this->hover = $status;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.image-uploader');
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,15 @@ use App\Models\Project;
|
||||
use App\Models\Folder;
|
||||
use App\Models\Document;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ProjectShow extends Component
|
||||
{
|
||||
|
||||
use WithFileUploads;
|
||||
|
||||
protected $middleware = ['auth']; // Añade esto
|
||||
|
||||
public Project $project;
|
||||
public ?Folder $currentFolder = null;
|
||||
public $expandedFolders = [];
|
||||
@@ -22,8 +25,13 @@ class ProjectShow extends Component
|
||||
public $folderName = '';
|
||||
|
||||
public $selectedFolderId = null;
|
||||
public $showFolderModal = false;
|
||||
public $showUploadModal = false;
|
||||
public $tempFiles = [];
|
||||
public $selectedFiles = []; // Archivos temporales en el modal
|
||||
public $uploadProgress = [];
|
||||
|
||||
|
||||
|
||||
public function mount(Project $project)
|
||||
{
|
||||
$this->project = $project->load('rootFolders');
|
||||
@@ -57,27 +65,27 @@ class ProjectShow extends Component
|
||||
})
|
||||
]
|
||||
]);
|
||||
|
||||
Folder::create([
|
||||
'name' => $this->folderName,
|
||||
'project_id' => $this->project->id,
|
||||
'parent_id' => $this->currentFolder?->id
|
||||
'parent_id' => $this->currentFolder?->id,
|
||||
]);
|
||||
|
||||
$this->hideCreateFolderModal();
|
||||
$this->reset('folderName');
|
||||
$this->project->load('rootFolders'); // Recargar carpetas raíz
|
||||
$this->project->load('rootFolders'); // Recargar carpetas raíz
|
||||
if ($this->currentFolder) {
|
||||
$this->currentFolder->load('children'); // Recargar hijos si está en una subcarpeta
|
||||
}
|
||||
$this->project->refresh();
|
||||
}
|
||||
|
||||
public function uploadFiles(): void
|
||||
/*public function uploadFiles(): void
|
||||
{
|
||||
$this->validate([
|
||||
'files.*' => 'file|max:10240|mimes:pdf,docx,xlsx,jpg,png'
|
||||
]);
|
||||
|
||||
dd($this->files);
|
||||
foreach ($this->files as $file) {
|
||||
Document::create([
|
||||
'name' => $file->getClientOriginalName(),
|
||||
@@ -92,7 +100,7 @@ class ProjectShow extends Component
|
||||
$this->currentFolder->refresh(); // Recargar documentos
|
||||
}
|
||||
$this->reset('files');
|
||||
}
|
||||
}*/
|
||||
|
||||
public function getDocumentsProperty()
|
||||
{
|
||||
@@ -116,11 +124,128 @@ class ProjectShow extends Component
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
public function showCreateFolderModal()
|
||||
{
|
||||
$this->folderName = '';
|
||||
$this->showFolderModal = true;
|
||||
}
|
||||
|
||||
public function hideCreateFolderModal()
|
||||
{
|
||||
$this->showFolderModal = false;
|
||||
}
|
||||
|
||||
// Método para abrir el modal
|
||||
public function openUploadModal(): void
|
||||
{
|
||||
$this->showUploadModal = true;
|
||||
}
|
||||
|
||||
// Método para manejar archivos seleccionados
|
||||
public function selectFiles($files): void
|
||||
{
|
||||
$this->validate([
|
||||
'selectedFiles.*' => 'file|max:10240|mimes:pdf,docx,xlsx,jpg,png'
|
||||
]);
|
||||
|
||||
$this->selectedFiles = array_merge($this->selectedFiles, $files);
|
||||
}
|
||||
|
||||
// Método para eliminar un archivo de la lista
|
||||
public function removeFile($index): void
|
||||
{
|
||||
unset($this->selectedFiles[$index]);
|
||||
$this->selectedFiles = array_values($this->selectedFiles); // Reindexar array
|
||||
}
|
||||
|
||||
// Método para confirmar y guardar
|
||||
public function uploadFiles(): void
|
||||
{
|
||||
foreach ($this->selectedFiles as $file) {
|
||||
Document::create([
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'file_path' => $file->store("projects/{$this->project->id}/documents"),
|
||||
'project_id' => $this->project->id, // Asegurar que se envía
|
||||
'folder_id' => $this->currentFolder?->id,
|
||||
//'user_id' => Auth::id(),
|
||||
//'status' => 'active' // Añadir si tu modelo lo requiere
|
||||
]);
|
||||
}
|
||||
|
||||
$this->resetUpload();
|
||||
$this->project->refresh();
|
||||
}
|
||||
|
||||
// Método para procesar los archivos
|
||||
protected function processFiles(): void
|
||||
{
|
||||
foreach ($this->files as $file) {
|
||||
Document::create([
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'file_path' => $file->store("projects/{$this->project->id}/documents"),
|
||||
'project_id' => $this->project->id, // Asegurar que se envía
|
||||
'folder_id' => $this->currentFolder?->id,
|
||||
//'user_id' => Auth::id(),
|
||||
//'status' => 'active' // Añadir si tu modelo lo requiere
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Método para resetear
|
||||
public function resetUpload(): void
|
||||
{
|
||||
$this->reset(['selectedFiles', 'showUploadModal', 'uploadProgress']);
|
||||
}
|
||||
|
||||
#[On('upload-progress')]
|
||||
public function updateProgress($name, $progress)
|
||||
{
|
||||
$this->uploadProgress[$name] = $progress;
|
||||
}
|
||||
|
||||
public function addFiles($files)
|
||||
{
|
||||
$this->validate([
|
||||
'selectedFiles.*' => 'file|max:10240|mimes:pdf,docx,xlsx,jpg,png'
|
||||
]);
|
||||
|
||||
$this->selectedFiles = array_merge($this->selectedFiles, $files);
|
||||
}
|
||||
|
||||
public function startUpload()
|
||||
{
|
||||
foreach ($this->selectedFiles as $file) {
|
||||
try {
|
||||
$path = $file->store(
|
||||
"projects/{$this->project->id}/".($this->currentFolder ? "folders/{$this->currentFolder->id}" : ""),
|
||||
'public'
|
||||
);
|
||||
|
||||
Document::create([
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'file_path' => $path,
|
||||
'project_id' => $this->project->id,
|
||||
'folder_id' => $this->currentFolder?->id,
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->addError('upload', "Error subiendo {$file->getClientOriginalName()}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->resetUpload();
|
||||
$this->project->refresh();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project-show')
|
||||
->layout('layouts.livewire-app', [
|
||||
'title' => $this->project->name
|
||||
]);
|
||||
return view('livewire.project.show')->layout('components.layouts.documents', [
|
||||
'title' => __('Project: :name', ['name' => $this->project->name]),
|
||||
'breadcrumbs' => [
|
||||
['name' => __('Projects'), 'url' => route('projects.index')],
|
||||
['name' => $this->project->name, 'url' => route('projects.show', $this->project)],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
81
app/Livewire/UserPhotoUpload.php
Normal file
81
app/Livewire/UserPhotoUpload.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\User;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UserPhotoUpload extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $photo;
|
||||
public $existingPhoto;
|
||||
public $tempPhoto;
|
||||
public $userId;
|
||||
|
||||
protected $listeners = ['photoUpdated' => 'refreshPhoto'];
|
||||
|
||||
public function mount($existingPhoto = null, $userId = null)
|
||||
{
|
||||
$this->existingPhoto = $existingPhoto;
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function uploadPhoto()
|
||||
{
|
||||
$this->validate([
|
||||
'photo' => 'image|max:2048|mimes:jpg,png,jpeg,gif',
|
||||
]);
|
||||
|
||||
$this->tempPhoto = $this->photo->temporaryUrl();
|
||||
}
|
||||
|
||||
public function updatedPhoto()
|
||||
{
|
||||
$this->validate([
|
||||
'photo' => 'image|max:2048|mimes:jpg,png,jpeg,gif',
|
||||
]);
|
||||
|
||||
$this->tempPhoto = $this->photo->temporaryUrl();
|
||||
|
||||
// Emitir evento con la foto temporal
|
||||
$this->emit('photoUploaded', $this->photo->getRealPath());
|
||||
}
|
||||
|
||||
public function removePhoto()
|
||||
{
|
||||
$this->reset('photo', 'tempPhoto');
|
||||
}
|
||||
|
||||
public function deletePhoto()
|
||||
{
|
||||
if ($this->existingPhoto) {
|
||||
Storage::delete('public/photos/' . $this->existingPhoto);
|
||||
|
||||
if ($this->userId) {
|
||||
$user = User::find($this->userId);
|
||||
$user->update(['profile_photo_path' => null]);
|
||||
}
|
||||
|
||||
$this->existingPhoto = null;
|
||||
$this->emit('photoDeleted');
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshPhoto()
|
||||
{
|
||||
$this->existingPhoto = User::find($this->userId)->profile_photo_path;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.user-photo-upload', [
|
||||
//'photo' => $this->photo,
|
||||
'tempPhoto' => $this->tempPhoto,
|
||||
'existingPhoto' => $this->existingPhoto,
|
||||
]);
|
||||
}
|
||||
}
|
||||
109
app/Livewire/UserTable.php
Normal file
109
app/Livewire/UserTable.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use App\Models\User;
|
||||
|
||||
class UserTable extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
|
||||
public $sortField = 'first_name';
|
||||
public $sortDirection = 'asc';
|
||||
|
||||
public $columns = [
|
||||
'full_name' => true,
|
||||
'is_active' => true,
|
||||
'username' => true,
|
||||
'email' => true,
|
||||
'phone' => false,
|
||||
'access_start' => false,
|
||||
'created_at' => false,
|
||||
'is_active' => false,
|
||||
];
|
||||
|
||||
public $filters = [
|
||||
'full_name' => '',
|
||||
'is_active' => '',
|
||||
'username' => '',
|
||||
'email' => '',
|
||||
'phone' => '',
|
||||
'access_start' => '',
|
||||
'created_at' => ''
|
||||
];
|
||||
|
||||
protected $queryString = [
|
||||
'filters' => ['except' => ''],
|
||||
'columns' => ['except' => '']
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
// Recuperar preferencias de columnas de la sesión
|
||||
$this->columns = session('user_columns', $this->columns);
|
||||
}
|
||||
|
||||
public function updatedColumns()
|
||||
{
|
||||
// Guardar preferencias en sesión
|
||||
session(['user_columns' => $this->columns]);
|
||||
}
|
||||
|
||||
public function sortBy($field)
|
||||
{
|
||||
if ($this->sortField === $field) {
|
||||
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
$this->sortDirection = 'asc';
|
||||
}
|
||||
|
||||
$this->sortField = $field;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$users = User::query()
|
||||
->when($this->filters['full_name'], fn($q, $search) =>
|
||||
$q->whereRaw("CONCAT(title, ' ', first_name, ' ', last_name) LIKE ?", ["%$search%"]))
|
||||
->when($this->sortField === 'full_name', fn($q) =>
|
||||
$q->orderByRaw("CONCAT(title, ' ', first_name, ' ', last_name) " . $this->sortDirection)
|
||||
)
|
||||
->when($this->sortField === 'created_at', fn($q) =>
|
||||
$q->orderBy('created_at', $this->sortDirection)
|
||||
)
|
||||
->when($this->sortField === 'access_start', fn($q) =>
|
||||
$q->orderBy('access_start', $this->sortDirection)
|
||||
)
|
||||
->when(in_array($this->sortField, ['username', 'email', 'phone']), fn($q) =>
|
||||
$q->orderBy($this->sortField, $this->sortDirection)
|
||||
)
|
||||
->when($this->filters['full_name'], fn($q, $search) =>
|
||||
$q->whereRaw("CONCAT(title, ' ', first_name, ' ', last_name) LIKE ?", ["%$search%"]))
|
||||
->when($this->filters['username'], fn($q, $search) =>
|
||||
$q->where('username', 'LIKE', "%$search%"))
|
||||
->when($this->filters['email'], fn($q, $search) =>
|
||||
$q->where('email', 'LIKE', "%$search%"))
|
||||
->when($this->filters['phone'], fn($q, $search) =>
|
||||
$q->where('phone', 'LIKE', "%$search%"))
|
||||
->when($this->filters['access_start'], fn($q, $date) =>
|
||||
$q->whereDate('access_start', $date))
|
||||
->when($this->filters['created_at'], fn($q, $date) =>
|
||||
$q->whereDate('created_at', $date))
|
||||
->paginate(10);
|
||||
|
||||
return view('livewire.user-table', [
|
||||
'users' => $users,
|
||||
'available_columns' => [
|
||||
'full_name' => 'Nombre Completo',
|
||||
'username' => 'Usuario',
|
||||
'email' => 'Correo',
|
||||
'phone' => 'Teléfono',
|
||||
'access_start' => 'Fecha de acceso',
|
||||
'created_at' => 'Fecha creación',
|
||||
'is_active' => 'Estado'
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
36
app/Models/Comment.php
Normal file
36
app/Models/Comment.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class Comment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'content',
|
||||
'user_id',
|
||||
'document_id',
|
||||
'parent_id',
|
||||
'page',
|
||||
'x',
|
||||
'y'
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function document()
|
||||
{
|
||||
return $this->belongsTo(Document::class);
|
||||
}
|
||||
|
||||
public function replies()
|
||||
{
|
||||
return $this->hasMany(Comment::class, 'parent_id');
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,11 @@ class Document extends Model
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'status',
|
||||
'project_id',
|
||||
'file_path',
|
||||
'project_id', // Asegurar que está en fillable
|
||||
'folder_id',
|
||||
'current_version_id'
|
||||
'user_id',
|
||||
'status'
|
||||
];
|
||||
|
||||
|
||||
@@ -36,6 +37,7 @@ class Document extends Model
|
||||
return $this->hasMany(Comment::class)->whereNull('parent_id');
|
||||
}
|
||||
|
||||
|
||||
public function createVersion($file)
|
||||
{
|
||||
return $this->versions()->create([
|
||||
|
||||
22
app/Models/DocumentComment.php
Normal file
22
app/Models/DocumentComment.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DocumentComment extends Model
|
||||
{
|
||||
protected $fillable = ['content', 'page', 'x', 'y', 'parent_id'];
|
||||
|
||||
public function user() {
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function children() {
|
||||
return $this->hasMany(DocumentComment::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function parent() {
|
||||
return $this->belongsTo(DocumentComment::class, 'parent_id');
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@ class Folder extends Model
|
||||
'name',
|
||||
'parent_id',
|
||||
'project_id',
|
||||
'icon',
|
||||
'color',
|
||||
'description',
|
||||
//'icon',
|
||||
//'color',
|
||||
//'description',
|
||||
];
|
||||
|
||||
public function descendants()
|
||||
|
||||
@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
@@ -21,9 +22,18 @@ class User extends Authenticatable
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'title',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'username',
|
||||
'email',
|
||||
'password',
|
||||
'phone',
|
||||
'address',
|
||||
'access_start',
|
||||
'access_end',
|
||||
'is_active',
|
||||
'profile_photo_path',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -39,26 +49,25 @@ class User extends Authenticatable
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'is_protected' => 'boolean',
|
||||
];
|
||||
}
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'access_start' => 'date',
|
||||
'access_end' => 'date',
|
||||
'is_active' => 'boolean'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user's initials
|
||||
*/
|
||||
public function initials(): string
|
||||
{
|
||||
return Str::of($this->name)
|
||||
/*return Str::of($this->name)
|
||||
->explode(' ')
|
||||
->map(fn (string $name) => Str::of($name)->substr(0, 1))
|
||||
->implode('');
|
||||
->implode('');*/
|
||||
return Str::of('');
|
||||
}
|
||||
|
||||
public function groups()
|
||||
@@ -89,5 +98,16 @@ class User extends Authenticatable
|
||||
return $group->hasAnyPermission($permissions);
|
||||
});
|
||||
}
|
||||
|
||||
public function getFullNameAttribute()
|
||||
{
|
||||
return "{$this->title} {$this->first_name} {$this->last_name}";
|
||||
}
|
||||
|
||||
// Accesor para la URL completa
|
||||
public function getProfilePhotoUrlAttribute()
|
||||
{
|
||||
return $this->profile_photo ? Storage::url($this->profile_photo) : asset('images/default-user.png');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ class DashboardPolicy
|
||||
|
||||
public function view(User $user)
|
||||
{
|
||||
return $user->hasPermissionTo('view dashboard');
|
||||
return true; //$user->hasPermissionTo('view.dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class DocumentPolicy
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return false;
|
||||
return $user->hasPermissionTo('document.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ class DocumentPolicy
|
||||
*/
|
||||
public function view(User $user, Document $document)
|
||||
{
|
||||
return $user->hasPermissionTo('view documents')
|
||||
return $user->hasPermissionTo('document.view')
|
||||
&& $user->hasProjectAccess($document->project_id)
|
||||
&& $user->hasPermissionToResource($document->resource(), 'view');
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class DocumentPolicy
|
||||
*/
|
||||
public function update(User $user, Document $document): bool
|
||||
{
|
||||
return $user->hasPermissionToResource($document->resource(), 'edit');
|
||||
return $user->hasPermissionToResource($document->resource(), 'document.edit');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +50,7 @@ class DocumentPolicy
|
||||
*/
|
||||
public function delete(User $user, Document $document): bool
|
||||
{
|
||||
return $user->hasPermissionTo('delete documents');
|
||||
return $user->hasPermissionTo('document.delete');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,18 +22,18 @@ class FolderPolicy
|
||||
$user->projects->contains($folder->project_id);
|
||||
}
|
||||
|
||||
return $user->can('manage-projects');
|
||||
return $user->can('project.create');
|
||||
}
|
||||
|
||||
public function move(User $user, Folder $folder)
|
||||
{
|
||||
return $user->can('manage-projects') &&
|
||||
return $user->can('project.create') &&
|
||||
$user->projects->contains($folder->project_id);
|
||||
}
|
||||
|
||||
public function delete(User $user, Folder $folder)
|
||||
{
|
||||
return $user->can('delete-projects') &&
|
||||
return $user->can('project.delete') &&
|
||||
$user->projects->contains($folder->project_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class PermissionPolicy
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view permissions');
|
||||
return $user->hasPermissionTo('permission.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,7 +22,7 @@ class PermissionPolicy
|
||||
*/
|
||||
public function view(User $user, Permission $permission): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view permissions');
|
||||
return $user->hasPermissionTo('permission.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ class PermissionPolicy
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('create permissions');
|
||||
return $user->hasPermissionTo('permission.create');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +40,7 @@ class PermissionPolicy
|
||||
{
|
||||
if($permission->is_system) return false;
|
||||
|
||||
return $user->hasPermissionTo('edit permissions');
|
||||
return $user->hasPermissionTo('permission.edit');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ class PermissionPolicy
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->hasPermissionTo('delete permissions');
|
||||
return $user->hasPermissionTo('permission.delete');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ class PermissionPolicy
|
||||
*/
|
||||
public function restore(User $user, Permission $permission): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage permissions');
|
||||
return $user->hasPermissionTo('permission.create');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,6 +68,6 @@ class PermissionPolicy
|
||||
*/
|
||||
public function forceDelete(User $user, Permission $permission): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage permissions');
|
||||
return $user->hasPermissionTo('permission.delete');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class ProjectPolicy
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view projects');
|
||||
return $user->hasPermissionTo('project.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,13 @@ class ProjectPolicy
|
||||
*/
|
||||
public function view(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view projects') &&
|
||||
// Admin ve todo, otros usuarios solo proyectos asignados
|
||||
/*
|
||||
return $user->hasRole('admin') ||
|
||||
$project->users->contains($user->id) ||
|
||||
$project->manager_id === $user->id;*/
|
||||
|
||||
return $user->hasPermissionTo('project.view') &&
|
||||
$this->hasProjectAccess($user, $project);
|
||||
}
|
||||
|
||||
@@ -30,7 +36,7 @@ class ProjectPolicy
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('create projects');
|
||||
return $user->hasPermissionTo('project.create');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +44,7 @@ class ProjectPolicy
|
||||
*/
|
||||
public function update(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermissionTo('edit projects') &&
|
||||
return $user->hasPermissionTo('project.edit') &&
|
||||
$this->hasProjectAccess($user, $project);
|
||||
}
|
||||
|
||||
@@ -47,7 +53,7 @@ class ProjectPolicy
|
||||
*/
|
||||
public function delete(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermissionTo('delete projects') &&
|
||||
return $user->hasPermissionTo('project.delete') &&
|
||||
$this->hasProjectAccess($user, $project);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class RolePolicy
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('view roles');
|
||||
return $user->hasPermissionTo('role.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ class RolePolicy
|
||||
*/
|
||||
public function view(User $user, Role $role): bool
|
||||
{
|
||||
return false;
|
||||
return $user->hasPermissionTo('role.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ class RolePolicy
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('create roles');
|
||||
return $user->hasPermissionTo('role.create');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ class RolePolicy
|
||||
*/
|
||||
public function update(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermissionTo('edit roles') && !$role->is_protected;
|
||||
return $user->hasPermissionTo('role.edit') && !$role->is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +45,7 @@ class RolePolicy
|
||||
*/
|
||||
public function delete(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermissionTo('delete roles') && !$role->is_protected;
|
||||
return $user->hasPermissionTo('role.delete') && !$role->is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ class UserPolicy
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users');
|
||||
return $user->hasPermissionTo('user.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,7 +20,7 @@ class UserPolicy
|
||||
*/
|
||||
public function view(User $user, User $model): bool
|
||||
{
|
||||
return false;
|
||||
return $user->hasPermissionTo('user.view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,7 +28,7 @@ class UserPolicy
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users');
|
||||
return $user->hasPermissionTo('user.create');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ class UserPolicy
|
||||
*/
|
||||
public function update(User $user, User $model): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users') && !$model->is_protected;
|
||||
return $user->hasPermissionTo('user.create') && !$model->is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +44,7 @@ class UserPolicy
|
||||
*/
|
||||
public function delete(User $user, User $model): bool
|
||||
{
|
||||
return $user->hasPermissionTo('manage users')
|
||||
return $user->hasPermissionTo('user.delete')
|
||||
&& !$model->is_protected
|
||||
&& $user->id !== $model->id;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Livewire\FileUpload;
|
||||
use App\Livewire\ImageUploader;
|
||||
use App\Livewire\ProjectShow;
|
||||
use App\Livewire\Toolbar;
|
||||
use App\View\Components\Multiselect;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -25,12 +30,13 @@ class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
// Configuración de componentes Blade
|
||||
Blade::componentNamespace('App\\View\\Components', 'icons');
|
||||
Blade::component('multiselect', \App\View\Components\Multiselect::class);
|
||||
Blade::component('multiselect', Multiselect::class);
|
||||
|
||||
// Registro de componentes Livewire
|
||||
Livewire::component('project-show', \App\Http\Livewire\ProjectShow::class);
|
||||
Livewire::component('file-upload', \App\Http\Livewire\FileUpload::class);
|
||||
Livewire::component('toolbar', \App\Http\Livewire\Toolbar::class);
|
||||
Livewire::component('project-show', ProjectShow::class);
|
||||
Livewire::component('file-upload', FileUpload::class);
|
||||
Livewire::component('toolbar', Toolbar::class);
|
||||
Livewire::component('image-uploader', ImageUploader::class);
|
||||
|
||||
// Validación personalizada
|
||||
Validator::extend('max_upload_size', function ($attribute, $value, $parameters, $validator) {
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\Folder;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Policies\DocumentPolicy;
|
||||
use App\Policies\FolderPolicy;
|
||||
use App\Policies\PermissionPolicy;
|
||||
use App\Policies\ProfilePolicy;
|
||||
use App\Policies\ProjectPolicy;
|
||||
use App\Policies\RolePolicy;
|
||||
use App\Policies\UserPolicy;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Livewire;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
protected $policies = [
|
||||
User::class => UserPolicy::class,
|
||||
User::class => ProfilePolicy::class,
|
||||
Role::class => RolePolicy::class,
|
||||
Permission::class => PermissionPolicy::class,
|
||||
Document::class => DocumentPolicy::class,
|
||||
Project::class => ProjectPolicy::class,
|
||||
Folder::class => FolderPolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
Blade::componentNamespace('App\\View\\Components', 'icons');
|
||||
Blade::component('multiselect', \App\View\Components\Multiselect::class);
|
||||
|
||||
Livewire::component('project-show', \App\Http\Livewire\ProjectShow::class);
|
||||
Livewire::component('project-show', \App\Http\Livewire\FileUpload::class);
|
||||
Livewire::component('toolbar', \App\Http\Livewire\Toolbar::class);
|
||||
|
||||
Validator::extend('max_upload_size', function ($attribute, $value, $parameters, $validator) {
|
||||
$maxSize = env('MAX_UPLOAD_SIZE', 51200); // Default 50MB
|
||||
$totalSize = array_reduce($value, function($sum, $file) {
|
||||
return $sum + $file->getSize();
|
||||
}, 0);
|
||||
|
||||
return $totalSize <= ($maxSize * 1024);
|
||||
});
|
||||
}
|
||||
}
|
||||
69
app/Rules/PasswordRule.php
Normal file
69
app/Rules/PasswordRule.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class PasswordRule implements ValidationRule
|
||||
{
|
||||
|
||||
protected $minLength;
|
||||
protected $requireUppercase;
|
||||
protected $requireNumeric;
|
||||
protected $requireSpecialCharacter;
|
||||
|
||||
public function __construct(
|
||||
int $minLength = 8,
|
||||
bool $requireUppercase = true,
|
||||
bool $requireNumeric = true,
|
||||
bool $requireSpecialCharacter = true
|
||||
) {
|
||||
$this->minLength = $minLength;
|
||||
$this->requireUppercase = $requireUppercase;
|
||||
$this->requireNumeric = $requireNumeric;
|
||||
$this->requireSpecialCharacter = $requireSpecialCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
$passes = true;
|
||||
|
||||
if (strlen($value) < $this->minLength) {
|
||||
$passes = false;
|
||||
}
|
||||
|
||||
if ($this->requireUppercase && !preg_match('/[A-Z]/', $value)) {
|
||||
$passes = false;
|
||||
}
|
||||
|
||||
if ($this->requireNumeric && !preg_match('/[0-9]/', $value)) {
|
||||
$passes = false;
|
||||
}
|
||||
|
||||
if ($this->requireSpecialCharacter && !preg_match('/[\W_]/', $value)) {
|
||||
$passes = false;
|
||||
}
|
||||
|
||||
return $passes;
|
||||
}
|
||||
|
||||
public function message()
|
||||
{
|
||||
return 'La contraseña debe contener al menos '.$this->minLength.' caracteres'.
|
||||
($this->requireUppercase ? ', una mayúscula' : '').
|
||||
($this->requireNumeric ? ', un número' : '').
|
||||
($this->requireSpecialCharacter ? ' y un carácter especial' : '');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user