Commit graph

492 commits

Author SHA1 Message Date
f819181872 ✨ (user): Asigna roles desde la edición de usuario 2026-10-05 00:36:47 +02:00
806ed9e0dc 💄 (user): Muestra sólo permisos concedidos al rol 2026-10-05 00:10:53 +02:00
f337119219 💥 (admin): Exige un permiso propio en cada página 2026-10-04 23:50:58 +02:00
2299e63585 🔧 (tools): Añade pagetop-admin/user al changelog 2026-10-04 23:38:40 +02:00
70744a51a5 ✨ (auth): Añade idioma y tema preferidos al usuario
- CurrentUser pasa de enum a struct con `id` opcional (anónimo = None) y
  preferencias de idioma, zona horaria y tema validadas en `with_*()`.
- `RequestLocale` tiene en cuenta el idioma preferido del usuario, y
  Context usa su tema si lo tiene (`default_theme()` en otro caso).
- Nuevos componentes `form::SelectLanguage`, `form::SelectTheme` y
  `form::SelectTimezone`, con `Timezone::supported_by_region()` para
  listar y validar las zonas IANA ofrecidas.
2026-10-04 23:09:07 +02:00
35f2b66161 💄 (user): Usa componentes base en Administración
Sustituye marcado propio y clases sin estilo por componentes de base,
para que la interfaz se vea igual con cualquier tema.
2026-10-03 12:25:15 +02:00
c848b97450 🎨 (basic): Organiza estilos de Basic e Intro
Divide `basic.css` en parciales SCSS bajo `assets/scss/basic/`, integra
`normalize.css` en `basic.min.css` y compila `intro.scss` por separado a
`intro.min.css`.
2026-10-03 11:15:10 +02:00
b5da11777a ✨ (user): Deshabilita roles si no hay asignables 2026-10-02 23:48:18 +02:00
56a2471090 💄 (button): Aplica estilo deshabilitado a enlaces
Un `<a>` deshabilitado no tiene un equivalente a `:disabled` sobre el
que aplicar estilos. Button añade ahora la clase `disabled`.
2026-10-02 23:33:22 +02:00
c06efbd0d1 🎨 Ajustes menores de separadores de sección 2026-10-02 22:59:26 +02:00
04502ef81e ✨ (user): Añade zona horaria propia del usuario
Sustituye el campo de texto libre por un selector de zonas horarias IANA
canónicas agrupadas por región, con la opción de usar la del sitio (que
muestra la zona efectiva).
2026-10-02 22:31:58 +02:00
4cd75af8d2 🐛 (seaorm): Crea timestamp() con zona horaria
Las entidades guardan los instantes como DateTimeUtc, que PostgreSQL
sólo lee desde TIMESTAMPTZ. timestamp() y timestamp_null() pasan a crear
columnas con zona horaria; en MySQL y SQLite no cambia nada en la
práctica.
2026-10-02 06:27:36 +02:00
90e4f85bd4 ✨ (datetime): Añade zona horaria y formato de fechas
Añade formatos `DateFormat`, `TimeFormat`, `RelativeFormat` y
`DatePrecision`, con estilos traducidos vía Fluent (orden día/mes/año,
nombre del mes, plurales). La zona horaria efectiva se resuelve por
petición, la propia del usuario autenticado si la tiene, si no la
configurada para la aplicación (`app.timezone`), o UTC en su defecto- y
se expone en `Contextual` junto a format_date(), format_time(),
format_datetime(), format_iso_datetime(), format_relative(),
format_since() y format_until().
2026-10-01 21:53:57 +02:00
3df73f0551 ✨ (locale): Añade Lc::with_number() para plurales
`with_arg()` sólo interpola texto, y el selector de plural de Fluent
(`{ $n -> [one] ... *[other] ... }`) sólo aplica las reglas de plural
cuando el argumento es un FluentValue::Number.
2026-09-29 07:08:48 +02:00
5d199ef10d 🐛 (examples): Evita 405 al enviar los formularios 2026-09-27 15:04:17 +02:00
dbdaeb6151 ✨ (user): Ordena alfabéticamente grupos de permisos
Añade `Lc::collation_key()` en PageTop para obtener una clave de
ordenación no sensible a mayúsculas y acentos, con `ñ` como letra propia
entre `n` y `o` en español.
2026-09-27 13:12:57 +02:00
93cd3ba6c5 📝 (macros): Corrige enlace a builder_fn en la doc
Esa ruta sólo se resuelve en crates que dependen directamente de
pagetop-macros, quedando roto en las extensiones. Ahora apunta a
pagetop::builder_fn, que la reexporta y está disponible en todos los
crates que usan la macro.
2026-09-26 17:00:05 +02:00
9e2fc2ccfa ✨ (user): Añade el menú de cuenta AccountMenu 2026-09-26 16:57:57 +02:00
165cb49880 ✨ (base): Añade with_menu_end() para menú al final 2026-09-26 14:21:39 +02:00
f265f217bd 🔒️ (admin): Deniega /admin sin páginas accesibles
`render_sections()` devuelve `Option<Container>`, con `None` si no queda
ninguna sección visible con páginas accesibles. `dashboard()` y
`section_page()` responden con `AccessDenied` en ese caso, sin crear un
permiso nuevo.
2026-09-26 07:43:22 +02:00
3f3634fdab ♻️ Atributos class e id explícitos en html!
Se usa `class="..."` e `id="..."` en lugar de `div.a #x`. El HTML
generado es idéntico.
2026-09-26 07:05:59 +02:00
58d1fef4f1 ⚡️ (core): Registro de acciones inmutable 2026-09-26 00:56:30 +02:00
5b2ddeb6cc ⚡️ (pagetop): Mejora uso de Props, Grid y HttpRequest 2026-09-24 22:29:27 +02:00
58b4b4e036 ♻️ (user): El rol authenticated es implícito 2026-09-23 21:04:00 +02:00
1254e650f1 🔥 Elimina la extensión pagetop-menu del workspace 2026-09-23 20:46:25 +02:00
11c2bdffb1 🔥 (bootsier): Elimina AdminLTE
Retira AdminLTE 4 de Bootsier: CSS/JS, `bs::sidebar`, `BootsierRegions`
y el shell propio de `CoreTemplates::Admin`. Sólo quedaba un sidebar de
5 secciones ya alcanzables desde el dashboard, las páginas de sección y
el breadcrumb de `AdminFrame`.
2026-09-22 21:34:16 +02:00
6df71885cc ♻️ (bootsier): Cambia Offcanvas por Panel en Navbar 2026-09-22 18:58:43 +02:00
c47724bb68 ✨ (base): Implementa Navbar con funcionalidad base
Añade `Navbar::with_position()` y `navbar::Position` al núcleo, con las
clases de Basic para barras fijas y pegajosas, y `navbar::Item::form()`
para mostrar un formulario dentro de la barra.

Las barras sin botón de despliegue (`Simple` y `SimpleBrandLeft`) ya no
colapsan: el punto de corte no les afecta y su menú se mantiene en
línea, con salto de línea si no cabe. En Bootsier usan `navbar-expand` y
`SimpleBrandLeft` agrupa los contenidos junto a la marca.
2026-09-21 16:27:44 +02:00
f8f023b513 ✨ (pagetop): Añade PageTopSvg::Tile y ajusta Image 2026-09-21 13:51:18 +02:00
16eec316c4 ♻️ (base): Reorganiza el componente Intro 2026-09-20 23:53:18 +02:00
404739a3fd ♻️ Importa PageTop usando el prelude 2026-09-20 18:33:37 +02:00
8651b91ab2 ♻️ (minimal): Reimplementa join! y join_pair!
Usa `[&str]::concat()` de la biblioteca estándar y elimina la
dependencia de `concat-string`. Cada argumento se evalúa una sola vez y
no se consume.
2026-09-20 18:18:51 +02:00
c6cd4fcd7d ✨ (base): Añade with_width() a Container 2026-09-19 03:14:21 +02:00
9143f1eb26 ✨ (pagetop): Añade with_expand() a Navbar
Navbar guarda el punto de corte a partir del cual deja de colapsar
(`Breakpoint::Md` por defecto) y añade la clase `navbar-expand-{nombre}`
o `navbar-expand` si no tiene ancho mínimo. El nombre lo resuelve el
tema activo con `Theme::breakpoint_entry()`.
2026-09-19 01:52:55 +02:00
5bf28ef4f0 ♻️ (bootsier): Sustituye BreakPoint por Breakpoint
Elimina el enum BreakPoint propio de Bootsier. Los componentes pasan a
usar el Breakpoint del núcleo, cuyo nombre y ancho resuelve el tema
activo.
2026-09-18 18:40:59 +02:00
307ca6f62c ✨ (pagetop): Añade Grid y extrae Flex de Container 2026-09-18 16:49:09 +02:00
dfff12881f 🚧 (pagetop): Comparte align entre Flex y Grid
Extrae `Align`/`AlignContent`/`ItemAlign`/`Gap` de Flexbox a un nuevo
módulo neutral `html::align` (`Items`/`Content`/`ItemSelf`/`Gap`) para
que tambien lo use CSS Grid.
2026-09-13 23:08:39 +02:00
c2ec7568f0 ✅ (pagetop): Añade tests de Margin y Padding 2026-09-13 01:46:21 +02:00
3c3ffc91e6 ♻️ (pagetop): with_prop() acepta impl Into<PropsOp>
`FlexItem`, `Margin` y `Padding` se pasan directamente gracias a `From`
 hacia `PropsOp`, sin `PropsOp::flex_item()`/`margin()`/`padding()` ni
`.into()` explícito. Revisadas todas las llamadas en el código para
simplificarlas.
2026-09-13 01:38:33 +02:00
50e4b42fe4 ✨ (pagetop): Añade Margin y Padding nativos
- Nuevo módulo `html::spacing` con `Margin`/`Padding`: igual que
  `FlexItem`, se resuelven generando clases CSS dinámicamente y se
  aplican con `PropsOp::margin()`/`PropsOp::padding()` sobre cualquier
  componente.
- Elimina `Margin`/`Padding` de `pagetop-bootsier`.
- Añade el ejemplo `intro-spacing` con los patrones de uso típicos.
2026-09-13 00:48:11 +02:00
64113aff09 ♻️ (pagetop): Sustituye StyleSheet::inline
- `StyleSheet::inline` se resuelve ahora con `ResponsiveStyles`.
- `StyleSheet` se simplifica a sólo hojas de estilo externas.
- `ResponsiveStyles` añade `add_styles()` para declarar varias
  propiedades en una sola llamada.
- `AssetsOp` gana un constructor por variante y conversiones
  "From<Favicon/Preload/StyleSheet/JavaScript>" para pasarlos
  directamente a `with_assets()`.
2026-09-12 20:44:17 +02:00
0b8f3f3000 ✨ (pagetop): Añade puntos de corte a Flex/FlexItem
- `Breakpoint` gana variante `Xxxl` y métodos `name()`/`min_width()`/
  `resolved()`, para consultarse contra el tema activo.
- `Theme::breakpoint_entry()` sustituye a `breakpoint_min_width()`:
  devuelve un `BreakpointEntry` con nombre y ancho mínimo.
- Nuevo `Responsive<T>`, valor en cascada mobile-first por punto de
  corte.
- Ejemplo `examples/intro-responsive.rs` con los patrones de uso.
2026-09-12 11:13:19 +02:00
8577ca8a59 ♻️ (pagetop): Props::unpack() exige &mut Context 2026-09-08 07:28:50 +02:00
ee1b48ff3f ♻️ (pagetop): Divide props.rs en varios módulos 2026-09-06 20:36:12 +02:00
28f1eee391 ♻️ (pagetop): Separa handle_component() en 2 métodos
`Theme::handle_component()` se divide en `setup_component()` (síncrono,
muta por tipo con la macro homónima) y `render_component()` (async,
decide el renderizado con su propia macro). Aplica mismo criterio que ya
separa `Component::setup()` de `prepare()`.
2026-09-06 13:07:01 +02:00
1e2d171805 ♻️ (pagetop): setup() recibe &mut Context
Antes sólo mutaba el propio componente; ahora también puede aplicar
ajustes síncronos sobre el Context, visibles para las extensiones que
intercepten BeforeRender. La frontera con prepare() pasa a ser de
sincronía, no de qué puede mutar cada uno.
2026-09-06 09:04:44 +02:00
f2a7507317 ✨ (theme): Añade Breakpoint y ResponsiveStyles
- Nuevo `Breakpoint` (`Xs`/`Sm`/`Md`/`Lg`/`Xl`/`Xxl`) y
  `Theme::breakpoint_min_width()`, que traduce cada variante al ancho
  mínimo CSS del tema activo (mismo patrón que `Theme::intent_color()`).
- `ResponsiveStyles` acumula declaraciones `property: value` agrupadas
  por punto de corte (opcional: `None` para reglas siempre activas, sin
  depender del tema) y por clase o clases. Aplica first-write-wins para
  clases utilitarias repetidas por muchos componentes. `render()` las
  agrupa en bloques `@media (min-width: ...)` mobile-first, sin saltos
  de línea.
2026-09-06 01:46:06 +02:00
b163b7726e ♻️ (util): Simplifica normalize_ascii*
`normalize_ascii_or_empty()` pasa a llamarse `normalize_ascii()` (la
variante indulgente, la más usada); la antigua `normalize_ascii()` pasa
a `normalize_ascii_non_blank()`.
2026-09-05 21:19:15 +02:00
5939d33964 🐛 (examples): Corrige ejemplo en intro-flex
- El segundo Container anidado con `push_end()` no se justificaba contra
  el ancho completo. Ahora es un único Container con Flex, con
  `push_end()` sobre el elemento "Perfil".
- `caption()` acepta Lc también para el fragmento de código, y la
  descripción del ejemplo pasa a ser una traducción Fluent.
2026-09-05 06:22:58 +02:00
ab1d4f0efb ♻️ (pagetop): Incorpora Context al render de Props 2026-09-05 00:19:39 +02:00