We will now build our final page, the Project page. This will showcase all of your personal work. In this example, our projects will be software that we’ve built. Feel free to update the properties to match your personal work (artwork, photography, etc). If you don’t want this page, you can skip this step.

  1. In your Models folder, add Project.cs and the following code. As mentioned previously, update your namespace.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PersonalResume.Models
{
    public class Project
    {
        public int Id { get; set; }
        public String Title { get; set; }
        public String Description { get; set; }
        public String Stack { get; set; }
        public String Image { get; set; }
        public bool HasWebLink { get; set; }
        public String WebUrl { get; set; }
        public bool HasGithubLink { get; set; }
        public String GithubUrl { get; set; }
        public bool HasDownloadLink { get; set; }
        public String DownloadUrl { get; set; }
    }
}

2. In your images folder, add another folder called projects and put your project image in there. This could be an image of your website, artwork, etc.

3. In your Data folder, add projects.json and fill out the properties with your project information. If your project has a URL, keep HasWebLink as true and provide a WebUrl. The same can be done for HasGithubLink and HasDownloadLink.

[
  {
    "Id": 1,
    "Title": "Personal Porfolio",
    "Image": "test.png",
    "Description": "A template to build your own personal portfolio website. Built in .NET Core and React.js",
    "HasWebLink": true,
    "WebUrl": "https://personal-resume.azurewebsites.net/",
    "HasGithubLink": true,
    "GithubUrl": "gkoutr/PersonalResume",
    "HasDownloadLink": false,
    "DownloadUrl": ""
  }
]

4. In the Controllers folder, add ProjectController with the following code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using PersonalResume.Models;

namespace PersonalResume.Controllers
{
    [Route("api/projects")]
    [ApiController]
    public class ProjectController : ControllerBase
    {
        public IEnumerable<Project> GetProjects()
        {
            string jsonText = System.IO.File.ReadAllText("./Data/projects.json");
            return JsonConvert.DeserializeObject<IEnumerable<Project>>(jsonText);
        }
    }
}

5. Add the Projects.js component in the components folder.

import React, { Component } from 'react';
import {
    Row, CardImg, CardTitle, CardSubtitle, CardText, Button
} from 'reactstrap';
import Project from './subcomponents/Project';

export class Projects extends Component {
    displayName = Projects.name;

    constructor(props) {
        super(props);
        this.state = {
            projects: [],
            isLoading: true
        }
    }

    componentDidMount() {
        fetch('api/projects')
            .then(response => response.json())
            .then(data => {
                this.setState({ projects: data, isLoading: false })
            })
    }


    render() {
        if (this.state.isLoading) {
            return (<div className="spinner-border image-center" style={{ width: '5rem', height: '5rem' }}> </div>);
        }
        else {
            return (
                <div>
                    <div>
                        <h1 className="text-center">Projects</h1>
                        <hr></hr>
                        <Row>
                            {this.state.projects.map(project =>
                                <Project key={project.id} project={project} />
                            )}
                        </Row>
                    </div>
                </div>
            );
        }
    }
}; 

6. In the subcomponents folder, add Project.js and Project.css. The Project component contains functions that checks the HasWebLink, HasGithubLink and HasDownloadLink properties and will print out a fontawesome icon if true.

import React, { Component } from 'react';
import {
    Row, Col, Card, CardImg, CardText, CardBody,
    CardTitle, CardSubtitle, Button
} from 'reactstrap';
import './Project.css';

function Project(props) {

    function getProjectLink(project) {
        if (project.hasWebLink) {
            return <a className="project-links" href={project.webUrl} target="_blank">
                <i className="fas fa-external-link-alt fa-lg"></i>
            </a>
        }
        if (project.hasDownloadLink) {
            return <a className="project-links" target="_blank">
                <i className=" fas fa-download fa-lg"></i>
            </a>
        }
    }

    function getGithubLink(project) {
        if (project.hasGithubLink) {
            return <a className="project-links" href={"https://github.com/" + project.githubUrl} title="view source code" alt="view source code" target="_blank">
                <i className="fab fa-github fa-lg"></i>
            </a>
        }
    }

    const project = props.project;
    return (
        <Col md={4}>
            <Card className="project-card">
                <CardImg top width="100%" className="project-image" src={require('../../images/projects/' + project.image)} alt="Card image cap" />
                <CardBody>
                    <CardTitle>{project.title}</CardTitle>
                    <CardText>{project.description}</CardText>
                    {getGithubLink(project)}
                    {getProjectLink(project)}
                </CardBody>
            </Card>
        </Col>
    );
}

export default Project;

.project-links {
    padding-right: 7px;
}

.project-image {
    padding: 5px;
}

7. Update the following line in NavMenu.js to add Projects to the navbar.

import React, { Component } from 'react';
import { Collapse, Container, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
import './NavMenu.css';
import { Jobs } from './Jobs';

export class NavMenu extends Component {
  static displayName = NavMenu.name;

  constructor (props) {
    super(props);

    this.toggleNavbar = this.toggleNavbar.bind(this);
    this.state = {
      collapsed: true
    };
  }

  toggleNavbar () {
    this.setState({
      collapsed: !this.state.collapsed
    });
  }

  render () {
    return (
      <header>
        <Navbar className="navbar-expand-sm navbar-toggleable-sm ng-white border-bottom box-shadow mb-3" light>
          <Container>
            <NavbarBrand tag={Link} to="/">PersonalResume</NavbarBrand>
            <NavbarToggler onClick={this.toggleNavbar} className="mr-2" />
            <Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={!this.state.collapsed} navbar>
              <ul className="navbar-nav flex-grow">
                <NavItem>
                  <NavLink tag={Link} className="text-dark" to="/jobs">Jobs</NavLink>
                </NavItem>
                <NavItem>
                  <NavLink tag={Link} className="text-dark" to="/education">Education</NavLink>
                </NavItem>
                <NavItem>
                  <NavLink tag={Link} className="text-dark" to="/projects">Projects</NavLink>
                </NavItem>
              </ul>
            </Collapse>
          </Container>
        </Navbar>
      </header>
    );
  }
}

8. Update App.js to tell the application to load the Projects component at /projects.

import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Jobs } from './components/Jobs';
import { Education } from './components/Education';
import { Projects } from './components/Projects';

import './custom.css'

export default class App extends Component {
  static displayName = App.name;

  render () {
    return (
      <Layout>
        <Route exact path='/' component={Jobs} />
        <Route path='/jobs' component={Jobs} />
        <Route path='/education' component={Education} />
        <Route path='/projects' component={Projects} />
      </Layout>
    );
  }
}

We have now added all of our components. The last few steps are to cleanup our application by removing old files and adding icons. Then we will deploy our app and be done!