DEV Community

Cover image for Watchlist application using ReactJS, NodeJS
Deepak Jaiswal
Deepak Jaiswal

Posted on

Watchlist application using ReactJS, NodeJS

today we create watchlist application using reactjs, nodejs.

Backend

config.js

{
    "DB_URL": "mongodb+srv://user:password@cluster0.wb0iyao.mongodb.net/?retryWrites=true&w=majority"
}
Enter fullscreen mode Exit fullscreen mode

default.js

module.exports.defaultSymbols=[
    {
        symbol:"SYY",
        bid:"sp",
        ask:"sp",
        bidSize:3,
        askSize:4,
        volume:5,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"KJJ",
        bid:"d",
        ask:"d",
        bidSize:3,
        askSize:4,
        volume:5,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"LLL",
        bid:"ru",
        ask:"ru",
        bidSize:3,
        askSize:4,
        volume:5,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"NHD",
        bid:"nd",
        ask:"nd",
        bidSize:2,
        askSize:6,
        volume:0,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    },
    {
        symbol:"QWA",
        bid:"ts",
        ask:"ts",
        bidSize:7,
        askSize:4,
        volume:1,
        description:"Fixed wrong interpolated variables with node-sass/Hugo"
    }
]
Enter fullscreen mode Exit fullscreen mode

watchlist.controller.js

const WatchListModel = require("../models/WatchList")

module.exports.getWatchLists=async(req,res,next)=>{
    try {
        const watchLists=await WatchListModel.find({});
        res.status(200).send(watchLists);
    } catch (error) {
        next(error)
    }
}

module.exports.addSymbol=async(req,res,next)=>{
    try {
        const {symbol,bid,ask,bidSize,askSize,volume}=req.body;
        const symbolObj={
            symbol,
            bid,
            ask,
            bidSize,
            askSize,
            volume
        }
        const watchList=await WatchListModel.findOneAndUpdate({
            _id: req.params.id
        },{
          $push:{
            symbols:symbolObj
          }
        },{new:true})
        res.status(200).send(watchList);
    } catch (error) {
        next(error)
    }
}

module.exports.updateSymbol=async(req,res,next)=>{
    try {
        const {symbolId,symbol,bid,ask,bidSize,askSize,volume,description}=req.body;
        const symbolObj={
            symbol,
            bid,
            ask,
            bidSize,
            askSize,
            volume,description
        }
        let watchList=await WatchListModel.findOne({
            _id: req.params.id
        })
        const index=watchList?.symbols.findIndex((symbol)=>symbol?._id?.toString()==symbolId);
        watchList.symbols[index]=symbolObj;
        watchList=await WatchListModel.findOneAndUpdate({_id: req.params.id},{$set:{
            symbols:watchList?.symbols
        }},{new:true})
        res.status(200).send(watchList);
    } catch (error) {
        next(error)
    }
}

module.exports.removeSymbol=async(req,res,next)=>{
    try {
        const {symbolId}=req.body
        let watchList=await WatchListModel.findOne({
            _id: req.params.id
        })
        const updatedSymbols=watchList?.symbols.filter((symbol)=>symbol?._id?.toString()!==symbolId);
        watchList=await WatchListModel.findOneAndUpdate({_id: req.params.id},{$set:{
            symbols:updatedSymbols
        }},{new:true})
        res.status(200).send(watchList);
    } catch (error) {
        next(error)
    }
}


Enter fullscreen mode Exit fullscreen mode

createDefaultwatchlist.middleware.js

const WatchListModel = require("../models/WatchList")
const symbols=require('../constants/defaults')

module.exports.createDefaultWatchList=async(req,res,next)=>{
    try {
        let watchList=await WatchListModel.findOne({});
        console.log(watchList)
        if(!watchList){
            watchList=new WatchListModel({symbols:symbols.defaultSymbols});
            watchList=await watchList.save();
            next(watchList)
        }
    } catch (error) {
        next(error)
    }
}
Enter fullscreen mode Exit fullscreen mode

error.middleware.js


module.exports=async(err,req,res)=>{
    try {
        if(err){
            return res.status(500).send({status:500,message:err})
        }
    } catch (error) {
        console.log(error.body);
    }
}
Enter fullscreen mode Exit fullscreen mode

validateObjectId.middleware.js

const mongoose= require("mongoose")

module.exports.validateObjectId=(req,res,next)=>{
    try {
        if(!mongoose.Types.ObjectId.isValid(req.params.id))
            return res.status(400).send({message:"Invalid Id",status:400});
        next(); 
    } catch (error) {
        next(error)
    }
}
Enter fullscreen mode Exit fullscreen mode

watchlist.model.js

const mongoose=require('mongoose');

const {Schema,model}=mongoose;

const watchlistSchema=new Schema({
    symbols:[
        {
            symbol:{
                type:String,
                required: true,
                index:true
            },
            bid:{
                type:String,
                default:''
            },
            ask:{
                type:String,
                default:''
            },
            bidSize:{
                type:Number,
                default:null
            },
            askSize:{
                type:Number,
                default:null
            },
            volume:{
                type:Number,
                default:null
            },
            description:{
                type:String,
                default: ""
            }
        }
    ]
})

const WatchListModel=model('watchlist',watchlistSchema);

module.exports=WatchListModel;
Enter fullscreen mode Exit fullscreen mode

watchlist.route.js

const express=require('express');
const { createDefaultWatchList } = require("../middlewares/createDefaultWatchList.middleware");
const WatchListModel = require("../models/WatchList.model");
const router=express.Router();
const symbols=require('../constants/defaults');
const { addSymbol, getWatchLists, removeSymbol, updateSymbol } = require("../controllers/watchlist.controller");
const { validateObjectId } = require('../middlewares/validateObjectId.middleware');

router.get('',getWatchLists);

router.post('/create-default',createDefaultWatchList);

router.post('/add-symbol/:id',validateObjectId,addSymbol);

router.patch('/update-symbol/:id',validateObjectId,updateSymbol);

router.patch('/remove-symbol/:id',validateObjectId,removeSymbol);

module.exports=router;


(async ()=>{
    let watchList=await WatchListModel.findOne({});
    if(!watchList){
        watchList=new WatchListModel({symbols:symbols.defaultSymbols});
        watchList=await watchList.save();
        console.log('default watch list created')
    }
})()
Enter fullscreen mode Exit fullscreen mode

connection.js

const mongoose=require('mongoose');
const config=require('config');

const DB_URL=config.get('DB_URL');

module.exports=mongoose.connect(DB_URL).then((conn)=>{
    console.log(`Database connected on mongo server ${DB_URL}`);   
}).catch((err)=>{
    console.log(`Database Error ->:  ${err}`);  
})
Enter fullscreen mode Exit fullscreen mode

app.js

const express=require('express');
const cors=require('cors');

require('./services/connection');
const errorMiddleware=require('./middlewares/error.middleware');
const watchListRouter=require('./routes/watchlist.route')

const app=express();
const PORT=process.env.PORT|| 8000;
//middlewares
app.use(express.json());
app.use(express.urlencoded({extended:false}));
app.use(cors());
//routes of app
app.use('/api/v1/watchlist',watchListRouter);
//global error middleware 

app.use(errorMiddleware)

app.listen(PORT,()=>{
    console.log(`server is running on the port ${PORT}`)
})

Enter fullscreen mode Exit fullscreen mode

package.json

{
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "keywords": [],
  "author": "Sandeep Jaiswal",
  "license": "MIT",
  "dependencies": {
    "config": "^3.3.9",
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "mongoose": "^7.0.3"
  }
}

Enter fullscreen mode Exit fullscreen mode

Frontend

Header.js

import React from 'react'

const Header = () => {
    return (
        <div className="container-fluid bg-primary">
            <nav className="navbar navbar-expand-lg bg-body-tertiary">
                <div className="container-fluid">
                    <a className="navbar-brand text-white fw-bold" href="#">Watchlist</a>
                </div>
            </nav>
        </div>
    )
}

export default Header
Enter fullscreen mode Exit fullscreen mode

UpdateWatch.js

import React, { useEffect, useState } from 'react'

const UpdateWatch = ({ data, updateSymbol }) => {
  const [form, setForm] = useState({
    symbolId: '', symbol: '', bid: '', ask: '', bidSize: null, askSize: null, volume: null, description: ''
  })
  useEffect(() => {
    setForm({ symbolId: data?._id, symbol: data?.symbol,
       bid: data?.bid, ask: data?.ask, bidSize: data?.bidSize||0,
        askSize: data?.askSize||0 ,volume:data?.volume||0,description:data?.description});
  }, [data?._id])

  const change = (event) => {
    const { name, value } = event.target;
    setForm({ ...form, [name]: value })
  }

  const submit = () => {
    updateSymbol(form);
    setForm({
      symbolId: '', symbol: '', bid: '', ask: '', bidSize: 0, askSize: 0, volume: 0, description: ''
    })
  }
  return (
    <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h1 class="modal-title fs-5" id="exampleModalLabel">Update Symbol</h1>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            <form>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Symbol:</label>
                <input type="text" name="symbol" value={form.symbol} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Bid:</label>
                <input type="text" name="bid" value={form.bid} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">ask:</label>
                <input type="text" name="ask" value={form.ask} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Bid Size:</label>
                <input type="number" name="bidSize" value={form.bidSize} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Ask Size:</label>
                <input type="number" name="askSize" value={form.askSize} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Volume:</label>
                <input type="number" name="volume" value={form.volume} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
              <div class="mb-3">
                <label for="recipient-name" class="col-form-label">Description:</label>
                <input type="text" name="description" value={form.description} onChange={(e) => change(e)} class="form-control" id="recipient-name" />
              </div>
            </form>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary" data-bs-dismiss="modal" onClick={() => submit()}> Update</button>
          </div>
        </div>
      </div>
    </div>
  )
}

export default UpdateWatch
Enter fullscreen mode Exit fullscreen mode

watchlist.js

import React, { useContext, useState } from 'react'
import WatchListContext from "../contexts/WatchListContext";
import { addSymbols, removeSymbols, updateSymbols } from "../services/watchList.service";
import UpdateWatch from "./common/UpdateWatch";

const WatchList = () => {
    const [watchlist, setWatchlist] = useContext(WatchListContext);
    const [updateObject,setUpdateObject]=useState({});
    const [selectedSymbol,setSelectedSymbol]=useState([])
    const addSymbol = async (event) => {
        if (event.keyCode === 13) {
            const res = await addSymbols(watchlist?._id, { symbol: event.target.value });
            setWatchlist(res.data);
            event.target.value = '';
        }
    }

    const removeSymbol = async (symbolId) => {
            const res = await removeSymbols(watchlist?._id, { symbolId });
            setWatchlist(res.data);
    }

    const updateSymbol = async (data) => {
        const res = await updateSymbols(watchlist?._id, data)
        setWatchlist(res.data);
    }
    const toggleSelect=(symbol)=>{
        const check=selectedSymbol.find(s=>s?._id===symbol?._id)
        if(check){
            let s=selectedSymbol.filter(sym=>sym?._id!==symbol?._id)
            setSelectedSymbol(s);
        }else{
            let s=[...selectedSymbol,symbol]
            setSelectedSymbol(s);
        }
    }
    return (
        <div>
            <div className="p-4">
                <div className="row">
                    <div className="col-3 fs-2">
                        Quote Details
                    </div>
                    <div className="col-9">
                        {selectedSymbol?.length ? 
                            selectedSymbol?.map((s)=>(
                                <div key={s?._id}>
                                    <h5>{s?.symbol}</h5>
                                    <p>{s?.description}</p>
                                </div>
                            )
                        ):"No Any row selected"}
                    </div>
                </div>
                <div className="border">
                    <div className="bg-light p-2 fw-bold">My Watchlist</div>
                    <table className="table table-bordered">
                        <thead>
                            <tr>
                                <th scope="col"></th>
                                <th scope="col">Bid</th>
                                <th scope="col">Ask</th>
                                <th scope="col">Bid Size</th>
                                <th scope="col">Ask Size</th>
                                <th scope="col">Volume</th>
                                <th scope="col">Action</th>
                            </tr>
                        </thead>
                        <tbody>
                        {watchlist?.symbols.map((symbol) => (
                        <tr key={symbol?._id} onClick={()=>toggleSelect(symbol)} className={selectedSymbol?.find(s=>s?._id===symbol?._id)?'table-success':''}>
                        <th scope="row">{symbol?.symbol}</th>
                        <td>{symbol?.bid}</td>
                        <td>{symbol?.ask}</td>
                        <td>{symbol?.bidSize}</td>
                        <td>{symbol?.askSize}</td>
                        <td>{symbol?.volume}</td>
                        <td>
                            <div className="d-flex justify-content-around">
                            <button className="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@mdo" onClick={()=>setUpdateObject(symbol)}>Edit</button>
                            <button className="btn btn-danger" onClick={()=>removeSymbol(symbol?._id)}>Delete</button>
                            </div></td>
                    </tr>    
                    ))}

                        </tbody>
                    </table>
                    <div className="bg-light p-1 w-100" style={{ height: 40 }}>
                        Add Symbol <input className="ms-2 rounded" placeholder="" onKeyUp={(e) => addSymbol(e)} />
                    </div>
                </div>
                <UpdateWatch data={updateObject} updateSymbol={updateSymbol}/>
            </div>
        </div>
    )
}

export default WatchList
Enter fullscreen mode Exit fullscreen mode

watchlist.service.js

import { toast } from "react-toastify";

const { http, URL } = require("./http.service")


export const getWatchList = async () => {
    try {
        const res = await http.get(URL + '/watchlist');
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const addSymbols = async (watchListId,payload) => {
    try {
        const res = await http.post(URL + '/watchlist/add-symbol/'+watchListId, payload);
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const removeSymbols = async (watchListId, payload) => {
    try {
        const res = await http.patch(URL +'/watchlist/remove-symbol/' + watchListId, payload);
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const updateSymbols = async (watchListId, payload) => {
    try {
        const res = await http.patch(URL +'/watchlist/update-symbol/' + watchListId, payload);
        return res;
    } catch (error) {
        handleError(error);
    }
}

export const isLogged = () => {
    const token = localStorage.getItem('token');
    return token ? true : false;
}

export const logoutUser = () => {
    localStorage.removeItem('token');
}

export const getToken = () => {
    return localStorage.getItem('token');
}

export const handleError = (error) => {
    toast.error(error?.message)
}

export const setCredentials=(token)=>{
    http.defaults.headers.common['x-auth-token'] = token;
}
Enter fullscreen mode Exit fullscreen mode

App.js

import { useEffect, useState } from "react";
import './App.css';
import WatchList from "./components/WatchList";
import WatchListContext from "./contexts/WatchListContext";
import { getWatchList } from "./services/watchList.service";
import Header from "./components/common/Header";
import Loader from "./components/common/Loader";

function App() {
  const [watchlist,setWatchlist]=useState();
  const [isLoading,setIsLoading]=useState(false);

  useEffect(()=>{
    getWatchListData()
  },[]);

  const getWatchListData=async ()=>{
    setIsLoading(true)
    const result=await getWatchList();
    if(result.status ===200 ){
      setWatchlist(result?.data?.[0])
      setIsLoading(false)
    }else{
      setIsLoading(false)
    }
  }

  return (
    <WatchListContext.Provider value={[watchlist,setWatchlist]}>
         <Header />
         {isLoading?<Loader />:<WatchList />}
    </WatchListContext.Provider>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

this is not of the complete code if you have any doubt regarding this comment in this post. I give the answer as soon as possible. thank you for reading my post.

Top comments (0)