Porcentaje de tareas por proyecto
This commit is contained in:
@@ -10,7 +10,12 @@ class ProjectController extends Controller
|
|||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$projects = auth()->user()->projects()->latest()->get();
|
$projects = auth()->user()->projects()
|
||||||
|
->with(['tasks' => function ($query) {
|
||||||
|
$query->select('id', 'project_id', 'status');
|
||||||
|
}])
|
||||||
|
->latest()
|
||||||
|
->get();
|
||||||
|
|
||||||
return Inertia::render('Projects/Index', [
|
return Inertia::render('Projects/Index', [
|
||||||
'projects' => $projects,
|
'projects' => $projects,
|
||||||
|
|||||||
@@ -111,6 +111,44 @@ .feature-icon {
|
|||||||
width: 3rem;
|
width: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 18px 35px rgba(13, 110, 253, 0.16) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-top {
|
||||||
|
background: linear-gradient(135deg, rgba(13, 110, 253, 0.15), rgba(111, 66, 193, 0.20));
|
||||||
|
height: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-icon-badge {
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, #0d6efd, #6f42c1);
|
||||||
|
border-radius: 0.85rem;
|
||||||
|
color: #fff;
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
height: 2.2rem;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
border-radius: 999px;
|
||||||
|
height: 0.55rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
background: linear-gradient(135deg, #0d6efd, #6f42c1);
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
.offcanvas {
|
.offcanvas {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,23 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Link, router } from '@inertiajs/vue3';
|
import { Link, router } from '@inertiajs/vue3';
|
||||||
import { Modal } from 'bootstrap';
|
import { Modal } from 'bootstrap';
|
||||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
|
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
|
||||||
|
|
||||||
defineProps({ projects: Array });
|
const props = defineProps({ projects: Array });
|
||||||
|
|
||||||
|
const projectStats = computed(() => props.projects.map((project) => {
|
||||||
|
const tasks = Array.isArray(project.tasks) ? project.tasks : [];
|
||||||
|
const totalTasks = tasks.length;
|
||||||
|
const completedTasks = tasks.filter(task => task.status === 'done').length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
project,
|
||||||
|
totalTasks,
|
||||||
|
completedTasks,
|
||||||
|
progress: totalTasks ? Math.round((completedTasks / totalTasks) * 100) : 0,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
// Referencia al elemento del DOM para manejar el modal de Bootstrap
|
// Referencia al elemento del DOM para manejar el modal de Bootstrap
|
||||||
const modalElement = ref(null);
|
const modalElement = ref(null);
|
||||||
@@ -122,19 +135,25 @@ const guardarRegistro = async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div v-for="project in projects" :key="project.id" class="col-sm-6 col-lg-4">
|
<div v-for="item in projectStats" :key="item.project.id" class="col-sm-6 col-lg-4">
|
||||||
<div class="card h-100 shadow-sm">
|
<div class="project-card card h-100 rounded-4 shadow-sm border-0 overflow-hidden">
|
||||||
|
<div class="project-card-top"></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between align-items-start gap-2 mb-3">
|
<div class="d-flex justify-content-between align-items-start gap-2 mb-3">
|
||||||
<Link :href="`/projects/${project.id}`" class="text-decoration-none text-dark flex-grow-1">
|
<Link :href="`/projects/${item.project.id}`" class="text-decoration-none text-dark flex-grow-1">
|
||||||
<h5 class="card-title mb-0">{{ project.name }}</h5>
|
<div class="d-flex align-items-center gap-2 mb-2">
|
||||||
|
<span class="project-icon-badge">
|
||||||
|
<i class="bi bi-kanban"></i>
|
||||||
|
</span>
|
||||||
|
<h5 class="card-title mb-0">{{ item.project.name }}</h5>
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-link text-dark text-decoration-none p-0"
|
class="btn btn-link text-dark text-decoration-none p-0"
|
||||||
@click.stop="abrirEditar(project)"
|
@click.stop="abrirEditar(item.project)"
|
||||||
aria-label="Editar proyecto"
|
aria-label="Editar proyecto"
|
||||||
>
|
>
|
||||||
<i class="bi bi-pencil-square fs-5"></i>
|
<i class="bi bi-pencil-square fs-5"></i>
|
||||||
@@ -143,14 +162,26 @@ const guardarRegistro = async () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-link text-danger text-decoration-none p-0"
|
class="btn btn-link text-danger text-decoration-none p-0"
|
||||||
@click.stop="eliminarProyecto(project)"
|
@click.stop="eliminarProyecto(item.project)"
|
||||||
aria-label="Eliminar proyecto"
|
aria-label="Eliminar proyecto"
|
||||||
>
|
>
|
||||||
<i class="bi bi-trash fs-5"></i>
|
<i class="bi bi-trash fs-5"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="card-text text-muted mb-0">{{ project.description }}</p>
|
|
||||||
|
<p class="card-text text-muted mb-3">
|
||||||
|
{{ item.project.description || 'Sin descripción' }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center small text-muted mb-2">
|
||||||
|
<span>{{ item.totalTasks }} tareas</span>
|
||||||
|
<span>{{ item.completedTasks }} completas</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="progress" role="progressbar" aria-label="Progreso del proyecto" aria-valuemin="0" aria-valuemax="100">
|
||||||
|
<div class="progress-bar" :style="{ width: `${item.progress}%` }"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user