1184 lines
44 KiB
Markdown
1184 lines
44 KiB
Markdown
**INFORME TÉCNICO COMPLETO**
|
|
|
|
**I. Estructura y Componentes Lógicos:**
|
|
|
|
La página presenta una estructura de panel de control (`dashboard`) dividida en un diseño de dos columnas principales: una barra lateral de navegación fija a la izquierda y un área de contenido principal dinámica a la derecha.
|
|
|
|
* **Componentes Lógicos Modulares:**
|
|
1. **`PageContainer.tsx`:** Actúa como el contenedor principal, organizando la disposición global.
|
|
* **Jerarquía DOM:** Un `div` principal que encapsula una `aside` para la barra lateral y una `main` para el contenido principal.
|
|
* **Medidas Clave:**
|
|
* Layout: `display: flex;` para posicionar la barra lateral y el contenido.
|
|
* Ancho de `aside` (barra lateral): Aproximadamente `260px` (fijo).
|
|
* `main` (contenido principal): `flex-grow: 1;` para ocupar el espacio restante.
|
|
* El contenido dentro de `main` tiene un `padding` horizontal de aproximadamente `32px`.
|
|
* No se observa un `max-width` global para toda la aplicación; el diseño se adapta al ancho de la ventana, con el contenido interno centrado o con padding.
|
|
|
|
2. **`Sidebar.tsx`:** Contiene la navegación principal de la aplicación.
|
|
* **Jerarquía DOM:** `aside` (contenedor) > `div` (logo y título "StreamYard") > `nav` (para enlaces de navegación) > `ul` > `li` (elementos de menú) > `div` (sección de almacenamiento) > `div` (sección de configuración y ayuda).
|
|
* **Medidas Clave:**
|
|
* Ancho: `260px` (fijo).
|
|
* Altura: `100vh` (altura completa de la ventana).
|
|
* Fondo: Blanco.
|
|
* Items de menú: `padding` vertical de `10px` y horizontal de `20px`.
|
|
* Elemento activo: `background-color` (azul claro) y `border-left` (azul primario) de `4px`.
|
|
* Separación entre grupos de enlaces y secciones: `margin-top` de `20px` a `30px`.
|
|
|
|
3. **`Header.tsx`:** La barra superior del área de contenido principal.
|
|
* **Jerarquía DOM:** `header` (contenedor) > `div` (botones "Mejora tu plan") > `div` (grupo de iconos y "Mi cuenta").
|
|
* **Medidas Clave:**
|
|
* Altura: Aproximadamente `64px` (fija).
|
|
* Fondo: Blanco.
|
|
* Layout: `display: flex; justify-content: flex-end; align-items: center;` (elementos alineados a la derecha, con espacio entre ellos). `padding` horizontal de `32px`.
|
|
* Borde inferior sutil: `border-bottom: 1px solid` con color gris claro.
|
|
|
|
4. **`HeroSection.tsx`:** La sección "Crear", que presenta las opciones principales al usuario.
|
|
* **Jerarquía DOM:** `section` (contenedor) > `h2` (título "Crear") > `div` (contenedor de tarjetas) > `div` (tarjeta individual, por ejemplo, "Transmisión en vivo").
|
|
* **Medidas Clave:**
|
|
* Sistema de Rejilla: `display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;` (para escritorio).
|
|
* Tarjetas: `min-height` de aproximadamente `120px` - `150px`. `padding: 20px;`.
|
|
* Radio de borde: `border-radius: 8px`.
|
|
* Separación: `margin-bottom` de `40px` después de la sección.
|
|
|
|
5. **`TransmissionsTable.tsx`:** La sección de "Transmisiones y grabaciones".
|
|
* **Jerarquía DOM:** `section` (contenedor) > `h2` (título "Transmisiones y grabaciones") > `div` (contenedor de pestañas) > `button` (pestañas "Próximamente" y "Anteriores") > `div` (contenedor de la tabla) > `table` > `thead`, `tbody`, `tr`, `th`, `td`.
|
|
* **Medidas Clave:**
|
|
* Pestañas: `display: flex;` con `padding: 10px 0; margin-right: 20px;` para cada botón. La pestaña activa tiene `border-bottom: 3px solid` (azul primario).
|
|
* Tabla: `width: 100%; border-collapse: collapse;`.
|
|
* Celdas de tabla: `padding: 12px 15px;`.
|
|
* Filas de tabla: `border-bottom: 1px solid` (gris claro).
|
|
* Separación: `margin-bottom` de `30px` después del título de la sección, `margin-bottom` de `20px` después de las pestañas.
|
|
|
|
**II. Métricas Pixel-Perfect:**
|
|
|
|
* **Tipografía:**
|
|
* **Familia de Fuentes:** 'Inter', 'Roboto', sans-serif (estimado visualmente). Se utilizará 'Inter' como principal.
|
|
* **Títulos (H1/H2, por ejemplo, "Crear", "Transmisiones y grabaciones"):**
|
|
* `font-size`: `22px`
|
|
* `font-weight`: `600` (Semi-bold)
|
|
* `line-height`: `1.4`
|
|
* **Subtítulos (ej. "Próximamente", "Anteriores"):**
|
|
* `font-size`: `16px`
|
|
* `font-weight`: `500` (Medium)
|
|
* `line-height`: `1.5`
|
|
* **Texto Principal (P, elementos de menú, contenido de tabla):**
|
|
* `font-size`: `14px`
|
|
* `font-weight`: `400` (Regular)
|
|
* `line-height`: `1.6`
|
|
* **Texto Secundario/Pequeño (ej. "0 de 5 horas", "Agregar más"):**
|
|
* `font-size`: `12px`
|
|
* `font-weight`: `400` (Regular)
|
|
* `line-height`: `1.5`
|
|
|
|
* **Paleta de Colores (Códigos HEX/RGB estimados):**
|
|
* **Fondo de Página (Main Background):** `#F7F8FA` (`rgb(247, 248, 250)`) - Gris muy claro.
|
|
* **Fondo de Componentes/Tarjetas/Sidebar/Header:** `#FFFFFF` (`rgb(255, 255, 255)`) - Blanco.
|
|
* **Texto Principal (Dark Grey):** `#212121` (`rgb(33, 33, 33)`) - Casi negro.
|
|
* **Texto Secundario (Medium Grey):** `#6B7280` (`rgb(107, 114, 128)`) - Gris medio para subtítulos y texto menos prominente.
|
|
* **Color de Acento (Primary Blue):** `#1876F2` (`rgb(24, 118, 242)`) - Azul vibrante para elementos interactivos.
|
|
* **Fondo Activo de Sidebar/Hover:** `#EBF3FF` (`rgb(235, 243, 255)`) - Azul muy claro.
|
|
* **Bordes/Divisores (Light Grey):** `#E5E7EB` (`rgb(229, 231, 235)`) - Gris claro.
|
|
* **Sombras (Box Shadow):** `0px 1px 3px rgba(0, 0, 0, 0.08)` (sutil para tarjetas y botón flotante).
|
|
* **Color de Iconos (Grey):** `#6B7280`
|
|
|
|
* **Espaciado y Bordeado:**
|
|
* **Separación vertical entre secciones:** `margin-top: 32px;` o `padding-top: 32px;`.
|
|
* **Padding horizontal global en contenido principal:** `32px`.
|
|
* **Padding vertical en Header:** `16px`.
|
|
* **Radius de Borde:**
|
|
* Botones ("Mejora tu plan", "Entrar al estudio", "Ayuda"): `border-radius: 6px`.
|
|
* Tarjetas ("Crear" section): `border-radius: 8px`.
|
|
* Alternadores (Theme Toggle): `border-radius: 9999px` (píldora).
|
|
|
|
**III. Funcionalidad y Scripts Internos:**
|
|
|
|
* **Funcionalidad del Aplicativo Web:**
|
|
1. **Navegación Interactiva del Sidebar:** Los elementos del menú en la barra lateral son clicables. El elemento actualmente seleccionado ("Inicio") se destaca con un fondo azul claro y un borde azul a la izquierda para indicar su estado activo.
|
|
2. **Toggle de Tema (Modo Claro/Oscuro):** En la esquina superior derecha del área de contenido principal, hay un control que permite al usuario alternar entre modos de visualización (claro y oscuro), representado por iconos de sol y luna. El modo "claro" (sol) está activo por defecto.
|
|
3. **Pestañas de Contenido (Transmisiones y Grabaciones):** La sección "Transmisiones y grabaciones" contiene dos pestañas ("Próximamente" y "Anteriores"). Al hacer clic en cada pestaña, el contenido de la tabla subyacente debe actualizarse para mostrar las transmisiones correspondientes a la selección. La pestaña "Próximamente" está activa.
|
|
4. **Botón de Acción Flotante ("Ayuda"):** Un botón persistente en la parte inferior derecha de la pantalla que ofrece acceso rápido a la ayuda o soporte. Este botón tiene una sombra para darle un efecto elevado.
|
|
5. **Estados de Interacción (Hover/Active):** Botones, enlaces de navegación y tarjetas presentan cambios visuales (ej. cambio de color de fondo, elevación de sombra) al pasar el cursor (hover) y al hacer clic (active) para proporcionar retroalimentación al usuario.
|
|
|
|
* **Scripts Internos (Comportamiento):**
|
|
1. **Manejo de Estado para Navegación Activa:** Se utilizará `useState` en el componente `Sidebar` para controlar el `id` o la `ruta` del elemento de navegación activo, aplicando dinámicamente las clases CSS (`styles.activeLink`) al elemento seleccionado.
|
|
2. **Lógica de Toggle de Tema:** Un `useState` en `PageContainer` o un contexto global para gestionar el estado del tema (`light`/`dark`). Se aplicará una clase CSS (`.theme-dark` o similar) al `body` o al contenedor principal para modificar los colores a través de variables CSS (ej. `--background-color`, `--text-color`).
|
|
3. **Manejo de Estado para Pestañas:** Dentro del componente `TransmissionsTable`, se utilizará `useState` para mantener el control de la pestaña activa. Esto determinará qué conjunto de datos de transmisiones se renderiza en la tabla.
|
|
4. **Renderizado Condicional y Mapeo de Datos:** El contenido de la tabla en `TransmissionsTable` se renderizará condicionalmente o mediante `.map()` basándose en el estado de la pestaña activa y un array de objetos de transmisiones.
|
|
5. **Event Handlers:** Funciones de `onClick` serán implementadas en los elementos interactivos (enlaces de navegación, botones de tema, pestañas) para actualizar los estados correspondientes y desencadenar los cambios en la UI.
|
|
6. **Desplazamiento Suave (Scroll Behavior):** Aunque no es explícitamente interactivo, la barra lateral debe permanecer fija mientras el contenido principal es desplazable. El botón "Ayuda" también se fijará en la esquina inferior derecha (`position: fixed`).
|
|
|
|
---
|
|
|
|
**GENERACIÓN DE CÓDIGO**
|
|
|
|
A continuación, se presenta el código para los componentes React/TypeScript y sus módulos CSS, siguiendo las métricas y la funcionalidad descritas en el informe técnico. Se incluirá un componente `Sidebar` adicional por su relevancia estructural en el diseño.
|
|
|
|
**1. `src/components/PageContainer.tsx`**
|
|
|
|
```tsx
|
|
import React, { useState } from 'react';
|
|
import styles from './PageContainer.module.css';
|
|
import Sidebar from './Sidebar/Sidebar'; // Assuming Sidebar component
|
|
import Header from './Header/Header'; // Assuming Header component
|
|
import HeroSection from './HeroSection/HeroSection'; // Assuming HeroSection component
|
|
import TransmissionsTable from './TransmissionsTable/TransmissionsTable'; // Assuming TransmissionsTable component
|
|
|
|
const PageContainer: React.FC = () => {
|
|
// Estado para manejar el tema (claro/oscuro)
|
|
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
|
|
|
const toggleTheme = () => {
|
|
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
|
|
};
|
|
|
|
// Aplica la clase del tema al body o al contenedor principal
|
|
// En un proyecto real, esto podría hacerse con un contexto o CSS variables
|
|
// document.body.className = theme === 'dark' ? 'theme-dark' : 'theme-light';
|
|
|
|
return (
|
|
<div className={`${styles.pageContainer} ${theme === 'dark' ? styles.themeDark : styles.themeLight}`}>
|
|
{/* Sidebar - componente lateral de navegación */}
|
|
<Sidebar activeLink="Inicio" />
|
|
|
|
{/* Main Content Area */}
|
|
<div className={styles.mainContent}>
|
|
{/* Header - barra superior del contenido principal */}
|
|
<Header currentTheme={theme} onToggleTheme={toggleTheme} />
|
|
|
|
<div className={styles.contentWrapper}>
|
|
{/* Hero Section - sección "Crear" */}
|
|
<HeroSection />
|
|
|
|
{/* Transmissions Table - sección de tablas */}
|
|
<TransmissionsTable />
|
|
</div>
|
|
|
|
{/* Botón flotante de Ayuda */}
|
|
<button className={styles.helpButton}>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-help-circle">
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
|
|
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
|
</svg>
|
|
Ayuda
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PageContainer;
|
|
```
|
|
|
|
**2. `src/components/PageContainer.module.css`**
|
|
|
|
```css
|
|
/* Variables CSS para el tema */
|
|
:root {
|
|
--background-color: #f7f8fa; /* Gris muy claro para el fondo de la página */
|
|
--surface-color: #ffffff; /* Blanco para componentes/tarjetas */
|
|
--text-primary: #212121; /* Gris oscuro casi negro */
|
|
--text-secondary: #6b7280; /* Gris medio para texto secundario */
|
|
--primary-blue: #1876f2; /* Azul vibrante */
|
|
--active-bg-light: #ebf3ff; /* Azul muy claro para activo/hover */
|
|
--border-light: #e5e7eb; /* Gris claro para bordes */
|
|
--shadow-light: rgba(0, 0, 0, 0.08); /* Sombra sutil */
|
|
}
|
|
|
|
/* Tema oscuro (ejemplo, no completamente implementado en la imagen) */
|
|
.themeDark {
|
|
--background-color: #1a1a1a;
|
|
--surface-color: #2c2c2c;
|
|
--text-primary: #f0f0f0;
|
|
--text-secondary: #a0a0a0;
|
|
--border-light: #444;
|
|
/* Otros colores oscuros */
|
|
}
|
|
|
|
.pageContainer {
|
|
display: flex;
|
|
min-height: 100vh; /* Asegura que ocupe al menos la altura de la ventana */
|
|
background-color: var(--background-color);
|
|
font-family: 'Inter', 'Roboto', sans-serif; /* Familia de fuentes */
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.mainContent {
|
|
flex-grow: 1; /* Ocupa el espacio restante */
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative; /* Para el botón flotante */
|
|
background-color: var(--background-color); /* Fondo del área de contenido */
|
|
overflow-y: auto; /* Permite el scroll vertical en el contenido principal */
|
|
}
|
|
|
|
.contentWrapper {
|
|
padding: 32px; /* Padding global para el contenido principal */
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.helpButton {
|
|
position: fixed;
|
|
bottom: 24px;
|
|
right: 24px;
|
|
background-color: var(--primary-blue);
|
|
color: #ffffff;
|
|
padding: 12px 20px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
box-shadow: 0px 4px 12px var(--shadow-light); /* Sombra para elevarlo */
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
z-index: 1000; /* Asegura que esté por encima de otros elementos */
|
|
}
|
|
|
|
.helpButton:hover {
|
|
background-color: #146bdc; /* Azul un poco más oscuro al hacer hover */
|
|
}
|
|
|
|
/* Media Queries para responsividad */
|
|
@media (max-width: 768px) {
|
|
.pageContainer {
|
|
flex-direction: column; /* La barra lateral se apilará en móviles */
|
|
}
|
|
|
|
.mainContent {
|
|
width: 100%; /* El contenido principal ocupa todo el ancho */
|
|
}
|
|
|
|
.contentWrapper {
|
|
padding: 20px; /* Reducir padding en móviles */
|
|
}
|
|
|
|
.helpButton {
|
|
bottom: 16px;
|
|
right: 16px;
|
|
padding: 10px 16px;
|
|
}
|
|
}
|
|
```
|
|
|
|
**3. `src/components/Sidebar/Sidebar.tsx`**
|
|
|
|
```tsx
|
|
import React from 'react';
|
|
import styles from './Sidebar.module.css';
|
|
|
|
interface SidebarProps {
|
|
activeLink: string;
|
|
}
|
|
|
|
const Sidebar: React.FC<SidebarProps> = ({ activeLink }) => {
|
|
const navItems = [
|
|
{ id: 'Inicio', label: 'Inicio', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg> },
|
|
{ id: 'Biblioteca', label: 'Biblioteca', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-video"><path d="M23 7l-7 5V2l7 5z"></path><path d="M22 17H7a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h15v16z"></path></svg> },
|
|
{ id: 'Destinos', label: 'Destinos', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-share-2"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg> },
|
|
{ id: 'Miembros', label: 'Miembros', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> },
|
|
];
|
|
|
|
const secondaryItems = [
|
|
{ id: 'Referidos', label: 'Referidos', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-gift"><polyline points="20 12 20 22 4 22 4 12"></polyline><rect x="2" y="7" width="20" height="5"></rect><line x1="12" y1="22" x2="12" y2="7"></line><path d="M12 7H7.5a2.5 2 0 0 1 0-5C11 2 12 7 12 7z"></path><path d="M12 7h4.5a2.5 2 0 0 0 0-5C13 2 12 7 12 7z"></path></svg> },
|
|
{ id: 'Configuracion', label: 'Configuración del equipo', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-settings"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0-.33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg> },
|
|
{ id: 'Estado', label: 'Estado del sistema', icon: <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-bar-chart-2"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg> },
|
|
];
|
|
|
|
return (
|
|
<aside className={styles.sidebar}>
|
|
<div className={styles.logoSection}>
|
|
{/* Usar una imagen real o un SVG para el logo de StreamYard */}
|
|
<img src="https://static.streamyard.com/images/logo-icon.svg" alt="StreamYard Logo" className={styles.logoIcon} />
|
|
<span className={styles.logoText}>StreamYard</span>
|
|
</div>
|
|
|
|
<nav className={styles.navMenu}>
|
|
<ul className={styles.navList}>
|
|
{navItems.map((item) => (
|
|
<li key={item.id} className={`${styles.navItem} ${activeLink === item.id ? styles.activeLink : ''}`}>
|
|
<a href="#" onClick={(e) => e.preventDefault()} className={styles.navLink}>
|
|
{item.icon}
|
|
<span>{item.label}</span>
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<div className={styles.secondaryNavGroup}>
|
|
<ul className={styles.navList}>
|
|
{secondaryItems.map((item) => (
|
|
<li key={item.id} className={`${styles.navItem} ${activeLink === item.id ? styles.activeLink : ''}`}>
|
|
<a href="#" onClick={(e) => e.preventDefault()} className={styles.navLink}>
|
|
{item.icon}
|
|
<span>{item.label}</span>
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div className={styles.storageInfo}>
|
|
<h4 className={styles.storageTitle}>Almacenamiento <span className={styles.infoIcon}>?</span></h4>
|
|
<div className={styles.progressBarContainer}>
|
|
<div className={styles.progressBarFill} style={{ width: '0%' }}></div> {/* Simula el progreso */}
|
|
</div>
|
|
<p className={styles.storageUsage}>0 de 5 horas</p>
|
|
<a href="#" className={styles.addMoreLink}>Agregar más</a>
|
|
</div>
|
|
</aside>
|
|
);
|
|
};
|
|
|
|
export default Sidebar;
|
|
```
|
|
|
|
**4. `src/components/Sidebar/Sidebar.module.css`**
|
|
|
|
```css
|
|
.sidebar {
|
|
width: 260px; /* Ancho fijo */
|
|
min-width: 260px;
|
|
height: 100vh; /* Ocupa toda la altura */
|
|
background-color: var(--surface-color);
|
|
padding: 20px 0; /* Padding vertical */
|
|
box-shadow: 1px 0 5px rgba(0, 0, 0, 0.05); /* Sombra sutil a la derecha */
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: auto; /* Para permitir scroll si el contenido es largo */
|
|
}
|
|
|
|
.logoSection {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 20px 20px; /* Padding bottom para separación */
|
|
border-bottom: 1px solid var(--border-light); /* Separador sutil */
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.logoIcon {
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.logoText {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.navMenu {
|
|
flex-grow: 1; /* Permite que el menú crezca y ocupe el espacio */
|
|
}
|
|
|
|
.navList {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.navItem {
|
|
margin-bottom: 4px; /* Pequeña separación entre items */
|
|
}
|
|
|
|
.navLink {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 20px;
|
|
color: var(--text-secondary); /* Color por defecto */
|
|
text-decoration: none;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
border-left: 4px solid transparent; /* Borde transparente por defecto */
|
|
transition: all 0.2s ease-in-out;
|
|
gap: 10px;
|
|
}
|
|
|
|
.navLink svg {
|
|
color: var(--text-secondary); /* Color de icono por defecto */
|
|
}
|
|
|
|
.navLink:hover {
|
|
background-color: var(--active-bg-light); /* Fondo azul claro al hover */
|
|
color: var(--primary-blue); /* Texto azul al hover */
|
|
}
|
|
|
|
.navLink:hover svg {
|
|
color: var(--primary-blue); /* Icono azul al hover */
|
|
}
|
|
|
|
.activeLink .navLink {
|
|
background-color: var(--active-bg-light); /* Fondo azul claro para activo */
|
|
color: var(--primary-blue); /* Texto azul para activo */
|
|
border-left-color: var(--primary-blue); /* Borde azul para activo */
|
|
}
|
|
|
|
.activeLink .navLink svg {
|
|
color: var(--primary-blue); /* Icono azul para activo */
|
|
}
|
|
|
|
.secondaryNavGroup {
|
|
margin-top: 30px; /* Separación para el segundo grupo */
|
|
padding-top: 20px;
|
|
border-top: 1px solid var(--border-light);
|
|
}
|
|
|
|
.storageInfo {
|
|
padding: 20px;
|
|
margin-top: auto; /* Empuja el almacenamiento al final */
|
|
border-top: 1px solid var(--border-light); /* Separador */
|
|
}
|
|
|
|
.storageTitle {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.infoIcon {
|
|
background-color: var(--border-light);
|
|
border-radius: 50%;
|
|
width: 16px;
|
|
height: 16px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 10px;
|
|
color: var(--text-secondary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.progressBarContainer {
|
|
width: 100%;
|
|
height: 6px;
|
|
background-color: var(--border-light);
|
|
border-radius: 3px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.progressBarFill {
|
|
height: 100%;
|
|
background-color: var(--primary-blue);
|
|
border-radius: 3px;
|
|
width: 0%; /* Se controlará con JS para el progreso real */
|
|
}
|
|
|
|
.storageUsage {
|
|
font-size: 12px;
|
|
color: var(--text-secondary);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.addMoreLink {
|
|
font-size: 12px;
|
|
color: var(--primary-blue);
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.addMoreLink:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* Media Queries para responsividad */
|
|
@media (max-width: 768px) {
|
|
.sidebar {
|
|
width: 100%; /* Ocupa todo el ancho en móviles */
|
|
height: auto; /* Altura automática */
|
|
padding: 10px 0;
|
|
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05); /* Sombra inferior */
|
|
position: relative;
|
|
z-index: 100; /* Asegura que esté encima de otros elementos */
|
|
}
|
|
|
|
.navMenu {
|
|
display: none; /* Ocultar el menú de navegación principal en móvil por defecto */
|
|
/* En un caso real, aquí iría la lógica para un menú hamburguesa */
|
|
}
|
|
|
|
.logoSection {
|
|
padding: 10px 20px;
|
|
border-bottom: none;
|
|
margin-bottom: 0;
|
|
justify-content: center; /* Centrar logo en móvil */
|
|
}
|
|
|
|
.storageInfo {
|
|
display: none; /* Ocultar info de almacenamiento en móvil */
|
|
}
|
|
}
|
|
```
|
|
|
|
**5. `src/components/Header/Header.tsx`**
|
|
|
|
```tsx
|
|
import React from 'react';
|
|
import styles from './Header.module.css';
|
|
|
|
interface HeaderProps {
|
|
currentTheme: 'light' | 'dark';
|
|
onToggleTheme: () => void;
|
|
}
|
|
|
|
const Header: React.FC<HeaderProps> = ({ currentTheme, onToggleTheme }) => {
|
|
return (
|
|
<header className={styles.header}>
|
|
<div className={styles.headerActions}>
|
|
<button className={styles.planButton}>Mejora tu plan</button>
|
|
|
|
<div className={styles.themeToggleGroup}>
|
|
<button
|
|
className={`${styles.themeToggleButton} ${currentTheme === 'light' ? styles.active : ''}`}
|
|
onClick={onToggleTheme}
|
|
aria-label="Toggle light theme"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-sun">
|
|
<circle cx="12" cy="12" r="5"></circle>
|
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
className={`${styles.themeToggleButton} ${currentTheme === 'dark' ? styles.active : ''}`}
|
|
onClick={onToggleTheme}
|
|
aria-label="Toggle dark theme"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-moon">
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<button className={styles.notificationButton}>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-bell">
|
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
|
|
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
|
|
</svg>
|
|
<span className={styles.notificationDot}></span> {/* Punto de notificación */}
|
|
</button>
|
|
|
|
<div className={styles.userMenu}>
|
|
<span>Mi cuenta</span>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-chevron-down">
|
|
<polyline points="6 9 12 15 18 9"></polyline>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default Header;
|
|
```
|
|
|
|
**6. `src/components/Header/Header.module.css`**
|
|
|
|
```css
|
|
.header {
|
|
height: 64px; /* Altura fija del header */
|
|
background-color: var(--surface-color);
|
|
border-bottom: 1px solid var(--border-light); /* Borde inferior sutil */
|
|
display: flex;
|
|
justify-content: flex-end; /* Alinea el contenido a la derecha */
|
|
align-items: center;
|
|
padding: 0 32px; /* Padding horizontal */
|
|
flex-shrink: 0; /* Previene que el header se encoja */
|
|
}
|
|
|
|
.headerActions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px; /* Espacio entre los grupos de elementos */
|
|
}
|
|
|
|
.planButton {
|
|
background-color: transparent;
|
|
color: var(--primary-blue);
|
|
border: 1px solid var(--primary-blue);
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
transition: all 0.2s ease-in-out;
|
|
}
|
|
|
|
.planButton:hover {
|
|
background-color: var(--primary-blue);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.themeToggleGroup {
|
|
display: flex;
|
|
background-color: var(--background-color); /* Fondo gris claro */
|
|
border-radius: 9999px; /* Forma de píldora */
|
|
padding: 4px;
|
|
gap: 4px;
|
|
}
|
|
|
|
.themeToggleButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
border-radius: 9999px; /* Forma redonda */
|
|
padding: 8px;
|
|
cursor: pointer;
|
|
color: var(--text-secondary); /* Icono gris por defecto */
|
|
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.themeToggleButton.active {
|
|
background-color: var(--surface-color); /* Fondo blanco para el activo */
|
|
color: var(--text-primary); /* Icono oscuro para el activo */
|
|
box-shadow: 0px 1px 3px var(--shadow-light); /* Sombra sutil para el activo */
|
|
}
|
|
|
|
.themeToggleButton:hover:not(.active) {
|
|
background-color: var(--border-light); /* Fondo ligeramente más oscuro al hover */
|
|
}
|
|
|
|
.notificationButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
cursor: pointer;
|
|
color: var(--text-secondary);
|
|
position: relative;
|
|
padding: 0; /* Eliminar padding para que el icono se centre mejor */
|
|
display: flex; /* Asegura que el SVG se centre */
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 36px; /* Ajusta la altura para que coincida con otros elementos interactivos */
|
|
width: 36px;
|
|
border-radius: 50%;
|
|
transition: background-color 0.2s ease-in-out;
|
|
}
|
|
|
|
.notificationButton:hover {
|
|
background-color: var(--border-light);
|
|
}
|
|
|
|
.notificationDot {
|
|
position: absolute;
|
|
top: 6px;
|
|
right: 6px;
|
|
width: 8px;
|
|
height: 8px;
|
|
background-color: var(--primary-blue); /* Color del punto de notificación */
|
|
border-radius: 50%;
|
|
border: 1px solid var(--surface-color); /* Pequeño borde blanco */
|
|
}
|
|
|
|
.userMenu {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
color: var(--text-primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.userMenu svg {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* Media Queries para responsividad */
|
|
@media (max-width: 768px) {
|
|
.header {
|
|
padding: 0 16px;
|
|
height: 56px;
|
|
justify-content: space-between; /* Alinear elementos al principio y al final */
|
|
}
|
|
|
|
.headerActions {
|
|
gap: 10px;
|
|
}
|
|
|
|
.planButton {
|
|
padding: 6px 12px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.themeToggleGroup {
|
|
display: none; /* Ocultar el selector de tema en móviles para simplificar */
|
|
}
|
|
|
|
.notificationButton, .userMenu {
|
|
/* Ajustes menores si es necesario */
|
|
}
|
|
}
|
|
```
|
|
|
|
**7. `src/components/HeroSection/HeroSection.tsx`**
|
|
|
|
```tsx
|
|
import React from 'react';
|
|
import styles from './HeroSection.module.css';
|
|
|
|
interface CardProps {
|
|
icon: React.ReactNode;
|
|
title: string;
|
|
}
|
|
|
|
const CreateCard: React.FC<CardProps> = ({ icon, title }) => {
|
|
return (
|
|
<div className={styles.createCard}>
|
|
<div className={styles.cardIcon}>{icon}</div>
|
|
<p className={styles.cardTitle}>{title}</p>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const HeroSection: React.FC = () => {
|
|
return (
|
|
<section className={styles.heroSection}>
|
|
<h2 className={styles.sectionTitle}>Crear</h2>
|
|
<div className={styles.cardGrid}>
|
|
<CreateCard
|
|
icon={<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-video"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg>}
|
|
title="Transmisión en vivo"
|
|
/>
|
|
<CreateCard
|
|
icon={<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-radio"><circle cx="12" cy="12" r="2"></circle><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path></svg>}
|
|
title="Grabación"
|
|
/>
|
|
<CreateCard
|
|
icon={<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-monitor"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>}
|
|
title="Seminario web On-Air"
|
|
/>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default HeroSection;
|
|
```
|
|
|
|
**8. `src/components/HeroSection/HeroSection.module.css`**
|
|
|
|
```css
|
|
.heroSection {
|
|
margin-bottom: 40px; /* Separación con la siguiente sección */
|
|
}
|
|
|
|
.sectionTitle {
|
|
font-size: 22px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
margin-bottom: 24px; /* Espacio debajo del título */
|
|
}
|
|
|
|
.cardGrid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); /* Adaptativo */
|
|
gap: 20px; /* Espacio entre las tarjetas */
|
|
}
|
|
|
|
.createCard {
|
|
background-color: var(--surface-color);
|
|
border: 1px solid var(--border-light);
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start; /* Alinea los elementos a la izquierda */
|
|
cursor: pointer;
|
|
transition: all 0.2s ease-in-out;
|
|
min-height: 120px; /* Altura mínima para la tarjeta */
|
|
box-shadow: 0px 1px 3px var(--shadow-light); /* Sombra sutil */
|
|
}
|
|
|
|
.createCard:hover {
|
|
border-color: var(--primary-blue); /* Borde azul al hover */
|
|
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.12); /* Sombra más pronunciada */
|
|
}
|
|
|
|
.cardIcon {
|
|
color: var(--primary-blue); /* Color del icono */
|
|
margin-bottom: 12px;
|
|
width: 32px;
|
|
height: 32px;
|
|
}
|
|
|
|
.cardTitle {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
margin: 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Media Queries para responsividad */
|
|
@media (max-width: 768px) {
|
|
.heroSection {
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.sectionTitle {
|
|
font-size: 20px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.cardGrid {
|
|
grid-template-columns: 1fr; /* Una columna en móviles */
|
|
gap: 16px;
|
|
}
|
|
|
|
.createCard {
|
|
padding: 16px;
|
|
min-height: 100px;
|
|
}
|
|
|
|
.cardIcon {
|
|
width: 28px;
|
|
height: 28px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.cardTitle {
|
|
font-size: 15px;
|
|
}
|
|
}
|
|
```
|
|
|
|
**9. `src/components/TransmissionsTable/TransmissionsTable.tsx`**
|
|
|
|
```tsx
|
|
import React, { useState } from 'react';
|
|
import styles from './TransmissionsTable.module.css';
|
|
|
|
interface Transmission {
|
|
id: string;
|
|
title: string;
|
|
platformIcon: React.ReactNode;
|
|
created: string;
|
|
scheduled: string;
|
|
}
|
|
|
|
const dummyTransmissions: Transmission[] = [
|
|
{
|
|
id: '1',
|
|
title: 'Transmision',
|
|
platformIcon: <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="#FF0000" stroke="none" className="youtube-icon"><path d="M2.5 17.5V6.5L12 12.5L2.5 17.5Z"></path><path d="M22.5 6.5C22.5 6.5 22.5 4.5 20 4.5C17.5 4.5 12.5 4.5 12.5 4.5L12.5 4.5C12.5 4.5 7.5 4.5 5 4.5C2.5 4.5 2.5 6.5 2.5 6.5V17.5C2.5 17.5 2.5 19.5 5 19.5C7.5 19.5 12.5 19.5 12.5 19.5L12.5 19.5C12.5 19.5 17.5 19.5 20 19.5C22.5 19.5 22.5 17.5 22.5 17.5V6.5Z" stroke="#FF0000" strokeWidth="1.5" strokeLinejoin="round"></path></svg>,
|
|
created: '01:56',
|
|
scheduled: '-',
|
|
},
|
|
// Más datos de transmisiones pueden ir aquí
|
|
];
|
|
|
|
const TransmissionsTable: React.FC = () => {
|
|
const [activeTab, setActiveTab] = useState<'upcoming' | 'past'>('upcoming');
|
|
|
|
// Datos simulados para las pestañas
|
|
const upcomingTransmissions: Transmission[] = dummyTransmissions; // De momento, los mismos datos
|
|
const pastTransmissions: Transmission[] = []; // Sin datos pasados por ahora
|
|
|
|
const displayedTransmissions = activeTab === 'upcoming' ? upcomingTransmissions : pastTransmissions;
|
|
|
|
return (
|
|
<section className={styles.transmissionsSection}>
|
|
<h2 className={styles.sectionTitle}>Transmisiones y grabaciones</h2>
|
|
|
|
<div className={styles.tabContainer}>
|
|
<button
|
|
className={`${styles.tabButton} ${activeTab === 'upcoming' ? styles.activeTab : ''}`}
|
|
onClick={() => setActiveTab('upcoming')}
|
|
>
|
|
Próximamente
|
|
</button>
|
|
<button
|
|
className={`${styles.tabButton} ${activeTab === 'past' ? styles.activeTab : ''}`}
|
|
onClick={() => setActiveTab('past')}
|
|
>
|
|
Anteriores
|
|
</button>
|
|
</div>
|
|
|
|
<div className={styles.tableWrapper}>
|
|
<table className={styles.transmissionsTable}>
|
|
<thead>
|
|
<tr>
|
|
<th className={styles.tableHeader}>Título</th>
|
|
<th className={styles.tableHeader}>Creada</th>
|
|
<th className={styles.tableHeader}>Programado</th>
|
|
<th className={styles.tableHeader}>Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{displayedTransmissions.length > 0 ? (
|
|
displayedTransmissions.map((transmission) => (
|
|
<tr key={transmission.id} className={styles.tableRow}>
|
|
<td className={styles.tableCell}>
|
|
<div className={styles.titleCellContent}>
|
|
<span className={styles.platformAvatar}>
|
|
{/* Avatar simulado con "a" o imagen de usuario */}
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="#E5E7EB" stroke="none" className="avatar"><circle cx="12" cy="12" r="10"></circle><text x="50%" y="50%" dominantBaseline="middle" textAnchor="middle" fill="#6B7280" fontSize="12px">a</text></svg>
|
|
</span>
|
|
<div className={styles.titleTextContent}>
|
|
<span className={styles.transmissionTitle}>{transmission.title}</span>
|
|
<span className={styles.transmissionPlatformIcon}>
|
|
{transmission.platformIcon}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td className={styles.tableCell}>{transmission.created}</td>
|
|
<td className={styles.tableCell}>{transmission.scheduled}</td>
|
|
<td className={`${styles.tableCell} ${styles.actionsCell}`}>
|
|
<button className={styles.enterStudioButton}>Entrar al estudio</button>
|
|
<button className={styles.moreOptionsButton}>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-more-horizontal">
|
|
<circle cx="12" cy="12" r="1"></circle>
|
|
<circle cx="19" cy="12" r="1"></circle>
|
|
<circle cx="5" cy="12" r="1"></circle>
|
|
</svg>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr className={styles.tableRow}>
|
|
<td colSpan={4} className={styles.noDataCell}>No hay transmisiones en esta categoría.</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default TransmissionsTable;
|
|
```
|
|
|
|
**10. `src/components/TransmissionsTable/TransmissionsTable.module.css`**
|
|
|
|
```css
|
|
.transmissionsSection {
|
|
margin-bottom: 40px; /* Separación con el pie de página o final */
|
|
}
|
|
|
|
.sectionTitle {
|
|
font-size: 22px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.tabContainer {
|
|
display: flex;
|
|
border-bottom: 1px solid var(--border-light); /* Borde debajo de las pestañas */
|
|
margin-bottom: 20px; /* Espacio debajo del contenedor de pestañas */
|
|
}
|
|
|
|
.tabButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
padding: 10px 0;
|
|
margin-right: 20px; /* Espacio entre pestañas */
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: var(--text-secondary);
|
|
cursor: pointer;
|
|
position: relative;
|
|
transition: color 0.2s ease-in-out;
|
|
}
|
|
|
|
.tabButton:hover {
|
|
color: var(--primary-blue);
|
|
}
|
|
|
|
.tabButton.activeTab {
|
|
color: var(--primary-blue);
|
|
border-bottom: 3px solid var(--primary-blue); /* Borde azul para la pestaña activa */
|
|
padding-bottom: 7px; /* Ajustar padding para compensar el borde */
|
|
}
|
|
|
|
.tableWrapper {
|
|
overflow-x: auto; /* Permite el scroll horizontal en tablas grandes */
|
|
background-color: var(--surface-color);
|
|
border-radius: 8px;
|
|
box-shadow: 0px 1px 3px var(--shadow-light);
|
|
border: 1px solid var(--border-light);
|
|
}
|
|
|
|
.transmissionsTable {
|
|
width: 100%;
|
|
border-collapse: collapse; /* Elimina los espacios entre bordes de celda */
|
|
}
|
|
|
|
.tableHeader {
|
|
text-align: left;
|
|
padding: 12px 15px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
color: var(--text-secondary);
|
|
text-transform: uppercase;
|
|
border-bottom: 1px solid var(--border-light);
|
|
}
|
|
|
|
.tableRow {
|
|
transition: background-color 0.2s ease-in-out;
|
|
}
|
|
|
|
.tableRow:hover {
|
|
background-color: var(--active-bg-light); /* Fondo suave al hover en la fila */
|
|
}
|
|
|
|
.tableCell {
|
|
padding: 12px 15px;
|
|
font-size: 14px;
|
|
color: var(--text-primary);
|
|
border-bottom: 1px solid var(--border-light);
|
|
}
|
|
|
|
.tableRow:last-child .tableCell {
|
|
border-bottom: none; /* Elimina el borde inferior de la última fila */
|
|
}
|
|
|
|
.titleCellContent {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.platformAvatar {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: var(--border-light); /* Fondo para el avatar genérico */
|
|
flex-shrink: 0; /* Evita que el avatar se encoja */
|
|
}
|
|
|
|
.transmissionTitle {
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.transmissionPlatformIcon {
|
|
margin-left: 8px; /* Espacio entre el título y el icono de la plataforma */
|
|
display: inline-flex; /* Para alinear el SVG correctamente */
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.transmissionPlatformIcon svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
.actionsCell {
|
|
white-space: nowrap; /* Evita que los botones se rompan en varias líneas */
|
|
text-align: right;
|
|
}
|
|
|
|
.enterStudioButton {
|
|
background-color: transparent;
|
|
color: var(--primary-blue);
|
|
border: 1px solid var(--primary-blue);
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
transition: all 0.2s ease-in-out;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.enterStudioButton:hover {
|
|
background-color: var(--primary-blue);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.moreOptionsButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
padding: 8px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
color: var(--text-secondary);
|
|
transition: background-color 0.2s ease-in-out;
|
|
}
|
|
|
|
.moreOptionsButton:hover {
|
|
background-color: var(--border-light);
|
|
}
|
|
|
|
.noDataCell {
|
|
text-align: center;
|
|
padding: 40px;
|
|
color: var(--text-secondary);
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Media Queries para responsividad */
|
|
@media (max-width: 768px) {
|
|
.sectionTitle {
|
|
font-size: 20px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.tabButton {
|
|
font-size: 15px;
|
|
margin-right: 15px;
|
|
padding-bottom: 5px;
|
|
}
|
|
|
|
.tabButton.activeTab {
|
|
padding-bottom: 2px; /* Ajustar por el borde */
|
|
}
|
|
|
|
.tableWrapper {
|
|
/* El overflow-x: auto ya maneja el responsive */
|
|
}
|
|
|
|
.tableHeader, .tableCell {
|
|
padding: 10px 12px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.titleCellContent {
|
|
gap: 8px;
|
|
}
|
|
|
|
.platformAvatar {
|
|
width: 28px;
|
|
height: 28px;
|
|
}
|
|
|
|
.platformAvatar text {
|
|
font-size: 10px;
|
|
}
|
|
|
|
.transmissionTitle {
|
|
font-size: 13px;
|
|
}
|
|
|
|
.enterStudioButton {
|
|
padding: 6px 12px;
|
|
font-size: 13px;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
.moreOptionsButton {
|
|
padding: 6px;
|
|
}
|
|
}
|
|
``` |