diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx
index 3c87a8c..b3d9144 100644
--- a/src/app/chat/page.tsx
+++ b/src/app/chat/page.tsx
@@ -1,5 +1,12 @@
-import Chat from "@/sections/chat/Chat";
+import type { Metadata } from 'next';
+import Chat from '@/sections/chat/Chat';
+
+export const metadata: Metadata = {
+ title: 'Asistente AI',
+ description:
+ 'Interactúa con nuestra inteligencia artificial para resolver dudas sobre SCADA, BMS y AIIoT.',
+};
export default async function Index() {
- return ;
+ return ;
}
diff --git a/src/app/industrias/page.tsx b/src/app/industrias/page.tsx
index d7e180f..7539ef5 100644
--- a/src/app/industrias/page.tsx
+++ b/src/app/industrias/page.tsx
@@ -1,16 +1,23 @@
-import { Box } from "@mui/material";
-import { IndustrySlider } from "@/sections/industries/IndustrySlider";
-import { IndustryCards } from "@/sections/industries/IndustryCards";
-import { IndustryTitle } from "@/sections/industries/IndustryTitle";
-import { IndustryFooter } from "@/sections/industries/IndustryFooter";
+import type { Metadata } from 'next';
+import { Box } from '@mui/material';
+import { IndustrySlider } from '@/sections/industries/IndustrySlider';
+import { IndustryCards } from '@/sections/industries/IndustryCards';
+import { IndustryTitle } from '@/sections/industries/IndustryTitle';
+import { IndustryFooter } from '@/sections/industries/IndustryFooter';
+
+export const metadata: Metadata = {
+ title: 'Soluciones por Industria',
+ description:
+ 'Descubre cómo LogOS optimiza operaciones en Smart Cities, gestión de edificios y entornos industriales.',
+};
export default function Industrias() {
- return (
-
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+
+ );
}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 05e527e..ad704bc 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,91 +1,74 @@
-import { MuiRootProvider } from "@/providers/MuiRootProvider";
-import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
-import InitColorSchemeScript from "@mui/material/InitColorSchemeScript";
+import { MuiRootProvider } from '@/providers/MuiRootProvider';
+import type { Metadata } from 'next';
+import { Geist, Geist_Mono } from 'next/font/google';
+import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
+ variable: '--font-geist-sans',
+ subsets: ['latin'],
});
const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
+ variable: '--font-geist-mono',
+ subsets: ['latin'],
});
export const metadata: Metadata = {
- title: "LogOS",
- description:
- "Mejora tu eficiencia usando nuestro sistema de gestión inteligente. SCADA, BMS, AIIoT",
- applicationName: "LogOS",
- keywords: [
- "SCADA",
- "industrial",
- "AIIoT",
- "AI",
- "IIoT",
- "BMS",
- "HMI",
- "SEEED",
- "KUNBUS",
- "Smart Cities",
- "Smart City",
- "Building Management System",
- "Building",
- "Management",
- "System",
- "events",
- "alarms",
- "bylogosio",
- "logosia",
- "bylogosia",
- "bylogosiot",
- "logosio",
- "logosiot",
- "IoT",
- "automatización",
- "LogOS",
- "ByLogos",
- "IA",
- "Logos",
- "monitorización",
- "solución",
- "local-first",
- "software",
- "hardware",
- ],
- robots: {
- googleBot: {
- index: true,
- follow: true,
- },
- },
- authors: [
- { name: "LogOS", url: "https://www.linkedin.com/company/bylogos/" },
- ],
- creator: "Javier Vargas",
- metadataBase: new URL("https://bylogos.io"),
+ title: {
+ default: 'Inteligencia Industrial SCADA, BMS, AIIoT | LogOS',
+ template: '%s | LogOS',
+ },
+ description:
+ 'LogOS: Sistema de gestión inteligente para SCADA, BMS y AIIoT. Mejora la eficiencia operacional en Smart Cities e industria.',
+ applicationName: 'LogOS',
+ keywords: [
+ 'LogOS',
+ 'SCADA',
+ 'BMS',
+ 'AIIoT',
+ 'IIoT',
+ 'Industrial AI',
+ 'Smart Cities',
+ 'Automatización Industrial',
+ 'Monitorización en Tiempo Real',
+ 'Gestión de Edificios',
+ 'HMI',
+ 'ByLogos',
+ ],
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ },
+ },
+ authors: [
+ { name: 'LogOS', url: 'https://www.linkedin.com/company/bylogos/' },
+ ],
+ creator: 'Javier Vargas',
+ metadataBase: new URL('https://bylogos.io'),
};
-import { ConditionalHeader } from "@/sections/layout/ConditionalHeader";
-import { ConditionalFooter } from "@/sections/layout/ConditionalFooter";
+import { ConditionalHeader } from '@/sections/layout/ConditionalHeader';
+import { ConditionalFooter } from '@/sections/layout/ConditionalFooter';
export default function RootLayout({
- children,
+ children,
}: Readonly<{
- children: React.ReactNode;
+ children: React.ReactNode;
}>) {
- return (
-
-
-
-
-
- {children}
-
-
-
-
- );
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+ );
}
diff --git a/src/app/news/[slug]/page.tsx b/src/app/news/[slug]/page.tsx
index 8b4dd72..0f34068 100644
--- a/src/app/news/[slug]/page.tsx
+++ b/src/app/news/[slug]/page.tsx
@@ -1,42 +1,44 @@
-import { news } from "@/velite";
-import { notFound } from "next/navigation";
-import { Metadata } from "next";
-import { NewsPost } from "@/sections/news/NewsPost";
+import { news } from '@/velite';
+import { notFound } from 'next/navigation';
+import { Metadata } from 'next';
+import { NewsPost } from '@/sections/news/NewsPost';
interface PageProps {
- params: Promise<{ slug: string }>;
+ params: Promise<{ slug: string }>;
}
export async function generateStaticParams() {
- return news.map((post) => ({
- slug: post.slug.split("/").pop(), // "news/my-post" -> "my-post"
- }));
+ return news.map((post) => ({
+ slug: post.slug.split('/').pop(), // "news/my-post" -> "my-post"
+ }));
}
export async function generateMetadata({
- params,
+ params,
}: PageProps): Promise {
- const resolvedParams = await params;
- // Velite slug is "news/slug", params.slug is "slug"
- const post = news.find((p) => p.slug === `news/${resolvedParams.slug}`);
-
- if (!post) {
- return {};
- }
-
- return {
- title: `${post.title} | Logos News`,
- description: post.description,
- };
+ const resolvedParams = await params;
+ // Velite slug is 'news/slug', params.slug is 'slug'
+ const post = news.find((p) => p.slug === `news/${resolvedParams.slug}`);
+
+ if (!post) {
+ return {
+ title: 'Noticia no encontrada',
+ };
+ }
+
+ return {
+ title: post.title,
+ description: post.description,
+ };
}
export default async function NewsPostPage({ params }: PageProps) {
- const resolvedParams = await params;
- const post = news.find((p) => p.slug === `news/${resolvedParams.slug}`);
+ const resolvedParams = await params;
+ const post = news.find((p) => p.slug === `news/${resolvedParams.slug}`);
- if (!post) {
- notFound();
- }
+ if (!post) {
+ notFound();
+ }
- return ;
+ return ;
}
diff --git a/src/app/news/page.tsx b/src/app/news/page.tsx
index daaa5ad..d7169fe 100644
--- a/src/app/news/page.tsx
+++ b/src/app/news/page.tsx
@@ -1,9 +1,10 @@
-import { news } from "@/velite"; // Using alias
-import { Box } from "@mui/material";
-import { NewsTitle } from "@/sections/news/NewsTitle";
-import { NewsSlider } from "@/sections/news/NewsSlider";
-import { NewsPosts } from "@/sections/news/NewsPosts";
-import { IndustryFooter } from "@/sections/industries/IndustryFooter"; // Reusing footer as requested ('mismo footer') - actually prompt said "same header and footer as most sections" which usually means global layout, but Industry page has IndustryFooter.
+import { news } from '@/velite'; // Using alias
+import { Box } from '@mui/material';
+import { NewsTitle } from '@/sections/news/NewsTitle';
+import { NewsSlider } from '@/sections/news/NewsSlider';
+import { NewsPosts } from '@/sections/news/NewsPosts';
+import { IndustryFooter } from '@/sections/industries/IndustryFooter'; // Reusing footer as requested ('mismo footer') - actually prompt said "same header and footer as most sections" which usually means global layout, but Industry page has IndustryFooter.
+import { Metadata } from 'next';
// User said: "usará mismo header y footer que la mayoria de las secciones del website"
// Industry page uses `IndustryFooter`. Global layout likely handles Header/Footer if not overridden.
// Industries page explicitly imports `IndustryFooter`. I will check if I should use that or default.
@@ -52,26 +53,26 @@ export default function Industrias() {
// Better yet, I'll just write the page without footer first, as that's safer for "most sections".
// But I need to sort the news.
-export const metadata = {
- title: "Noticias y Novedades | Logos",
- description:
- "Mantente informado con las últimas noticias sobre tecnología industrial, IA e IoT.",
+export const metadata: Metadata = {
+ title: 'Noticias y Blog',
+ description:
+ 'Mantente informado con las últimas noticias sobre tecnología industrial, IA e IoT de la mano de LogOS.',
};
export default function NewsPage() {
- // Sort posts by date descending (newest first)
- const sortedPosts = news.sort((a, b) => {
- return new Date(b.date).getTime() - new Date(a.date).getTime();
- });
+ // Sort posts by date descending (newest first)
+ const sortedPosts = news.sort((a, b) => {
+ return new Date(b.date).getTime() - new Date(a.date).getTime();
+ });
- // Top 5 for slider
- const sliderPosts = sortedPosts.slice(0, 5);
+ // Top 5 for slider
+ const sliderPosts = sortedPosts.slice(0, 5);
- return (
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+ );
}
diff --git a/src/app/nosotros/page.tsx b/src/app/nosotros/page.tsx
index 095e00a..9dee9d8 100644
--- a/src/app/nosotros/page.tsx
+++ b/src/app/nosotros/page.tsx
@@ -1,15 +1,22 @@
-import { Container, Typography } from "@mui/material";
+import type { Metadata } from 'next';
+import { Container, Typography } from '@mui/material';
+
+export const metadata: Metadata = {
+ title: 'Sobre Nosotros',
+ description:
+ 'Conoce al equipo detrás de LogOS y nuestra misión de transformar la gestión industrial mediante IA y AIIoT.',
+};
export default function Nosotros() {
- return (
-
-
- Nosotros
-
-
- Esta página está en construcción. Pronto conocerás más sobre LogOS y
- nuestro equipo.
-
-
- );
+ return (
+
+
+ Nosotros
+
+
+ Esta página está en construcción. Pronto conocerás más sobre LogOS y
+ nuestro equipo.
+
+
+ );
}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 9e421a1..bf8a234 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,43 +1,14 @@
-"use client";
+import type { Metadata } from 'next';
+import { LandingPage } from '@/components/layout/LandingPage';
-import { Box, useTheme } from "@mui/material";
-import { Hero } from "@/sections/landing/Hero";
-import { Features } from "@/sections/landing/Features";
-import { HardwareAlternatives } from "@/sections/landing/HardwareAlternatives";
-import { Stack } from "@/sections/landing/Stack";
-import { Testimonials } from "@/sections/landing/Testimonials";
-import { Newsletter } from "@/sections/landing/Newsletter";
+export const metadata: Metadata = {
+ title: {
+ absolute: 'LogOS | Inteligencia Industrial SCADA, BMS, AIIoT',
+ },
+ description:
+ 'Sistema de gestión inteligente LogOS. Soluciones avanzadas de SCADA, BMS y AIIoT para mejorar la eficiencia operacional en industrias y ciudades inteligentes.',
+};
export default function Home() {
- const theme = useTheme();
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Spacer para el efecto de revelación del footer */}
-
-
- );
+ return ;
}
diff --git a/src/app/precios/page.tsx b/src/app/precios/page.tsx
index cd04e98..b70e1d9 100644
--- a/src/app/precios/page.tsx
+++ b/src/app/precios/page.tsx
@@ -1,15 +1,22 @@
-import { Container, Typography } from "@mui/material";
+import type { Metadata } from 'next';
+import { Container, Typography } from '@mui/material';
+
+export const metadata: Metadata = {
+ title: 'Planes y Precios',
+ description:
+ 'Encuentra el plan que mejor se adapte a tus necesidades industriales. Precios competitivos para soluciones SCADA y BMS de última generación.',
+};
export default function Precios() {
- return (
-
-
- Precios
-
-
- Esta página está en construcción. Pronto encontrarás nuestros planes y
- tarifas.
-
-
- );
+ return (
+
+
+ Precios
+
+
+ Esta página está en construcción. Pronto encontrarás nuestros planes y
+ tarifas.
+
+
+ );
}
diff --git a/src/app/soluciones/page.tsx b/src/app/soluciones/page.tsx
index 7a1195d..960c716 100644
--- a/src/app/soluciones/page.tsx
+++ b/src/app/soluciones/page.tsx
@@ -1,15 +1,22 @@
-import { Container, Typography } from "@mui/material";
+import type { Metadata } from 'next';
+import { Container, Typography } from '@mui/material';
+
+export const metadata: Metadata = {
+ title: 'Nuestras Soluciones',
+ description:
+ 'Explora nuestras soluciones Edge y Cloud para el control y monitorización inteligente de activos.',
+};
export default function Soluciones() {
- return (
-
-
- Soluciones
-
-
- Esta página está en construcción. Pronto verás todos nuestros servicios
- Edge y Cloud.
-
-
- );
+ return (
+
+
+ Soluciones
+
+
+ Esta página está en construcción. Pronto verás todos nuestros servicios
+ Edge y Cloud.
+
+
+ );
}
diff --git a/src/app/terms/page.tsx b/src/app/terms/page.tsx
index 9320a20..f1314dc 100644
--- a/src/app/terms/page.tsx
+++ b/src/app/terms/page.tsx
@@ -1,181 +1,181 @@
-"use client";
+'use client';
-import Link from "next/link";
-import ArrowBackIcon from "@mui/icons-material/ArrowBack";
+import Link from 'next/link';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import {
- Box,
- Container,
- Typography,
- Stack,
- Button,
- useTheme,
- alpha,
-} from "@mui/material";
+ Box,
+ Container,
+ Typography,
+ Stack,
+ Button,
+ useTheme,
+ alpha,
+} from '@mui/material';
export default function Terms() {
- const theme = useTheme();
+ const theme = useTheme();
- const sections = [
- {
- title: "Aceptación de los Términos",
- content:
- "Al utilizar el software Logos, desarrollado por ByLogos SpA, el usuario acepta estos Términos y Condiciones en su totalidad. Si no está de acuerdo, no debe utilizar la plataforma ni los servicios asociados.",
- },
- {
- title: "Licencia de Uso",
- list: [
- "Logos otorga al usuario una licencia limitada, no exclusiva e intransferible para utilizar el software conforme a estos Términos.",
- "El software y todos sus componentes, incluidos algoritmos de IA, interfaces, documentación y datos generados, son propiedad exclusiva de ByLogos SpA.",
- "Está prohibida la copia, modificación, distribución o sublicencia del software sin autorización explícita de ByLogos SpA.",
- ],
- },
- {
- title: "Uso del Software",
- list: [
- "El usuario se compromete a utilizar Logos únicamente para fines legales y conforme a las instrucciones de operación.",
- "Queda prohibido interferir con el funcionamiento de Logos, intentar acceder a sistemas no autorizados o manipular datos de terceros.",
- "La ejecución de Logos en entornos Edge Computing garantiza que los datos permanezcan locales; cualquier transmisión de información se realizará conforme a la política de privacidad vigente.",
- ],
- },
- {
- title: "Responsabilidad y Garantías",
- list: [
- "Logos se proporciona “tal cual” y no garantiza resultados específicos de optimización de consumo eléctrico o predicciones de IA.",
- "ByLogos SpA no será responsable por daños directos, indirectos, incidentales, consecuentes o pérdidas derivadas del uso del software.",
- "El usuario es responsable de implementar medidas de seguridad adicionales según sus necesidades industriales y legales.",
- ],
- },
- {
- title: "Actualizaciones y Modificaciones",
- list: [
- "ByLogos SpA puede actualizar Logos en cualquier momento, incluyendo nuevas funcionalidades, mejoras de seguridad y correcciones de errores.",
- "El usuario acepta que las actualizaciones pueden aplicarse automáticamente y sin previo aviso.",
- ],
- },
- {
- title: "Propiedad Intelectual",
- list: [
- "Todos los derechos de propiedad intelectual relacionados con Logos, sus algoritmos, interfaces gráficas, documentación y contenido generado por la IA pertenecen exclusivamente a ByLogos SpA.",
- "El uso no autorizado, reproducción, distribución o ingeniería inversa de cualquier componente está prohibido.",
- ],
- },
- {
- title: "Terminación",
- list: [
- "ByLogos SpA puede suspender o cancelar el acceso al software en caso de incumplimiento de estos Términos.",
- "Tras la terminación, el usuario deberá eliminar todas las copias del software y cualquier dato derivado de él, salvo que la ley exija su retención.",
- ],
- },
- {
- title: "Contacto",
- customContent: (
-
- Consultas sobre estos Términos pueden enviarse a:{" "}
-
-
- ),
- },
- ];
+ const sections = [
+ {
+ title: 'Aceptación de los Términos',
+ content:
+ 'Al utilizar el software Logos, desarrollado por ByLogos SpA, el usuario acepta estos Términos y Condiciones en su totalidad. Si no está de acuerdo, no debe utilizar la plataforma ni los servicios asociados.',
+ },
+ {
+ title: 'Licencia de Uso',
+ list: [
+ 'Logos otorga al usuario una licencia limitada, no exclusiva e intransferible para utilizar el software conforme a estos Términos.',
+ 'El software y todos sus componentes, incluidos algoritmos de IA, interfaces, documentación y datos generados, son propiedad exclusiva de ByLogos SpA.',
+ 'Está prohibida la copia, modificación, distribución o sublicencia del software sin autorización explícita de ByLogos SpA.',
+ ],
+ },
+ {
+ title: 'Uso del Software',
+ list: [
+ 'El usuario se compromete a utilizar Logos únicamente para fines legales y conforme a las instrucciones de operación.',
+ 'Queda prohibido interferir con el funcionamiento de Logos, intentar acceder a sistemas no autorizados o manipular datos de terceros.',
+ 'La ejecución de Logos en entornos Edge Computing garantiza que los datos permanezcan locales; cualquier transmisión de información se realizará conforme a la política de privacidad vigente.',
+ ],
+ },
+ {
+ title: 'Responsabilidad y Garantías',
+ list: [
+ 'Logos se proporciona “tal cual” y no garantiza resultados específicos de optimización de consumo eléctrico o predicciones de IA.',
+ 'ByLogos SpA no será responsable por daños directos, indirectos, incidentales, consecuentes o pérdidas derivadas del uso del software.',
+ 'El usuario es responsable de implementar medidas de seguridad adicionales según sus necesidades industriales y legales.',
+ ],
+ },
+ {
+ title: 'Actualizaciones y Modificaciones',
+ list: [
+ 'ByLogos SpA puede actualizar Logos en cualquier momento, incluyendo nuevas funcionalidades, mejoras de seguridad y correcciones de errores.',
+ 'El usuario acepta que las actualizaciones pueden aplicarse automáticamente y sin previo aviso.',
+ ],
+ },
+ {
+ title: 'Propiedad Intelectual',
+ list: [
+ 'Todos los derechos de propiedad intelectual relacionados con Logos, sus algoritmos, interfaces gráficas, documentación y contenido generado por la IA pertenecen exclusivamente a ByLogos SpA.',
+ 'El uso no autorizado, reproducción, distribución o ingeniería inversa de cualquier componente está prohibido.',
+ ],
+ },
+ {
+ title: 'Terminación',
+ list: [
+ 'ByLogos SpA puede suspender o cancelar el acceso al software en caso de incumplimiento de estos Términos.',
+ 'Tras la terminación, el usuario deberá eliminar todas las copias del software y cualquier dato derivado de él, salvo que la ley exija su retención.',
+ ],
+ },
+ {
+ title: 'Contacto',
+ customContent: (
+
+ Consultas sobre estos Términos pueden enviarse a:{' '}
+
+
+ ),
+ },
+ ];
- return (
-
-
- }
- sx={{
- mb: 4,
- textTransform: "none",
- color: "text.secondary",
- "&:hover": { color: "primary.main" },
- }}
- >
- Volver al inicio
-
+ return (
+
+
+ }
+ sx={{
+ mb: 4,
+ textTransform: 'none',
+ color: 'text.secondary',
+ '&:hover': { color: 'primary.main' },
+ }}
+ >
+ Volver al inicio
+
-
- Términos y Condiciones
-
+
+ Términos y Condiciones
+
-
- {sections.map((section, index) => (
-
-
- {section.title}
-
+
+ {sections.map((section, index) => (
+
+
+ {section.title}
+
- {section.content && (
-
- {section.content}
-
- )}
+ {section.content && (
+
+ {section.content}
+
+ )}
- {section.list && (
-
- {section.list.map((item, i) => (
-
- {item}
-
- ))}
-
- )}
+ {section.list && (
+
+ {section.list.map((item, i) => (
+
+ {item}
+
+ ))}
+
+ )}
- {section.customContent}
-
- ))}
-
-
-
- );
+ {section.customContent}
+
+ ))}
+
+
+
+ );
}
diff --git a/src/components/MdxContent.tsx b/src/components/MdxContent.tsx
index 6116f8e..957a45a 100644
--- a/src/components/MdxContent.tsx
+++ b/src/components/MdxContent.tsx
@@ -1,11 +1,18 @@
-import * as runtime from 'react/jsx-runtime'
+import React, { useMemo } from 'react';
+import * as runtime from 'react/jsx-runtime';
-const useMDX = (code: string) => {
- const fn = new Function(code)
- return fn({ ...runtime }).default
-}
+const evaluateMDX = (code: string) => {
+ const fn = new Function(code);
+ return fn({ ...runtime }).default;
+};
-export const MDXContent = ({ code, components }: { code: string, components?: any }) => {
- const Component = useMDX(code)
- return
-}
+export const MDXContent = ({
+ code,
+ components,
+}: {
+ code: string;
+ components?: any;
+}) => {
+ const MDXComponent = useMemo(() => evaluateMDX(code), [code]);
+ return React.createElement(MDXComponent, { components });
+};
diff --git a/src/components/layout/LandingPage.tsx b/src/components/layout/LandingPage.tsx
new file mode 100644
index 0000000..3969905
--- /dev/null
+++ b/src/components/layout/LandingPage.tsx
@@ -0,0 +1,41 @@
+'use client';
+
+import { Box } from '@mui/material';
+import { Hero } from '@/sections/landing/Hero';
+import { Features } from '@/sections/landing/Features';
+import { HardwareAlternatives } from '@/sections/landing/HardwareAlternatives';
+import { Stack } from '@/sections/landing/Stack';
+import { Testimonials } from '@/sections/landing/Testimonials';
+import { Newsletter } from '@/sections/landing/Newsletter';
+
+export function LandingPage() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Spacer para el efecto de revelación del footer */}
+
+
+ );
+}
diff --git a/src/sections/chat/Chat.tsx b/src/sections/chat/Chat.tsx
index ba4cb55..7dbd9d7 100644
--- a/src/sections/chat/Chat.tsx
+++ b/src/sections/chat/Chat.tsx
@@ -1,424 +1,426 @@
-"use client";
-import { useState, useEffect, useRef } from "react";
+'use client';
+import { useState, useEffect, useRef } from 'react';
// MUI Icons - Outlined version
-import SendOutlinedIcon from "@mui/icons-material/SendOutlined";
-import ArrowBackOutlinedIcon from "@mui/icons-material/ArrowBackOutlined";
-import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined";
+import SendOutlinedIcon from '@mui/icons-material/SendOutlined';
+import ArrowBackOutlinedIcon from '@mui/icons-material/ArrowBackOutlined';
+import PersonOutlineOutlinedIcon from '@mui/icons-material/PersonOutlineOutlined';
-import ReactMarkdown from "react-markdown";
-import { useChatContext } from "@/providers/ChatProvider";
-import Link from "next/link";
-import Image from "next/image";
-import Logos from "@public/icon.svg";
-import { motion } from "framer-motion";
+import ReactMarkdown from 'react-markdown';
+import { useChatContext } from '@/providers/ChatProvider';
+import Link from 'next/link';
+import Image from 'next/image';
+import Logos from '@public/icon.svg';
+import { motion } from 'framer-motion';
import {
- Box,
- Button,
- TextField,
- Typography,
- alpha,
- useTheme,
- IconButton,
-} from "@mui/material";
-import { CopyButton } from "@/components/CopyButton";
+ Box,
+ Button,
+ TextField,
+ Typography,
+ alpha,
+ useTheme,
+ IconButton,
+} from '@mui/material';
+import { CopyButton } from '@/components/CopyButton';
const Chat = () => {
- const theme = useTheme();
- const [input, setInput] = useState("");
- const { messages, sendMessage, status } = useChatContext();
- const isLoading = status === "streaming" || status === "submitted";
+ const theme = useTheme();
+ const [input, setInput] = useState('');
+ const { messages, sendMessage, status } = useChatContext();
+ const isLoading = status === 'streaming' || status === 'submitted';
- const messagesEndRef = useRef(null);
+ const messagesEndRef = useRef(null);
- const scrollToBottom = () => {
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
- };
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
- useEffect(() => {
- scrollToBottom();
- }, [messages, messages[messages.length - 1]?.parts]);
+ const lastMessageParts = messages[messages.length - 1]?.parts;
- const handleSend = () => {
- if (!input.trim() || isLoading) return;
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages, lastMessageParts]);
- sendMessage({ text: input })
- .then(() => setInput(""))
- .catch((error: any) =>
- console.error("❌ [Server Action] Error in chat.tsx:", error),
- );
- };
+ const handleSend = () => {
+ if (!input.trim() || isLoading) return;
- return (
-
- {/* Main Chat Area */}
-
- {/* Header */}
-
-
-
-
-
- Volver al inicio
-
-
-
-
-
-
-
+ sendMessage({ text: input })
+ .then(() => setInput(''))
+ .catch((error: any) =>
+ console.error('❌ [Server Action] Error in chat.tsx:', error),
+ );
+ };
- {messages.length === 0 && (
-
-
-
- Chatbot
- {" "}
-
- de LogOS
-
-
-
- ¡Pregunta lo que desees saber sobre LogOS!
-
-
- )}
+ return (
+
+ {/* Main Chat Area */}
+
+ {/* Header */}
+
+
+
+
+
+ Volver al inicio
+
+
+
+
+
+
+
- {/* Messages */}
-
-
-
- {messages.map((message: any, index: number) => (
-
- {message.role !== "user" && (
-
-
-
- )}
-
-
- (
-
- ),
- ol: ({ node, ...props }) => (
-
- ),
- li: ({ node, ...props }) => (
-
- ),
- strong: ({ node, ...props }) => (
-
- ),
- a: ({ node, ...props }) => (
-
- ),
- p: ({ node, ...props }) => (
-
- ),
- h1: ({ node, ...props }) => (
-
- ),
- h2: ({ node, ...props }) => (
-
- ),
- }}
- >
- {message.parts
- ? message.parts
- .map((part: any) =>
- part.type === "text" ? part.text : "",
- )
- .join("")
- : message.content}
-
-
- {message.role !== "user" && (
-
-
- part.type === "text" ? part.text : "",
- )
- .join("")
- : message.content
- }
- />
-
- )}
-
- {message.role === "user" && (
-
-
-
- )}
-
- ))}
- {isLoading && (
-
-
- {[0, 1, 2].map((item) => (
-
- ))}
-
-
- )}
-
-
-
-
+ {messages.length === 0 && (
+
+
+
+ Chatbot
+ {' '}
+
+ de LogOS
+
+
+
+ ¡Pregunta lo que desees saber sobre LogOS!
+
+
+ )}
- {/* Input Area */}
-
-
- setInput(e.target.value)}
- onKeyDown={(e) => {
- if (
- e.key === "Enter" &&
- !e.shiftKey &&
- !!input.trim() &&
- !isLoading
- ) {
- e.preventDefault();
- handleSend();
- }
- }}
- placeholder="Pregunta lo que desees saber sobre LogOS..."
- disabled={isLoading}
- slotProps={{
- input: {
- endAdornment: (
-
-
-
- ),
- },
- }}
- />
-
- La IA puede cometer errores. Considere verificar información
- importante.
-
-
-
-
-
- );
+ {/* Messages */}
+
+
+
+ {messages.map((message: any, index: number) => (
+
+ {message.role !== 'user' && (
+
+
+
+ )}
+
+
+ (
+
+ ),
+ ol: ({ node, ...props }) => (
+
+ ),
+ li: ({ node, ...props }) => (
+
+ ),
+ strong: ({ node, ...props }) => (
+
+ ),
+ a: ({ node, ...props }) => (
+
+ ),
+ p: ({ node, ...props }) => (
+
+ ),
+ h1: ({ node, ...props }) => (
+
+ ),
+ h2: ({ node, ...props }) => (
+
+ ),
+ }}
+ >
+ {message.parts
+ ? message.parts
+ .map((part: any) =>
+ part.type === 'text' ? part.text : '',
+ )
+ .join('')
+ : message.content}
+
+
+ {message.role !== 'user' && (
+
+
+ part.type === 'text' ? part.text : '',
+ )
+ .join('')
+ : message.content
+ }
+ />
+
+ )}
+
+ {message.role === 'user' && (
+
+
+
+ )}
+
+ ))}
+ {isLoading && (
+
+
+ {[0, 1, 2].map((item) => (
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ {/* Input Area */}
+
+
+ setInput(e.target.value)}
+ onKeyDown={(e) => {
+ if (
+ e.key === 'Enter' &&
+ !e.shiftKey &&
+ !!input.trim() &&
+ !isLoading
+ ) {
+ e.preventDefault();
+ handleSend();
+ }
+ }}
+ placeholder='Pregunta lo que desees saber sobre LogOS...'
+ disabled={isLoading}
+ slotProps={{
+ input: {
+ endAdornment: (
+
+
+
+ ),
+ },
+ }}
+ />
+
+ La IA puede cometer errores. Considere verificar información
+ importante.
+
+
+
+
+
+ );
};
export default Chat;
diff --git a/src/sections/chat/ChatWidget.tsx b/src/sections/chat/ChatWidget.tsx
index 1e9e749..8922154 100644
--- a/src/sections/chat/ChatWidget.tsx
+++ b/src/sections/chat/ChatWidget.tsx
@@ -1,482 +1,484 @@
-"use client";
+'use client';
-import { useState, useRef, useEffect } from "react";
-import { useChatContext } from "@/providers/ChatProvider";
-import { usePathname } from "next/navigation";
+import { useState, useRef, useEffect } from 'react';
+import { useChatContext } from '@/providers/ChatProvider';
+import { usePathname } from 'next/navigation';
// MUI Icons - Outlined version to match Lucide aesthetic
-import OpenInFullOutlinedIcon from "@mui/icons-material/OpenInFullOutlined";
-import SendOutlinedIcon from "@mui/icons-material/SendOutlined";
-import ForumOutlinedIcon from "@mui/icons-material/ForumOutlined";
-import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined";
-import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined";
+import OpenInFullOutlinedIcon from '@mui/icons-material/OpenInFullOutlined';
+import SendOutlinedIcon from '@mui/icons-material/SendOutlined';
+import ForumOutlinedIcon from '@mui/icons-material/ForumOutlined';
+import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined';
+import PersonOutlineOutlinedIcon from '@mui/icons-material/PersonOutlineOutlined';
-import Image from "next/image";
-import Logos from "@public/icon.svg";
-import ReactMarkdown from "react-markdown";
-import { AnimatePresence, motion } from "framer-motion";
-import Link from "next/link";
+import Image from 'next/image';
+import Logos from '@public/icon.svg';
+import ReactMarkdown from 'react-markdown';
+import { AnimatePresence, motion } from 'framer-motion';
+import Link from 'next/link';
import {
- Box,
- Button,
- TextField,
- Typography,
- alpha,
- useTheme,
- IconButton,
-} from "@mui/material";
-import { CopyButton } from "@/components/CopyButton";
+ Box,
+ Button,
+ TextField,
+ Typography,
+ alpha,
+ useTheme,
+ IconButton,
+} from '@mui/material';
+import { CopyButton } from '@/components/CopyButton';
export function ChatWidget() {
- const theme = useTheme();
- const pathname = usePathname();
- const [isOpen, setIsOpen] = useState(false);
- const [input, setInput] = useState("");
- const { messages, sendMessage, status } = useChatContext();
- const isLoading = status === "streaming" || status === "submitted";
- const messagesEndRef = useRef(null);
+ const theme = useTheme();
+ const pathname = usePathname();
+ const [isOpen, setIsOpen] = useState(false);
+ const [input, setInput] = useState('');
+ const { messages, sendMessage, status } = useChatContext();
+ const isLoading = status === 'streaming' || status === 'submitted';
+ const messagesEndRef = useRef(null);
- const isChatPage = pathname === "/chat" || pathname === "/chat/";
+ const isChatPage = pathname === '/chat' || pathname === '/chat/';
- const scrollToBottom = () => {
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
- };
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
- useEffect(() => {
- if (isOpen) {
- scrollToBottom();
- }
- }, [messages, isOpen, messages[messages.length - 1]?.parts]);
+ const lastMessageParts = messages[messages.length - 1]?.parts;
- const handleSend = async () => {
- if (!input.trim() || isLoading) return;
+ useEffect(() => {
+ if (isOpen) {
+ scrollToBottom();
+ }
+ }, [messages, isOpen, lastMessageParts]);
- const currentInput = input;
- setInput("");
+ const handleSend = async () => {
+ if (!input.trim() || isLoading) return;
- try {
- await sendMessage({ text: currentInput });
- } catch (error) {
- console.error("Error sending message:", error);
- }
- };
+ const currentInput = input;
+ setInput('');
- if (isChatPage) return null;
+ try {
+ await sendMessage({ text: currentInput });
+ } catch (error) {
+ console.error('Error sending message:', error);
+ }
+ };
- return (
-
-
- {isOpen && (
-
- {/* Header */}
-
-
-
-
-
-
-
- LogOS
-
-
-
- ●
- {" "}
- En línea
-
-
-
+ if (isChatPage) return null;
-
-
-
-
-
-
- setIsOpen(false)}
- sx={{
- "&:hover": {
- bgcolor: alpha(theme.palette.error.main, 0.1),
- color: "error.main",
- },
- }}
- >
-
-
-
-
+ return (
+
+
+ {isOpen && (
+
+ {/* Header */}
+
+
+
+
+
+
+
+ LogOS
+
+
+
+ ●
+ {' '}
+ En línea
+
+
+
- {/* Body */}
-
- {messages.length === 0 ? (
-
-
-
- ¡Hola! Soy el asistente de LogOS.
-
-
- Pregúntame sobre funcionalidades, protocolos o monitoreo.
-
-
- ) : (
-
- {messages.map((message: any, index: number) => (
-
- {message.role !== "user" && (
-
-
-
- )}
-
-
- (
-
- ),
- p: ({ node, ...props }) => (
-
- ),
- a: ({ node, ...props }) => (
-
- ),
- }}
- >
- {message.parts
- ? message.parts
- .map((p: any) =>
- p.type === "text" ? p.text : "",
- )
- .join("")
- : message.content}
-
- {message.role !== "user" && (
-
-
- p.type === "text" ? p.text : "",
- )
- .join("")
- : message.content
- }
- />
-
- )}
-
-
- {message.role === "user" && (
-
-
-
- )}
-
- ))}
- {isLoading && (
-
-
- {[0, 1, 2].map((i) => (
-
- ))}
-
-
- )}
-
-
- )}
-
+
+
+
+
+
+
+ setIsOpen(false)}
+ sx={{
+ '&:hover': {
+ bgcolor: alpha(theme.palette.error.main, 0.1),
+ color: 'error.main',
+ },
+ }}
+ >
+
+
+
+
- {/* Footer Input */}
-
- setInput(e.target.value)}
- onKeyDown={(e) => {
- if (
- e.key === "Enter" &&
- !e.shiftKey &&
- !isLoading &&
- !!input.trim()
- ) {
- e.preventDefault();
- handleSend();
- }
- }}
- placeholder="Escribe un mensaje..."
- disabled={isLoading}
- slotProps={{
- input: {
- endAdornment: (
-
-
-
- ),
- },
- }}
- />
-
- IA puede cometer errores.
-
-
-
- )}
-
+ {/* Body */}
+
+ {messages.length === 0 ? (
+
+
+
+ ¡Hola! Soy el asistente de LogOS.
+
+
+ Pregúntame sobre funcionalidades, protocolos o monitoreo.
+
+
+ ) : (
+
+ {messages.map((message: any, index: number) => (
+
+ {message.role !== 'user' && (
+
+
+
+ )}
+
+
+ (
+
+ ),
+ p: ({ node, ...props }) => (
+
+ ),
+ a: ({ node, ...props }) => (
+
+ ),
+ }}
+ >
+ {message.parts
+ ? message.parts
+ .map((p: any) =>
+ p.type === 'text' ? p.text : '',
+ )
+ .join('')
+ : message.content}
+
+ {message.role !== 'user' && (
+
+
+ p.type === 'text' ? p.text : '',
+ )
+ .join('')
+ : message.content
+ }
+ />
+
+ )}
+
+
+ {message.role === 'user' && (
+
+
+
+ )}
+
+ ))}
+ {isLoading && (
+
+
+ {[0, 1, 2].map((i) => (
+
+ ))}
+
+
+ )}
+
+
+ )}
+
- {/* Trigger Button */}
-
- {!isOpen && (
- setIsOpen(true)}
- sx={{
- width: 56,
- height: 56,
- borderRadius: "50%",
- bgcolor: "primary.main",
- color: "primary.contrastText",
- border: "none",
- cursor: "pointer",
- boxShadow: theme.shadows[10],
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- }}
- >
-
-
- )}
-
-
- );
+ {/* Footer Input */}
+
+ setInput(e.target.value)}
+ onKeyDown={(e) => {
+ if (
+ e.key === 'Enter' &&
+ !e.shiftKey &&
+ !isLoading &&
+ !!input.trim()
+ ) {
+ e.preventDefault();
+ handleSend();
+ }
+ }}
+ placeholder='Escribe un mensaje...'
+ disabled={isLoading}
+ slotProps={{
+ input: {
+ endAdornment: (
+
+
+
+ ),
+ },
+ }}
+ />
+
+ IA puede cometer errores.
+
+
+
+ )}
+
+
+ {/* Trigger Button */}
+
+ {!isOpen && (
+ setIsOpen(true)}
+ sx={{
+ width: 56,
+ height: 56,
+ borderRadius: '50%',
+ bgcolor: 'primary.main',
+ color: 'primary.contrastText',
+ border: 'none',
+ cursor: 'pointer',
+ boxShadow: theme.shadows[10],
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ }}
+ >
+
+
+ )}
+
+
+ );
}
diff --git a/src/sections/landing/HardwareAlternatives.tsx b/src/sections/landing/HardwareAlternatives.tsx
index d343465..e60d108 100644
--- a/src/sections/landing/HardwareAlternatives.tsx
+++ b/src/sections/landing/HardwareAlternatives.tsx
@@ -1,443 +1,445 @@
-"use client";
+'use client';
-import { motion } from "framer-motion";
-import Link from "next/link";
-import Image from "next/image";
+import { motion } from 'framer-motion';
+import Link from 'next/link';
+import Image from 'next/image';
// MUI Icons - Outlined version
-import MonitorOutlinedIcon from "@mui/icons-material/MonitorOutlined";
-import DnsOutlinedIcon from "@mui/icons-material/DnsOutlined";
-import WifiOutlinedIcon from "@mui/icons-material/WifiOutlined";
-import VideocamOutlinedIcon from "@mui/icons-material/VideocamOutlined";
-import StorageOutlinedIcon from "@mui/icons-material/StorageOutlined";
-import ElectricBoltOutlinedIcon from "@mui/icons-material/ElectricBoltOutlined";
-import MemoryOutlinedIcon from "@mui/icons-material/MemoryOutlined";
+import MonitorOutlinedIcon from '@mui/icons-material/MonitorOutlined';
+import DnsOutlinedIcon from '@mui/icons-material/DnsOutlined';
+import WifiOutlinedIcon from '@mui/icons-material/WifiOutlined';
+import VideocamOutlinedIcon from '@mui/icons-material/VideocamOutlined';
+import StorageOutlinedIcon from '@mui/icons-material/StorageOutlined';
+import ElectricBoltOutlinedIcon from '@mui/icons-material/ElectricBoltOutlined';
+import MemoryOutlinedIcon from '@mui/icons-material/MemoryOutlined';
-import reterminalImage from "@public/assets/hardware-alternatives/reterminal.png";
-import recomputerImage from "@public/assets/hardware-alternatives/recomputer.png";
-import seeed from "@public/seeed.png";
+import reterminalImage from '@public/assets/hardware-alternatives/reterminal.png';
+import recomputerImage from '@public/assets/hardware-alternatives/recomputer.png';
+import seeed from '@public/seeed.png';
import {
- Box,
- Container,
- Grid2 as Grid,
- Typography,
- Stack,
- useTheme,
- alpha,
- Chip,
- Card,
- Button,
-} from "@mui/material";
+ Box,
+ Container,
+ Grid2 as Grid,
+ Typography,
+ Stack,
+ useTheme,
+ alpha,
+ Chip,
+ Card,
+ Button,
+} from '@mui/material';
const hardwareOptions = [
- {
- name: "ReTerminalDM",
- subtitle: "Panel de Control Interactivo",
- image: reterminalImage.src,
- description:
- "Terminal industrial con pantalla táctil integrada para control directo y visualización en tiempo real con sistema LogOS pre-instalado.",
- features: [
- { icon: MonitorOutlinedIcon, text: 'Pantalla táctil 10.1"' },
- { icon: VideocamOutlinedIcon, text: "Cámara integrada" },
- { icon: WifiOutlinedIcon, text: "WiFi 6 + Bluetooth 5.0" },
- { icon: ElectricBoltOutlinedIcon, text: "PoE+ alimentación" },
- ],
- specs: [
- "Resolución 1280x800",
- "IP65 clasificación",
- "Serial RS485",
- "IEC 61850 compatible",
- ],
- useCase:
- "Ideal para salas de control, cabinas de operador y puntos de supervisión local.",
- price: "Desde UF 18,5",
- highlight: "Interactividad Local",
- },
- {
- name: "ReComputer",
- subtitle: "Servidor Edge Industrial",
- image: recomputerImage.src,
- description:
- "Computadora industrial compacta optimizada para ejecutar LogOS como servidor web local y gateway de comunicaciones de alto rendimiento.",
- features: [
- { icon: DnsOutlinedIcon, text: "Server-grade performance" },
- { icon: StorageOutlinedIcon, text: "SSD NVMe 256GB" },
- { icon: WifiOutlinedIcon, text: "Ethernet Gigabit x2" },
- { icon: MemoryOutlinedIcon, text: "ARM Cortex-A72 Quad-core" },
- ],
- specs: [
- "8GB RAM DDR4",
- "Ventilación silenciosa",
- "Riel DIN para montaje",
- "Temperaturas críticas",
- ],
- useCase:
- "Perfecto para despliegues centralizados, edge computing y servidores locales de alta disponibilidad.",
- price: "Desde UF 8,3",
- highlight: "Edge Computing",
- },
+ {
+ name: 'ReTerminalDM',
+ subtitle: 'Panel de Control Interactivo',
+ image: reterminalImage.src,
+ description:
+ 'Terminal industrial con pantalla táctil integrada para control directo y visualización en tiempo real con sistema LogOS pre-instalado.',
+ features: [
+ { icon: MonitorOutlinedIcon, text: 'Pantalla táctil 10.1"' },
+ { icon: VideocamOutlinedIcon, text: 'Cámara integrada' },
+ { icon: WifiOutlinedIcon, text: 'WiFi 6 + Bluetooth 5.0' },
+ { icon: ElectricBoltOutlinedIcon, text: 'PoE+ alimentación' },
+ ],
+ specs: [
+ 'Resolución 1280x800',
+ 'IP65 clasificación',
+ 'Serial RS485',
+ 'IEC 61850 compatible',
+ ],
+ useCase:
+ 'Ideal para salas de control, cabinas de operador y puntos de supervisión local.',
+ price: 'Desde UF 18,5',
+ highlight: 'Interactividad Local',
+ },
+ {
+ name: 'ReComputer',
+ subtitle: 'Servidor Edge Industrial',
+ image: recomputerImage.src,
+ description:
+ 'Computadora industrial compacta optimizada para ejecutar LogOS como servidor web local y gateway de comunicaciones de alto rendimiento.',
+ features: [
+ { icon: DnsOutlinedIcon, text: 'Server-grade performance' },
+ { icon: StorageOutlinedIcon, text: 'SSD NVMe 256GB' },
+ { icon: WifiOutlinedIcon, text: 'Ethernet Gigabit x2' },
+ { icon: MemoryOutlinedIcon, text: 'ARM Cortex-A72 Quad-core' },
+ ],
+ specs: [
+ '8GB RAM DDR4',
+ 'Ventilación silenciosa',
+ 'Riel DIN para montaje',
+ 'Temperaturas críticas',
+ ],
+ useCase:
+ 'Perfecto para despliegues centralizados, edge computing y servidores locales de alta disponibilidad.',
+ price: 'Desde UF 8,3',
+ highlight: 'Edge Computing',
+ },
];
export function HardwareAlternatives() {
- const theme = useTheme();
+ const theme = useTheme();
- return (
-
-
-
-
-
- Hardware
- {" "}
-
- Industrial
-
-
-
- Soluciones de hardware especializadas de Seeed Studio, optimizadas
- para ejecutar LogOS en entornos industriales exigentes con la mayor
- estabilidad.
-
-
+ return (
+
+
+
+
+
+ Hardware
+ {' '}
+
+ Industrial
+
+
+
+ Soluciones de hardware especializadas de Seeed Studio, optimizadas
+ para ejecutar LogOS en entornos industriales exigentes con la mayor
+ estabilidad.
+
+
-
- {hardwareOptions.map((hardware, index) => (
-
-
-
- {/* Image Section */}
-
-
-
-
-
-
-
-
+
+ {hardwareOptions.map((hardware, index) => (
+
+
+
+ {/* Image Section */}
+
+
+
+
+
+
+
+
- {/* Content Section */}
-
-
-
-
- {hardware.name}
-
-
- {hardware.subtitle.toUpperCase()}
-
-
-
-
- {hardware.price}
-
-
- IVA NO INCLUIDO
-
-
-
+ {/* Content Section */}
+
+
+
+
+ {hardware.name}
+
+
+ {hardware.subtitle.toUpperCase()}
+
+
+
+
+ {hardware.price}
+
+
+ IVA NO INCLUIDO
+
+
+
-
- {hardware.description}
-
+
+ {hardware.description}
+
-
- {hardware.features.map((feature, fIdx) => (
-
-
-
-
-
-
- {feature.text}
-
-
-
- ))}
-
+
+ {hardware.features.map((feature, fIdx) => (
+
+
+
+
+
+
+ {feature.text}
+
+
+
+ ))}
+
-
-
- ESPECIFICACIONES TÉCNICAS
-
-
- {hardware.specs.map((spec, sIdx) => (
-
-
-
- {spec}
-
-
- ))}
-
-
+
+
+ ESPECIFICACIONES TÉCNICAS
+
+
+ {hardware.specs.map((spec, sIdx) => (
+
+
+
+ {spec}
+
+
+ ))}
+
+
-
-
-
- Caso de uso:
- {" "}
- {hardware.useCase}
-
-
+
+
+
+ Caso de uso:
+ {' '}
+ {hardware.useCase}
+
+
-
-
-
-
-
-
-
-
-
- ))}
-
+
+
+
+
+
+
+
+
+
+ ))}
+
- {/* Partnership info */}
-
-
-
-
-
-
- Logos está probado y optimizado para hardware{" "}
- Seeed Studio, garantizando máximo rendimiento,
- seguridad y compatibilidad total en entornos industriales críticos
- de alta exigencia.
-
-
-
-
-
- );
+ {/* Partnership info */}
+
+
+
+
+
+
+ Logos está probado y optimizado para hardware{' '}
+ Seeed Studio, garantizando máximo rendimiento,
+ seguridad y compatibilidad total en entornos industriales críticos
+ de alta exigencia.
+
+
+
+
+
+ );
}