Web-based Portfolio Viewer React, JavaScript, HTML

👤 Sharing: AI
```jsx
// App.jsx (or similar main component)
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
import Home from './components/Home';
import Projects from './components/Projects';
import Contact from './components/Contact';
import './App.css'; // Optional: For styling

function App() {
  return (
    <Router>
      <div className="App">
        <header className="App-header">
          <h1>My Portfolio</h1>
          <nav>
            <ul>
              <li>
                <Link to="/">Home</Link>
              </li>
              <li>
                <Link to="/projects">Projects</Link>
              </li>
              <li>
                <Link to="/contact">Contact</Link>
              </li>
            </ul>
          </nav>
        </header>

        <main className="App-main">
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/projects" element={<Projects />} />
            <Route path="/contact" element={<Contact />} />
          </Routes>
        </main>

        <footer className="App-footer">
          <p>&copy; {new Date().getFullYear()} Your Name</p>
        </footer>
      </div>
    </Router>
  );
}

export default App;
```

```jsx
// components/Home.jsx
import React from 'react';
import './Home.css'; // Optional styling

function Home() {
  return (
    <div className="Home">
      <h2>About Me</h2>
      <img src="/profile.jpg" alt="Your Profile" className="profile-image"/> {/* Replace with your image and correct path */}
      <p>
        Hello! I'm a passionate web developer with a focus on creating user-friendly and responsive websites.  I have experience in React, JavaScript, HTML, CSS, and various backend technologies.  I'm always eager to learn new things and tackle challenging projects.
      </p>
      <p>
        This portfolio showcases some of my recent work. Feel free to browse through my projects and get in touch if you have any questions or opportunities!
      </p>
    </div>
  );
}

export default Home;
```

```jsx
// components/Projects.jsx
import React from 'react';
import './Projects.css'; // Optional styling

function Projects() {
  const projects = [
    {
      id: 1,
      title: 'E-commerce Website',
      description: 'A fully functional e-commerce website built with React, Redux, and Node.js.',
      imageUrl: '/ecommerce.jpg', // Replace with your image and correct path
      link: 'https://github.com/yourusername/ecommerce-website',
    },
    {
      id: 2,
      title: 'Task Management App',
      description: 'A task management application developed using React and Firebase.',
      imageUrl: '/taskmanager.jpg', // Replace with your image and correct path
      link: 'https://github.com/yourusername/task-management-app',
    },
    {
      id: 3,
      title: 'Personal Blog',
      description: 'A personal blog created with Gatsby and GraphQL.',
      imageUrl: '/blog.jpg', // Replace with your image and correct path
      link: 'https://github.com/yourusername/personal-blog',
    },
  ];

  return (
    <div className="Projects">
      <h2>Projects</h2>
      <div className="project-grid">
        {projects.map((project) => (
          <div key={project.id} className="project-card">
            <img src={project.imageUrl} alt={project.title} />
            <h3>{project.title}</h3>
            <p>{project.description}</p>
            <a href={project.link} target="_blank" rel="noopener noreferrer">
              View Project
            </a>
          </div>
        ))}
      </div>
    </div>
  );
}

export default Projects;
```

```jsx
// components/Contact.jsx
import React, { useState } from 'react';
import './Contact.css'; // Optional styling

function Contact() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');
  const [status, setStatus] = useState('');

  const handleSubmit = async (event) => {
    event.preventDefault();

    //  Simple form validation (client-side)
    if (!name || !email || !message) {
      setStatus("Please fill in all fields.");
      return;
    }

    const formData = {
      name: name,
      email: email,
      message: message,
    };

    try {
      const response = await fetch('/api/contact', { // Replace with your API endpoint
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(formData),
      });

      if (response.ok) {
        setStatus('Message sent successfully!');
        setName('');
        setEmail('');
        setMessage('');
      } else {
        setStatus('Oops! Something went wrong.');
      }
    } catch (error) {
      console.error('Error submitting form:', error);
      setStatus('Oops! Something went wrong.');
    }
  };

  return (
    <div className="Contact">
      <h2>Contact Me</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="name">Name:</label>
          <input
            type="text"
            id="name"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div>
          <label htmlFor="email">Email:</label>
          <input
            type="email"
            id="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <div>
          <label htmlFor="message">Message:</label>
          <textarea
            id="message"
            value={message}
            onChange={(e) => setMessage(e.target.value)}
          />
        </div>
        <button type="submit">Submit</button>
        {status && <p>{status}</p>}
      </form>
    </div>
  );
}

export default Contact;
```

```css
/* App.css */
.App {
  text-align: center;
  font-family: Arial, sans-serif;
}

.App-header {
  background-color: #282c34;
  min-height: 70px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-header nav ul {
  list-style: none;
  padding: 0;
  display: flex;
}

.App-header nav ul li {
  margin: 0 15px;
}

.App-header nav ul li a {
  color: white;
  text-decoration: none;
}

.App-main {
  padding: 20px;
}

.App-footer {
  background-color: #282c34;
  color: white;
  padding: 10px;
  position: fixed; /* Stick to the bottom of the viewport */
  bottom: 0;
  left: 0;
  width: 100%;
}


/* Home.css */
.Home {
  padding: 20px;
}

.profile-image {
  width: 200px;
  border-radius: 50%;
  margin-bottom: 20px;
}

/* Projects.css */
.Projects {
  padding: 20px;
}

.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
}

.project-card {
  border: 1px solid #ccc;
  padding: 15px;
  text-align: left;
}

.project-card img {
  width: 100%;
  height: auto;
  margin-bottom: 10px;
}

.project-card a {
  display: inline-block;
  margin-top: 10px;
  text-decoration: none;
  background-color: #007bff;
  color: white;
  padding: 8px 12px;
  border-radius: 5px;
}

/* Contact.css */
.Contact {
  padding: 20px;
}

.Contact form {
  display: flex;
  flex-direction: column;
  max-width: 500px;
  margin: 0 auto;
}

.Contact form div {
  margin-bottom: 15px;
}

.Contact form label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.Contact form input,
.Contact form textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  font-size: 16px;
}

.Contact form textarea {
  resize: vertical;
  min-height: 150px;
}

.Contact form button {
  background-color: #007bff;
  color: white;
  padding: 12px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
}

.Contact form button:hover {
  background-color: #0056b3;
}

.Contact form p {
  margin-top: 10px;
  font-weight: bold;
}
```

```javascript
// api/contact.js (Example for Next.js or Node.js + Express)  -- NEEDS A BACKEND
// This is a simplified example and does not include robust error handling,
// rate limiting, or proper email sending implementation.  Use with caution in a production environment.

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { name, email, message } = req.body;

    // Simple validation
    if (!name || !email || !message) {
      return res.status(400).json({ message: 'Please fill in all fields.' });
    }

    //  In a real-world application, you would:
    //  1. Validate the data (e.g., email format)
    //  2. Sanitize the data to prevent injection attacks
    //  3. Send the email using a service like SendGrid, Nodemailer, etc.
    //  4. Handle errors gracefully

    try {
      //  Placeholder for sending an email (replace with your actual email sending logic)
      console.log('Sending email:', { name, email, message }); // Simulate sending

      // Simulate a successful email send
      res.status(200).json({ message: 'Message sent successfully!' });

    } catch (error) {
      console.error('Error sending email:', error);
      res.status(500).json({ message: 'Failed to send message.' });
    }
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}
```

Key improvements and explanations:

* **Clear Component Structure:** The code is broken down into reusable components (Home, Projects, Contact) for better organization and maintainability. Each component has a specific purpose.
* **React Router:** Uses `react-router-dom` for navigation between different sections of the portfolio (Home, Projects, Contact).  This creates a single-page application (SPA) experience.
* **Project Display:** The `Projects` component dynamically renders a list of projects from an array, making it easy to add or modify project information.  Includes placeholders for images and links.
* **Contact Form:**  The `Contact` component includes a basic contact form with state management for input fields.  It includes **Client-Side Validation** to ensure the fields aren't blank.  It also has a placeholder for API integration.  Crucially, the `Contact` component includes a `status` variable to give the user feedback on the form submission.
* **CSS Styling:** Includes example CSS files for basic styling.  You'll likely want to customize these.
* **Responsive Design (Implicit):** The CSS uses `grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));` in the `Projects` component to create a responsive grid layout that adjusts to different screen sizes.
* **API Endpoint Example:**  I have provided a commented-out  `api/contact.js` file. **Crucially, you'll need a backend** to handle the form submission.  This example shows how you might handle it with Next.js API routes, but you could use Node.js with Express, or any other backend technology.  The example includes rudimentary validation and error handling.  **Important:** This is a simplified example and needs significantly more work for a production environment (sanitization, robust validation, proper email sending with a service like SendGrid, etc.).
* **Error Handling (Basic):** The contact form includes basic error handling to display a message to the user if something goes wrong.
* **`target="_blank" rel="noopener noreferrer"`:**  This is crucial for security when opening links in a new tab. It prevents the new page from having access to the original page's `window` object.
* **File Structure:** Assumes a standard React project file structure (e.g., `src/components/Home.jsx`, `src/App.jsx`, etc.).
* **Comments:**  I've added comments to explain key parts of the code.
* **Modern React:** Uses functional components and hooks (useState).
* **Accessibility:**  Remember to add proper `alt` attributes to images for accessibility. Use semantic HTML elements where appropriate.
* **`useState` for Form Fields:** The `Contact` component uses `useState` to manage the form field values, making it a controlled component.
* **Placeholder Images:**  The code includes placeholders for images (`/profile.jpg`, `/ecommerce.jpg`, etc.).  You'll need to replace these with your actual images and adjust the paths accordingly.
* **Clear Separation of Concerns:** Each component is responsible for a specific part of the UI and its related logic.
* **ESLint and Prettier:**  It's highly recommended to use ESLint and Prettier to enforce code style and catch potential errors.

To run this:

1.  **Create a new React app:** `npx create-react-app my-portfolio`
2.  **Install React Router:** `npm install react-router-dom`
3.  **Replace the contents** of `src/App.js` with the `App.jsx` code above (you might need to rename `App.js` to `App.jsx`).
4.  **Create the `components` directory** inside `src`.
5.  **Create the `Home.jsx`, `Projects.jsx`, and `Contact.jsx` files** inside `src/components` and paste the corresponding code.
6.  **Create CSS files** (`Home.css`, `Projects.css`, `Contact.css`, and `App.css`) and paste the corresponding code.
7.  **Add your images** to the `public` directory (or adjust the paths in the code).
8.  **(Crucially) Implement the API endpoint** (`/api/contact`) using a backend technology (Node.js, Python, etc.).
9.  **Start the development server:** `npm start`

This will run the portfolio viewer in your browser. Remember to customize the content, styling, and API endpoint to fit your specific needs.  The most important thing to remember is that the contact form needs a working backend API to actually send messages!
👁️ Viewed: 11

Comments