webs-sdk 0.19.64 → 0.19.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/AGENTS.md +44 -0
  2. package/CLAUDE.md +80 -0
  3. package/README.md +513 -490
  4. package/dist/components/ARFilters/ARFilters.js +54 -54
  5. package/dist/components/ArtGenerator.js +141 -141
  6. package/dist/components/AvatarAI.js +14 -14
  7. package/dist/components/CookieConsent/constants.js +31 -31
  8. package/dist/components/CreativeFaceSwap.js +23 -23
  9. package/dist/components/GifsIa.js +76 -76
  10. package/dist/components/MemeGenerator.js +12 -12
  11. package/dist/components/PasswordReset/constants.js +33 -33
  12. package/dist/components/RecoveryPassword/constants.js +27 -27
  13. package/dist/components/SpinningWheel/SpinningWheel.js +7 -7
  14. package/dist/components/SpinningWheel/index.d.ts +1 -1
  15. package/dist/components/SpinningWheel/index.d.ts.map +1 -1
  16. package/dist/components/SpinningWheel/index.js +2 -1
  17. package/dist/components/SpinningWheel/index.js.map +1 -1
  18. package/dist/components/ui/ConfiguredMediaPlayer.d.ts +49 -0
  19. package/dist/components/ui/ConfiguredMediaPlayer.d.ts.map +1 -0
  20. package/dist/components/ui/ConfiguredMediaPlayer.js +122 -0
  21. package/dist/components/ui/ConfiguredMediaPlayer.js.map +1 -0
  22. package/dist/components/ui/badge.d.ts +1 -0
  23. package/dist/components/ui/badge.d.ts.map +1 -1
  24. package/dist/components/ui/badge.js +1 -1
  25. package/dist/components/ui/badge.js.map +1 -1
  26. package/dist/components/ui/compose-refs.d.ts +6 -0
  27. package/dist/components/ui/compose-refs.d.ts.map +1 -0
  28. package/dist/components/ui/compose-refs.js +57 -0
  29. package/dist/components/ui/compose-refs.js.map +1 -0
  30. package/dist/components/ui/dropdown-menu.d.ts +3 -1
  31. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  32. package/dist/components/ui/dropdown-menu.js +2 -2
  33. package/dist/components/ui/dropdown-menu.js.map +1 -1
  34. package/dist/components/ui/index.d.ts +2 -0
  35. package/dist/components/ui/index.d.ts.map +1 -1
  36. package/dist/components/ui/index.js +2 -0
  37. package/dist/components/ui/index.js.map +1 -1
  38. package/dist/components/ui/media-player.d.ts +127 -0
  39. package/dist/components/ui/media-player.d.ts.map +1 -0
  40. package/dist/components/ui/media-player.js +1633 -0
  41. package/dist/components/ui/media-player.js.map +1 -0
  42. package/dist/components/ui/tooltip.d.ts +3 -1
  43. package/dist/components/ui/tooltip.d.ts.map +1 -1
  44. package/dist/components/ui/tooltip.js +2 -2
  45. package/dist/components/ui/tooltip.js.map +1 -1
  46. package/dist/components/ui/use-lazy-ref.d.ts +4 -0
  47. package/dist/components/ui/use-lazy-ref.d.ts.map +1 -0
  48. package/dist/components/ui/use-lazy-ref.js +45 -0
  49. package/dist/components/ui/use-lazy-ref.js.map +1 -0
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +4 -3
  53. package/dist/index.js.map +1 -1
  54. package/dist/network.d.ts +12 -0
  55. package/dist/network.d.ts.map +1 -0
  56. package/dist/network.js +15 -0
  57. package/dist/network.js.map +1 -0
  58. package/dist/server.d.ts +54 -0
  59. package/dist/server.d.ts.map +1 -0
  60. package/dist/server.js +74 -0
  61. package/dist/server.js.map +1 -0
  62. package/dist/types/media-chrome-store-types.d.ts +107 -0
  63. package/dist/types/media-chrome-store-types.d.ts.map +1 -0
  64. package/dist/types/media-chrome-store-types.js +3 -0
  65. package/dist/types/media-chrome-store-types.js.map +1 -0
  66. package/dist/utils/QRCode.d.ts +1 -0
  67. package/dist/utils/QRCode.d.ts.map +1 -0
  68. package/dist/utils/QRCode.js +2 -0
  69. package/dist/utils/QRCode.js.map +1 -0
  70. package/dist/utils/imageValidation.d.ts +6 -0
  71. package/dist/utils/imageValidation.d.ts.map +1 -0
  72. package/dist/utils/imageValidation.js +64 -0
  73. package/dist/utils/imageValidation.js.map +1 -0
  74. package/docs/CONFIGURED_MEDIA_PLAYER.md +40 -0
  75. package/docs/CORP_AUTH_USAGE.md +687 -0
  76. package/docs/EXAMPLES_WALLPAPERSNAME_IMPLEMENTATION.md +1445 -0
  77. package/docs/EXAMPLE_ENDPOINTS_COMUNICATION.md +55 -0
  78. package/docs/EXTERNAL_SDK_GUIDE.md +1340 -0
  79. package/docs/FOR_YOU_360_FACESWAP_FIX.md +400 -0
  80. package/docs/FOR_YOU_IMPLEMENTATION_GUIDE.md +1413 -0
  81. package/docs/FOR_YOU_QUIZ_ARCHITECTURE.md +753 -0
  82. package/docs/FOR_YOU_SDK_WALLPAPERSNAME_BUG.md +106 -0
  83. package/docs/FOR_YOU_WALLPAPERSNAME_ENHANCEMENT_PLAN.md +210 -0
  84. package/docs/LEGAL_USAGE.md +190 -0
  85. package/docs/STREAK_IMPLEMENTATION_GUIDE.md +430 -0
  86. package/package.json +106 -84
  87. package/public/spin2win/assets/border-afristream.svg +26 -26
  88. package/public/spin2win/assets/border-efc.svg +23 -23
  89. package/public/spin2win/assets/border-streetreel.svg +23 -23
  90. package/public/spin2win/assets/border-totalgym.svg +27 -27
  91. package/public/spin2win/assets/border-womantoday.svg +26 -26
  92. package/public/spin2win/assets/indicator-afristream.svg +28 -28
  93. package/public/spin2win/assets/indicator-efc.svg +29 -29
  94. package/public/spin2win/assets/indicator-streetreel.svg +29 -29
  95. package/public/spin2win/assets/indicator-totalgym.svg +29 -29
  96. package/public/spin2win/fonts/switzer/README.md +22 -22
  97. package/scripts/install-agent-skill.cjs +34 -0
  98. package/skills/webs-sdk-package/SKILL.md +21 -0
  99. package/skills/webs-sdk-package/references/components.md +32 -0
  100. package/skills/webs-sdk-package/references/integration.md +32 -0
  101. package/skills/webs-sdk-package/references/ui.md +197 -0
@@ -0,0 +1,430 @@
1
+ # Streak — Guía de implementación
2
+
3
+ Módulo de racha de actividad del usuario. Muestra un calendario mensual/semanal con los días en que el usuario registró actividad, su racha actual, la mejor racha y una celebración cuando suma un nuevo día.
4
+
5
+ ---
6
+
7
+ ## Índice
8
+
9
+ 1. [Arquitectura](#1-arquitectura)
10
+ 2. [Requisito previo: proxy API](#2-requisito-previo-proxy-api)
11
+ 3. [Implementación mínima](#3-implementación-mínima)
12
+ 4. [Implementación completa con i18n y config remota](#4-implementación-completa-con-i18n-y-config-remota)
13
+ 5. [Página dedicada con fondo configurable](#5-página-dedicada-con-fondo-configurable)
14
+ 6. [Props de StreakWidget](#6-props-de-streakwidget)
15
+ 7. [StreakSdkConfig — configuración de negocio](#7-streaksdkconfig--configuración-de-negocio)
16
+ 8. [StreakTexts — i18n](#8-streaktexts--i18n)
17
+ 9. [StreakTheme — theming visual](#9-streaktheme--theming-visual)
18
+ 10. [CSS Custom Properties — theming sin props](#10-css-custom-properties--theming-sin-props)
19
+ 11. [Primitivos avanzados](#11-primitivos-avanzados)
20
+ 12. [Checklist de implementación](#12-checklist-de-implementación)
21
+
22
+ ---
23
+
24
+ ## 1. Arquitectura
25
+
26
+ ```
27
+ webs-sdk
28
+ └── StreakWidget ← punto de entrada recomendado
29
+ ├── useStreak ← fetch de datos (agnóstico de portal)
30
+ ├── StreakCard ← UI del calendario (mensual + semanal)
31
+ ├── StreakModal ← envuelve la card en un modal overlay
32
+ └── StreakCelebration ← popup de hito de racha
33
+
34
+ portal (ej. efcweb)
35
+ └── [Portal]StreakWidget ← thin wrapper
36
+ ├── lee sesión y websiteId del store del portal
37
+ ├── construye `texts` desde i18n del portal
38
+ └── renderiza <StreakWidget> del SDK con props planas
39
+ ```
40
+
41
+ **Regla de oro:** el portal solo aporta lo que el SDK no puede conocer — sesión, websiteId, router y textos i18n. Todo lo demás lo gestiona el SDK internamente.
42
+
43
+ ---
44
+
45
+ ## 2. Requisito previo: proxy API
46
+
47
+ El SDK **nunca llama directamente al backend**. El portal debe exponer un endpoint proxy. El SDK llamará a `apiBaseUrl` con `POST` y este body:
48
+
49
+ ```ts
50
+ { user_id: string; app_user_id: string; website_id: string; keywords: string[]; days: number; }
51
+ ```
52
+
53
+ Respuesta esperada:
54
+
55
+ ```ts
56
+ { success: 1, data: { "login": ["2026-08-01", "2026-08-02"] } }
57
+ ```
58
+
59
+ ### Ejemplo de proxy en Next.js
60
+
61
+ ```ts
62
+ // app/api/streak/route.ts
63
+ import { NextRequest, NextResponse } from 'next/server';
64
+
65
+ export async function POST(req: NextRequest) {
66
+ const body = await req.json();
67
+ const res = await fetch(`${process.env.BACKEND_URL}/streak/get`, {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/json' },
70
+ body: JSON.stringify(body),
71
+ });
72
+ return NextResponse.json(await res.json());
73
+ }
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 3. Implementación mínima
79
+
80
+ Sin i18n ni config remota. Usa los textos en español por defecto del SDK.
81
+
82
+ ```tsx
83
+ import { StreakWidget } from 'webs-sdk';
84
+
85
+ function MyStreakPage({ userId, websiteId }: { userId: string; websiteId: string }) {
86
+ return <StreakWidget userId={userId} websiteId={websiteId} apiBaseUrl="/api/streak" />;
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 4. Implementación completa con i18n y config remota
93
+
94
+ Patrón recomendado para producción. El portal crea un thin wrapper que lee sesión, config remota y textos i18n, y los pasa como props planas al SDK.
95
+
96
+ ```tsx
97
+ // components/streak/PortalStreakWidget.tsx
98
+ 'use client';
99
+
100
+ import { StreakWidget } from 'webs-sdk';
101
+ import type { StreakSdkConfig, StreakTexts, StreakTheme } from 'webs-sdk';
102
+ import { useAuthStore } from '@/store/authStore'; // adaptar al portal
103
+ import { useWebsite } from '@/hooks/useWebsite'; // adaptar al portal
104
+ import { useWebsiteData } from '@/hooks/useWebsiteData'; // adaptar al portal
105
+ import { useRouter } from '@/i18n/navigation'; // adaptar al portal
106
+ import { useTranslations } from 'next-intl'; // adaptar al portal
107
+
108
+ export function PortalStreakWidget({ asModal = true, className, theme, activityIcon, config }) {
109
+ const { userSession, isAuthenticated, _hasHydrated } = useAuthStore();
110
+ const { websiteData } = useWebsite();
111
+ const { getWebsiteData } = useWebsiteData();
112
+ const router = useRouter();
113
+ const t = useTranslations('streak'); // adaptar namespace
114
+
115
+ // Config: prop explícita > product_config remoto > defaults del SDK
116
+ const remoteConfig: StreakSdkConfig | undefined = getWebsiteData('product_config.streak') || undefined;
117
+ const streakConfig = config ?? remoteConfig;
118
+
119
+ // Guard: no renderizar sin sesión o con feature inactivo
120
+ if (!_hasHydrated || !isAuthenticated || !userSession?.us_id || !websiteData?.web_id || streakConfig?.active === false) {
121
+ return null;
122
+ }
123
+
124
+ const texts: StreakTexts = {
125
+ title: t('title'),
126
+ subtitle: t('subtitle'),
127
+ loading: t('loading'),
128
+ errorMessage: t('errorMessage'),
129
+ currentStreakLabel: t('currentStreakLabel'),
130
+ bestStreakLabel: t('bestStreakLabel'),
131
+ streakCountLabel: t('streakCountLabel', { count: '{count}' }),
132
+ celebrationTitle: t('celebrationTitle'),
133
+ celebrationMessage: t('celebrationMessage'),
134
+ closeButton: t('closeButton'),
135
+ weekdayLabels: t('weekdayLabels').split(',').map((s) => s.trim()),
136
+ calendarLocale: t('calendarLocale'),
137
+ prevMonthLabel: t('prevMonthLabel'),
138
+ nextMonthLabel: t('nextMonthLabel'),
139
+ thisWeekLabel: t('thisWeekLabel'),
140
+ switchToWeeklyLabel: t('switchToWeeklyLabel'),
141
+ switchToMonthlyLabel: t('switchToMonthlyLabel'),
142
+ };
143
+
144
+ return (
145
+ <StreakWidget
146
+ userId={String(userSession.us_id)}
147
+ websiteId={websiteData.web_id}
148
+ apiBaseUrl="/api/streak"
149
+ config={streakConfig}
150
+ texts={texts}
151
+ theme={theme}
152
+ activityIcon={activityIcon}
153
+ asModal={asModal}
154
+ className={className}
155
+ onCtaClick={(action) => router.push(action)}
156
+ />
157
+ );
158
+ }
159
+ ```
160
+
161
+ ### Claves i18n necesarias
162
+
163
+ ```json
164
+ {
165
+ "streak": {
166
+ "title": "Tu racha",
167
+ "subtitle": "Consigue actividad todos los días para mantener tu racha",
168
+ "loading": "Cargando tu racha...",
169
+ "errorMessage": "No se pudo cargar tu racha. Inténtalo más tarde.",
170
+ "currentStreakLabel": "Racha actual",
171
+ "bestStreakLabel": "Mejor racha",
172
+ "streakCountLabel": "{count} días de racha (esta semana)",
173
+ "celebrationTitle": "¡Racha conseguida!",
174
+ "celebrationMessage": "Has sumado un nuevo día a tu racha. ¡Sigue así!",
175
+ "closeButton": "Cerrar",
176
+ "weekdayLabels": "Lu, Ma, Mi, Ju, Vi, Sa, Do",
177
+ "calendarLocale": "es-ES",
178
+ "prevMonthLabel": "Mes anterior",
179
+ "nextMonthLabel": "Mes siguiente",
180
+ "thisWeekLabel": "Esta semana",
181
+ "switchToWeeklyLabel": "Ver semanal",
182
+ "switchToMonthlyLabel": "Ver mensual"
183
+ }
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 5. Página dedicada con fondo configurable
190
+
191
+ ```tsx
192
+ // app/[locale]/my_account/streak/page.tsx
193
+ 'use client';
194
+
195
+ import { useEffect } from 'react';
196
+ import { useRouter } from '@/i18n/navigation';
197
+ import { useAuthStore } from '@/store/authStore';
198
+ import { useWebsiteData } from '@/hooks/useWebsiteData';
199
+ import { resolveStreakPageBackground, type StreakSdkConfig } from 'webs-sdk';
200
+ import { PortalStreakWidget } from '@/components/streak/PortalStreakWidget';
201
+
202
+ export default function StreakPage() {
203
+ const router = useRouter();
204
+ const { userSession, isAuthenticated, _hasHydrated } = useAuthStore();
205
+ const { getWebsiteData } = useWebsiteData();
206
+
207
+ useEffect(() => {
208
+ if (_hasHydrated && !isAuthenticated) router.push('/login?redirect=/my_account/streak');
209
+ }, [_hasHydrated, isAuthenticated, router]);
210
+
211
+ if (!_hasHydrated || !isAuthenticated || !userSession) return null;
212
+
213
+ // Adaptar la ruta según product_config del portal
214
+ const childs: StreakSdkConfig[] = getWebsiteData('product_config.my_account.childs') || [];
215
+ const streakConfig = childs.find((c) => c.key === 'streak');
216
+
217
+ if (!streakConfig || streakConfig.active === false) {
218
+ router.replace('/my_account');
219
+ return null;
220
+ }
221
+
222
+ const bgStyle = resolveStreakPageBackground(streakConfig.background);
223
+
224
+ return (
225
+ <div className="relative flex flex-col flex-1 w-full" style={{ background: 'var(--color-background, #000000)' }}>
226
+ <div className="absolute bottom-0 left-0 right-0 h-1/2" style={bgStyle} aria-hidden="true" />
227
+ <div className="relative z-10 flex flex-col flex-1 w-full">
228
+ <PortalStreakWidget asModal={false} config={streakConfig} />
229
+ </div>
230
+ </div>
231
+ );
232
+ }
233
+ ```
234
+
235
+ ### Valores de `config.background`
236
+
237
+ | Valor | Resultado |
238
+ |---|---|
239
+ | `undefined` / `""` | `--streak-page-bg` del CSS del tema (fallback: `--primary-500`) |
240
+ | `"none"` | `--color-background` del tema |
241
+ | `"#E4002B"` / `"rgb(...)"` | `background-color` con ese valor |
242
+ | `"https://..."` / `"/img/bg.jpg"` | `background-image: url(...)` con `cover` |
243
+
244
+ ---
245
+
246
+ ## 6. Props de StreakWidget
247
+
248
+ | Prop | Tipo | Req | Descripción |
249
+ |---|---|---|---|
250
+ | `userId` | `string` | ✅ | ID del usuario autenticado. Sin valor, no hay fetch. |
251
+ | `websiteId` | `string` | ✅ | ID del portal. |
252
+ | `apiBaseUrl` | `string` | ✅ | Ruta del proxy. Ej: `"/api/streak"`. |
253
+ | `config` | `StreakSdkConfig` | — | Configuración de negocio. |
254
+ | `texts` | `StreakTexts` | — | Textos i18n. Default: español. |
255
+ | `theme` | `StreakTheme` | — | Overrides de color. |
256
+ | `activityIcon` | `string` | — | URL de imagen para días completados. |
257
+ | `asModal` | `boolean` | — | `true` = modal overlay. Default: `false`. |
258
+ | `className` | `string` | — | Clase extra sobre el contenedor raíz. |
259
+ ---
260
+
261
+ ## 7. StreakSdkConfig — configuración de negocio
262
+
263
+ Viene de `product_config` del portal. Todos los campos son opcionales.
264
+
265
+ ```ts
266
+ {
267
+ key?: string; // identificador, ej. "streak"
268
+ active?: boolean; // false → no renderizar. Default: true
269
+ period?: 'monthly' | 'weekly' | 'both'; // Default: "both"
270
+ defaultView?: 'monthly' | 'weekly'; // Default: "monthly"
271
+ icon?: string; // URL de imagen para días completados
272
+ keywords?: string[]; // actividades a consultar. Default: ["login"]
273
+ days?: number; // rango de días a pedir. Default: 30
274
+ is_navigable?: boolean; // muestra botones ‹ ›. Default: true
275
+ theme?: string; // nombre del theme CSS → clase "streak-theme-{nombre}"
276
+ background?: string; // fondo del área decorativa
277
+ custom_btn?: {
278
+ active?: boolean; // muestra CTA personalizado bajo la card
279
+ action?: string; // URL que se recibe en onCtaClick
280
+ };
281
+ celebration?: {
282
+ active?: boolean; // Default: true
283
+ icon?: string; // icono del popup (fallback: config.icon)
284
+ frequency?: number; // cada cuántos días celebrar. Default: 1
285
+ };
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 8. StreakTexts — i18n
292
+
293
+ Todos opcionales. Los no definidos usan el default en español del SDK.
294
+
295
+ ```ts
296
+ {
297
+ title?: string;
298
+ subtitle?: string;
299
+ loading?: string;
300
+ errorMessage?: string;
301
+ currentStreakLabel?: string;
302
+ bestStreakLabel?: string;
303
+ streakCountLabel?: string; // usar {count} como placeholder
304
+ celebrationTitle?: string;
305
+ celebrationMessage?: string;
306
+ closeButton?: string;
307
+ weekdayLabels?: string[]; // ['Lu','Ma','Mi','Ju','Vi','Sa','Do']
308
+ calendarLocale?: string; // BCP-47: "es-ES", "en-GB", "fr-FR"...
309
+ prevMonthLabel?: string;
310
+ nextMonthLabel?: string;
311
+ thisWeekLabel?: string;
312
+ switchToWeeklyLabel?: string;
313
+ switchToMonthlyLabel?: string;
314
+ }
315
+ ```
316
+
317
+ ---
318
+
319
+ ## 9. StreakTheme — theming visual
320
+
321
+ Solo las claves definidas generan una CSS custom property inline.
322
+
323
+ ```ts
324
+ {
325
+ primaryColor?: string; // bordes completados, CTA, dot de hoy
326
+ cardBackgroundColor?: string; // fondo del panel del calendario
327
+ backgroundTopColor?: string; // mitad superior del fondo decorativo
328
+ backgroundBottomColor?: string; // mitad inferior del fondo decorativo
329
+ pillColor?: string; // pill de la semana en curso
330
+ emptyBorderColor?: string; // borde días vacíos del mes
331
+ outOfMonthBorderColor?: string; // borde días de meses adyacentes
332
+ todayDotColor?: string; // punto bajo el día de hoy
333
+ textColor?: string; // texto principal en el panel
334
+ mutedTextColor?: string; // texto secundario en el panel
335
+ borderColor?: string; // borde del modal y del panel
336
+ heroTextColor?: string; // título/subtítulo fuera del panel
337
+ heroMutedTextColor?: string; // subtítulo secundario fuera del panel
338
+ ctaTextColor?: string; // texto del botón CTA
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ## 10. CSS Custom Properties — theming sin props
345
+
346
+ Para themes completos del portal, definir las variables en CSS sin tocar props.
347
+
348
+ ```css
349
+ /* Opción A: apuntar al componente directamente */
350
+ .streak-card {
351
+ --streak-primary-color: #E4002B;
352
+ --streak-card-bg: #FFFFFF;
353
+ --streak-bg-top: #FFFFFF;
354
+ --streak-bg-bottom: #FFDCDC;
355
+ --streak-pill-bg: #FFDCDC;
356
+ --streak-empty-border: #B5B5B5;
357
+ --streak-out-of-month-border: #E8E8E8;
358
+ --streak-today-dot: #E4002B;
359
+ --streak-text-color: #2B2B2B;
360
+ --streak-muted-text-color: #6D6665;
361
+ --streak-border-color: #E5E5E5;
362
+ --streak-hero-text-color: #2B2B2B;
363
+ --streak-hero-muted-text-color: #6D6665;
364
+ --streak-cta-text-color: #FFFFFF;
365
+ }
366
+ ```
367
+
368
+ ```css
369
+ /* Opción B: config.theme = "my-portal" → añade clase "streak-theme-my-portal" */
370
+ .streak-theme-my-portal {
371
+ --streak-primary-color: #E4002B;
372
+ /* ... resto de variables ... */
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ## 11. Primitivos avanzados
379
+
380
+ Solo si `StreakWidget` no cubre el caso específico:
381
+
382
+ ```tsx
383
+ import {
384
+ StreakCard, // solo el calendario (sin fetch, sin modal, sin celebración)
385
+ StreakModal, // modal genérico que envuelve cualquier contenido
386
+ StreakCelebration, // popup de celebración independiente
387
+ useStreak, // hook de fetch puro
388
+ } from 'webs-sdk';
389
+ ```
390
+
391
+ > Usar primitivos solo si hay una razón específica. Para el 99% de los casos `StreakWidget` es suficiente.
392
+
393
+ ---
394
+
395
+ ## 12. Checklist de implementación
396
+
397
+ ### Backend / Proxy
398
+ - [ ] Endpoint proxy creado en el portal (ej. `POST /api/streak`)
399
+ - [ ] El proxy forwardea el body tal cual al backend real
400
+ - [ ] La respuesta tiene la forma `{ success: 1, data: { "login": ["YYYY-MM-DD", ...] } }`
401
+
402
+ ### SDK
403
+ - [ ] `webs-sdk` instalado y actualizado a la versión que incluye `StreakWidget`
404
+ - [ ] `StreakWidget` importado desde `webs-sdk`
405
+
406
+ ### Thin wrapper del portal
407
+ - [ ] Creado `[Portal]StreakWidget` que lee sesión, websiteId y config
408
+ - [ ] Guard implementado: no renderiza sin sesión o con `active === false`
409
+ - [ ] `texts` construido desde el sistema i18n del portal
410
+ - [ ] `onCtaClick` conectado al router del portal
411
+
412
+ ### i18n
413
+ - [ ] Todas las claves de `StreakTexts` añadidas a los archivos de traducción de cada idioma
414
+ - [ ] `weekdayLabels` como string separado por comas (`"Lu, Ma, Mi, ..."`)
415
+ - [ ] `calendarLocale` con el código BCP-47 correcto para cada idioma
416
+
417
+ ### Página dedicada (si aplica)
418
+ - [ ] Ruta creada con guard de autenticación y redirect a `/login`
419
+ - [ ] Guard de feature con redirect si `active === false`
420
+ - [ ] `resolveStreakPageBackground` importado de `webs-sdk` y usado para el fondo
421
+
422
+ ### Visual
423
+ - [ ] Theming aplicado (prop `theme`, CSS variables o `config.theme`)
424
+ - [ ] `activityIcon` configurado si el portal tiene imagen de actividad propia
425
+ - [ ] Validar skeleton de carga en el primer fetch
426
+ - [ ] Validar overlay sutil al navegar entre meses
427
+ - [ ] Validar popup de celebración al sumar un nuevo día de racha
428
+
429
+
430
+
package/package.json CHANGED
@@ -1,84 +1,106 @@
1
- {
2
- "name": "webs-sdk",
3
- "version": "0.19.64",
4
- "private": false,
5
- "main": "dist/index.js",
6
- "module": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "require": "./dist/index.js",
12
- "import": "./dist/index.js",
13
- "default": "./dist/index.js"
14
- },
15
- "./ui": {
16
- "types": "./dist/components/ui/index.d.ts",
17
- "require": "./dist/components/ui/index.js",
18
- "import": "./dist/components/ui/index.js",
19
- "default": "./dist/components/ui/index.js"
20
- }
21
- },
22
- "files": [
23
- "dist/**/*",
24
- "public/**/*"
25
- ],
26
- "scripts": {
27
- "dev": "next dev --turbopack",
28
- "build": "tsc --project tsconfig.build.json",
29
- "build:watch": "tsc --project tsconfig.build.json --watch",
30
- "prepublishOnly": "npm run build",
31
- "start": "next start",
32
- "lint": "next lint"
33
- },
34
- "dependencies": {
35
- "@microsoft/clarity": "^1.0.2",
36
- "@radix-ui/react-accordion": "^1.2.12",
37
- "@radix-ui/react-alert-dialog": "^1.1.15",
38
- "@radix-ui/react-aspect-ratio": "^1.1.7",
39
- "@radix-ui/react-avatar": "^1.1.10",
40
- "@radix-ui/react-checkbox": "^1.3.3",
41
- "@radix-ui/react-collapsible": "^1.1.12",
42
- "@radix-ui/react-context-menu": "^2.2.16",
43
- "@radix-ui/react-dialog": "^1.1.15",
44
- "@radix-ui/react-dropdown-menu": "^2.1.16",
45
- "@radix-ui/react-hover-card": "^1.1.15",
46
- "@radix-ui/react-label": "^2.1.7",
47
- "@radix-ui/react-navigation-menu": "^1.2.14",
48
- "@radix-ui/react-select": "^2.2.6",
49
- "@radix-ui/react-separator": "^1.1.7",
50
- "@radix-ui/react-slot": "^1.2.4",
51
- "@radix-ui/react-tabs": "^1.1.13",
52
- "@radix-ui/react-tooltip": "^1.2.8",
53
- "class-variance-authority": "^0.7.1",
54
- "clsx": "^2.1.1",
55
- "dompurify": "^3.4.12",
56
- "embla-carousel-react": "^8.3.2",
57
- "hls.js": "^1.6.16",
58
- "mixpanel-browser": "^2.72.0",
59
- "motion": "^12.38.0",
60
- "qrcode": "^1.5.4",
61
- "sonner": "^2.0.7",
62
- "tailwind-merge": "^3.6.0"
63
- },
64
- "peerDependencies": {
65
- "lucide-react": "^1.14.0",
66
- "react": "^18.0.0 || ^19.0.0",
67
- "react-dom": "^18.0.0 || ^19.0.0"
68
- },
69
- "devDependencies": {
70
- "@eslint/eslintrc": "^3",
71
- "@types/dompurify": "^3.0.5",
72
- "@types/node": "^24.3.0",
73
- "@types/qrcode": "^1.5.6",
74
- "@types/react": "^19.2.14",
75
- "@types/react-dom": "^19.2.3",
76
- "eslint": "^9",
77
- "eslint-config-next": "^15.5.2",
78
- "lucide-react": "^1.14.0",
79
- "next": "^16.0.8",
80
- "react": "^19.0.0",
81
- "react-dom": "^19.0.0",
82
- "typescript": "^5.9.3"
83
- }
84
- }
1
+ {
2
+ "name": "webs-sdk",
3
+ "version": "0.19.66",
4
+ "private": false,
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "pi": {
9
+ "skills": ["./skills/webs-sdk-package"]
10
+ },
11
+ "bin": {
12
+ "webs-sdk-skill": "./scripts/install-agent-skill.cjs"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "require": "./dist/index.js",
18
+ "import": "./dist/index.js",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./ui": {
22
+ "types": "./dist/components/ui/index.d.ts",
23
+ "require": "./dist/components/ui/index.js",
24
+ "import": "./dist/components/ui/index.js",
25
+ "default": "./dist/components/ui/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist/**/*",
30
+ "public/**/*",
31
+ "AGENTS.md",
32
+ "CLAUDE.md",
33
+ "docs/**/*",
34
+ "skills/**/*",
35
+ "scripts/install-agent-skill.cjs"
36
+ ],
37
+ "scripts": {
38
+ "dev": "next dev --turbopack",
39
+ "build": "tsc --project tsconfig.build.json",
40
+ "build:watch": "tsc --project tsconfig.build.json --watch",
41
+ "prepublishOnly": "npm run build",
42
+ "start": "next start",
43
+ "lint": "eslint .",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "test:coverage": "vitest run --coverage",
47
+ "test:types": "tsc --project tsconfig.tests.json",
48
+ "test:package": "npm run build && node scripts/check-package.cjs"
49
+ },
50
+ "dependencies": {
51
+ "@microsoft/clarity": "^1.0.2",
52
+ "@radix-ui/react-accordion": "^1.2.12",
53
+ "@radix-ui/react-alert-dialog": "^1.1.15",
54
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
55
+ "@radix-ui/react-avatar": "^1.1.10",
56
+ "@radix-ui/react-checkbox": "^1.3.3",
57
+ "@radix-ui/react-collapsible": "^1.1.12",
58
+ "@radix-ui/react-context-menu": "^2.2.16",
59
+ "@radix-ui/react-dialog": "^1.1.15",
60
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
61
+ "@radix-ui/react-hover-card": "^1.1.15",
62
+ "@radix-ui/react-label": "^2.1.7",
63
+ "@radix-ui/react-navigation-menu": "^1.2.14",
64
+ "@radix-ui/react-select": "^2.2.6",
65
+ "@radix-ui/react-separator": "^1.1.7",
66
+ "@radix-ui/react-slider": "^1.4.7",
67
+ "@radix-ui/react-slot": "^1.2.4",
68
+ "@radix-ui/react-tabs": "^1.1.13",
69
+ "@radix-ui/react-tooltip": "^1.2.8",
70
+ "class-variance-authority": "^0.7.1",
71
+ "clsx": "^2.1.1",
72
+ "dompurify": "^3.4.12",
73
+ "embla-carousel-react": "^8.3.2",
74
+ "hls.js": "^1.6.16",
75
+ "media-chrome": "^4.19.3",
76
+ "mixpanel-browser": "^2.72.0",
77
+ "motion": "^12.38.0",
78
+ "qrcode": "^1.5.4",
79
+ "sonner": "^2.0.7",
80
+ "tailwind-merge": "^3.6.0"
81
+ },
82
+ "peerDependencies": {
83
+ "lucide-react": "^1.14.0",
84
+ "react": "^18.0.0 || ^19.0.0",
85
+ "react-dom": "^18.0.0 || ^19.0.0"
86
+ },
87
+ "devDependencies": {
88
+ "@testing-library/react": "^16.3.3",
89
+ "@testing-library/user-event": "^14.6.7",
90
+ "@types/dompurify": "^3.0.5",
91
+ "@types/node": "^24.3.0",
92
+ "@types/qrcode": "^1.5.6",
93
+ "@types/react": "^19.2.14",
94
+ "@types/react-dom": "^19.2.3",
95
+ "@vitest/coverage-v8": "^4.1.11",
96
+ "eslint": "^9",
97
+ "eslint-config-next": "^16.3.7",
98
+ "jsdom": "^26.1.0",
99
+ "lucide-react": "^1.14.0",
100
+ "next": "^16.0.8",
101
+ "react": "^19.0.0",
102
+ "react-dom": "^19.0.0",
103
+ "typescript": "^5.9.3",
104
+ "vitest": "^4.1.11"
105
+ }
106
+ }