type: message

role: assistant

type: thinking

thinking:
I'll create a new Next.js project with the specific structure I need. Let me:
1. Initialize npm project
2. Install dependencies: next, react, react-dom, framer-motion, lucide-react, tailwindcss
3. Set up Tailwind config with Tet colors
4. Create the page structure
5. Add animations
6. Populate with content

Let's start systematically.

thinkingSignature: reasoning

type: text

text: Let me create the project structure manually with all the Tet-themed styling and components:

write

type: toolCall

clawteam-portfolio

dev: next dev

build: next build

start: next start

lint: eslint . --ext .ts,.tsx

content:
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        tet: {
          red: {
            50: '#fef2f2',
            100: '#fee2e2',
            200: '#fecaca',
            300: '#fca5a5',
            400: '#f87171',
            500: '#dc2626',
            600: '#b91c1c',
            700: '#991b1b',
            800: '#7f1d1d',
            900: '#450a0a',
          },
          gold: {
            50: '#fefce8',
            100: '#fef9c3',
            200: '#fef08a',
            300: '#fde047',
            400: '#facc15',
            500: '#eab308',
            600: '#ca8a04',
            700: '#a16207',
            800: '#854d0e',
            900: '#422006',
          },
          bg: {
            primary: '#8B0000',
            secondary: '#FFD700',
            dark: '#1a0000',
          }
        },
        background: 'var(--background)',
        foreground: 'var(--foreground)',
      },
      animation: {
        'fall-slow': 'fall 15s linear infinite',
        'fall-medium': 'fall 10s linear infinite',
        'fall-fast': 'fall 5s linear infinite',
        'float': 'float 3s ease-in-out infinite',
        'pulse-glow': 'pulseGlow 2s ease-in-out infinite',
        'slide-up': 'slideUp 0.8s ease-out',
        'slide-down': 'slideDown 0.8s ease-out',
        'lantern-sway': 'lanternSway 3s ease-in-out infinite',
        'lions-dance': 'lionsDance 2s ease-in-out infinite',
      },
      keyframes: {
        fall: {
          '0%': { transform: 'translateY(-100vh) rotate(0deg)', opacity: 0.8 },
          '100%': { transform: 'translateY(100vh) rotate(720deg)', opacity: 0.3 },
        },
        float: {
          '0%, 100%': { transform: 'translateY(0)' },
          '50%': { transform: 'translateY(-10px)' },
        },
        pulseGlow: {
          '0%, 100%': { boxShadow: '0 0 5px 0 rgba(255, 215, 0, 0.7)' },
          '50%': { boxShadow: '0 0 20px 5px rgba(255, 215, 0, 0.9)' },
        },
        slideUp: {
          '0%': { transform: 'translateY(100px)', opacity: 0 },
          '100%': { transform: 'translateY(0)', opacity: 1 },
        },
        slideDown: {
          '0%': { transform: 'translateY(-100px)', opacity: 0 },
          '100%': { transform: 'translateY(0)', opacity: 1 },
        },
        lanternSway: {
          '0%, 100%': { transform: 'rotate(-5deg)' },
          '50%': { transform: 'rotate(5deg)' },
        },
        lionsDance: {
          '0%, 100%': { transform: 'translateX(0px)' },
          '25%': { transform: 'translateX(-10px)' },
          '75%': { transform: 'translateX(10px)' },
        },
      },
      fontFamily: {
        'tet': ['var(--font-playfair-display)'],
        'body': ['var(--font-inter)'],
      },
    },
  },
  plugins: [],
};
export default config;

content:
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --background: #0a0a0a;
  --foreground: #ededed;
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
}

body {
  background: linear-gradient(135deg, #1a0000 0%, #0a0a0a 50%, #1a0000 100%);
  color: var(--foreground);
  font-family: var(--font-body), system-ui, sans-serif;
  min-height: 100vh;
  overflow-x: hidden;
}

/* Tet decorative patterns */
.tet-pattern {
  background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M30 10 L35 20 L30 30 L25 20 Z' fill='%23FFD700' opacity='0.1'/%3E%3Ccircle cx='30' cy='30' r='2' fill='%23dc2626' opacity='0.15'/%3E%3C/svg%3E");
}

/* Glowing text effect */
.text-glow {
  text-shadow: 0 0 10px rgba(255, 215, 0, 0.7), 0 0 20px rgba(255, 215, 0, 0.5);
}

/* Gold border glow */
.border-glow {
  box-shadow: 0 0 15px rgba(255, 215, 0, 0.4), inset 0 0 15px rgba(255, 215, 0, 0.1);
}

/* Scrollbar styling */
::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: #1a0000;
}

::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #FFD700, #dc2626);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #fbbf24, #ef4444);
}

/* Flower petals */
.petal {
  position: absolute;
  width: 20px;
  height: 20px;
  background: radial-gradient(ellipse at center, #fbbf24 0%, #f59e0b 100%);
  border-radius: 150% 0 150% 0;
  opacity: 0.8;
  pointer-events: none;
  will-change: transform, opacity;
}

/* Lantern decoration */
.lantern {
  position: relative;
  display: inline-block;
}

.lantern::before {
  content: '';
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  width: 20px;
  height: 15px;
  background: linear-gradient(180deg, #854d0e, #a16207);
  border-radius: 5px 5px 0 0;
}

.lantern::after {
  content: '';
  position: absolute;
  bottom: -15px;
  left: 50%;
  transform: translateX(-50%);
  width: 8px;
  height: 20px;
  background: linear-gradient(180deg, #854d0e, #a16207);
  border-radius: 0 0 4px 4px;
}

/* Lion dance animation */
.lion {
  position: relative;
  display: inline-block;
}

.lion-head {
  width: 60px;
  height: 50px;
  background: linear-gradient(135deg, #dc2626, #991b1b);
  border-radius: 50% 50% 40% 40%;
  position: relative;
  box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
}

.lion-head::before {
  content: '';
  position: absolute;
  top: 10px;
  left: 10px;
  width: 15px;
  height: 15px;
  background: #FFD700;
  border-radius: 50%;
  box-shadow: 20px 0 0 #FFD700;
}

.lion-body {
  width: 80px;
  height: 40px;
  background: linear-gradient(135deg, #dc2626, #991b1b);
  border-radius: 0 0 50% 50%;
  margin-top: -10px;
  margin-left: -20px;
}

.lion-body::before {
  content: '';
  position: absolute;
  top: 5px;
  left: 60px;
  width: 30px;
  height: 20px;
  background: #FFD700;
  border-radius: 0 50% 50% 0;
  opacity: 0.8;
}

@keyframes lionDance {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-5px) rotate(2deg); }
}

.animate-lion {
  animation: lionDance 0.6s ease-in-out infinite;
}

/* Smooth scrolling */
html {
  scroll-behavior: smooth;
}

/* Selection color */
::selection {
  background: rgba(255, 215, 0, 0.3);
  color: #fff;
}

api: openai-completions

provider: openrouter

stopReason: toolUse


role: toolResult

toolName: write

text: Successfully wrote 596 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/package.json


text: Successfully wrote 2957 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/tailwind.config.ts


text: Successfully wrote 3376 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/app/globals.css


thinking:
Good! Now I need to continue building the project. Let me create:
1. postcss.config.js
2. next.config.js (or ts)
3. tsconfig.json
4. Components directory with Tet-themed components
5. Pages structure
6. Data file with user info

Let me continue systematically.

text: Now let's continue with the configuration files and core components:

content:
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

lib: dom dom.iterable esnext

module: esnext

moduleResolution: bundler

jsx: preserve

content:
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'nguyenngoctrivi.vercel.app',
      },
    ],
  },
};

module.exports = nextConfig;

content:
import type { Metadata } from "next";
import { Inter, Playfair_Display } from "next/font/google";
import "./globals.css";

const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
  display: "swap",
});

const playfairDisplay = Playfair_Display({
  variable: "--font-playfair-display",
  subsets: ["latin"],
  display: "swap",
});

export const metadata: Metadata = {
  title: "Trí Vĩ | AI Engineer Portfolio",
  description: "Portfolio của Nguyễn Ngọc Trí Vĩ - AI Engineer & Computer Vision Specialist",
  keywords: ["AI Engineer", "Computer Vision", "Machine Learning", "Portfolio", "Trí Vĩ"],
  authors: [{ name: "Nguyễn Ngọc Trí Vĩ" }],
  creator: "Nguyễn Ngọc Trí Vĩ",
  openGraph: {
    type: "website",
    locale: "vi_VN",
    url: "https://nguyenngoctrivi.vercel.app",
    title: "Trí Vĩ | AI Engineer Portfolio",
    description: "Portfolio của Nguyễn Ngọc Trí Vĩ - AI Engineer & Computer Vision Specialist",
    siteName: "Trí Vĩ Portfolio",
  },
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="vi" className={`${inter.variable} ${playfairDisplay.variable}`}>
      <body className={`${inter.variable} font-sans antialiased`}>
        {children}
      </body>
    </html>
  );
}


text: Successfully wrote 83 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/postcss.config.js


text: Successfully wrote 512 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/tsconfig.json


text: Successfully wrote 233 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/next.config.js


text: Successfully wrote 34 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/next.config.ts


text: Successfully wrote 1286 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/app/layout.tsx


thinking: Great! Now I need to create the data file with the user's information, then the components and pages. Let me create a comprehensive data file that combines all the CV info and transforms it into a structured format for the portfolio.

text: Now let's create the data file with all your information, and the core components:

content:
export const personalInfo = {
  name: "Nguyễn Ngọc Trí Vĩ",
  alias: "Thomas",
  title: "AI Engineer & Computer Vision Specialist",
  phone: "090 321 5095",
  email: "nntrivi2001@gmail.com",
  linkedin: "https://linkedin.com/in/nguyen-ngoc-tri-vi",
  facebook: "https://www.facebook.com/NguyenNgocTriVi/",
  portfolio: "https://nguyenngoctrivi.vercel.app",
  avatar: "/avatar.jpeg",
  location: "Hồ Chí Minh, Việt Nam",
  summary: `
    Kỹ sư AI với chuyên môn sâu về Thị giác Máy tính và Machine Learning.
    Từng tốt nghiệp Thủ khoa tại Đại học Công Thương TP.HCM với GPA 3.74/4.0,
    sở hữu kinh nghiệm thực tế trong triển khai các hệ thống AI phức tạp cho
    doanh nghiệp và công nghiệp.
  `,
  education: [
    {
      degree: "Kỹ sư Phân tích Dữ liệu & AI",
      school: "Đại học Công Thương TP.HCM",
      period: "2020 - 2024",
      gpa: "3.74/4.0",
      honors: [
        "Thủ khoa ngành Công nghệ Thông tin",
        "Giải Nhì - Cuộc thi Sản phẩm và Chủ đề IT Thiết yếu (2024)",
        "Giải Khuyến khích - TOFAS (2023)",
        "Giải Mã nguồn sạch & Giải Khuyến khích - Tìm kiếm Tài năng & Sản phẩm IT Sáng tạo (2022)",
      ],
    },
  ],
  skills: {
    "Lập trình & Scripting": ["Python", "C++", "HTML/CSS", "JavaScript", "PineScript", "n8n", "Telegram bots"],
    "Framework AI/ML": ["TensorFlow", "PyTorch", "Scikit-learn", "OpenCV"],
    "Dữ liệu & Đám mây": ["SQL Server", "Supabase", "Firebase", "MongoDB"],
    "Quản lý & Vận hành": ["Tối ưu hóa quy trình", "Phối hợp liên nhóm", "Tài liệu hóa"],
    "Ngôn ngữ": ["Tiếng Anh (Đọc chuyên ngành, giao tiếp trung cấp)", "Tiếng Việt (Bản ngữ)"],
  },
};

export const experience = [
  {
    company: "SmartGift Solution",
    position: "Kỹ sư AI",
    period: "03/2026 - Hiện tại",
    description: [
      "Nghiên cứu và triển khai AI hỗ trợ nhằm cải thiện hiệu suất của đội ngũ phát triển.",
      "Khám phá và áp dụng các công nghệ phù hợp cho các bài toán kinh doanh.",
      "Xây dựng các bản thử nghiệm (POC) và tài liệu hóa các đề xuất thực tế.",
    ],
  },
  {
    company: "Learning Chain Vietnam",
    position: "Nghiên cứu viên AI Ứng dụng & Cộng tác viên Vận hành",
    period: "07/2025 - 03/2026",
    description: [
      "Nghiên cứu và triển khai tự động hóa giao dịch và vận hành có hỗ trợ AI.",
      "Thiết kế và giảng dạy chương trình đào tạo AI ứng dụng, hội thảo.",
      "Hỗ trợ Giám đốc vận hành tối ưu hóa quy trình làm việc và tiêu chuẩn hóa.",
    ],
  },
  {
    company: "Vườn đậu - Thương hiệu Sữa đậu nành Thảo mộc",
    position: "Founder",
    period: "10/2024 - 04/2025",
    description: [
      "Quản lý toàn bộ quy trình sản xuất và kinh doanh.",
      "Tối ưu hóa chi phí và duy trì chất lượng sản phẩm ổn định.",
      "Phát triển chiến lược giá và kênh bán hàng trực tiếp.",
    ],
    highlight: "Tỷ lệ mua lại trung bình 2.51 lần mỗi khách hàng",
  },
  {
    company: "VSTECH Company Limited",
    position: "Kỹ sư Thị giác Máy tính (Computer Vision Engineer)",
    period: "08/2023 - 02/2025",
    description: [
      "Kiểm tra chất lượng vòi phun (Nozzle Quality Inspection) cho khách hàng Nhật Bản.",
      "Tích hợp hệ thống thị giác 3D: Đo chiều cao tấm nền LED Samsung với độ chính xác ±7%.",
      "Giải pháp OCR đọc mã gói hàng và nhãn văn bản trong môi trường công nghiệp.",
    ],
  },
];

export const projects = [
  {
    id: "nozzle-inspection",
    title: "Hệ thống Kiểm tra Chất lượng Vòi phun",
    description:
      "Xây dựng hệ thống kiểm tra chất lượng vòi phun tự động cho dây chuyền sản xuất nhà máy, đảm bảo độ chính xác cao trong môi trường công nghiệp.",
    tags: ["Computer Vision", "OpenCV", "Python", "Industrial Automation"],
    image: "/projects/nozzle-inspection.jpg",
    status: "completed",
  },
  {
    id: "3d-optical-measurement",
    title: "Đo chiều cao Tấm nền LED 3D",
    description:
      "Tích hợp hệ thống thị giác 3D để đo chiều cao tấm nền LED Samsung với độ chính xác đạt ±7%.",
    tags: ["3D Vision", "Precision Measurement", "Hardware Integration"],
    image: "/projects/led-measurement.jpg",
    status: "completed",
  },
  {
    id: "industrial-ocr",
    title: "Giải pháp OCR cho Công nghiệp",
    description:
      "Xây dựng hệ thống OCR đọc mã gói hàng và nhãn văn bản với độ chính xác cao trong môi trường công nghiệp khắc nghiệt.",
    tags: ["OCR", "Computer Vision", "Text Detection", "Python"],
    image: "/projects/industrial-ocr.jpg",
    status: "completed",
  },
  {
    id: "ai-assistant-poc",
    title: "AI Assistant cho Đội ngũ Phát triển",
    description:
      "Nghiên cứu và triển khai các POC về AI hỗ trợ cải thiện hiệu suất làm việc của developers.",
    tags: ["AI Assistant", "Automation", "POC", "Developer Tools"],
    image: "/projects/ai-assistant.jpg",
    status: "in-progress",
  },
  {
    id: "trading-automation",
    title: "Tự động hóa Giao dịch với AI",
    description:
      "Nghiên cứu và triển khai hệ thống tự động hóa giao dịch có hỗ trợ AI tại Learning Chain Vietnam.",
    tags: ["AI", "Automation", "Trading", "n8n"],
    image: "/projects/trading-auto.jpg",
    status: "completed",
  },
  {
    id: "training-program",
    title: "Chương trình Đào tạo AI Ứng dụng",
    description:
      "Thiết kế và giảng dạy chương trình đào tạo AI ứng dụng, các buổi hội thảo chuyên môn.",
    tags: ["Education", "AI Training", "Workshops", "n8n Templates"],
    image: "/projects/ai-training.jpg",
    status: "completed",
  },
];

export const certifications = [
  "Chứng chỉ chuyên môn chi tiết trên LinkedIn",
];


text: Successfully wrote 5533 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/lib/data.ts


thinking:
Now I need to create the components. I'll create:
1. Header/Navigation component with Tet styling
2. Footer component
3. Floating petals animation component
4. Decorations (lanterns, lions)
5. Home page (combining about, experience, contact)
6. Projects page
7. Individual project cards

Let me create these systematically with beautiful Tet-themed design and smooth animations.

text: Now let's create the components with Tet-themed animations:

content:
"use client";

import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { personalInfo } from "@/lib/data";
import { Menu, X, Linkedin, Facebook, Mail, Phone } from "lucide-react";

export default function Header() {
  const [isScrolled, setIsScrolled] = useState(false);
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      setIsScrolled(window.scrollY > 50);
    };
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  const navLinks = [
    { href: "#home", label: "Trang chủ" },
    { href: "#about", label: "Giới thiệu" },
    { href: "#experience", label: "Kinh nghiệm" },
    { href: "#projects", label: "Dự án" },
    { href: "#contact", label: "Liên hệ" },
  ];

  return (

          {/* Logo / Name */}
          <motion.a
            href="#home"
            initial={{ opacity: 0, x: -20 }}
            animate={{ opacity: 1, x: 0 }}
            className="text-xl md:text-2xl font-bold text-yellow-400 hover:text-yellow-300 transition-colors"
          >
            Trí Vĩ
          </motion.a>

          {/* Desktop Navigation */}

            {navLinks.map((link, index) => (
              <motion.a
                key={link.href}
                href={link.href}
                initial={{ opacity: 0, y: -20 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ delay: index * 0.1 }}
                className="text-gray-200 hover:text-yellow-400 transition-colors font-medium relative group"
              >
                {link.label}

              </motion.a>
            ))}

          {/* Social Icons */}

            <motion.a
              href={personalInfo.linkedin}
              target="_blank"
              rel="noopener noreferrer"
              whileHover={{ scale: 1.1, y: -2 }}
              className="p-2 text-gray-300 hover:text-yellow-400 transition-colors"
              aria-label="LinkedIn"
            >
              <Linkedin size={20} />
            </motion.a>
            <motion.a
              href={personalInfo.facebook}
              target="_blank"
              rel="noopener noreferrer"
              whileHover={{ scale: 1.1, y: -2 }}
              className="p-2 text-gray-300 hover:text-yellow-400 transition-colors"
              aria-label="Facebook"
            >
              <Facebook size={20} />
            </motion.a>
            <motion.a
              href={`mailto:${personalInfo.email}`}
              whileHover={{ scale: 1.1, y: -2 }}
              className="p-2 text-gray-300 hover:text-yellow-400 transition-colors"
              aria-label="Email"
            >
              <Mail size={20} />
            </motion.a>
            <motion.a
              href={`tel:${personalInfo.phone}`}
              whileHover={{ scale: 1.1, y: -2 }}
              className="p-2 text-gray-300 hover:text-yellow-400 transition-colors"
              aria-label="Phone"
            >
              <Phone size={20} />
            </motion.a>

          {/* Mobile Menu Button */}
          <button
            className="md:hidden p-2 text-yellow-400"
            onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
            aria-label="Toggle menu"
          >
            {mobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
          </button>

        {/* Mobile Menu */}
        <AnimatePresence>
          {mobileMenuOpen && (
            <motion.div
              initial={{ opacity: 0, height: 0 }}
              animate={{ opacity: 1, height: "auto" }}
              exit={{ opacity: 0, height: 0 }}
              className="md:hidden bg-red-950/95 backdrop-blur-md rounded-lg mt-2 overflow-hidden"
            >

                {navLinks.map((link) => (
                   setMobileMenuOpen(false)}
                  >
                    {link.label}

                ))}

                    <Linkedin className="text-gray-300 hover:text-yellow-400" size={20} />

                    <Facebook className="text-gray-300 hover:text-yellow-400" size={20} />

                  <a href={`mailto:${personalInfo.email}`}>
                    <Mail className="text-gray-300 hover:text-yellow-400" size={20} />

                  <a href={`tel:${personalInfo.phone}`}>
                    <Phone className="text-gray-300 hover:text-yellow-400" size={20} />

            </motion.div>
          )}
        </AnimatePresence>

  );
}

content:
"use client";

import { useEffect, useState } from "react";
import { motion } from "framer-motion";

interface Petal {
  id: number;
  left: number;
  size: number;
  duration: number;
  delay: number;
}

export default function FallingPetals() {
  const [petals, setPetals] = useState<Petal[]>([]);

  useEffect(() => {
    // Generate random petals
    const newPetals: Petal[] = Array.from({ length: 30 }, (_, i) => ({
      id: i,
      left: Math.random() * 100,
      size: Math.random() * 10 + 10,
      duration: Math.random() * 10 + 8,
      delay: Math.random() * 5,
    }));
    setPetals(newPetals);
  }, []);

  return (
    <div className="fixed inset-0 pointer-events-none overflow-hidden z-0">
      {petals.map((petal) => (
        <motion.div
          key={petal.id}
          className="petal"
          style={{
            left: `${petal.left}%`,
            width: `${petal.size}px`,
            height: `${petal.size}px`,
            background: `radial-gradient(ellipse at center, ${
              Math.random() > 0.5 ? "#fbbf24" : "#f87171"
            } 0%, ${Math.random() > 0.5 ? "#f59e0b" : "#dc2626"} 100%)`,
          }}
          initial={{
            y: -100,
            rotate: 0,
            opacity: 0.8,
          }}
          animate={{
            y: "105vh",
            rotate: 720,
            opacity: [0.8, 0.6, 0.3],
          }}
          transition={{
            duration: petal.duration,
            repeat: Infinity,
            delay: petal.delay,
            ease: "linear",
          }}
        />
      ))}
    </div>
  );
}

content:
"use client";

import { motion } from "framer-motion";
import { Award, Code2, Database, Brain, MessageSquare, Globe } from "lucide-react";

interface SkillsSectionProps {
  skills: Record<string, string[]>;
}

const iconMap: Record<string, React.ReactNode> = {
  "Lập trình & Scripting": <Code2 className="w-6 h-6" />,
  "Framework AI/ML": <Brain className="w-6 h-6" />,
  "Dữ liệu & Đám mây": <Database className="w-6 h-6" />,
  "Quản lý & Vận hành": <MessageSquare className="w-6 h-6" />,
  "Ngôn ngữ": <Globe className="w-6 h-6" />,
};

export default function SkillsSection({ skills }: SkillsSectionProps) {
  const skillEntries = Object.entries(skills);

  return (

          Kỹ năng chuyên môn

          {skillEntries.map(([category, items], idx) => (
            <motion.div
              key={category}
              initial={{ opacity: 0, y: 30 }}
              whileInView={{ opacity: 1, y: 0 }}
              viewport={{ once: true }}
              transition={{ delay: idx * 0.1 }}
              className="bg-gradient-to-br from-red-900/30 to-red-950/50 border border-red-800/50 rounded-2xl p-6 hover:border-yellow-500/50 transition-all duration-300 group"
            >

                  {iconMap[category] || <Award className="w-6 h-6" />}

{category}

                {items.map((skill) => (

                    {skill}

                ))}

            </motion.div>
          ))}

  );
}

content:
"use client";

import { motion } from "framer-motion";
import { GraduationCap, Star, Trophy, Award } from "lucide-react";

interface EducationSectionProps {
  education: Array<{
    degree: string;
    school: string;
    period: string;
    gpa: string;
    honors: string[];
  }>;
}

export default function EducationSection({ education }: EducationSectionProps) {
  return (

          Học vấn

          {education.map((edu, index) => (
            <motion.div
              key={index}
              initial={{ opacity: 0, x: -50 }}
              whileInView={{ opacity: 1, x: 0 }}
              viewport={{ once: true }}
              transition={{ delay: index * 0.1 }}
              className="relative pl-8 border-l-2 border-yellow-500/30 hover:border-yellow-400 transition-colors"
            >

                  <GraduationCap className="text-yellow-400" size={20} />

{edu.degree}

{edu.school}

{edu.period} | GPA: {edu.gpa}

                {edu.honors && (

                      <Trophy size={16} />
                      Giải thưởng & Học bổng:

                    {edu.honors.map((honor, i) => (

                        <Star size={14} className="text-yellow-400 mt-0.5 flex-shrink-0" />
                        {honor}

                    ))}

                )}

            </motion.div>
          ))}

  );
}

content:
"use client";

import { motion } from "framer-motion";
import { Briefcase, Calendar, MapPin } from "lucide-react";

interface ExperienceSectionProps {
  experience: Array<{
    company: string;
    position: string;
    period: string;
    description: string[];
    highlight?: string;
  }>;
}

export default function ExperienceSection({ experience }: ExperienceSectionProps) {
  return (

          Kinh nghiệm làm việc

          {/* Timeline line */}

            {experience.map((exp, index) => (
              <motion.div
                key={index}
                initial={{ opacity: 0, y: 50 }}
                whileInView={{ opacity: 1, y: 0 }}
                viewport={{ once: true }}
                transition={{ delay: index * 0.1 }}
                className={`relative flex flex-col md:flex-row gap-8 ${
                  index % 2 === 0 ? "md:flex-row" : "md:flex-row-reverse"
                }`}
              >
                {/* Timeline dot */}

                {/* Content */}
                <div className={`flex-1 ${index % 2 === 0 ? "md:pr-12 md:text-right" : "md:pl-12"}`}>
                  <motion.div
                    whileHover={{ scale: 1.02 }}
                    className="bg-gradient-to-br from-red-900/30 to-red-950/50 border border-red-800/50 p-6 rounded-2xl hover:border-yellow-500/50 transition-all duration-300 shadow-lg hover:shadow-yellow-900/10"
                  >
                    <div className={`flex items-center gap-2 mb-2 ${index % 2 === 0 ? "md:justify-end" : ""}`}>
                      <Briefcase className="text-yellow-400" size={20} />

{exp.position}

{exp.company}

                    <div className={`flex items-center gap-2 text-gray-400 text-sm mb-4 ${index % 2 === 0 ? "md:justify-end" : ""}`}>
                      <Calendar size={14} />
                      {exp.period}

                    <ul className={`space-y-2 ${index % 2 === 0 ? "md:items-end" : ""}`}>
                      {exp.description.map((item, i) => (

                          <span className={`text-yellow-400 mt-1 ${index % 2 === 0 ? "md:order-2" : ""}`}>▹
                          {item}

                      ))}

                    {exp.highlight && (

⭐ {exp.highlight}

                    )}
                  </motion.div>

                {/* Spacer for alternating layout */}

              </motion.div>
            ))}

  );
}


text: Successfully wrote 6000 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/components/Header.tsx


text: Successfully wrote 1582 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/components/FallingPetals.tsx


text: Successfully wrote 2322 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/components/SkillsSection.tsx


text: Successfully wrote 2490 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/components/EducationSection.tsx


text: Successfully wrote 3730 bytes to /home/node/.openclaw/workspace/projects/clawteam-portfolio/components/ExperienceSection.tsx