home
  • Español
  • English
  • Português
  • Cómo trabajo
  • star Proyectos Destacados
  • Experiencia
  • mail Contacto
arrow_back Volver al proyecto Inicio

Documentación del proyecto

Documentación · Task Cloud

11 archivos markdown (producto, arquitectura, API, auth, datos, deploy…).

En esta página Visión general 0% expand_more
Visión general Enlaces Stack Producto y features Problema Solución Características Captura en la cabecera Prioridad y etiquetas Completar, editar y borrar Filtros y búsqueda Offline-first Sync unitario y bulk Auth PIN + sesión PWA instalable i18n y temas Diseño «Ballpoint Ink» Arquitectura Diagrama lógico Capas Principios Monorepo Turborepo Scripts raíz Workspaces Superficie API Nest Rutas públicas Auth (protegidas) Usuarios (protegidas) Tareas (protegidas) Rate limit Ownership Autenticación PIN + JWT Flujo Seguridad Por qué PIN y no email/password Modelo de datos Neon Tablas users tasks sessions Índices Migraciones Deploy Vercel (Turborepo) Idea Config (vercel.json) Variables de entorno Smoke Local Decisiones de producto y tech Neon + Nest en lugar de Supabase client Turborepo monorepo + un deploy Offline primero, sync opcional PIN en vez de email/password Angular 22 con Zone (no zoneless) por Ionic Rediseño «Ballpoint Ink» (2026-10-19) Controles propios en vez de campos de Angular Material Deshacer en vez de confirmar Instalar con el diálogo del navegador Retos y resultados Serverless Nest en Vercel Convergencia local ↔ nube Env dual (web + api) Una PWA que no se podía instalar Instalar en cada plataforma fakeAsync y la carga perezosa de Ionic Skill Angular Revisión 2026-08-13 (Angular 22) Revisión 2026-10-20 (rediseño «Ballpoint Ink») Qué Angular 22 no se puede (o no se debe) aplicar por Ionic 8 House style (extracto) Referencia en el repo de la app

01-overview.md

Visión general · Task Cloud

Task Cloud es una Progressive Web App de gestión de tareas personales con soporte offline real, instalable en la pantalla de inicio y con sincronización opcional en la nube mediante un PIN de 8 dígitos.

El repositorio es un monorepo pnpm workspaces + Turborepo:

  • apps/web — Angular 22 + Ionic 8 (PWA)
  • apps/api — NestJS 11 sobre Neon Postgres

En producción se despliega como un único proyecto Vercel: el front se sirve como estáticos y Nest se envuelve en una función serverless (api/index.js) detrás de /api/* (same-origin, sin CORS).

Enlaces

  • Web: https://task-manager-cristiancode.vercel.app
  • GitHub: Criscode2022/task-manager-cloud
  • Health: GET /api/health → { "ok": true, "service": "task-cloud-nest-api" }

Stack

CapaTecnología
MonorepoTurborepo + pnpm workspaces
WebAngular 22 (Signal Forms, signals), Ionic 8, service worker + manifest, tokens SCSS («Ballpoint Ink»)
APINestJS 11, jose, bcryptjs
DatosNeon Postgres (@neondatabase/serverless)
DeployVercel (static + serverless function)

02-product.md

Producto y features

Problema

Las apps de tareas suelen exigir registro completo, fallan sin red o acoplan el cliente a un BaaS monolítico. El objetivo era una PWA instalable, usable 100% offline, con sync opcional controlada por una API propia sobre Postgres serverless (Nest + Neon + Vercel).

Solución

Cliente offline-first con Ionic Storage. Con sesión activa, sube y baja tareas vía Nest. Auth ligera por PIN de 8 dígitos (hash + pepper) y sesiones JWT. Deploy unificado en Vercel.

Características

Captura en la cabecera

El campo de captura vive en la cabecera de tinta: se escribe el título y se pulsa Intro. Al escribir se despliega una nota opcional, la prioridad y las etiquetas; tras añadir, el campo se vacía y conserva el foco para la siguiente tarea. Escape con un borrador pide confirmación antes de descartarlo.

Prioridad y etiquetas

Campos priority (low / medium / high) y tags[] en Postgres, con índices GIN. En la UI la prioridad se escribe como palabra (Alta, Media, Baja) junto a su color, nunca solo por color. Hasta seis etiquetas por tarea, en minúsculas y sin duplicados, con sugerencias de las que ya existen.

Completar, editar y borrar

Tocar el anillo completa la tarea: un trazo de tinta tacha el título y la fila espera 650 ms antes de pasar a Hechas. Tocar el texto abre el editor en su sitio. En el móvil, deslizar a la derecha edita y a la izquierda borra; una pulsación larga copia el texto. Borrar es inmediato y ofrece Deshacer durante cinco segundos.

Filtros y búsqueda

Un control segmentado Todas / Pendientes / Hechas con contadores, chips de prioridad y de etiqueta, y una búsqueda en la cabecera sobre título, nota y etiquetas.

Offline-first

Ionic Storage mantiene la lista usable sin red; la nube no es SPOF de la UI.

Sync unitario y bulk

API REST: POST /tasks, POST /tasks/bulk, PUT, DELETE protegidos por JWT.

Auth PIN + sesión

Registro/login con PIN, JWT (sub + sid), tabla sessions y rate-limit en auth. El PIN se muestra una sola vez, en dos grupos de cuatro, con botón de copiar.

PWA instalable

Service worker Angular y manifest con iconos propios. El botón de instalar de la cabecera abre el diálogo de instalación del navegador en Chrome, Edge y Samsung Internet. En iPhone y iPad, donde una web no puede lanzar la instalación, muestra los pasos de Compartir → Añadir a pantalla de inicio. Se oculta cuando la app ya está instalada.

i18n y temas

ngx-translate con idiomas EN/ES en el cliente, y modo claro y oscuro.

Diseño «Ballpoint Ink»

Cabecera azul bolígrafo con el campo de captura, tareas en grupos blancos sobre un fondo de tinta pálida y Bricolage Grotesque para las palabras que se leen y escriben. Sistema completo en apps/web/DESIGN.md.

03-architecture.md

Arquitectura

Diagrama lógico

Browser (PWA Ionic/Angular)
    │  offline: Ionic Storage
    │  online + sesión: HTTP /api/*
    ▼
NestJS (apps/api)  ──@neondatabase/serverless──►  Neon Postgres
    ▲
api/index.js (Vercel serverless, warm cache)

mcp-server/ (opcional, Python FastMCP) ──► Neon

Capas

  1. Cliente — Angular 22 + Ionic 8. Estilos en SCSS con los tokens de «Ballpoint Ink» (apps/web/src/theme/variables.scss); Angular Material solo aporta MatDialog y MatSnackBar.
    • Lista: TabListPage con TaskComposerComponent (captura) y TaskEditorComponent (edición en línea), ambos con Signal Forms.
    • Controles compartidos en core/components/: control segmentado, campo de etiquetas, diálogo del PIN y guía de instalación.
    • Servicios: TaskService (lista y almacenamiento), TaskNeonService y NeonApiService (sync), UserService (PIN y sesión), InstallService (instalación), ThemeService y LanguageService.
    • Techo v22×Ionic: 11-angular-skill.md.
  2. PWA — apps/web/public/ aporta el manifest y los iconos, que el build copia a la raíz; el service worker se registra solo en producción.
  3. API — NestJS 11: módulos auth, users, tasks, health, rate-limit y exception filter.
  4. Datos — Neon Postgres: users, tasks, sessions.
  5. Deploy — Un proyecto Vercel: turbo build web+api; output apps/web/www; rewrites /api/* → función.
  6. Local — API :3001, web :4200 con proxy /api; pnpm start:full.

Principios

  • Offline primero; sync opcional.
  • Secretos solo en servidor (DATABASE_URL, JWT_SECRET, PIN_PEPPER).
  • Same-origin en producción (API_BASE_URL=/api).

04-monorepo.md

Layout del monorepo

PathRol
apps/webPWA Ionic/Angular (output www/; public/ aporta manifest e iconos)
apps/apiNestJS (dist/) consumido por la función Vercel
api/index.jsEntry serverless: boot Nest + cache warm
e2e/ + playwright.config.tsTests e2e con Playwright
docs/Este caso de estudio
packages/Reservado para shared packages
mcp-server/MCP Python opcional (FastMCP + uv), fuera de los workspaces pnpm
vercel.jsonBuild, functions, rewrites SPA + /api
turbo.jsonTasks, env y remote cache

Scripts raíz

ScriptDescripción
pnpm apiNest en watch
pnpm startAngular + proxy /api
pnpm start:full / pnpm devAPI + web vía turbo
pnpm buildBuild de todos los packages
pnpm build:web / build:apiBuild selectivo
pnpm lint / lint:ciESLint
pnpm test:ciTests unitarios web headless (Karma)
pnpm test:apiTests unitarios de la API (Vitest)
pnpm test:integrationIntegración HTTP de la API (Supertest)
pnpm test:e2ePlaywright (Chromium)
pnpm test:scriptsTests de los scripts de scripts/

El smoke contra Neon real es pnpm --filter @task-cloud/api test:api (ver integración).

Workspaces

pnpm-workspace.yaml:

packages:
  - "apps/*"
  - "packages/*"

Package manager: pnpm 11. Node ≥ 22.

05-api.md

Superficie API (Nest)

Prefijo global: /api. Todo lo que no es público exige Authorization: Bearer <jwt> (guard global; las rutas públicas llevan @Public()).

Rutas públicas

MétodoPathNotas
GET/api, /api/healthHealthcheck del servicio Nest
POST/api/usersRegistro con PIN → sesión JWT
POST/api/auth/loginLogin por PIN → token + expires_at
GET/api/users/by-pin/:hashRuta antigua: responde 410 Gone y remite a POST /api/auth/login

Auth (protegidas)

MétodoPathNotas
POST/api/auth/logoutRevoca sesión
GET/api/auth/meUsuario actual

Usuarios (protegidas)

MétodoPathNotas
GET/api/users/:idSolo el propio usuario
DELETE/api/users/:idRevoca sus sesiones y borra el propio usuario con sus tareas en la nube

Tareas (protegidas)

MétodoPathNotas
GET/api/tasksLista del usuario autenticado
POST/api/tasksCrear tarea
POST/api/tasks/bulkCarga masiva
PUT/api/tasks/:idActualizar (ownership)
DELETE/api/tasks/:idBorrar una
DELETE/api/tasksBorrar todas del usuario

Rate limit

@nestjs/throttler en memoria, por instancia de función: 100 peticiones por minuto en general, y 10 cada 15 minutos por IP en POST /api/users y POST /api/auth/login.

Ownership

Las mutaciones validan user_id === auth.userId. No hay multi-tenant organizacional: cada PIN/usuario es un namespace de tareas.

06-auth.md

Modelo de autenticación

Flujo

  1. En Ajustes (Settings) de la PWA, el usuario elige «Subir tareas y obtener un PIN»: se crea la cuenta y la app muestra el PIN de 8 dígitos una sola vez.
  2. El servidor hashea el PIN (bcryptjs + PIN_PEPPER) y guarda pin_hash / pin_lookup.
  3. Se crea una fila en sessions (UUID) y se emite un JWT (jose, HS256) con claims sub (userId) y sid (sessionId).
  4. El cliente guarda el token y lo envía en Authorization: Bearer ….
  5. Logout revoca la sesión; TTL configurable (SESSION_TTL_SECONDS, default 24h).

Seguridad

  • Rate-limit en registro y login: 10 intentos cada 15 minutos por IP, en memoria por instancia de función. El límite está fijado en el código: AUTH_RATE_LIMIT y AUTH_RATE_WINDOW_MS pasan la validación del entorno, pero hoy no se leen.
  • DATABASE_URL nunca se expone al browser.
  • PIN no se almacena en claro.

Por qué PIN y no email/password

Onboarding en segundos para uso personal multi-dispositivo, sin buzones ni reset de contraseña, manteniendo hash + pepper + rate-limit.

07-data-model.md

Modelo de datos (Neon Postgres)

Tablas

users

  • id BIGSERIAL PK
  • pin_hash TEXT NOT NULL
  • pin_lookup TEXT (índice único parcial)
  • created_at timestamptz

tasks

  • id BIGSERIAL PK
  • user_id BIGINT FK → users ON DELETE CASCADE
  • title TEXT NOT NULL
  • description TEXT
  • done BOOLEAN DEFAULT false
  • priority TEXT DEFAULT medium CHECK (low|medium|high)
  • tags TEXT[] DEFAULT {}
  • created_at / updated_at (trigger handle_updated_at)

sessions

  • id UUID PK
  • user_id BIGINT FK
  • expires_at / revoked_at / created_at

Índices

  • tasks(user_id), tasks(done), tasks(priority)
  • GIN en tasks(tags)
  • tasks(created_at DESC)
  • sessions(user_id), sessions(expires_at)

Migraciones

SQL versionado en el repo: neon-migration.sql, neon-auth-migration.sql.

08-deploy-vercel.md

Deploy en Vercel (un solo proyecto)

Idea

  • apps/web → estáticos en CDN (apps/web/www)
  • apps/api → función serverless vía api/index.js (incluye apps/api/dist/**)
  • /api/* reescrito a la función; el resto a la SPA

Config (vercel.json)

  • buildCommand: pnpm exec turbo run build --filter=@task-cloud/web --filter=@task-cloud/api
  • outputDirectory: apps/web/www
  • functions.api/index.js.maxDuration: 30
  • Rewrites: /api/:path* → /api/index; resto → /index.html

Variables de entorno

NombreRequeridoNotas
DATABASE_URLsíConnection string Neon
JWT_SECRETsíopenssl rand -hex 32
PIN_PEPPERsíopenssl rand -hex 32
SESSION_TTL_SECONDSnodefault 86400
API_BASE_URLnodefault /api (same origin)
ALLOWED_ORIGINSnoSolo si otro origen debe llamar a la API
ALLOW_VERCEL_PREVIEWSnodefault true: acepta https://*.vercel.app

AUTH_RATE_LIMIT y AUTH_RATE_WINDOW_MS se validan pero hoy no se leen: el límite de auth está fijado en 10 cada 15 minutos.

Smoke

  1. GET https://task-manager-cristiancode.vercel.app/api/health
  2. GET /manifest.webmanifest devuelve el JSON del manifest, no el index.html. Si devuelve HTML, el build no copió apps/web/public/ y el navegador no ofrecerá instalar la app.
  3. Abrir la web → Ajustes → «Subir tareas y obtener un PIN» → crear/sync tareas
  4. En Chrome, el botón de instalar de la cabecera abre el diálogo de instalación

Local

pnpm install
cp .env.example .env   # DATABASE_URL, JWT_SECRET, PIN_PEPPER
pnpm api               # :3001
pnpm start             # :4200 con proxy /api

09-decisions.md

Decisiones

Neon + Nest en lugar de Supabase client

La nube es Postgres en Neon y una API Nest propia: SQL y esquema controlados, secretos solo en servidor, mismo patrón que el resto del portfolio. Se dejó el acoplamiento a Supabase Auth/Realtime porque el producto solo necesita PIN, sesiones y tareas.

Turborepo monorepo + un deploy

Web y API comparten lockfile, turbo cache y un solo pipeline Vercel. Evita dos proyectos desincronizados y simplifica same-origin /api.

Offline primero, sync opcional

Las features core de tareas no requieren API; la nube es continuidad multi-dispositivo, no el camino crítico de cada tap.

PIN en vez de email/password

Onboarding en segundos para uso personal, sin buzones ni flujos de reset, manteniendo hash + pepper + rate-limit.

Angular 22 con Zone (no zoneless) por Ionic

La skill Angular 22 pide zoneless, [formField] en todos los controles y View Transitions del router. Ionic 8.8 no lo permite del todo: sigue pidiendo zone.js, sus ion-* no implementan FormValueControl y la navegación va por ion-router-outlet. Se mantiene Zone y se usan Signal Forms + httpResource. Los formularios de tareas ya no dependen de controles Ionic: usan inputs nativos y controles propios que implementan FormValueControl, así que [formField] cubre todos sus campos. El techo está en 11-angular-skill.md.

Rediseño «Ballpoint Ink» (2026-10-19)

Sustituye el look de teal, piedra y Literata. La captura sube a la cabecera porque añadir una tarea es lo más frecuente; completar dibuja un trazo de tinta para que el gesto principal tenga respuesta visible. La prioridad sigue en rojo, ámbar y verde escrita como palabra, separada del azul de la marca. Sistema en apps/web/DESIGN.md.

Controles propios en vez de campos de Angular Material

El control segmentado (estado, prioridad, idioma) y el campo de etiquetas son componentes propios con FormValueControl, teclado y ARIA de radiogroup. Material queda solo para MatDialog y MatSnackBar. Así los formularios funcionan con [formField] y el aspecto no depende de los temas de Material.

Deshacer en vez de confirmar

Borrar una tarea es inmediato y ofrece Deshacer durante cinco segundos. Una confirmación en cada borrado frena el uso diario; Deshacer protege del error sin interrumpir. Los borrados masivos de Ajustes sí piden confirmación y dicen qué se borra y dónde.

Instalar con el diálogo del navegador

En Chrome, Edge y Samsung Internet la app guarda el evento beforeinstallprompt y el botón de la cabecera abre el diálogo de instalación del navegador en un toque. iOS no ofrece ninguna API de instalación, así que en iPhone y iPad el mismo botón muestra los pasos de Compartir → Añadir a pantalla de inicio. Donde no hay forma de instalar (Firefox, Safari de escritorio) o la app ya está instalada, el botón no aparece.

10-challenges-results.md

Retos y aprendizajes

Serverless Nest en Vercel

Adapter Express, cold start, maxDuration y reutilizar la app entre invocaciones warm (api/index.js).

Convergencia local ↔ nube

Mapear ids locales tras upload, bulk sync y no pisar ownership entre usuarios.

Env dual (web + api)

set-env.js genera environment.*.local.ts desde el .env raíz; secretos nunca van al bundle del browser.

Una PWA que no se podía instalar

El manifest y los iconos viven en apps/web/public/, pero el builder browser de Angular solo copia las carpetas que angular.json lista en assets, y public/ no estaba. El build salía sin manifest y Vercel respondía a /manifest.webmanifest con el index.html, así que Chrome nunca ofrecía instalar. Se arregló añadiendo public/ a los assets, y las comprobaciones tras cada deploy (VERCEL.md, 08-deploy-vercel.md) incluyen que el manifest devuelva JSON.

Instalar en cada plataforma

Solo los navegadores Chromium exponen beforeinstallprompt. El evento puede llegar antes de que cargue la página de la lista, así que InstallService se crea al arrancar con provideAppInitializer. En iOS no hay API: la app muestra los pasos, que en iOS 26 cambiaron (Compartir puede estar dentro del botón •••).

fakeAsync y la carga perezosa de Ionic

Ionic carga el código de cada componente la primera vez que aparece. Si eso ocurre dentro de un test fakeAsync, el vaciado de temporizadores al final del test dispara el timeout de carga del chunk y Karma registra ChunkLoadError, aunque el test pase. El test de la tarea tachada usa el reloj de Jasmine, que solo avanza los temporizadores que el test pide.

Resultados

  • Monorepo operativo con CI en GitHub Actions: lint, unitarios web (77 tests) y API, integración HTTP de la API, build de la API y e2e con Playwright.
  • PWA en producción en Vercel (estáticos + API Nest en /api) sobre Neon.
  • API con health, auth PIN/JWT, CRUD/bulk de tareas y esquema Neon versionado en SQL.
  • Rediseño «Ballpoint Ink» con captura en la cabecera, edición en línea, Deshacer e instalación en un toque.
  • Código abierto como referencia Angular/Ionic + Nest + Neon + Turborepo + Vercel.

11-angular-skill.md

Skill Angular

La guía de estilo Angular de Task Cloud está en .claude/skills/angular/.

ArchivoContenido
SKILL.mdDefaults Angular 22 + house style
style-standards.mdSignals first, visibilidad, const, spread, early returns
signals.mdcomputed, effect, resource/httpResource, zoneless
signal-forms.mdSignal Forms (estable en v22)
components.mdStandalone, @if/@for, SSR, Vitest
animations.mdanimate.enter / View Transitions

La skill pide mirar el major del repo y no mezclar eras. Task Cloud está en Angular 22.1 + Ionic 8.8.18 + TypeScript 6.0. v22 hace zoneless el default en apps nuevas; esta app declara provideZoneChangeDetection() porque Ionic 8 sigue apoyándose en Zone.js.

Revisión 2026-08-13 (Angular 22)

Hallazgos aplicados:

  1. Upgrade a Angular 22.1.1 + Material/CDK 22.1 + CLI 22.1. TypeScript 6.0.3. Ionic 8.8.18 (peer >=16).
  2. Signal Forms en crear y editar tarea: form() + FormField + required/maxLength + submit(). El modelo es un signal plano (taskModel / editModel). ion-select y mat-select escriben el modelo (no tienen CVA de Signal Forms).
  3. httpResource para lecturas GET: cloudTasks (GET /tasks) y meResource (GET /auth/me), disparados por session. Mutaciones (POST/PUT/DELETE) siguen en HttpClient; tras mutar se llama cloudTasks.reload().
  4. HTTP Fetch, @if, OnPush y Zone explícito se mantienen.

Revisión 2026-10-20 (rediseño «Ballpoint Ink»)

  1. Formularios de tareas sin controles Ionic. TaskComposerComponent extiende la base TaskForm; TaskEditorComponent crea su form() sobre un borrador linkedSignal de la tarea. Ambos validan con required / maxLength. Título y nota son inputs nativos; prioridad y etiquetas usan app-segmented-control y app-tag-field, que implementan FormValueControl (model() para el valor). Todos los campos van con [formField]; ya no hay ion-select ni mat-select que escriban el modelo a mano.
  2. Estado derivado con computed. Filtros, contadores, resumen y grupos (pendientes / hechas) de la lista son computed.
  3. Servicio nuevo con @Service(). InstallService usa el decorador de v22 y se instancia al arrancar con provideAppInitializer, para no perder el beforeinstallprompt del navegador.
  4. Material reducido a MatDialog y MatSnackBar.

Qué Angular 22 no se puede (o no se debe) aplicar por Ionic 8

Ionic 8.8 declara zone.js como peer y sus web components (ion-select, overlays, ion-router-outlet, tabs) no implementan las APIs signal-first de v22. Esto no es deuda de la skill: es un techo del runtime Ionic. No forzar estas APIs hasta que Ionic las soporte.

API Angular 22Por qué Ionic la bloqueaQué hacemos aquí
Zoneless (provideZonelessChangeDetection(), quitar zone.js)Peer oficial zone.js >= 0.13. Eventos, overlays y navegación de Ionic siguen notificando la vista vía Zone. Sin Zone, taps y modales se quedan mudos.provideZoneChangeDetection() + zone.js
[formField] en controles Ionicion-select, ion-checkbox, ion-toggle, ion-input no implementan FormValueControl. Signal Forms solo enlaza nativos o controles con FormValueControl.Formularios de tareas con inputs nativos y controles propios (app-segmented-control, app-tag-field) enlazados con [formField]; ion-toggle / ion-checkbox solo fuera de formularios (modo oscuro, confirmación del PIN)
View Transitions del router AngularLa navegación real pasa por ion-router-outlet + IonicRouteStrategy, no por las transiciones del Router de Angular.Transiciones de Ionic; no withViewTransitions()
Angular ARIA en overlaysAlertas, popovers y action sheets son de Ionic. Sustituirlos por Angular ARIA rompe el look y el focus trap nativo.Alertas de Ionic; diálogos del PIN y de instalación con MatDialog. Los controles nuevos no-Ionic llevan su propio ARIA (el segmentado es un radiogroup con tabindex itinerante)
SSR / hidrataciónPWA + Capacitor + IonicStorage son cliente. No hay servidor de render para ion-*.SPA + service worker

No atribuir a Ionic (queda fuera por el builder/legado, no por el peer):

  • Vitest — el builder webpack/Karma de esta app
  • Shell NgModule (IonicModule.forRoot(), TabsModule) — Ionic 8 sí tiene provideIonicAngular(); no se ha migrado el andamiaje de tabs

Revisar esta tabla al subir de major de Ionic. Si el peer deja de pedir zone.js, se puede pasar a zoneless; los formularios de tareas ya no dependen de que los ion-* implementen FormValueControl.

House style (extracto)

  • Estado y derivación en signal / computed / linkedSignal / httpResource. RxJS solo para streams reales; al entrar, toSignal().
  • private readonly por defecto; protected solo si la plantilla lo lee.
  • const para locales; nunca var.
  • Arrays/objetos por spread; nunca push/splice sobre estado compartido.
  • Guards arriba, happy path plano.
  • En código nuevo: standalone, inject(), input()/output(), Signal Forms, httpResource para lecturas.

Referencia en el repo de la app

Los agentes que lean .claude/skills/angular/SKILL.md aplican estas reglas al tocar apps/web.

En esta página

Visión general Enlaces Stack Producto y features Problema Solución Características Captura en la cabecera Prioridad y etiquetas Completar, editar y borrar Filtros y búsqueda Offline-first Sync unitario y bulk Auth PIN + sesión PWA instalable i18n y temas Diseño «Ballpoint Ink» Arquitectura Diagrama lógico Capas Principios Monorepo Turborepo Scripts raíz Workspaces Superficie API Nest Rutas públicas Auth (protegidas) Usuarios (protegidas) Tareas (protegidas) Rate limit Ownership Autenticación PIN + JWT Flujo Seguridad Por qué PIN y no email/password Modelo de datos Neon Tablas users tasks sessions Índices Migraciones Deploy Vercel (Turborepo) Idea Config (vercel.json) Variables de entorno Smoke Local Decisiones de producto y tech Neon + Nest en lugar de Supabase client Turborepo monorepo + un deploy Offline primero, sync opcional PIN en vez de email/password Angular 22 con Zone (no zoneless) por Ionic Rediseño «Ballpoint Ink» (2026-10-19) Controles propios en vez de campos de Angular Material Deshacer en vez de confirmar Instalar con el diálogo del navegador Retos y resultados Serverless Nest en Vercel Convergencia local ↔ nube Env dual (web + api) Una PWA que no se podía instalar Instalar en cada plataforma fakeAsync y la carga perezosa de Ionic Skill Angular Revisión 2026-08-13 (Angular 22) Revisión 2026-10-20 (rediseño «Ballpoint Ink») Qué Angular 22 no se puede (o no se debe) aplicar por Ionic 8 House style (extracto) Referencia en el repo de la app

0% leído

folder_zip Descargar todos (ZIP)
Pregunta sobre Cristian Asistente con IA, solo datos públicos

Es un asistente automático y puede equivocarse. Para algo importante, escribe a Cristian.