SOURCE CODE : https://github.com/CyrilCartoux/kickstarter_solidity_nextJs

npm install next react react-dom
npm install --save semantic-ui-react
npm install --save semantic-ui-css
npm install next-routes (--legacy-peer-deps if needed)

Create a folder /pages

On ajoute dans package.json

"scripts": {
    "test": "mocha",
    "dev": "next dev"
  },

On se rend ensuite sur localhost:3000 pour voir notre app

On creer un fichier web3.js a la racine de notre projet

import Web3 from "web3";
 
let web3;
 
if (typeof window !== "undefined" && typeof window.ethereum !== "undefined") {
  // We are in the browser and metamask is running.
  window.ethereum.request({ method: "eth_requestAccounts" });
  web3 = new Web3(window.ethereum);
  
} else {
  // We are on the server *OR* the user is not running metamask
  const provider = new Web3.providers.HttpProvider(
    "<https://rinkeby.infura.io/v3/b015e80a4e4f408bb6e01c005263de11>"
  );
  web3 = new Web3(provider);
}
 
export default web3;

On creer factory.js pour interagir avec le campaignfactory

import web3 from "./web3";
import CampaignFactory from './build/CampaignFactory.json';

// create contract instance 
const instance = new web3.eth.Contract(
    // abi / interface
    JSON.parse(CampaignFactory.interface),
    // address
    '0x4BFF4AF6bbA80A9c4a8e0dd7702434F0b1BA9f96'
);

export default instance;

On va creer campaign.js pour interagir avec une campagne en particulier

On lui passera en parametre une addresse

import web3 from "./web3";
import Campaign from './build/Campaign.json';

// create contract instance 

export default address => {
    return new web3.eth.Contract(
        // abi / interface
        JSON.parse(Campaign.interface),
        address
    )
};

On va creer notre composant liste de campaigns

import React,{ Component }  from "react";
import { Card, Button } from "semantic-ui-react";
import factory from '../ethereum/factory';
import Layout from "../components/Layout";

class CampaignIndex extends Component {

    static async getInitialProps() {
        // fetch campaigns
        const campaigns = await factory.methods.getDeployedCampaigns().call();
        // returns props object
        return {
            campaigns
        }
    }

    renderCampaigns() {
        const items = this.props.campaigns.map(address => {
            return {
                header: address,
                description: <a>View campaign</a>,
                fluid: true
            }
        })

        return <Card.Group items={items} />;
    }

    render() {
        return (
            <Layout>
                <div>
                    <h3>Open campaigns</h3>
                    <Button floated="right" content="Create Campaign" icon="add circle" primary />
                    {this.renderCampaigns()}
                </div>
            </Layout>
        );
    }
}

export default CampaignIndex;

Composant détail d’une Campaign

import React, { Component } from "react";
import { Card, Grid, Button } from "semantic-ui-react";
import Layout from "../../components/Layout";
import Campaign from "../../ethereum/campaign";
import web3 from "../../ethereum/web3";
import ContributeForm from "../../components/ContributeForm";
import { Link } from "../../routes";

class CampaignShow extends Component {
  static async getInitialProps(props) {
    const campaign = Campaign(props.query.address);

    const summary = await campaign.methods.getSummary().call();

    return {
      address: props.query.address,
      minimumContribution: summary[0],
      balance: summary[1],
      requestsCount: summary[2],
      approversCount: summary[3],
      manager: summary[4],
    };
  }

  renderCards() {
    const {
      balance,
      manager,
      minimumContribution,
      requestsCount,
      approversCount,
    } = this.props;

    const items = [
      {
        header: manager,
        meta: "Address of Manager",
        description:
          "The manager created this campaign and can create requests to withdraw money",
        style: { overflowWrap: "break-word" },
      },
      {
        header: (minimumContribution/1e18),
        meta: "Minimum Contribution (ether)",
        description:
          "You must contribute at least this much wei to become an approver",
      },
      {
        header: requestsCount,
        meta: "Number of Requests",
        description:
          "A request tries to withdraw money from the contract. Requests must be approved by approvers",
      },
      {
        header: approversCount,
        meta: "Number of Approvers",
        description:
          "Number of people who have already donated to this campaign",
      },
      {
        header: web3.utils.fromWei(balance, "ether"),
        meta: "Campaign Balance (ether)",
        description:
          "The balance is how much money this campaign has left to spend.",
      },
    ];

    return <Card.Group items={items} />;
  }

  render() {
    return (
      <Layout>
        <h3>Campaign Show</h3>
        <Grid>
          <Grid.Row>
            <Grid.Column width={10}>{this.renderCards()}</Grid.Column>
            <Grid.Column width={6}>
              <ContributeForm address={this.props.address} />
            </Grid.Column>
          </Grid.Row>

          <Grid.Row>
            <Grid.Column>
              <Link route={`/campaigns/${this.props.address}/requests`}>
                <a>
                  <Button primary>View Requests</Button>
                </a>
              </Link>
            </Grid.Column>
          </Grid.Row>
        </Grid>
      </Layout>
    );
  }
}

export default CampaignShow;