feat(tareas): módulo de tablero kanban con drag & drop, comentarios y Telegram

- Modelos Tarea y TareaComentario con GORM, migración automática
- Estados: por_hacer → en_progreso → revisión → hecho; prioridades: baja/media/alta/urgente
- Drag & drop entre columnas via SortableJS (PUT /tarea/:id/estado)
- CRUD completo: crear, editar, eliminar; asignación a usuario + fecha límite
- Panel lateral de detalle: historial de comentarios, adjuntar archivos (uploads/tareas/:id)
- Telegram: notifica asignación, cambio de estado y comentarios nuevos via sendTelegramAdmin
- Seed automático bajo módulo "Administración", solo rol Administrador

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-06 20:18:26 -05:00
co-authored by Claude Sonnet 4.6
parent 0901ed0d53
commit bc79553b7e
7 changed files with 1178 additions and 0 deletions
+53
View File
@@ -254,6 +254,59 @@ func NotificarReniceAction(servidorNombre, accion string) {
sendTelegramAdmin(msg)
}
// ─── Tareas ──────────────────────────────────────────────────────────────────
func NotificarTareaAsignada(t *models.Tarea) {
if t == nil || t.AsignadoID == nil {
return
}
asignado := ""
if t.Asignado != nil {
asignado = t.Asignado.Name
}
msg := fmt.Sprintf("📋 <b>Nueva tarea asignada</b>\n<b>%s</b>\nAsignado a: %s\nPrioridad: %s\n\n%s",
escapeTelegramHTML(t.Titulo),
escapeTelegramHTML(asignado),
escapeTelegramHTML(t.Prioridad),
escapeTelegramHTML(t.Descripcion))
sendTelegramAdmin(msg)
}
func NotificarTareaEstado(t *models.Tarea) {
if t == nil {
return
}
etiquetas := map[string]string{
"por_hacer": "📥 Por hacer",
"en_progreso": "🔄 En progreso",
"revision": "🔍 En revisión",
"hecho": "✅ Hecho",
}
label := etiquetas[t.Estado]
if label == "" {
label = t.Estado
}
asignado := ""
if t.Asignado != nil {
asignado = "\nAsignado: " + escapeTelegramHTML(t.Asignado.Name)
}
msg := fmt.Sprintf("🔀 <b>Tarea movida → %s</b>\n<b>%s</b>%s",
label,
escapeTelegramHTML(t.Titulo),
asignado)
sendTelegramAdmin(msg)
}
func NotificarTareaComentario(t *models.Tarea, contenido string) {
if t == nil {
return
}
msg := fmt.Sprintf("💬 <b>Nuevo comentario en tarea</b>\n<b>%s</b>\n\n%s",
escapeTelegramHTML(t.Titulo),
escapeTelegramHTML(contenido))
sendTelegramAdmin(msg)
}
func sendTelegramAdmin(mensaje string) {
configs, err := models.GetAllTelegramConfigs()
if err != nil {