feat: periodo global, métricas negocio, seguimiento notas, Drive/Social/Orgánico
Selector de fechas en header (con Personalizado) compartido en el dashboard; Métricas con Ventas/CAC/ROAS CRM, cruce canal y gráficas diarias; Seguimiento solo notas + sync Superhuman; Activos con navegación Drive; Social con métricas IG/FB; Orgánico con prioridades SEO/GEO expertas. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,7 +10,9 @@ import {
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
/** GET — list Drive files for active tenant folder (or ?download=fileId). */
|
||||
const FOLDER_MIME = 'application/vnd.google-apps.folder';
|
||||
|
||||
/** GET — list Drive files for tenant folder (browse with ?folderId=). */
|
||||
export async function GET(req: NextRequest) {
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
@@ -28,11 +30,7 @@ export async function GET(req: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// Clients only see own tenant; agency via view cookie already in ctx
|
||||
const agency = isAgencyRole(userRole(user));
|
||||
if (!agency) {
|
||||
// resolveActiveTenant already scopes to home tenant
|
||||
}
|
||||
void isAgencyRole(userRole(user));
|
||||
|
||||
const downloadId = req.nextUrl.searchParams.get('download')?.trim();
|
||||
if (downloadId) {
|
||||
@@ -53,30 +51,47 @@ export async function GET(req: NextRequest) {
|
||||
files: [],
|
||||
linked: false,
|
||||
warning:
|
||||
'Sin carpeta Drive enlazada. Conecta marketing@ en Agencia y ejecuta auto-match.',
|
||||
'Sin carpeta Drive enlazada. Conecta marketing@ en Agencia y ejecuta auto-match bajo «Clientes».',
|
||||
});
|
||||
}
|
||||
|
||||
const requested = req.nextUrl.searchParams.get('folderId')?.trim();
|
||||
const folderId = requested || link.folderId;
|
||||
|
||||
try {
|
||||
const files = await listFilesInFolder(link.folderId);
|
||||
return NextResponse.json({
|
||||
linked: true,
|
||||
folderId: link.folderId,
|
||||
folderName: link.folderName,
|
||||
files: files.map((f) => ({
|
||||
const files = await listFilesInFolder(folderId);
|
||||
const mapped = files
|
||||
.map((f) => ({
|
||||
id: f.id,
|
||||
name: f.name,
|
||||
mimeType: f.mimeType,
|
||||
bytes: f.size ? Number(f.size) : null,
|
||||
modifiedTime: f.modifiedTime,
|
||||
webViewLink: f.webViewLink,
|
||||
isFolder: f.mimeType === FOLDER_MIME,
|
||||
source: 'drive' as const,
|
||||
})),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.isFolder !== b.isFolder) return a.isFolder ? -1 : 1;
|
||||
return a.name.localeCompare(b.name, 'es');
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
linked: true,
|
||||
rootFolderId: link.folderId,
|
||||
folderId,
|
||||
folderName: link.folderName,
|
||||
driveOpenUrl: `https://drive.google.com/drive/folders/${encodeURIComponent(folderId)}`,
|
||||
embedPossible: false,
|
||||
embedHint:
|
||||
'Réplica vía API (misma carpeta Clientes del cliente). Embed iframe de Drive exige carpeta pública; usamos listado nativo + Abrir en Drive.',
|
||||
files: mapped,
|
||||
});
|
||||
} catch (e) {
|
||||
return NextResponse.json({
|
||||
linked: true,
|
||||
folderId: link.folderId,
|
||||
rootFolderId: link.folderId,
|
||||
folderId,
|
||||
folderName: link.folderName,
|
||||
files: [],
|
||||
warning: e instanceof Error ? e.message : 'Drive error',
|
||||
|
||||
Reference in New Issue
Block a user