Documentation Index
Fetch the complete documentation index at: https://mintlify.com/gcsconsultores/centros-estrategicos-gcs/llms.txt
Use this file to discover all available pages before exploring further.
GCS Centros Estratégicos is built on the Next.js App Router and organized into four logical layers: the UI layer (React components in components/office/ and components/evaluation/), the state layer (context/AuthContext.tsx and VirtualOffice.tsx local state), the API layer (three App Router route handlers under app/api/), and the service layer (services/ — thin wrappers around the Gemini API, Microsoft Graph, and a localStorage-backed CRM). Understanding how these layers interact is the fastest path to extending or debugging the platform.
Directory Structure
The repository follows the Next.js App Router convention. The key directories are:
centros-estrategicos-gcs/
├── app/ # Next.js App Router pages and API routes
│ ├── page.tsx # Entry point — renders <VirtualOffice />
│ ├── layout.tsx # Root layout: metadata, fonts, AuthProvider, Analytics
│ ├── globals.css # Tailwind v4 base styles
│ ├── login/ # Login page (name + role selection)
│ ├── evaluacion/ # Revisoría Fiscal evaluation page
│ └── api/
│ ├── chat/ # POST /api/chat — Gemini streaming via Vercel AI SDK
│ ├── leads/ # POST /api/leads, GET /api/leads — SharePoint write/read
│ └── meetings/ # POST /api/meetings, GET /api/meetings — Teams scheduling
│
├── components/
│ ├── office/ # Virtual office UI
│ │ ├── VirtualOffice.tsx # Root container: room state, chat state, lead form state
│ │ ├── Header.tsx # Top navigation bar with room switcher
│ │ ├── Lobby.tsx # Main landing grid of room cards
│ │ ├── RoomCard.tsx # Individual room preview card component
│ │ ├── forms/
│ │ │ └── LeadFormModal.tsx # Lead capture modal (name, email, company, interest)
│ │ ├── rooms/ # One component per consulting room
│ │ │ ├── RoomEjecutiva.tsx
│ │ │ ├── ComplianceRoom/
│ │ │ │ └── RoomCompliance.tsx
│ │ │ ├── RoomEstrategia.tsx
│ │ │ ├── RoomTraining.tsx
│ │ │ └── RoomInnovacion.tsx
│ │ └── sally/
│ │ └── SallyChat.tsx # Slide-in chat panel powered by Gemini
│ ├── evaluation/ # Revisoría Fiscal evaluation flow components
│ └── ui/ # shadcn/ui component library (Radix UI wrappers)
│
├── lib/
│ ├── types.ts # All TypeScript interfaces and type aliases
│ ├── constants.ts # COMPANY_INFO, GCS_COLORS, ROOMS, SALLY_GREETINGS
│ ├── evaluation.ts # Diagnostic questions, scoring logic, recommendations
│ └── utils.ts # Shared utility functions (cn, formatters)
│
├── services/
│ ├── crmServices.ts # localStorage-backed CRM (leads + meetings cache)
│ ├── geminiService.ts # Gemini API client helper
│ ├── sharepointService.ts # Microsoft Graph → SharePoint list operations
│ └── notificationService.ts # Toast/notification helpers
│
├── context/
│ └── AuthContext.tsx # User state (name, role, eligible), login, logout
│
├── hooks/
│ ├── use-sally-chat.ts # Chat state management hook
│ ├── use-mobile.ts # Responsive breakpoint detection
│ └── use-toast.ts # Sonner toast hook
│
├── ai/
│ └── sallyBrain.ts # Sally system prompt and Gemini stream configuration
│
└── public/
├── videos/ # Per-room background .mp4 files
│ ├── sally-lobby.mp4
│ ├── sala-ejecutiva.mp4
│ ├── sala-compliance.mp4
│ ├── sala-estrategia.mp4
│ ├── sala-training.mp4
│ └── sala-innovacion.mp4
└── assets/
├── images/ # Room background .webp images and Sally avatar
└── gcs-logo.png
Component Hierarchy
VirtualOffice is the root stateful container of the entire office experience. It owns three major pieces of state — the active room, the chat history, and the lead form — and passes handler callbacks down to child components via props. No global state manager (Redux, Zustand, etc.) is used; all office-level state is co-located in this single component.
VirtualOffice
├── Header
│ └── Room navigation tabs (maps ROOMS constant → buttons)
│
├── [Active Room] — switched by currentRoom state (RoomId)
│ ├── Lobby → RoomCard × 5
│ ├── RoomEjecutiva
│ ├── RoomCompliance → Evaluation trigger + diagnostic results
│ ├── RoomEstrategia
│ ├── RoomTraining
│ └── RoomInnovacion
│
├── SallyChat — controlled by isChatOpen state
│ └── Message list (SallyMessage[]) + input field
│ └── → POST /api/chat (Gemini stream)
│
└── LeadFormModal — controlled by isLeadFormOpen state
└── → POST /api/leads (SharePoint write)
Each room component receives three props: onOpenChat, onOpenLeadForm, and onRoomChange. These callbacks bubble events back up to VirtualOffice without requiring a shared context. When a user switches rooms, VirtualOffice appends the room-specific SALLY_GREETINGS[room] message directly to chatHistory so Sally always speaks in context.
Room System
The platform defines six rooms through the RoomId union type and the ROOMS record constant in lib/constants.ts.
// lib/types.ts
export type RoomId =
| 'lobby'
| 'ejecutiva'
| 'compliance'
| 'estrategia'
| 'training'
| 'innovacion';
export type RoomAccess = 'public' | 'private';
export interface Room {
id: RoomId;
name: string;
type: RoomAccess; // governs client-side access control
shortName: string;
description: string;
longDescription: string;
icon: string; // Lucide icon name
color: 'primary' | 'secondary' | 'accent';
features: string[];
ctaText: string;
ctaAction: 'schedule' | 'contact' | 'diagnostic' | 'training';
}
Access control is enforced in VirtualOffice.tsx with a single boolean check before rendering the active room:
const hasAccess =
user?.role === 'consultor' || currentRoomData.type === 'public';
The three public rooms (Lobby, Compliance, Innovación) are always accessible to authenticated users with a valid User session. The three private rooms (Ejecutiva, Estrategia, Training) are restricted to consultor role users. Clients who navigate to a private room see an access-denied message and a button to return to the Lobby.
API Routes
The three route handler directories under app/api/ form the backend surface of the platform. All routes are Next.js App Router handlers (route.ts files) and run as Vercel serverless functions in production.
| Route | Method | Description |
|---|
/api/chat | POST | Accepts { messages, context } (messages is a UIMessage[] array), streams a Gemini 2.5 Flash response via the Vercel AI SDK streamText helper |
/api/leads | POST | Validates the LeadFormData payload and writes a new item to the configured SharePoint list via Microsoft Graph |
/api/leads | GET | Reads all lead items from the SharePoint list and returns them as ApiResponse<Lead[]> |
/api/meetings | POST | Creates a Teams online meeting calendar event via Graph using DEFAULT_ORGANIZER_EMAIL as the organizer; returns the joinUrl |
/api/meetings | GET | Retrieves scheduled meeting items from the Microsoft Graph calendar |
Data Flow
Understanding how leads and meetings flow from the UI through to Microsoft 365 is essential for debugging integration issues.
Lead capture flow:
User fills LeadFormModal (or Sally triggers shouldCaptureLead)
→ LeadFormModal.onSubmit fires handleSubmitLead in VirtualOffice
→ POST /api/leads { name, email, company, interest, source, ... }
→ sharepointService.ts acquires an OAuth 2.0 token from Azure AD
(client_credentials grant using AZURE_AD_CLIENT_ID + AZURE_AD_CLIENT_SECRET)
→ HTTP POST to Graph: /sites/{SHAREPOINT_SITE_ID}/lists/{SHAREPOINT_LIST_ID}/items
→ SharePoint creates a new list item
→ API returns ApiResponse<Lead> with the new item's ID
→ VirtualOffice appends a Sally confirmation message to chatHistory
→ SallyChat panel opens automatically (setIsChatOpen(true))
If the Microsoft 365 variables are absent, sharepointService.ts falls back to logging the lead payload to the server console and returning a mock success response, so the UI flow completes without errors.
Teams meeting flow:
User requests a meeting (ctaAction: 'schedule' in any room)
→ Meeting form collects name, email, company, date, time, topic
→ POST /api/meetings { MeetingRequest payload }
→ sharepointService / meeting handler acquires Graph token
→ HTTP POST to Graph: /users/{DEFAULT_ORGANIZER_EMAIL}/calendar/events
with isOnlineMeeting: true
→ Graph creates the calendar event and returns an onlineMeeting.joinUrl
→ API returns ApiResponse<Meeting> including teamsLink (the joinUrl)
→ UI displays the Teams link to the user
Authentication
Authentication is intentionally lightweight — the platform targets an embedded iframe context inside WordPress, so it avoids cookies and server-side sessions in favor of localStorage.
AuthContext.tsx exposes three values and two functions to the entire component tree via React Context:
interface AuthContextType {
user: User | null // null before login or after logout
loading: boolean // true during the initial localStorage read
login: (userData: User) => void
logout: () => void
}
// User shape from lib/types.ts
interface User {
name: string
role: 'cliente' | 'consultor'
eligible: boolean // must be true to enter VirtualOffice
}
eligible flag: This boolean controls whether a user can enter the virtual office. VirtualOffice.tsx checks user?.eligible before rendering any room content — if it is false, users see an inline message prompting them to complete the evaluation instead of the office UI. In the current implementation, the login page (app/login/page.tsx) sets eligible: true for all users at login time. The Revisoría Fiscal evaluation at /evaluacion is a standalone diagnostic tool; connecting its result to the eligible flag is a planned integration point.
Login and logout: The login function writes the User object to localStorage under the key "user" and sets it in React state. The logout function clears both. On page load, AuthContext reads localStorage in a useEffect and rehydrates the user session, so refreshing the page does not log users out.
Room background videos are stored in public/videos/ as .mp4 files — one per room, named after the room slug (e.g., sala-compliance.mp4, sala-ejecutiva.mp4). To replace a room’s video, swap the corresponding file in public/videos/ and ensure the filename matches the value in the SALLY_VIDEOS constant in lib/constants.ts. Videos are referenced with absolute public paths, so no import is required.