@trendify/cli 0.1.4
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/.env.example +2 -0
- package/README.md +52 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +300 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +28 -0
- package/dist/config/app-paths.d.ts +4 -0
- package/dist/config/app-paths.d.ts.map +1 -0
- package/dist/config/app-paths.js +5 -0
- package/dist/config/env.d.ts +14 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +58 -0
- package/dist/modules/auth/auth-service.d.ts +39 -0
- package/dist/modules/auth/auth-service.d.ts.map +1 -0
- package/dist/modules/auth/auth-service.js +288 -0
- package/dist/modules/auth/auth-storage.d.ts +11 -0
- package/dist/modules/auth/auth-storage.d.ts.map +1 -0
- package/dist/modules/auth/auth-storage.js +65 -0
- package/dist/modules/auth/auth-user.d.ts +3 -0
- package/dist/modules/auth/auth-user.d.ts.map +1 -0
- package/dist/modules/auth/auth-user.js +10 -0
- package/dist/modules/auth/page/login-page.d.ts +12 -0
- package/dist/modules/auth/page/login-page.d.ts.map +1 -0
- package/dist/modules/auth/page/login-page.js +22 -0
- package/dist/modules/discovery/components/discovery-step-header.d.ts +7 -0
- package/dist/modules/discovery/components/discovery-step-header.d.ts.map +1 -0
- package/dist/modules/discovery/components/discovery-step-header.js +5 -0
- package/dist/modules/discovery/page/discovery-page.d.ts +11 -0
- package/dist/modules/discovery/page/discovery-page.d.ts.map +1 -0
- package/dist/modules/discovery/page/discovery-page.js +58 -0
- package/dist/modules/profile/page/profile-page.d.ts +12 -0
- package/dist/modules/profile/page/profile-page.d.ts.map +1 -0
- package/dist/modules/profile/page/profile-page.js +180 -0
- package/dist/shared/components/action-menu-page.d.ts +13 -0
- package/dist/shared/components/action-menu-page.d.ts.map +1 -0
- package/dist/shared/components/action-menu-page.js +7 -0
- package/dist/shared/components/radio-select.d.ts +12 -0
- package/dist/shared/components/radio-select.d.ts.map +1 -0
- package/dist/shared/components/radio-select.js +16 -0
- package/dist/shared/components/step-header.d.ts +7 -0
- package/dist/shared/components/step-header.d.ts.map +1 -0
- package/dist/shared/components/step-header.js +5 -0
- package/dist/shared/components/text-field.d.ts +12 -0
- package/dist/shared/components/text-field.d.ts.map +1 -0
- package/dist/shared/components/text-field.js +6 -0
- package/dist/shared/template/app-logo.d.ts +2 -0
- package/dist/shared/template/app-logo.d.ts.map +1 -0
- package/dist/shared/template/app-logo.js +13 -0
- package/dist/shared/template/app-menu.d.ts +17 -0
- package/dist/shared/template/app-menu.d.ts.map +1 -0
- package/dist/shared/template/app-menu.js +85 -0
- package/dist/shared/template/app-shell.d.ts +12 -0
- package/dist/shared/template/app-shell.d.ts.map +1 -0
- package/dist/shared/template/app-shell.js +15 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { createClient, } from '@supabase/supabase-js';
|
|
2
|
+
import { TRENDIFY_AUTH_STORAGE_FILE } from '../../config/app-paths.js';
|
|
3
|
+
import { getCliEnvValidationResult } from '../../config/env.js';
|
|
4
|
+
import { FileStorage } from './auth-storage.js';
|
|
5
|
+
const AUTH_STORAGE_KEY = 'trendify.auth.token';
|
|
6
|
+
function toAuthErrorMessage(error, fallbackMessage) {
|
|
7
|
+
if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
|
|
8
|
+
return error.message;
|
|
9
|
+
}
|
|
10
|
+
return fallbackMessage;
|
|
11
|
+
}
|
|
12
|
+
export class AuthService {
|
|
13
|
+
client;
|
|
14
|
+
storageFilePath;
|
|
15
|
+
static create(env) {
|
|
16
|
+
const storage = new FileStorage(TRENDIFY_AUTH_STORAGE_FILE);
|
|
17
|
+
const client = createClient(env.supabaseUrl, env.supabasePublishableDefaultKey, {
|
|
18
|
+
auth: {
|
|
19
|
+
autoRefreshToken: true,
|
|
20
|
+
detectSessionInUrl: false,
|
|
21
|
+
persistSession: true,
|
|
22
|
+
storage,
|
|
23
|
+
storageKey: AUTH_STORAGE_KEY,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
return new AuthService(client, TRENDIFY_AUTH_STORAGE_FILE);
|
|
27
|
+
}
|
|
28
|
+
constructor(client, storageFilePath) {
|
|
29
|
+
this.client = client;
|
|
30
|
+
this.storageFilePath = storageFilePath;
|
|
31
|
+
}
|
|
32
|
+
getSessionStorageFilePath() {
|
|
33
|
+
return this.storageFilePath;
|
|
34
|
+
}
|
|
35
|
+
onAuthStateChange(listener) {
|
|
36
|
+
return this.client.auth.onAuthStateChange((_event, session) => {
|
|
37
|
+
listener({
|
|
38
|
+
session,
|
|
39
|
+
user: session?.user ?? null,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async restoreSession() {
|
|
44
|
+
try {
|
|
45
|
+
const { data: { session }, error: sessionError, } = await this.client.auth.getSession();
|
|
46
|
+
if (sessionError) {
|
|
47
|
+
return {
|
|
48
|
+
data: null,
|
|
49
|
+
error: sessionError.message,
|
|
50
|
+
errorCode: sessionError.code ?? null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!session) {
|
|
54
|
+
return {
|
|
55
|
+
data: {
|
|
56
|
+
session: null,
|
|
57
|
+
user: null,
|
|
58
|
+
},
|
|
59
|
+
error: null,
|
|
60
|
+
errorCode: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const { data: { user }, error: userError, } = await this.client.auth.getUser();
|
|
64
|
+
if (userError || !user) {
|
|
65
|
+
await this.client.auth.signOut({ scope: 'local' });
|
|
66
|
+
return {
|
|
67
|
+
data: {
|
|
68
|
+
session: null,
|
|
69
|
+
user: null,
|
|
70
|
+
},
|
|
71
|
+
error: userError?.message ?? 'Sua sessao expirou. Faca login novamente.',
|
|
72
|
+
errorCode: userError?.code ?? null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
data: {
|
|
77
|
+
session,
|
|
78
|
+
user,
|
|
79
|
+
},
|
|
80
|
+
error: null,
|
|
81
|
+
errorCode: null,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return {
|
|
86
|
+
data: null,
|
|
87
|
+
error: toAuthErrorMessage(error, 'Nao foi possivel restaurar a sessao atual.'),
|
|
88
|
+
errorCode: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async signInWithPassword(email, password) {
|
|
93
|
+
try {
|
|
94
|
+
const { data, error } = await this.client.auth.signInWithPassword({
|
|
95
|
+
email,
|
|
96
|
+
password,
|
|
97
|
+
});
|
|
98
|
+
if (error) {
|
|
99
|
+
return {
|
|
100
|
+
data: null,
|
|
101
|
+
error: error.message,
|
|
102
|
+
errorCode: error.code ?? null,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!data.session || !data.user) {
|
|
106
|
+
return {
|
|
107
|
+
data: null,
|
|
108
|
+
error: 'O Supabase nao retornou uma sessao valida para este usuario.',
|
|
109
|
+
errorCode: null,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
data: {
|
|
114
|
+
session: data.session,
|
|
115
|
+
user: data.user,
|
|
116
|
+
},
|
|
117
|
+
error: null,
|
|
118
|
+
errorCode: null,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
data: null,
|
|
124
|
+
error: toAuthErrorMessage(error, 'Nao foi possivel concluir o login agora.'),
|
|
125
|
+
errorCode: null,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async signOut() {
|
|
130
|
+
try {
|
|
131
|
+
const { error } = await this.client.auth.signOut({ scope: 'local' });
|
|
132
|
+
if (error) {
|
|
133
|
+
return {
|
|
134
|
+
data: null,
|
|
135
|
+
error: error.message,
|
|
136
|
+
errorCode: error.code ?? null,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
data: null,
|
|
141
|
+
error: null,
|
|
142
|
+
errorCode: null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
data: null,
|
|
148
|
+
error: toAuthErrorMessage(error, 'Nao foi possivel encerrar a sessao atual.'),
|
|
149
|
+
errorCode: null,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async updateDisplayName(displayName) {
|
|
154
|
+
try {
|
|
155
|
+
const { data: { user: currentUser }, error: currentUserError, } = await this.client.auth.getUser();
|
|
156
|
+
if (currentUserError) {
|
|
157
|
+
return {
|
|
158
|
+
data: null,
|
|
159
|
+
error: currentUserError.message,
|
|
160
|
+
errorCode: currentUserError.code ?? null,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (!currentUser) {
|
|
164
|
+
return {
|
|
165
|
+
data: null,
|
|
166
|
+
error: 'Nao existe um usuario autenticado para atualizar o profile.',
|
|
167
|
+
errorCode: null,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const currentMetadata = currentUser.user_metadata &&
|
|
171
|
+
typeof currentUser.user_metadata === 'object' &&
|
|
172
|
+
!Array.isArray(currentUser.user_metadata)
|
|
173
|
+
? currentUser.user_metadata
|
|
174
|
+
: {};
|
|
175
|
+
const { data, error } = await this.client.auth.updateUser({
|
|
176
|
+
data: {
|
|
177
|
+
...currentMetadata,
|
|
178
|
+
display_name: displayName,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
if (error) {
|
|
182
|
+
return {
|
|
183
|
+
data: null,
|
|
184
|
+
error: error.message,
|
|
185
|
+
errorCode: error.code ?? null,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (!data.user) {
|
|
189
|
+
return {
|
|
190
|
+
data: null,
|
|
191
|
+
error: 'O Supabase nao retornou o usuario atualizado.',
|
|
192
|
+
errorCode: null,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
data: data.user,
|
|
197
|
+
error: null,
|
|
198
|
+
errorCode: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
return {
|
|
203
|
+
data: null,
|
|
204
|
+
error: toAuthErrorMessage(error, 'Nao foi possivel atualizar o display name agora.'),
|
|
205
|
+
errorCode: null,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async sendPasswordReauthenticationCode() {
|
|
210
|
+
try {
|
|
211
|
+
const { error } = await this.client.auth.reauthenticate();
|
|
212
|
+
if (error) {
|
|
213
|
+
return {
|
|
214
|
+
data: null,
|
|
215
|
+
error: error.message,
|
|
216
|
+
errorCode: error.code ?? null,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
data: null,
|
|
221
|
+
error: null,
|
|
222
|
+
errorCode: null,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
data: null,
|
|
228
|
+
error: toAuthErrorMessage(error, 'Nao foi possivel solicitar o codigo de verificacao.'),
|
|
229
|
+
errorCode: null,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async updatePassword(password, nonce) {
|
|
234
|
+
try {
|
|
235
|
+
const { data, error } = await this.client.auth.updateUser(nonce
|
|
236
|
+
? {
|
|
237
|
+
nonce,
|
|
238
|
+
password,
|
|
239
|
+
}
|
|
240
|
+
: {
|
|
241
|
+
password,
|
|
242
|
+
});
|
|
243
|
+
if (error) {
|
|
244
|
+
return {
|
|
245
|
+
data: null,
|
|
246
|
+
error: error.message,
|
|
247
|
+
errorCode: error.code ?? null,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (!data.user) {
|
|
251
|
+
return {
|
|
252
|
+
data: null,
|
|
253
|
+
error: 'O Supabase nao retornou o usuario atualizado apos trocar a senha.',
|
|
254
|
+
errorCode: null,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
data: data.user,
|
|
259
|
+
error: null,
|
|
260
|
+
errorCode: null,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
return {
|
|
265
|
+
data: null,
|
|
266
|
+
error: toAuthErrorMessage(error, 'Nao foi possivel atualizar a senha agora.'),
|
|
267
|
+
errorCode: null,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
let cachedAuthBootstrap = null;
|
|
273
|
+
export function getAuthBootstrapResult() {
|
|
274
|
+
if (cachedAuthBootstrap) {
|
|
275
|
+
return cachedAuthBootstrap;
|
|
276
|
+
}
|
|
277
|
+
const envValidation = getCliEnvValidationResult();
|
|
278
|
+
if (!envValidation.ok) {
|
|
279
|
+
cachedAuthBootstrap = envValidation;
|
|
280
|
+
return cachedAuthBootstrap;
|
|
281
|
+
}
|
|
282
|
+
cachedAuthBootstrap = {
|
|
283
|
+
ok: true,
|
|
284
|
+
env: envValidation.env,
|
|
285
|
+
authService: AuthService.create(envValidation.env),
|
|
286
|
+
};
|
|
287
|
+
return cachedAuthBootstrap;
|
|
288
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SupportedStorage } from '@supabase/supabase-js';
|
|
2
|
+
export declare class FileStorage implements SupportedStorage {
|
|
3
|
+
private readonly filePath;
|
|
4
|
+
private queue;
|
|
5
|
+
constructor(filePath: string);
|
|
6
|
+
getItem(key: string): Promise<string | null>;
|
|
7
|
+
removeItem(key: string): Promise<void>;
|
|
8
|
+
setItem(key: string, value: string): Promise<void>;
|
|
9
|
+
private runExclusive;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=auth-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth-storage.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AA0C9D,qBAAa,WAAY,YAAW,gBAAgB;IAG/B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAF5C,OAAO,CAAC,KAAK,CAAuC;gBAEhB,QAAQ,EAAE,MAAM;IAEvC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ5C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAatC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,OAAO,CAAC,YAAY;CAUrB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
async function ensureParentDirectory(filePath) {
|
|
4
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
5
|
+
}
|
|
6
|
+
async function readStorageMap(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
const fileContents = await readFile(filePath, 'utf8');
|
|
9
|
+
const parsedContents = JSON.parse(fileContents);
|
|
10
|
+
if (!parsedContents || typeof parsedContents !== 'object' || Array.isArray(parsedContents)) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
return Object.fromEntries(Object.entries(parsedContents).filter((entry) => typeof entry[1] === 'string'));
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function writeStorageMap(filePath, data) {
|
|
23
|
+
await ensureParentDirectory(filePath);
|
|
24
|
+
const tempFilePath = `${filePath}.tmp`;
|
|
25
|
+
await writeFile(tempFilePath, JSON.stringify(data, null, 2), 'utf8');
|
|
26
|
+
await rename(tempFilePath, filePath);
|
|
27
|
+
if (process.platform !== 'win32') {
|
|
28
|
+
await chmod(filePath, 0o600).catch(() => undefined);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class FileStorage {
|
|
32
|
+
filePath;
|
|
33
|
+
queue = Promise.resolve();
|
|
34
|
+
constructor(filePath) {
|
|
35
|
+
this.filePath = filePath;
|
|
36
|
+
}
|
|
37
|
+
async getItem(key) {
|
|
38
|
+
return this.runExclusive(async () => {
|
|
39
|
+
const data = await readStorageMap(this.filePath);
|
|
40
|
+
return data[key] ?? null;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async removeItem(key) {
|
|
44
|
+
await this.runExclusive(async () => {
|
|
45
|
+
const data = await readStorageMap(this.filePath);
|
|
46
|
+
if (!(key in data)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
delete data[key];
|
|
50
|
+
await writeStorageMap(this.filePath, data);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async setItem(key, value) {
|
|
54
|
+
await this.runExclusive(async () => {
|
|
55
|
+
const data = await readStorageMap(this.filePath);
|
|
56
|
+
data[key] = value;
|
|
57
|
+
await writeStorageMap(this.filePath, data);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
runExclusive(operation) {
|
|
61
|
+
const nextOperation = this.queue.then(operation, operation);
|
|
62
|
+
this.queue = nextOperation.then(() => undefined, () => undefined);
|
|
63
|
+
return nextOperation;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-user.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth-user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAa1G"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getUserDisplayName(user) {
|
|
2
|
+
const value = user && user.user_metadata && typeof user.user_metadata === 'object' && !Array.isArray(user.user_metadata)
|
|
3
|
+
? user.user_metadata.display_name
|
|
4
|
+
: null;
|
|
5
|
+
if (typeof value !== 'string') {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const trimmedValue = value.trim();
|
|
9
|
+
return trimmedValue ? trimmedValue : null;
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type LoginPageProps = {
|
|
2
|
+
readonly busy: boolean;
|
|
3
|
+
readonly email: string;
|
|
4
|
+
readonly onEmailChange: (value: string) => void;
|
|
5
|
+
readonly onPasswordChange: (value: string) => void;
|
|
6
|
+
readonly onSubmit: () => void;
|
|
7
|
+
readonly password: string;
|
|
8
|
+
readonly sessionStorageFilePath: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function LoginPage({ busy, email, onEmailChange, onPasswordChange, onSubmit, password, sessionStorageFilePath, }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=login-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-page.d.ts","sourceRoot":"","sources":["../../../../src/modules/auth/page/login-page.tsx"],"names":[],"mappings":"AAIA,KAAK,cAAc,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC,CAAC;AAIF,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,sBAAsB,GACvB,EAAE,cAAc,2CAqDhB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { TextField } from '../../../shared/components/text-field.js';
|
|
5
|
+
export function LoginPage({ busy, email, onEmailChange, onPasswordChange, onSubmit, password, sessionStorageFilePath, }) {
|
|
6
|
+
const [focusField, setFocusField] = useState('email');
|
|
7
|
+
useInput((_input, key) => {
|
|
8
|
+
if (busy) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (key.tab || key.downArrow || key.upArrow) {
|
|
12
|
+
setFocusField((current) => (current === 'email' ? 'password' : 'email'));
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Entre com um usuario ja cadastrado no Supabase para liberar os comandos internos." }), _jsx(Text, { dimColor: true, children: "Cadastro nao esta habilitado nesta CLI." }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: focusField === 'email' ? 'greenBright' : 'white', children: "Email" }), _jsx(TextField, { focus: focusField === 'email', placeholder: "voce@empresa.com", value: email, onChange: onEmailChange, onSubmit: (value) => {
|
|
16
|
+
if (value.trim()) {
|
|
17
|
+
setFocusField('password');
|
|
18
|
+
}
|
|
19
|
+
} })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: focusField === 'password' ? 'greenBright' : 'white', children: "Senha" }), _jsx(TextField, { focus: focusField === 'password', mask: "*", placeholder: "Sua senha", value: password, onChange: onPasswordChange, onSubmit: () => {
|
|
20
|
+
onSubmit();
|
|
21
|
+
} })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Enter faz login. Tab ou setas alternam o campo ativo." }), _jsxs(Text, { dimColor: true, children: ["Sessao persistida localmente em: ", sessionStorageFilePath] })] })] }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type DiscoveryStepHeaderProps = {
|
|
2
|
+
readonly subtitle: string;
|
|
3
|
+
readonly title: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function DiscoveryStepHeader({ subtitle, title }: DiscoveryStepHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=discovery-step-header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-step-header.d.ts","sourceRoot":"","sources":["../../../../src/modules/discovery/components/discovery-step-header.tsx"],"names":[],"mappings":"AAEA,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,wBAAwB,2CAOhF"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export function DiscoveryStepHeader({ subtitle, title }) {
|
|
4
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "yellow", children: title }), _jsx(Text, { dimColor: true, children: subtitle })] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type DiscoveryScope = 'amplo' | 'restrito';
|
|
2
|
+
type DiscoveryPageProps = {
|
|
3
|
+
readonly onCancel: () => void;
|
|
4
|
+
readonly onComplete: (result: {
|
|
5
|
+
scope: DiscoveryScope;
|
|
6
|
+
theme: string;
|
|
7
|
+
}) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function DiscoveryPage({ onCancel, onComplete }: DiscoveryPageProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=discovery-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-page.d.ts","sourceRoot":"","sources":["../../../../src/modules/discovery/page/discovery-page.tsx"],"names":[],"mappings":"AAOA,KAAK,cAAc,GAAG,OAAO,GAAG,UAAU,CAAC;AAE3C,KAAK,kBAAkB,GAAG;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjF,CAAC;AAeF,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,kBAAkB,2CAsEzE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { ActionMenuPage } from '../../../shared/components/action-menu-page.js';
|
|
5
|
+
import { StepHeader } from '../../../shared/components/step-header.js';
|
|
6
|
+
import { TextField } from '../../../shared/components/text-field.js';
|
|
7
|
+
const SCOPE_OPTIONS = [
|
|
8
|
+
{
|
|
9
|
+
value: 'amplo',
|
|
10
|
+
label: 'Mais ampla',
|
|
11
|
+
description: 'Busca caminhos mais abrangentes em torno do tema informado.',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
value: 'restrito',
|
|
15
|
+
label: 'Mais restrita',
|
|
16
|
+
description: 'Foca em recortes mais especificos e direcionados do tema.',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
export function DiscoveryPage({ onCancel, onComplete }) {
|
|
20
|
+
const [step, setStep] = useState('theme');
|
|
21
|
+
const [theme, setTheme] = useState('');
|
|
22
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
23
|
+
useInput((input, key) => {
|
|
24
|
+
if (key.escape) {
|
|
25
|
+
onCancel();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (step !== 'scope') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (key.upArrow) {
|
|
32
|
+
setSelectedIndex((current) => current === 0 ? SCOPE_OPTIONS.length - 1 : current - 1);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (key.downArrow) {
|
|
36
|
+
setSelectedIndex((current) => current === SCOPE_OPTIONS.length - 1 ? 0 : current + 1);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (key.return) {
|
|
40
|
+
const option = SCOPE_OPTIONS[selectedIndex];
|
|
41
|
+
if (option) {
|
|
42
|
+
onComplete({
|
|
43
|
+
scope: option.value,
|
|
44
|
+
theme: theme.trim(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (step === 'theme') {
|
|
50
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StepHeader, { title: "Qual e o tema da descoberta?", subtitle: "Digite o tema e pressione Enter para continuar." }), _jsx(TextField, { placeholder: "Ex.: IA generativa", value: theme, onChange: setTheme, onSubmit: (value) => {
|
|
51
|
+
if (value.trim()) {
|
|
52
|
+
setTheme(value);
|
|
53
|
+
setStep('scope');
|
|
54
|
+
}
|
|
55
|
+
} }), _jsx(Text, { dimColor: true, children: "Esc volta para o menu inicial." })] }));
|
|
56
|
+
}
|
|
57
|
+
return (_jsx(ActionMenuPage, { title: "Como voce quer seguir com esse tema?", subtitle: `Tema selecionado: "${theme.trim()}". Escolha o tipo de exploracao.`, options: SCOPE_OPTIONS, selectedIndex: selectedIndex, hintText: "Use as setas para navegar. Enter confirma. Esc volta ao menu." }));
|
|
58
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AuthUser } from '@supabase/supabase-js';
|
|
2
|
+
import { type AuthService } from '../../auth/auth-service.js';
|
|
3
|
+
type NotificationTone = 'error' | 'info' | 'success';
|
|
4
|
+
type ProfilePageProps = {
|
|
5
|
+
readonly authService: AuthService;
|
|
6
|
+
readonly onBack: () => void;
|
|
7
|
+
readonly onNotificationChange: (notification: string | null, tone?: NotificationTone) => void;
|
|
8
|
+
readonly user: AuthUser;
|
|
9
|
+
};
|
|
10
|
+
export declare function ProfilePage({ authService, onBack, onNotificationChange, user }: ProfilePageProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=profile-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-page.d.ts","sourceRoot":"","sources":["../../../../src/modules/profile/page/profile-page.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAM9D,KAAK,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAKrD,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9F,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB,CAAC;AAwBF,wBAAgB,WAAW,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwQhG"}
|