webs-sdk 0.19.64 → 0.19.65
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.
- package/AGENTS.md +44 -0
- package/CLAUDE.md +80 -0
- package/README.md +513 -490
- package/dist/components/ARFilters/ARFilters.js +54 -54
- package/dist/components/ArtGenerator.js +141 -141
- package/dist/components/AvatarAI.js +14 -14
- package/dist/components/CookieConsent/constants.js +31 -31
- package/dist/components/CreativeFaceSwap.js +23 -23
- package/dist/components/GifsIa.js +76 -76
- package/dist/components/MemeGenerator.js +12 -12
- package/dist/components/PasswordReset/constants.js +33 -33
- package/dist/components/RecoveryPassword/constants.js +27 -27
- package/dist/components/SpinningWheel/SpinningWheel.js +7 -7
- package/dist/components/SpinningWheel/index.d.ts +1 -1
- package/dist/components/SpinningWheel/index.d.ts.map +1 -1
- package/dist/components/SpinningWheel/index.js +2 -1
- package/dist/components/SpinningWheel/index.js.map +1 -1
- package/dist/components/ui/ConfiguredMediaPlayer.d.ts +49 -0
- package/dist/components/ui/ConfiguredMediaPlayer.d.ts.map +1 -0
- package/dist/components/ui/ConfiguredMediaPlayer.js +111 -0
- package/dist/components/ui/ConfiguredMediaPlayer.js.map +1 -0
- package/dist/components/ui/badge.d.ts +1 -0
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/compose-refs.d.ts +6 -0
- package/dist/components/ui/compose-refs.d.ts.map +1 -0
- package/dist/components/ui/compose-refs.js +57 -0
- package/dist/components/ui/compose-refs.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +3 -1
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js +2 -2
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +2 -0
- package/dist/components/ui/index.js.map +1 -1
- package/dist/components/ui/media-player.d.ts +127 -0
- package/dist/components/ui/media-player.d.ts.map +1 -0
- package/dist/components/ui/media-player.js +1633 -0
- package/dist/components/ui/media-player.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +3 -1
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip.js +2 -2
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/components/ui/use-lazy-ref.d.ts +4 -0
- package/dist/components/ui/use-lazy-ref.d.ts.map +1 -0
- package/dist/components/ui/use-lazy-ref.js +45 -0
- package/dist/components/ui/use-lazy-ref.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/network.d.ts +12 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +15 -0
- package/dist/network.js.map +1 -0
- package/dist/server.d.ts +54 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +74 -0
- package/dist/server.js.map +1 -0
- package/dist/types/media-chrome-store-types.d.ts +107 -0
- package/dist/types/media-chrome-store-types.d.ts.map +1 -0
- package/dist/types/media-chrome-store-types.js +3 -0
- package/dist/types/media-chrome-store-types.js.map +1 -0
- package/dist/utils/QRCode.d.ts +1 -0
- package/dist/utils/QRCode.d.ts.map +1 -0
- package/dist/utils/QRCode.js +2 -0
- package/dist/utils/QRCode.js.map +1 -0
- package/dist/utils/imageValidation.d.ts +6 -0
- package/dist/utils/imageValidation.d.ts.map +1 -0
- package/dist/utils/imageValidation.js +64 -0
- package/dist/utils/imageValidation.js.map +1 -0
- package/docs/CONFIGURED_MEDIA_PLAYER.md +40 -0
- package/docs/CORP_AUTH_USAGE.md +687 -0
- package/docs/EXAMPLES_WALLPAPERSNAME_IMPLEMENTATION.md +1445 -0
- package/docs/EXAMPLE_ENDPOINTS_COMUNICATION.md +55 -0
- package/docs/EXTERNAL_SDK_GUIDE.md +1340 -0
- package/docs/FOR_YOU_360_FACESWAP_FIX.md +400 -0
- package/docs/FOR_YOU_IMPLEMENTATION_GUIDE.md +1413 -0
- package/docs/FOR_YOU_QUIZ_ARCHITECTURE.md +753 -0
- package/docs/FOR_YOU_SDK_WALLPAPERSNAME_BUG.md +106 -0
- package/docs/FOR_YOU_WALLPAPERSNAME_ENHANCEMENT_PLAN.md +210 -0
- package/docs/LEGAL_USAGE.md +190 -0
- package/docs/STREAK_IMPLEMENTATION_GUIDE.md +430 -0
- package/package.json +106 -84
- package/public/spin2win/assets/border-afristream.svg +26 -26
- package/public/spin2win/assets/border-efc.svg +23 -23
- package/public/spin2win/assets/border-streetreel.svg +23 -23
- package/public/spin2win/assets/border-totalgym.svg +27 -27
- package/public/spin2win/assets/border-womantoday.svg +26 -26
- package/public/spin2win/assets/indicator-afristream.svg +28 -28
- package/public/spin2win/assets/indicator-efc.svg +29 -29
- package/public/spin2win/assets/indicator-streetreel.svg +29 -29
- package/public/spin2win/assets/indicator-totalgym.svg +29 -29
- package/public/spin2win/fonts/switzer/README.md +22 -22
- package/scripts/install-agent-skill.cjs +34 -0
- package/skills/webs-sdk-package/SKILL.md +11 -0
- package/skills/webs-sdk-package/references/components.md +30 -0
- package/skills/webs-sdk-package/references/integration.md +32 -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.
|
|
4
|
-
"private": false,
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"types": "./dist/
|
|
17
|
-
"require": "./dist/
|
|
18
|
-
"import": "./dist/
|
|
19
|
-
"default": "./dist/
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"@
|
|
52
|
-
"@radix-ui/react-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
"react": "^
|
|
67
|
-
"react-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "webs-sdk",
|
|
3
|
+
"version": "0.19.65",
|
|
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
|
+
}
|