CoolFace
Apppublic

DataScope26/WEB

sourceHugging Faceupdated 7d agoView on Hugging Face
0likes
Dashboard.jsx387 linesDownload Raw Back to components
1import { useDashboardMetrics } from '../hooks/useDashboardMetrics.jsx';2import BarGraph from './BarGraph.jsx';3import NumberStat from './NumberStat.jsx';4import PieChartGraph from './PieChartGraph.jsx';5import ListWithPercentages from './ListWithPercentages.jsx';6import InfoIcon from './InfoIcon.jsx';7import { useAuth } from "../context/AuthContext.jsx"8import "./Dashboard.css";9import { useState, useRef, useEffect } from 'react';10import { useNavigate, useLocation } from "react-router-dom";11import ButtonPrimary from './ButtonPrimary.jsx';12 13const isTechnicalArea = (areaName) => {14  const normalizedName = String(areaName ?? '')15    .trim()16    .toLocaleLowerCase()17    .normalize('NFD')18    .replace(/[\u0300-\u036f]/g, '');19 20  return normalizedName === 'general' || normalizedName === 'sin area';21};22 23function Dashboard() {24  const { user, selectedCompany, setSelectedCompany } = useAuth()25  const navigate = useNavigate();26  const location = useLocation();27  const [selectedArea, setSelectedArea] = useState('general');28  const activeCompany = location.state?.company ?? selectedCompany ?? (user?.companies?.[0] ?? null);29  const [timePeriod, setTimePeriod] = useState('cuatrimestre');30  const { metrics, loading, isRefreshing, error } = useDashboardMetrics(selectedArea, activeCompany, timePeriod);31  32  33  const [isOpen, setIsOpen] = useState(false);34  const [isAreaOpen, setIsAreaOpen] = useState(false);35  const [showAllAdoption, setShowAllAdoption] = useState(false);36  const dropdownRef = useRef(null);37  const areaDropdownRef = useRef(null);38  const ADOPTION_PREVIEW_COUNT = 1; 39 40  const options = [41    { value: 'dia', label: 'Día' },42    { value: 'semana', label: 'Semana' },43    { value: 'mes', label: 'Mes' },44    { value: 'cuatrimestre', label: 'Cuatrimestre' },45    { value: 'año', label: 'Año' }46  ];47 48  const handleFilterChange = (period) => {49    console.log('Period seleccionado:', period);50    setTimePeriod(period);51    setIsOpen(false);52  };53 54  const handleAreaChange = (area) => {55    setSelectedArea(area);56    setIsAreaOpen(false);57    setShowAllAdoption(false); // al cambiar de filtro, siempre arrancamos colapsado58  };59 60  useEffect(() => {61    const handleClickOutside = (event) => {62      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {63        setIsOpen(false);64      }65      if (areaDropdownRef.current && !areaDropdownRef.current.contains(event.target)) {66        setIsAreaOpen(false);67      }68    };69 70    document.addEventListener('mousedown', handleClickOutside);71    return () => document.removeEventListener('mousedown', handleClickOutside);72  }, []);73 74  useEffect(() => {75    if (location.state?.company) {76      setSelectedCompany(location.state.company);77    }78  }, [location.state?.company, setSelectedCompany]);79 80  const selectedLabel = options.find(opt => opt.value === timePeriod)?.label;81 82  const availableAreas = (metrics?.availableAreas || []).filter(83    (area) => !isTechnicalArea(area.name)84  );85  const areaOptions = [{ id: 'general', name: 'General' }, ...availableAreas];86  const selectedAreaLabel = selectedArea === 'general' ? 'General' : areaOptions.find(a => String(a.id) === String(selectedArea))?.name;87 88  const fullAdoptionRate = (metrics?.adoptionRate || []).filter(89    (area) => !isTechnicalArea(area.area)90  );91  const isGeneralFilter = selectedArea === 'general';92  const adoptionRateToShow = (isGeneralFilter && !showAllAdoption)93    ? fullAdoptionRate.slice(0, ADOPTION_PREVIEW_COUNT)94    : fullAdoptionRate;95  const canExpandAdoption = isGeneralFilter && fullAdoptionRate.length > ADOPTION_PREVIEW_COUNT;96 97  const hasNoAreas = availableAreas.length === 0;98 99  const areaHasNoEmployees = !isGeneralFilter && (100    !metrics?.adoptionRate ||101    metrics.adoptionRate.length === 0 ||102    metrics.adoptionRate[0]?.area_total_users === 0103  );104 105  const handleSetupNavigation = (step) => {106    const companyId = activeCompany?.id ?? user?.companies?.[0]?.id;107    if (!companyId) return;108 109    const params = new URLSearchParams({ steps: step });110 111    if (step === 'invite' && selectedArea && selectedArea !== 'general') {112      params.set('areaId', String(selectedArea));113    }114 115    navigate(`/company-setup/${companyId}?${params.toString()}`);116  };117 118  if (!user) {119    return <h1 className="loading-dashboard">Loading...</h1>;120  }121 122  // If the user is logged but has no companies, show a helpful message123  if (user && !activeCompany) {124    return (125      <div className='loading-dashboard-container'>126        <h1 className="loading-dashboard">No tienes ninguna compañia asociada.</h1>127 128        <ButtonPrimary129          className="create-companies-btn"130          onClick={() => navigate('/create-company')}131          style={{ marginTop: '16px' }}132        >133          Crear mi primera empresa134        </ButtonPrimary>135      </div>136    );137  }138 139  return (140    <>141      {loading ? (142        <div className='loading-dashboard-container'>143          <h1 className="loading-dashboard">Loading...</h1>144        </div>145      ) : error ? (146        !activeCompany ?147          <div className='loading-dashboard-container'>148            <h1 className="loading-dashboard">No tienes ninguna compañia asociada.</h1>149 150            <ButtonPrimary151              className="my-companies__create-btn"152              onClick={() => navigate('/create-company')}153              style={{ marginTop: '16px' }}154            >155              Crear mi primera empresa156            </ButtonPrimary>157          </div>158          : error.status == 403 ?159            <div className='loading-dashboard-container'>160              <h1 className="loading-dashboard">No eres administrador de esta compañia.</h1>161            </div>162            :163            <div className='loading-dashboard-container'>164              <h1 className="loading-dashboard">Ocurrió un error</h1>165            </div>166 167      ) : (168        <div className="dashboard-root">169          {/* Header */}170          <div className='header-name-company'>171            <h1 className="dashboard-company-name">{activeCompany?.name || 'Nombre de la empresa'}</h1>172          </div>173 174          {/* Métricas generales */}175          <section className="dashboard-section">176            <h2 className="dashboard-section-title dashboard-section--spaced">Metricas generales</h2>177            <div className="dashboard-stats-row">178 179              <NumberStat180                title="TOTAL PROMPTS"181                metric={metrics?.estimatedUsageTime?.total_messages ?? 0}182                subtitle="Procesados en tiempo real"183                infoTitle="Total de Prompts"184                infoDescription="La cantidad total de Prompts realizados en la empresa. Un prompt es una instrucción, pregunta o texto que se le escribe a un sistema de IA (como ChatGPT o Gemini) para que realice una tarea u obtenga un resultado específico"185                accent186              />187              {/**188              <NumberStat189                title="PRECISIÓN GLOBAL"190                metric="0%"191                subtitle="Calibración optimizada"192                infoTitle="Precisión global"193                infoDescription="Work In Progress - not yet measured"194              />195               */}196            </div>197          </section>198 199          {/* Área */}200          <section className="dashboard-section">201            <div className="dashboard-section-header">202              <h2 className="dashboard-section-title">Area</h2>203 204              <div className="dashboard-header-filters">205                <div className="custom-dropdown" ref={areaDropdownRef}>206                  <ButtonPrimary207                    className="dropdown-button"208                    onClick={() => setIsAreaOpen(!isAreaOpen)}209                  >210                    <span className="dropdown-label">{selectedAreaLabel}</span>211                    <span className={`dropdown-arrow ${isAreaOpen ? 'open' : ''}`}>▼</span>212                  </ButtonPrimary>213 214                  {isAreaOpen && (215                    <div className="dropdown-menu">216                      {areaOptions.map(area => (217                        <ButtonPrimary218                          key={area.id}219                          type="button"220                          className={`dropdown-option ${String(selectedArea) === String(area.id) ? 'selected' : ''}`}221                          onClick={() => handleAreaChange(area.id)}222                        >223                          {area.name}224                        </ButtonPrimary>225                      ))}226                    </div>227                  )}228                </div>229 230                <div className="custom-dropdown" ref={dropdownRef}>231                  <ButtonPrimary232                    className="dropdown-button"233                    onClick={() => setIsOpen(!isOpen)}234                  >235                    <span className="dropdown-label">{selectedLabel}</span>236                    <span className={`dropdown-arrow ${isOpen ? 'open' : ''}`}>▼</span>237                  </ButtonPrimary>238 239                  {isOpen && (240                    <div className="dropdown-menu">241                      {options.map(option => (242                        <ButtonPrimary243                          key={option.value}244                          type="button"245                          className={`dropdown-option ${timePeriod === option.value ? 'selected' : ''}`}246                          onClick={() => handleFilterChange(option.value)}247                        >248                          {option.label}249                        </ButtonPrimary>250                      ))}251                    </div>252                  )}253                </div>254              </div>255            </div>256 257            {hasNoAreas ? (258              <div className="dashboard-no-employees">259                <p className="dashboard-no-employees__text">Todavía no creaste ningún área.</p>260                <ButtonPrimary261                  type="button"262                  className="dashboard-no-employees__btn"263                  onClick={() => handleSetupNavigation('area')}264                  style={{ border: 'none' }}265                >266                  Crear área267                </ButtonPrimary>268              </div>269            ) : areaHasNoEmployees ? (270              <div className="dashboard-no-employees">271                <p className="dashboard-no-employees__text">Esta area no tiene empleados asignados.</p>272                <ButtonPrimary273                  type="button"274                  className="dashboard-no-employees__btn"275                  onClick={() => handleSetupNavigation('invite')}276                  style={{ border: 'none' }}277                >278                  Invitar empleados279                </ButtonPrimary>280              </div>281            ) : (282              <div className="dashboard-area-grid">283 284                {/* Calidad de prompts: foco principal (full width) */}285                <div className="dashboard-card dashboard-card--full">286                  {metrics.promptQuality?.average_quality != null ? (287                    <div className="quality-score">288                      <div className="quality-score__header">289                        <span className="bar-graph-title">CALIDAD DE PROMPTS</span>290                        <InfoIcon description="La estadística se obtiene promediando la calidad evaluada en cada sesión. Una sesión empieza con el primer mensaje y se extiende mientras el usuario siga enviando mensajes: cada nuevo mensaje reinicia un contador de 10 minutos. La sesión solo termina cuando pasa ese tiempo sin actividad. Al cierre, se asigna una puntuación por sesión y se promedian todas." title="Calidad" />291                      </div>292                      <div className="quality-number">293                        {metrics.promptQuality.average_quality} <span className="quality-max">/100</span>294                      </div>295                      <div className="quality-bar-track">296                        <div className="quality-bar-fill" style={{ width: (metrics.promptQuality.average_quality + "%") }} />297                      </div>298                      <span className="quality-level">NIVEL: {(metrics.promptQuality.average_quality < 30 ? "BASICO" : metrics.promptQuality.average_quality < 70 ? "MEDIO" : "EXPERTO")}</span>299                    </div>300                  ) : (301                    <div className="dashboard-no-data">302                      <span className="bar-graph-title">CALIDAD DE PROMPTS</span>303                      <p className="dashboard-no-data__text">No hay datos disponibles</p>304                    </div>305                  )}306                </div>307 308                <div className="dashboard-card">309                  {adoptionRateToShow?.length > 0 ? (310                    <>311                      <BarGraph312                        metrics={adoptionRateToShow}313                        numberedAxisValueName="active_users"314                        numberedAxisTotalName="area_total_users"315                        title={isGeneralFilter ? "ADOPCIÓN DE IA POR ÁREA" : "ADOPCIÓN DE IA"}316                        namedAxis="area"317                        infoTitle="Adopcion de IA por Área"318                        infoDescription="Representa el porcentaje de uso de plataformas de IA en cada área de la empresa. Un usuario activo es aquel que mando al menos un mensaje a la IA en el tiempo especificado."319                      />320                      {canExpandAdoption && (321                        <ButtonPrimary322                          type="button"323                          className="adoption-toggle-btn"324                          onClick={() => setShowAllAdoption(!showAllAdoption)}325                        >326                          {showAllAdoption ? "Ver menos" : "Ver mas..."}327                        </ButtonPrimary>328                      )}329                    </>330                  ) : (331                    <div className="dashboard-no-data">332                      <span className="bar-graph-title">{isGeneralFilter ? "ADOPCIÓN DE IA POR ÁREA" : "ADOPCIÓN DE IA"}</span>333                      <p className="dashboard-no-data__text">No hay datos disponibles</p>334                    </div>335                  )}336                </div>337 338                <div className="dashboard-card dashboard-card-pie">339                  {metrics.platformUsage?.length > 0 ? (340                    <PieChartGraph341                      title="USO DE IA"342                      objectList={metrics.platformUsage}343                      label="platform"344                      percentage="percentage"345                      infoTitle="Distribución de Plataformas"346                      infoDescription="Muestra el porcentaje de uso de cada plataforma de IA. Se calcula tomando el total de prompts procesados en cada plataforma dividido por el total de prompts de todas las plataformas."347                    />348                  ) : (349                    <div className="dashboard-no-data">350                      <span className="bar-graph-title">USO DE IA</span>351                      <p className="dashboard-no-data__text">No hay datos disponibles</p>352                    </div>353                  )}354                </div>355 356                {/* Usos principales de la IA (abajo) */}357                <div className="dashboard-card dashboard-card-list-with-percentages dashboard-card--full">358                  {metrics.mainTasks?.length > 0 ? (359                    <ListWithPercentages360                      objectArray={metrics.mainTasks}361                      itemsTitle="category"362                      numberField="total_uses"363                      title="USOS PRINCIPALES DE LA IA"364                      infoTitle="Usos Principales de la IA"365                      infoDescription="Muestra el desglose de las tareas automatizadas por categoría. Se calcula dividiendo el total de usos de cada tarea entre el total general y expresando como porcentaje."366                    />367                  ) : (368                    <div className="dashboard-no-data">369                      <span className="bar-graph-title">USOS PRINCIPALES DE LA IA</span>370                      <p className="dashboard-no-data__text">No hay datos disponibles</p>371                    </div>372                  )}373                </div>374 375              </div>376            )}377          </section>378 379        </div>380      )381      }382 383    </>384  );385}386 387export default Dashboard;