añadir funicionalidades de permisos y grupos
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled

This commit is contained in:
2025-04-27 23:43:22 +02:00
parent fa7c92bee2
commit 883daf32ed
51 changed files with 2673 additions and 441 deletions

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Livewire;
use App\Models\Document;
use Livewire\Component;
use Livewire\WithFileUploads;
use App\Models\Project;
use App\Models\Folder;
class FileUpload extends Component
{
use WithFileUploads;
public Project $project;
public Folder|null $folder;
public $files = [];
public $previews = [];
public $maxSize = 50; // MB
public $folderId;
protected $listeners = ['folderChanged' => 'updateFolder'];
public function updateFolder($folderId)
{
$this->folder = Folder::find($folderId);
}
public function updatedFiles()
{
$this->validate([
'files.*' => "max:{$this->maxSize}1024|mimes:pdf,docx,xlsx,jpg,png,svg",
'folderId' => 'required|exists:folders,id'
]);
$this->previews = [];
foreach ($this->files as $file) {
$this->previews[] = [
'name' => $file->getClientOriginalName(),
'type' => $file->getMimeType(),
'size' => $file->getSize(),
'preview' => str_starts_with($file->getMimeType(), 'image/')
? $file->temporaryUrl()
: null
];
Document::create([
'name' => $file->getClientOriginalName(),
'file_path' => $file->store("projects/{$this->folderId}"),
'folder_id' => $this->folderId
]);
}
}
public function save()
{
$this->validate([
'files.*' => "required|max:{$this->maxSize}1024|mimes:pdf,docx,xlsx,jpg,png,svg"
]);
foreach ($this->files as $file) {
$path = $file->store("projects/{$this->project->id}/documents", 'public');
$this->project->documents()->create([
'name' => $file->getClientOriginalName(),
'file_path' => $path,
'folder_id' => $this->folder?->id,
'size' => $file->getSize(),
'type' => $file->getMimeType()
]);
}
$this->reset(['files', 'previews']);
$this->emit('documentsUpdated');
}
public function render()
{
return view('livewire.file-upload');
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Livewire;
use Livewire\Component;
class GroupSearch extends Component
{
public function render()
{
return view('livewire.group-search');
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\Project;
use App\Models\Folder;
use App\Models\User;
class PermissionManager extends Component
{
public $selectedProject;
public $selectedFolder;
public $assignTo = 'user';
public $selectedPermissions = [];
public $selectedUser;
public $selectedGroup;
protected $listeners = ['userSelected', 'groupSelected'];
public function rules()
{
return [
'selectedProject' => 'required|exists:projects,id',
'selectedFolder' => 'nullable|exists:folders,id',
'selectedPermissions' => 'required|array|min:1',
'selectedUser' => 'required_if:assignTo,user',
'selectedGroup' => 'required_if:assignTo,group',
];
}
public function getSelectedResourceIdProperty()
{
return $this->selectedFolder ?: $this->selectedProject;
}
public function getCanSaveProperty()
{
return $this->selectedProject &&
($this->assignTo === 'group' ? $this->selectedGroup : $this->selectedUser) &&
count($this->selectedPermissions) > 0;
}
public function getProjectsProperty()
{
return Project::accessibleBy(auth()->user())->get();
}
public function getFoldersProperty()
{
if (!$this->selectedProject) return collect();
return Folder::where('project_id', $this->selectedProject)
->withDepth()
->get()
->toTree();
}
public function getPermissionsProperty()
{
$type = $this->selectedFolder ? 'folder' : 'project';
return config("permissions.types.$type", []);
}
public function userSelected($userId)
{
$this->selectedUser = $userId;
}
public function groupSelected($groupId)
{
$this->selectedGroup = $groupId;
}
public function savePermissions()
{
$this->validate();
$model = $this->assignTo === 'user'
? User::find($this->selectedUser)
: Group::find($this->selectedGroup);
$resource = $this->selectedFolder
? Folder::find($this->selectedFolder)
: Project::find($this->selectedProject);
// Asignar permisos usando Spatie
$model->givePermissionTo(
$resource->permissions()->whereIn('name', $this->selectedPermissions)->get()
);
$this->reset(['selectedPermissions']);
$this->dispatch('permissionsUpdated');
session()->flash('message', 'Permisos actualizados correctamente.');
}
public function render()
{
return view('livewire.permission-manager');
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Livewire;
use Livewire\Component;
use Spatie\Permission\Models\Permission;
use App\Models\{User, Group};
class PermissionsList extends Component
{
public $resourceId;
public $resourceType;
public function mount($resourceId)
{
$this->resourceId = $resourceId;
$this->determineResourceType();
}
protected function determineResourceType()
{
if (Project::find($this->resourceId)) {
$this->resourceType = 'project';
} else {
$this->resourceType = 'folder';
}
}
public function getPermissions()
{
return Permission::where('name', 'like', "{$this->resourceType}-{$this->resourceId}-%")
->get()
->groupBy(function ($permission) {
return explode('-', $permission->name)[2]; // Obtener tipo de permiso
});
}
public function revokePermission($permissionId, $modelType, $modelId)
{
$permission = Permission::findOrFail($permissionId);
$model = $modelType === 'user'
? User::find($modelId)
: Group::find($modelId);
$model->revokePermissionTo($permission);
$this->dispatch('permissionsUpdated');
}
public function render()
{
return view('livewire.permissions-list', [
'permissions' => $this->getPermissions(),
'users' => User::withPermissionsForResource($this->resourceId, $this->resourceType)->get(),
'groups' => Group::withPermissionsForResource($this->resourceId, $this->resourceType)->get()
]);
}
}

View File

@@ -3,28 +3,40 @@
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Title;
use Livewire\WithFileUploads;
use App\Models\Project;
use App\Models\Folder;
use App\Models\Document;
use Illuminate\Validation\Rule;
class ProjectShow extends Component
{
use WithFileUploads;
public Project $project;
public $selectedFolderId = null;
public ?Folder $currentFolder = null;
public $expandedFolders = [];
public $files = [];
public $folderName = '';
public $selectedFolderId = null;
public function mount(Project $project)
{
$this->project = $project->load('rootFolders');
$this->currentFolder = $this->project->rootFolders->first() ?? null;
}
public function selectFolder($folderId)
{
$this->selectedFolderId = $folderId;
$this->currentFolder = Folder::with('children')->find($folderId);
}
public function toggleFolder($folderId)
public function toggleFolder($folderId): void
{
if (in_array($folderId, $this->expandedFolders)) {
$this->expandedFolders = array_diff($this->expandedFolders, [$folderId]);
@@ -33,18 +45,82 @@ class ProjectShow extends Component
}
}
public function createFolder(): void
{
$this->validate([
'folderName' => [
'required',
'max:255',
Rule::unique('folders', 'name')->where(function ($query) {
return $query->where('project_id', $this->project->id)
->where('parent_id', $this->currentFolder?->id);
})
]
]);
Folder::create([
'name' => $this->folderName,
'project_id' => $this->project->id,
'parent_id' => $this->currentFolder?->id
]);
$this->reset('folderName');
$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
{
$this->validate([
'files.*' => 'file|max:10240|mimes:pdf,docx,xlsx,jpg,png'
]);
foreach ($this->files as $file) {
Document::create([
'name' => $file->getClientOriginalName(),
'file_path' => $file->store("projects/{$this->project->id}/documents"),
'project_id' => $this->project->id,
'folder_id' => $this->currentFolder?->id
]);
}
$this->reset('files');
if ($this->currentFolder) {
$this->currentFolder->refresh(); // Recargar documentos
}
$this->reset('files');
}
public function getDocumentsProperty()
{
return Document::where('folder_id', $this->selectedFolderId)
->where('project_id', $this->project->id)
->with('versions')
->get();
return $this->currentFolder
? $this->currentFolder->documents()->with('versions')->get()
: Document::whereNull('folder_id')->where('project_id', $this->project->id)->with('versions')->get();
}
public function getBreadcrumbsProperty()
{
if (!$this->currentFolder) return collect();
$breadcrumbs = collect();
$folder = $this->currentFolder;
while ($folder) {
$breadcrumbs->prepend($folder);
$folder = $folder->parent;
}
return $breadcrumbs;
}
public function render()
{
return view('livewire.project-show', [
'rootFolders' => $this->project->rootFolders
]);
return view('livewire.project-show')
->layout('layouts.livewire-app', [
'title' => $this->project->name
]);
}
}

25
app/Livewire/Toolbar.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\Project;
use App\Models\Folder;
class Toolbar extends Component
{
public Project $project;
public ?Folder $currentFolder;
public function mount(Project $project, Folder $currentFolder = null)
{
$this->project = $project;
$this->currentFolder = $currentFolder;
}
public function render()
{
return view('livewire.toolbar');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Livewire;
use App\Models\User;
use Livewire\Component;
class UserSearch extends Component
{
public $search = '';
public $selectedUser;
public function selectUser($userId)
{
$this->selectedUser = $userId;
$this->emitUp('userSelected', $userId);
}
public function render()
{
$users = User::query()
->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
->limit(5)
->get();
return view('livewire.user-search', compact('users'));
}
}