diff --git a/pkg/models/portal_user.go b/pkg/models/portal_user.go index 879af06..f9cd35b 100644 --- a/pkg/models/portal_user.go +++ b/pkg/models/portal_user.go @@ -30,6 +30,7 @@ type PortalUser struct { Pais string `json:"pais" gorm:"column:pais"` Documento string `json:"documento" gorm:"column:documento"` // RUT, NIT, CC, etc. Empresa string `json:"empresa" gorm:"column:empresa"` // empresa que representa + SitioWeb string `json:"sitio_web" gorm:"column:sitio_web"` // sitio web del partner/cliente DocumentoRutFile string `json:"documento_rut_file" gorm:"column:documento_rut_file"` // ruta del archivo RUT DocumentoRutNombre string `json:"documento_rut_nombre" gorm:"column:documento_rut_nombre"` // nombre original PortalAccesos []PortalAcceso `json:"portal_accesos" gorm:"foreignKey:PortalUserID"` @@ -94,6 +95,7 @@ func UpdatePortalUser(u *PortalUser) error { "pais": u.Pais, "documento": u.Documento, "empresa": u.Empresa, + "sitio_web": u.SitioWeb, }).Error } diff --git a/resources/views/layouts/portal.html b/resources/views/layouts/portal.html index 0d74824..ec7baed 100644 --- a/resources/views/layouts/portal.html +++ b/resources/views/layouts/portal.html @@ -223,6 +223,13 @@ class="w-full border border-slate-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-[#8eb02f] focus:ring-2 focus:ring-[#8eb02f]/20"> +
+ + +

URL de tu sitio web (opcional).

+
+
@@ -401,7 +408,7 @@ function miCuenta() { open: false, tab: 'perfil', saving: false, - perfil: { nombre:'', email:'', telefono:'', indicativo:'', pais:'', documento:'', empresa:'', documento_rut_file:'', documento_rut_nombre:'' }, + perfil: { nombre:'', email:'', telefono:'', indicativo:'', pais:'', documento:'', empresa:'', sitio_web:'', documento_rut_file:'', documento_rut_nombre:'' }, perfilMsg: '', perfilOk: false, pass: { actual:'', nueva:'', confirma:'' }, passMsg: '', passOk: false, diff --git a/resources/views/portal_usuarios.html b/resources/views/portal_usuarios.html index 09c6ac0..461a28a 100644 --- a/resources/views/portal_usuarios.html +++ b/resources/views/portal_usuarios.html @@ -110,6 +110,7 @@

Se usa para enviar notificaciones al usuario vía Telegram.

+

Sitio web del partner o cliente.

@@ -150,6 +151,37 @@
+ +
+
+
+
+ +
+
+

Enviar credenciales de acceso

+

+
+
+

Se enviará un correo de bienvenida a con el enlace al portal y sus datos de inicio de sesión.

+
+ + +

Si la dejas vacía se indicará al usuario que use la contraseña que configuraste.

+
+

+
+ + +
+
+
+
@@ -194,54 +226,100 @@
- -
-
-

Teléfono

-

-
-
-

País

-

-
-
-

Empresa

-

-
-
-

NIT / Documento

-

-
-
-

Telegram Chat ID

-

-
-
-

Notas

-

+ +
+

Contacto

+
+
+

Teléfono

+

+
+
+

País

+

+
+
+

Sitio Web

+ + +
- -
-

Documento RUT / Cámara de comercio

-
- - - Descargar + +
+

Empresa / Legal

+
+
+

Empresa

+

+
+
+

NIT / Documento

+

+
+
+

Cliente asignado

+

+
- + +
+

Canales

+
+
+

Telegram Chat ID

+

+
+
+
+ + +
+

Notas internas

+

+
+ + +
+

Documento RUT / Cámara de comercio

+ + +
+ +
-

Accesos de cliente

-
+

Accesos de cliente

+
- Sin accesos asignados + Sin accesos asignados
@@ -252,8 +330,12 @@
-
+
+
@@ -270,7 +352,8 @@ function portalUsuariosApp() { showDetail: false, detailUser: null, loadingDetail: false, editId: null, deleteId: null, error: '', accesoUserId: null, accesoClienteId: '', - form: { nombre:'', email:'', password:'', rol:'cliente', cliente_id:'', activo:true, notas:'', telegram_chat_id:'' }, + showCredModal: false, credUserId: null, credNombre: '', credEmail: '', credPassword: '', credSending: false, credMsg: '', credOk: false, + form: { nombre:'', email:'', password:'', rol:'cliente', cliente_id:'', activo:true, notas:'', telegram_chat_id:'', sitio_web:'' }, async init() { await this.load(); }, @@ -299,25 +382,57 @@ function portalUsuariosApp() { openCreate() { this.editId=null; this.error=''; - this.form={nombre:'',email:'',password:'',rol:'cliente',cliente_id:'',activo:true,notas:'',telegram_chat_id:''}; + this.form={nombre:'',email:'',password:'',rol:'cliente',cliente_id:'',activo:true,notas:'',telegram_chat_id:'',sitio_web:''}; this.showModal=true; }, openEdit(u) { this.editId=u.ID; this.error=''; - this.form={nombre:u.nombre,email:u.email,password:'',rol:u.rol,cliente_id:u.cliente_id||'',activo:u.activo,notas:u.notas||'',telegram_chat_id:u.telegram_chat_id||''}; + this.form={nombre:u.nombre,email:u.email,password:'',rol:u.rol,cliente_id:u.cliente_id||'',activo:u.activo,notas:u.notas||'',telegram_chat_id:u.telegram_chat_id||'',sitio_web:u.sitio_web||''}; this.showModal=true; }, async save() { this.saving=true; this.error=''; const payload={...this.form}; + const plainPassword = payload.password; if(payload.cliente_id==='') payload.cliente_id=null; try { - if(this.editId) await axios.put(`/app/portal-usuarios/${this.editId}`, payload); - else await axios.post('/app/portal-usuarios', payload); - this.showModal=false; await this.load(); + if(this.editId) { + await axios.put(`/app/portal-usuarios/${this.editId}`, payload); + this.showModal=false; + } else { + const r = await axios.post('/app/portal-usuarios', payload); + this.showModal=false; + // Ofrecer envío de credenciales al crear + this.credUserId = r.data.ID; + this.credNombre = r.data.nombre; + this.credEmail = r.data.email; + this.credPassword = plainPassword; + this.credMsg = ''; this.credOk = false; + this.showCredModal = true; + } + await this.load(); } catch(e) { this.error=e.response?.data?.error||'Error al guardar'; } finally { this.saving=false; } }, + openCredModal(u) { + this.credUserId = u.ID; + this.credNombre = u.nombre; + this.credEmail = u.email; + this.credPassword = ''; + this.credMsg = ''; this.credOk = false; + this.showCredModal = true; + }, + async enviarCredenciales() { + this.credSending = true; this.credMsg = ''; + try { + const r = await axios.post(`/app/portal-usuarios/${this.credUserId}/send-credentials`, { password: this.credPassword }); + this.credMsg = '✅ ' + (r.data.message || 'Correo enviado correctamente.'); + this.credOk = true; + } catch(e) { + this.credMsg = e.response?.data?.error || 'Error al enviar el correo.'; + this.credOk = false; + } finally { this.credSending = false; } + }, confirmDelete(u) { this.deleteId=u.ID; this.showDelete=true; }, async doDelete() { this.saving=true; diff --git a/rest/controllers/portal_controller.go b/rest/controllers/portal_controller.go index 0fd20e3..4308b1e 100644 --- a/rest/controllers/portal_controller.go +++ b/rest/controllers/portal_controller.go @@ -431,6 +431,7 @@ func PortalGetMiPerfil(c *fiber.Ctx) error { "pais": full.Pais, "documento": full.Documento, "empresa": full.Empresa, + "sitio_web": full.SitioWeb, "documento_rut_file": full.DocumentoRutFile, "documento_rut_nombre": full.DocumentoRutNombre, }) @@ -450,6 +451,7 @@ func PortalUpdateMiPerfil(c *fiber.Ctx) error { Pais string `json:"pais"` Documento string `json:"documento"` Empresa string `json:"empresa"` + SitioWeb string `json:"sitio_web"` } var req Req if err := c.BodyParser(&req); err != nil { @@ -479,6 +481,7 @@ func PortalUpdateMiPerfil(c *fiber.Ctx) error { full.Pais = req.Pais full.Documento = req.Documento full.Empresa = req.Empresa + full.SitioWeb = req.SitioWeb if err := models.UpdatePortalUser(full); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } diff --git a/rest/controllers/portal_usuario_controller.go b/rest/controllers/portal_usuario_controller.go index 6fc960f..6f06f5d 100644 --- a/rest/controllers/portal_usuario_controller.go +++ b/rest/controllers/portal_usuario_controller.go @@ -51,6 +51,7 @@ func CreatePortalUsuario(c *fiber.Ctx) error { Rol string `json:"rol"` Notas string `json:"notas"` TelegramChatID string `json:"telegram_chat_id"` + SitioWeb string `json:"sitio_web"` } var req Req if err := c.BodyParser(&req); err != nil { @@ -86,6 +87,7 @@ func CreatePortalUsuario(c *fiber.Ctx) error { Activo: true, Notas: req.Notas, TelegramChatID: req.TelegramChatID, + SitioWeb: req.SitioWeb, } if err := models.CreatePortalUser(u); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) @@ -109,6 +111,7 @@ func UpdatePortalUsuario(c *fiber.Ctx) error { Activo bool `json:"activo"` Notas string `json:"notas"` TelegramChatID string `json:"telegram_chat_id"` + SitioWeb string `json:"sitio_web"` } var req Req if err := c.BodyParser(&req); err != nil { @@ -133,6 +136,7 @@ func UpdatePortalUsuario(c *fiber.Ctx) error { Activo: req.Activo, Notas: req.Notas, TelegramChatID: req.TelegramChatID, + SitioWeb: req.SitioWeb, } u.ID = uint(id) if err := models.UpdatePortalUser(u); err != nil {