first commit
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled

This commit is contained in:
2025-04-23 00:14:33 +06:00
commit 356f56eebd
197 changed files with 21536 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers;
use App\Models\ActivityLog;
use Illuminate\Http\Request;
class ActivityLogController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
$logs = ActivityLog::latest()->paginate(10);
return view('activity_log.index', compact('logs'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(ActivityLog $activityLog)
{
return view('activity_log.show', compact('activityLog'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(ActivityLog $activityLog)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, ActivityLog $activityLog)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(ActivityLog $activityLog)
{
//
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers;
use App\Models\Document;
use App\Notifications\DocumentStatusChanged;
use Illuminate\Http\Request;
class ApprovalController extends Controller
{
//
public function updateStatus(Request $request, Document $document)
{
$validated = $request->validate([
'status' => 'required|in:approved,rejected',
'comment' => 'required_if:status,rejected'
]);
$document->approvals()->create([
'user_id' => auth()->id(),
'status' => $validated['status'],
'comment' => $validated['comment'] ?? null
]);
$document->update(['status' => $validated['status']]);
event(new DocumentStatusChanged($document, $validated['status']));
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers;
use App\Models\ApprovalWorkflow;
use App\Models\Document;
use App\Notifications\ApprovalRequestNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Notification;
class ApprovalWorkflowController 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)
{
//
}
/**
* Display the specified resource.
*/
public function show(ApprovalWorkflow $approvalWorkflow)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(ApprovalWorkflow $approvalWorkflow)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, ApprovalWorkflow $approvalWorkflow)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(ApprovalWorkflow $approvalWorkflow)
{
//
}
public function initiateApproval(Document $document)
{
$workflow = $document->project->approvalWorkflow;
$currentStep = $workflow->getCurrentStep($document);
$document->approvals()->create([
'user_id' => auth()->id(),
'status' => 'pending',
'step_index' => 0,
'required_role' => $currentStep['role']
]);
Notification::sendUsersWithRole($currentStep['role'])->notify(
new ApprovalRequestNotification($document, $auth()->user())
);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
/** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
$user = $request->user();
event(new Verified($user));
}
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers;
use App\Models\Document;
use App\Models\User;
use Illuminate\Http\Request;
class CommentController extends Controller
{
public function store(Request $request, Document $document)
{
$comment = $document->comments()->create([
'user_id' => auth()->id(),
'content' => $request->content,
'parent_id' => $request->parent_id
]);
$this->processMentions($comment);
return back();
}
private function processMentions(Comment $comment)
{
preg_match_all('/@([\w\-]+)/', $comment->content, $matches);
foreach ($matches[1] as $username) {
$user = User::where('username', $username)->first();
if ($user) {
$user->notify(new MentionNotification($comment));
}
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
use App\Models\Document;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class DashboardController extends Controller
{
public function index()
{
// Estadísticas principales
$stats = [
'projects_count' => Project::count(),
'documents_count' => Document::count(),
'users_count' => User::count(),
'storage_used' => $this->calculateStorageUsed(),
'storage_limit' => 0,
'storage_percentage' => 0,
];
// Documentos recientes (últimos 7 días)
$recentDocuments = Document::with(['project', 'currentVersion'])
->where('created_at', '>=', now()->subDays(7))
->orderBy('created_at', 'desc')
->limit(5)
->get();
// Actividad reciente
$recentActivities = DB::table('activity_log')
->orderBy('created_at', 'desc')
->limit(10)
->get();
return view('dashboard', compact('stats', 'recentDocuments', 'recentActivities'));
}
private function calculateStorageUsed()
{
return Document::with('versions')
->get()
->sum(function($document) {
return $document->versions->sum('size');
});
}
public function storageUsage()
{
$total = $this->calculateStorageUsed();
$limit = config('app.storage_limit', 1073741824); // 1GB por defecto
return response()->json([
'used' => $total,
'limit' => $limit,
'percentage' => ($total / $limit) * 100
]);
}
private function calculateStorage($projects)
{
// Adaptación de tu lógica existente + nueva propuesta
return $projects->sum('storage_used') . ' GB';
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace App\Http\Controllers;
use App\Jobs\ProcessDocumentOCR;
use App\Models\Document;
use App\Models\Project;
use Illuminate\Http\Request;
class DocumentController 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)
{
$validated = $request->validate([
'files.*' => 'required|file|mimes:pdf,docx,xlsx,jpg,png|max:5120',
'project_id' => 'required|exists:projects,id',
'folder_id' => 'nullable|exists:folders,id'
]);
foreach ($request->file('files') as $file) {
$document = Document::create([
'name' => $file->getClientOriginalName(),
'project_id' => $request->project_id,
'folder_id' => $request->folder_id,
'created_by' => auth()->id()
]);
$document->addMedia($file)->toMediaCollection('documents');
ProcessDocumentOCR::dispatch($document->currentVersion);
}
return redirect()->back()->with('success', 'Files uploaded successfully');
}
/**
* Display the specified resource.
*/
public function show(Document $document)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Document $document)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Document $document)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Document $document)
{
//
}
public function upload(Request $request, Project $project)
{
$request->validate([
'files.*' => 'required|mimes:pdf,docx,xlsx,jpeg,png|max:2048'
]);
foreach ($request->file('files') as $file) {
$document = $project->documents()->create([
'name' => $file->getClientOriginalName(),
'status' => 'pending'
]);
$this->createVersion($document, $file);
}
}
private function createVersion(Document $document, $file)
{
$version = $document->versions()->create([
'file_path' => $file->store("projects/{$document->project_id}/documents"),
'hash' => hash_file('sha256', $file),
'user_id' => auth()->id()
]);
$document->update(['current_version_id' => $version->id]);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers;
use App\Models\Folder;
use Illuminate\Http\Request;
class FolderController 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)
{
//
}
/**
* Display the specified resource.
*/
public function show(Folder $folder)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Folder $folder)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Folder $folder)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Folder $folder)
{
//
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Permission;
class PermissionController extends Controller
{
use AuthorizesRequests; // <-- Añade este trait
public function index()
{
$this->authorize('viewAny', Permission::class);
$permissions = Permission::all()->groupBy('group');
return view('permissions.index', compact('permissions'));
}
public function store(Request $request)
{
$this->authorize('create', Permission::class);
$request->validate([
'name' => 'required|string|max:255|unique:permissions,name',
'group' => 'required|string|max:255'
]);
Permission::create($request->only('name', 'group'));
return redirect()->route('permissions.index')
->with('success', 'Permiso creado exitosamente');
}
public function update(Request $request, Permission $permission)
{
$this->authorize('update', $permission);
$request->validate([
'name' => 'required|string|max:255|unique:permissions,name,'.$permission->id,
'group' => 'required|string|max:255'
]);
$permission->update($request->only('name', 'group'));
return redirect()->route('permissions.index')
->with('success', 'Permiso actualizado correctamente');
}
public function updateRoles(User $user, Request $request)
{
$this->authorize('managePermissions', $user);
// Usar UserPolicy para autorizar
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers;
use App\Models\DocumentVersion;
use Illuminate\Http\Request;
class PreviewController extends Controller
{
public function show(DocumentVersion $version)
{
$filePath = storage_path("app/{$version->file_path}");
return match($version->mime_type) {
'application/pdf' => response()->file($filePath),
'image/*' => response()->file($filePath),
default => response()->file(
$this->convertToPdf($filePath)
)
};
}
private function convertToPdf($filePath)
{
// Usar OnlyOffice o LibreOffice para conversión
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Validation\Rules;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
class ProfileController extends Controller
{
public function edit()
{
$user = Auth::user();
return view('profile.edit', compact('user'));
}
public function update(Request $request)
{
$user = Auth::user();
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
'current_password' => ['nullable', 'required_with:password', 'current_password'],
'password' => ['nullable', 'confirmed', Rules\Password::defaults()],
]);
$user->update([
'name' => $request->name,
'email' => $request->email,
]);
if ($request->filled('password')) {
$user->update([
'password' => Hash::make($request->password)
]);
}
return redirect()->route('profile.edit')
->with('status', 'Perfil actualizado correctamente');
}
public function show(Request $request)
{
return view('profile.show', [
'user' => $request->user()
]);
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Project;
use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\Request;
class ProjectController extends Controller
{
use AuthorizesRequests; // ← Añadir este trait
/**
* Display a listing of the resource.
*/
public function index()
{
$projects = Project::withCount('documents')
->whereHas('users', function($query) {
$query->where('user_id', auth()->id());
})
->filter(['search' => request('search')])
->paginate(9);
return view('projects.index', compact('projects'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$this->authorize('create projects');
return view('projects.create', [
'categories' => Category::orderBy('name')->get(),
'users' => User::where('id', '!=', auth()->id())->get(),
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'required|string',
'status' => 'required|in:active,inactive',
'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',
'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')),
'start_date' => 'nullable|date',
'deadline' => 'nullable|date|after:start_date',
'categories' => 'array|exists:categories,id',
//'categories' => 'required|array',
'categories.*' => 'exists:categories,id',
'documents.*' => 'file|max:5120|mimes:pdf,docx,xlsx,jpg,png'
]);
try {
// Combinar datos del usuario autenticado
$validated = array_merge($validated, [
'creator_id' => auth()->id()
]);
// Manejar la imagen
if ($request->hasFile('project_image')) {
$path = $request->file('project_image')->store('project-images', 'public');
$validated['project_image_path'] = $path; // Usar el nombre correcto de columna
}
// Crear el proyecto con todos los datos validados
$project = Project::create($validated);
// Adjuntar categorías
if($request->has('categories')) {
$project->categories()->sync($request->categories);
}
// Manejar documentos adjuntos
if($request->hasFile('documents')) {
foreach ($request->file('documents') as $file) {
$project->documents()->create([
'file_path' => $file->store('project-documents', 'public'),
'original_name' => $file->getClientOriginalName()
]);
}
}
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());
}
}
/**
* Display the specified resource.
*/
public function show(Project $project)
{
$this->authorize('view', $project); // Si usas políticas
$project->load(['categories', 'documents']);
return view('projects.show', [
'project' => $project->load(['rootFolders', 'documents', 'categories']),
'documents' => $project->documents()->paginate(10),
]);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Project $project)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Project $project)
{
$this->authorize('update', $project);
// Lógica de actualización
$project->update($request->all());
return redirect()->route('projects.show', $project)->with('success', 'Project updated successfully.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Project $project)
{
//
}
public function __invoke(Project $project)
{
return view('projects.show', [
'project' => $project
]);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use App\Http\Requests\StoreRoleRequest;
class RoleController extends Controller
{
public function index()
{
$this->authorize('viewAny', Role::class);
$roles = Role::withCount('users')->paginate(10);
return view('roles.index', compact('roles'));
}
public function create()
{
$this->authorize('create', Role::class);
$permissions = Permission::all()->groupBy('group');
return view('roles.create', compact('permissions'));
}
public function store(StoreRoleRequest $request)
{
$role = Role::create($request->only('name'));
$role->syncPermissions($request->permissions);
return redirect()->route('roles.index')
->with('success', 'Rol creado exitosamente');
}
public function edit(Role $role)
{
$this->authorize('update', $role);
$permissions = Permission::all()->groupBy('group');
$rolePermissions = $role->permissions->pluck('id')->toArray();
return view('roles.edit', compact('role', 'permissions', 'rolePermissions'));
}
public function update(StoreRoleRequest $request, Role $role)
{
$role->update($request->only('name'));
$role->syncPermissions($request->permissions);
return redirect()->route('roles.index')
->with('success', 'Rol actualizado correctamente');
}
public function destroy(Role $role)
{
$this->authorize('delete', $role);
if($role->is_protected) {
return redirect()->back()
->with('error', 'No se puede eliminar un rol protegido');
}
$role->delete();
return redirect()->route('roles.index')
->with('success', 'Rol eliminado correctamente');
}
public function syncPermissions(Request $request, Role $role)
{
$this->authorize('update', $role);
$request->validate([
'permissions' => 'required|array',
'permissions.*' => 'exists:permissions,id'
]);
$role->syncPermissions($request->permissions);
return response()->json(['message' => 'Permisos actualizados correctamente']);
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use App\Http\Requests\UpdateUserRequest;
use Spatie\Permission\Models\Role;
class UserController extends Controller
{
public function index()
{
$this->authorize('viewAny', User::class);
$users = User::with('roles')->paginate(10);
return view('users.index', compact('users'));
}
public function create()
{
$this->authorize('create', User::class);
$roles = Role::all();
return view('users.create', compact('roles'));
}
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');
}
public function edit(User $user)
{
$this->authorize('update', $user);
$roles = Role::all();
$userRoles = $user->roles->pluck('id')->toArray();
return view('users.edit', compact('user', 'roles', 'userRoles'));
}
public function update(UpdateUserRequest $request, User $user)
{
$user->update($request->validated());
$user->syncRoles($request->roles);
return redirect()->route('users.index')
->with('success', 'Usuario actualizado correctamente');
}
public function updatePassword(Request $request, User $user)
{
$this->authorize('update', $user);
$request->validate([
'password' => 'required|min:8|confirmed'
]);
$user->update([
'password' => Hash::make($request->password)
]);
return redirect()->back()
->with('success', 'Contraseña actualizada correctamente');
}
public function destroy(User $user)
{
$this->authorize('delete', $user);
if($user->is_protected) {
return redirect()->back()
->with('error', 'No se puede eliminar un usuario protegido');
}
$user->delete();
return redirect()->route('users.index')
->with('success', 'Usuario eliminado correctamente');
}
}