Cargando…

Esto está tardando más de lo esperado.

Tu área de cuenta

Lo que una persona con sesión puede cambiar de sí misma bajo /account, y qué necesita de ti cada pantalla para funcionar.

Toda persona con sesión iniciada tiene un área de cuenta en /account: su perfil, sus notificaciones, los anuncios que publicas y, cuando cada módulo está encendido, sus referidos, sus tickets de soporte, sus chats en vivo y sus planes. Esta guía recorre cada pantalla, qué escribe en la tabla users y la configuración que decide qué partes aparecen. La necesitas cuando un cliente pregunta dónde cambia su contraseña, y cuando decides qué campos deja editar tu producto.

Las pantallas

Todas están definidas en routes/auth.php detrás de los middleware auth y security: la persona debe tener sesión, no estar bloqueada y no tener pendiente un cambio forzado de contraseña.

URL Nombre de ruta Componente Existe cuando
/account auth.profile App\Livewire\Auth\MyProfile Siempre
/account/notifications auth.notifications.center App\Livewire\Auth\Notifications\Index Siempre
/account/referrals auth.referrals App\Livewire\Auth\Referrals\Index FEATURE_REFERRALS_ENABLED=true; si no, 404
/account/announcements auth.announcements.index App\Livewire\Auth\Announcements\Index FEATURE_ANNOUNCEMENTS=true; si no, 404
/account/announcements/{announcement} auth.announcements.show App\Livewire\Auth\Announcements\Show La misma bandera; la URL lleva el slug
/account/tickets auth.tickets.index App\Livewire\Shared\Tickets\Index FEATURE_TICKETS_ENABLED=true; si no, 404
/account/tickets/create auth.tickets.create App\Livewire\Shared\Tickets\Create La misma bandera
/account/tickets/{ticket} auth.tickets.show App\Livewire\Shared\Tickets\Show La misma bandera, y sólo para un ticket propio de la persona; si no, 404
/account/chat auth.chat App\Livewire\Shared\Chats\Chat FEATURE_CHAT_ENABLED=true; si no, 404
/account/chats auth.chats.index App\Livewire\Shared\Chats\Index La misma bandera: los chats cerrados de la persona
/account/chats/{chat} auth.chats.show App\Livewire\Shared\Chats\Show La misma bandera, y sólo para un chat propio de la persona ya cerrado; si no, 404
/account/plans auth.plans WeblaborMx\BillingCore\Livewire\Auth\Plans La registra el paquete de cobros; 403 si la persona no puede administrar el cobro

El paquete de cobros registra el resto de sus pantallas de cuenta bajo el mismo prefijo: /account/add-ons (con cada complemento y medidor debajo), /account/billing, /account/payment-methods y /account/debt. El cobro en conjunto está en Enciende planes y cobros.

La barra lateral de toda el área es resources/views/components/project/layouts/sidebars/account.blade.php. El layout elige la barra por el primer segmento de la URL, así que cualquier pantalla que agregues bajo /account también la recibe. Lista Mi Perfil, Notificaciones, Referencias y Anuncios (los dos últimos sólo con su bandera encendida), Mis planes cuando hay facturación y la persona puede ver suscripciones, Tickets en /account/tickets cuando el módulo de tickets de soporte está encendido, Chat en /account/chat cuando el chat en vivo está encendido, incluya o no el plan de la persona el chat, y Ayuda, que abre el centro de ayuda en el idioma de la persona a través de help_url(). El botón Ir a la aplicación de arriba regresa a route('app.dashboard'). Tickets y Chat son entradas separadas, cada una con su propio interruptor: desde Tickets la persona llega a Nuevo ticket y a sus tickets, y desde Chat al chat y a su Historial de chats. Tickets también dice en cuánto tiempo se responde un ticket nuevo con el plan de la persona. Las pantallas del equipo de soporte (la bandeja, los chats del equipo y el asistente) viven en el panel de administración bajo /admin/support, en el grupo Soporte de su barra lateral, que quien atiende soporte abre sin el rol de administrador. Las pantallas de tickets y de chat son componentes compartidos (App\Livewire\Shared) que arman sus enlaces a partir del panel que las monta, así que un producto también puede montarlas en otro panel. Cómo funcionan los tickets y el chat en vivo está en las guías «Tickets de soporte» y «Chat de soporte en vivo», que el centro de ayuda lista sólo mientras su módulo está encendido.

La página de perfil

/account es un solo componente Livewire, App\Livewire\Auth\MyProfile, organizado en tarjetas. Cada tarjeta tiene su propio botón Guardar y su propio método, así que guardar la contraseña no vuelve a validar el nombre, ni al revés.

La página de perfil, organizada en tarjetas

Foto de perfil

La foto es un <x-image-uploader> enlazado con wire:model.live a photo: elegir un archivo lo sube de inmediato con una barra de progreso, y updatedPhoto() guarda el cambio con User::saveAvatarChanges(), así que no hay botón Guardar. El uploader acepta cualquier imagen hasta 5120 KB; el texto en pantalla sólo menciona JPG, GIF y PNG. El archivo cae en la carpeta Profile de la biblioteca de medios propia de la cuenta (se crea con la primera foto) como un registro Media, con las conversiones que la biblioteca genera para toda subida, y users.avatar_media_id apunta a él. Cuando ya hay una foto, la nueva no la reemplaza hasta que la persona responde la pregunta en un diálogo sobre la página: Mantenerla en la biblioteca desliga la foto anterior y la deja en la biblioteca, Borrarla de la biblioteca la borra salvo que otro registro la use, y Cancelar descarta la subida nueva, igual que cerrar el diálogo con su X, con Escape o con un clic fuera de él. Nada se guarda antes de esa respuesta. Un producto cuyas personas no tienen una biblioteca de medios que consultar puede quitar la pregunta con :confirm-replacement="false" en el uploader: la foto nueva reemplaza entonces a la anterior de inmediato, y la anterior se borra cuando la persona puede borrarla. La opción está en las notas de la biblioteca de medios de la documentación para desarrolladores. Quitar la actual ofrece Quitar de este registro, que la conserva en la biblioteca, o Eliminar de la biblioteca multimedia, que la borra. Como el archivo pertenece a la cuenta igual que cualquier otro de su biblioteca, cuenta contra la cuota de espacio en disco de la cuenta. El proceso completo y lo que necesita de tu almacenamiento están en /help/files-images-and-storage.

Mientras la persona no sube una foto, $user->avatar es un marcador generado por api.dicebear.com a partir del MD5 de su correo ($user->default_avatar).

La tarjeta usa la presentación compacta del uploader: la foto, de 96 px, a la izquierda, y el título, el texto y un cuadro para soltar de una sola línea a su derecha; en un teléfono la foto va arriba. Sin foto subida muestra ese marcador; con una, la foto ocupa su lugar con sus dos botones sobre la esquina, y su nombre de archivo y su peso van debajo del cuadro. La tarjeta oculta el campo que agrega un archivo de la biblioteca por su ID (hide-id-field), así que la foto sólo se elige o se suelta.

Información general

Nombre, los campos de identidad, y país, estado y ciudad. Qué campos de identidad existen lo decide LOGIN_IDENTITIES (config('auth.login_identities')): un campo Correo cuando email está en la lista, Teléfono cuando está phone, Nombre de usuario cuando está username. Cada identidad debe ser única entre usuarios; el nombre de usuario admite letras, dígitos, guiones y guiones bajos hasta 255 caracteres. Guardar también ejecuta la revisión de App\Livewire\Traits\ValidatesLoginIdentities: al menos una identidad debe estar llena, y cuando hay más de una configurada, el nombre de usuario solo no basta, tiene que acompañarlo un correo o un teléfono. El error abre un diálogo y no se guarda nada.

País, estado y ciudad se guardan en la columna JSON extra_data, no en columnas propias.

Estado y ciudad esperan al campo que tienen arriba. Mientras cargan los estados de un país nuevo, o las ciudades de un estado nuevo, el campo no se puede abrir y dice Cargando opciones… debajo. Cambiar el país vacía el estado y la ciudad, y cambiar el estado vacía la ciudad, porque ya no le pertenecen. Una lista que llegó se guarda un día, así que volver a abrir el perfil no la pide otra vez, y cuando el servicio falla el campo sigue mostrando la última lista que tuvo. Cuando un país no tiene estados, o un estado no tiene ciudades, el campo dice No hay opciones. Cuando las opciones no se pueden cargar y no hay una lista anterior, el campo dice No se pudieron cargar las opciones con un enlace Reintentar que vuelve a preguntar; si el servicio pidió esperar, el campo dice cuántos segundos y Reintentar no pregunta antes. No se pierde nada más de la página. Un campo cuyo padre sigue vacío se queda deshabilitado. Cómo lo hacen los selects está en docs/weblabor-base/country-detection.md.

Mientras AUTH_ENABLE_VALIDATION esté encendido, el campo de correo y el de teléfono muestran cada uno una etiqueta Verificado o No verificado y un enlace Verificar ahora que envía un código, y Guardar rechaza una identidad que cambió y no se confirmó: el propio campo dice que a ese valor todavía le falta su código. A una identidad que nadie tocó no se le vuelve a pedir nada, así que guardar el nombre o el país no pide ningún código. Vaciar una identidad se permite y se lleva su verificación consigo, mientras la revisión de arriba siga encontrando una identidad utilizable.

Al confirmar un código, onIdentityVerified() escribe esa identidad y su marca de tiempo en el momento, sin pulsar Guardar, y no escribe nada más: todo lo demás de la página espera a Guardar y a su validación. El teléfono se guarda en formato internacional, y la etiqueta compara números y no cadenas, así que el número guardado escrito sin su prefijo sigue siendo el mismo número. Con el interruptor apagado ninguno de los dos campos muestra la fila de verificación. Cómo viajan los códigos está en /help/verify-email-and-phone.

Contraseña

Dos campos: la contraseña nueva y su confirmación. La regla es al menos 8 caracteres y que ambos coincidan (changePassword(), required|min:8|confirmed). No se pide la contraseña actual. Nada cierra las otras sesiones; eso lo hace la tarjeta Dispositivos conectados de más abajo.

Idioma y zona horaria

Dos selectores. El idioma ofrece las llaves de config('app.languages'), es y en de fábrica; la zona horaria ofrece la lista completa de identificadores de PHP. Ambos son obligatorios para guardar, y escriben users.locale y users.timezone. El idioma es el de la interfaz para esa cuenta, y la zona horaria decide cómo se le muestra cada fecha. Mira /help/languages-and-translations y /help/timezones-and-dates.

Notificaciones

Un interruptor, Notificación por correo electrónico, enlazado a users.send_mail. Apagado quita el canal mail de toda notificación que envía la clase base; la campana y el centro de notificaciones siguen recibiendo todo. Cuando APP_PWA=true y las llaves VAPID están configuradas, aparece un segundo interruptor, Notificación push, que suscribe o desuscribe el navegador actual, y un botón Prueba mientras está suscrito. Prueba llama a testWebPushNotification(), que envía App\Notifications\TestWebPushNotification a las suscripciones push de la persona, o avisa en su lugar cuando push no está configurado o este navegador no tiene suscripción. Mira /help/send-notifications y /help/web-push-and-the-pwa.

PIN de seguridad

La tarjeta existe sólo cuando AUTH_ENABLE_PIN=true. Incrusta App\Livewire\Auth\ManagePin: cuatro casillas de un dígito para el PIN y cuatro para su confirmación, más cuatro para el PIN actual una vez que existe uno, que debe coincidir antes de que cambie nada. El PIN es exactamente cuatro dígitos y se guarda con hash en users.pin.

Mientras la bandera está encendida y la persona no tiene PIN, App\Http\Middleware\SystemValidations deja una advertencia en cada petición, y <x-design::system-warnings/> la muestra arriba de cada página del layout de la aplicación: "Necesitas configurar un PIN para tu cuenta." con un enlace Configurar PIN a /account. Un administrador puede borrar el PIN de un usuario desde la página del usuario en el panel de administración. Qué protege el PIN está en /help/the-security-pin.

Dispositivos conectados

La tarjeta lista los dispositivos de la cuenta, armados por App\Models\Session::devicesOf(): las filas de la tabla sessions cuyo user_id es la persona y cuyo last_activity cae dentro de los últimos session.devices_recent_days días (30, en config/session.php) y nunca más allá de SESSION_LIFETIME (525600 minutos, un año, por omisión). Las filas con el mismo navegador, sistema operativo e IP se agrupan en una sola línea, que muestra la última actividad del grupo; la línea usada más recientemente va primero. Las filas más viejas no se borran: sólo dejan de listarse y de contarse. Cada línea muestra el navegador y el sistema operativo leídos del user agent (Edge, Opera, Samsung Internet, Firefox, Chrome o Safari, incluidas las versiones de iPhone y iPad de Chrome, Firefox, Edge y Opera; Windows, macOS, Android, iOS o Linux), la IP, el país y hace cuánto estuvo activa; la línea que contiene la sesión actual está marcada como Este dispositivo y no tiene botón Desconectar. Desconectar borra todas las filas de su línea, y sólo las de la persona; cuando hay más de una línea, Desconectar todos los demás dispositivos pide confirmación (confirmDisconnectAll()) y borra todas las filas de la persona menos la actual, incluidas las viejas. La contraseña no cambia.

El límite devices del plan (App\Classes\PlanLimits::devices()) cuenta estas mismas líneas, así que el medidor y la tarjeta nunca muestran números distintos.

Dos requisitos. La lista lee la base de datos, así que sólo funciona con SESSION_DRIVER=database, que .env.example ya trae, y con la tabla sessions de las migraciones por omisión; con el driver file la tarjeta dice que no se encontraron dispositivos conectados. El país sale de App\Services\CountryDetectionService::detectFromIp(): una IP privada, que es toda petición en desarrollo local, regresa COUNTRY_CODE_FALLBACK; una IP pública se consulta en ipapi.co con dos segundos de espera, y una consulta fallida deja la ubicación vacía.

Eliminar cuenta

Eliminar abre un diálogo de confirmación (confirmDeletion()). Aceptar ejecuta deleteAccount(): cierra la sesión de la persona, elimina el usuario y redirige a /. El modelo User usa SoftDeletes, así que la fila se queda con deleted_at puesto aunque el diálogo anuncie una eliminación permanente, y nada más se borra: notificaciones, sesiones y registros relacionados siguen apuntando al id. Decide qué tiene que hacer tu producto con ellos y hazlo en un observer o en deleteAccount().

Una persona cuya suscripción todavía puede cobrarle no puede eliminar su cuenta. Antes de cerrar ninguna sesión, deleteAccount() pregunta User::hasActiveSubscription(); si es verdadero, la cuenta y la sesión se quedan, y un diálogo le pide cancelar la suscripción en Planes, con un botón que lleva ahí. La misma regla vive en UserObserver::deleting(), así que eliminar al usuario desde administración, a la papelera o para siempre, también se rechaza, con el error Este usuario tiene una suscripción activa. Cancélala antes de eliminar al usuario. Un usuario que ya está en la papelera se restaura primero, y se cancela la suscripción, antes de poder eliminarlo para siempre. Nada se cancela automáticamente. Una suscripción bloquea cuando está activa, en prueba, con pago vencido, sin pagar o incompleta, no tiene la cancelación pedida (ends_at vacío) y tiene al menos un precio de pago: un plan gratuito, una suscripción cancelada o una que termina al final de su periodo no bloquean. Sin el paquete de facturación, o con él apagado, nada bloquea.

El centro de notificaciones

/account/notifications lista las notificaciones de la cuenta, veinte por página, las no leídas primero y luego las más recientes, que es como las ordena User::notifications(). Cada fila es un enlace a la url de la notificación (# cuando no tiene) con su imagen, su icono, su título, su descripción y una fecha relativa; las filas no leídas tienen fondo ámbar.

El centro de notificaciones, con las filas no leídas primero

Las filas son App\Models\DatabaseNotification, el modelo de Laravel más la relación senderUser y cinco accesores: sender_id (el sender_id que guardó la notificación, cuando la mandó una persona), is_read (read_at tiene valor), image, url y target (_self por omisión). image sigue al remitente: una notificación que mandó una persona muestra la foto actual de esa persona, así que una foto cambiada o borrada después nunca deja una imagen rota, su marcador generado cuando no tiene foto, y el icono config('app.icon') cuando la cuenta ya no existe. Una notificación sin remitente muestra la imagen que guardó, o ese icono cuando no hay.

Abrir el centro no marca nada como leído. Eso pasa desde la campana de la barra superior, App\Livewire\Auth\Notifications\Notification: muestra el conteo de no leídas, las diez primeras notificaciones agrupadas por día, y al hacer clic en una la marca como leída, mientras que Marcar como leído marca todas las pendientes. Cómo se crean las notificaciones y qué canales toman está en /help/send-notifications.

Avisos

Con FEATURE_ANNOUNCEMENTS=true, el encabezado muestra un megáfono, App\Livewire\Auth\Announcements\Dropdown, con el conteo de avisos no leídos (9+ a partir de diez) y los diez publicados más recientes. /account/announcements los lista de diez en diez con los primeros 150 caracteres de cada uno, y la página del aviso muestra el Markdown completo con su fecha de publicación.

La lista de anuncios en /account/announcements

El estado de lectura es una sola marca de tiempo, users.last_announcement_read_at: un aviso está sin leer cuando se publicó después de ella. Abrir cualquier página de aviso la pone en ahora, y lo mismo hace Marcar como leído en el desplegable, así que ambos marcan todo como leído de una vez; la página de lista no cambia nada. Escribir y publicar avisos está en /help/announcements.

Referidos

Con FEATURE_REFERRALS_ENABLED=true, /account/referrals muestra el enlace de referido de la persona con un botón Copiar enlace, la recompensa por registro y el porcentaje de recompensa por suscripción pagada, el dinero virtual que ha generado, y una tabla de los usuarios que se registraron con su enlace. El programa, sus recompensas y su cookie están en /help/referrals.

Volver