# CRM Clone Final Code https://github.com/kubowania/monday-crm-clone https://raw.githubusercontent.com/RodrigoMvs123/CRM/main/README.md https://github.com/RodrigoMvs123/CRM/blame/main/README.md R: Microsoft Original Documentation https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.tooling.connector.crmserviceclient.clone?view=dataverse-sdk-latest https://docs.microsoft.com/en-us/power-apps/developer/data-platform/xrm-tooling/use-crmserviceclient-constructors-connect https://docs.microsoft.com/en-us/power-apps/developer/data-platform/xrm-tooling/sample-tpl-crmserviceclient 🛑 Build a Monday CRM Clone with GET POST PUT DELETE Requests | React + NodeJS + useContext Hook To Build a CRM Clone with “GET”, “POST”, “PUT”, “DELETE”, Requests | React + NodeJS + useContext, Hook. Final Code: https://github.com/kubowania/monday-crm-clone https://auth.cloud.datastax.com/auth/realms/CloudUsers/protocol/openid-connect/registrations?client_id=auth-proxy&response_type=code&scope=openid+profile+email&redirect_uri=https://astra.datastax.com/welcome&utm_source=youtube&utm_medium=referral&utm_campaign=ania-kubow&utm_term=frontend-devplay&utm_content=monday-crm-clone To Add New Project React Package.json [ Script ] 1 - // To Delete Index JS File JS File To Delete Line 01 // Logo Function Const To Delete To Delete // Header To Delete Index.Css File 2- // Context.js Project Clone SRC // Source Directory New / Directory JavaScript File context.js import {createContext} from “react” const CategoryContexts = createContext ({ categories=null, setCategories: () => {] }) export default CategoriesDefault New JavaScript File 3 - // AvatarDisplay Component Import blankAvatar from '../ images / blank-profile-avatar.png ' const AvatarDisplay = ( { ticket } ) = > { return ( <div> className = "avatar-container" <div className = "image-conteiner"> <image src = { ticket.avatar ? ticket.avatar: blankaAvatar } alt = { ' photo of ' + ticket owner } /> </div> </div> ) } export default AvatarDisplay // - Image Directory Components New New JavaScript File 4 - // DeleteBlock Component import axios from ‘axios’ const DeleteBlock = (documentId ) = > { const response await axios.delete(‘http://localhost:8000/tickets/${documentId}/’) const success = response.status == 200 if (success) window.location.reload() const deleteticket = async () => { } return ( <div className =”delete-block” > <div> className”delete-icon” onClick {deleteticket} </div> </div> ) } export default DeleteBlock Components New New JavaScript File Components New New JavaScript File 5 - // Nav Component import logo from ‘../import/crm-logo-png’ import { useNavigate } from ‘react-router-dom const = Nav () => { const navigate = useNavigate () return ( <nav> <div classname = “logo-conteiner”> <div calssName = “controls-conteiner”> <div className= “ icon ”onClick = { () => navigate (‘/ticket’ )} >+</div> <div className= “ icon ”onClick = { () => navigate (‘/ticket’ )} > << </div> <div className= ” icon ”></div> </div> <img src ( logo ) alt = “logo”/> </nav> ) } export default Nav.js 6 - // PriorityDisplay Component const PriorityDisplay = ( { priority } ) = > { return ( <div className=”priority-display”> <div className=”star-container” > <h3 style={{ color: priority } }> =1 ? ‘rgb(253, 253, 150)’ : ‘ ‘ } } >☆<h3/> <h3 style={{ color: priority } }> =2 ? ‘rgb(253, 253, 150)’ : ‘ ‘ } } >☆<h3/> <h3 style={{ color: priority } }> =3 ? ‘rgb(253, 253, 150)’ : ‘ ‘ } } >☆<h3/> <h3 style={{ color: priority } }> =4 ? ‘rgb(253, 253, 150)’ : ‘ ‘ } } >☆<h3/> <h3 style={{ color: priority } }> =5 ? ‘rgb(253, 253, 150)’ : ‘ ‘ } } >☆<h3/> </div> </div> ) } export default PriorityDisplay Components New New JavaScript File 7 - // ProgressDisplay Component const ProgressDisplay = ( { progress } ) = > { return ( <div className=”progress-display”> <div>className=”progress-bar”</div> <div> style={{ witdh:progress + ‘%’ }} className=”progressindicator” </div> </div> </div> ) } export default ProgressDisplay Components New New JavaScript File 8 - // StatusDisplay Component const StatusDisplay = ( { status } ) => { const getColor = ( status ) = > let color switch = ( status ) { case ‘done’: color: ‘rgb ( 186, 255, 201 )’ break case ‘working on it’: color = color ‘ rgb ( 255, 223, 186 )’ break case: ‘stuck’ : color = ‘ rgb ( 255, 179, 186 )’ break default: color: ‘ rgb ( 186, 255, 255 )’ } return color } return ( <div>className=”status-display” style= {{ backgroundcolor: getColor(status)}} { status } <div/> ) } Components New New JavaScript File 9 - // TicketCardjs. Component import { Link } from ‘react-from-dom’ import AvatarDisplay from “/.AvatarDisplay” import StatusDisplay from “/.StatusDisplay” import PriorityDisplay from “/.PriorityDisplay” import ProgressDisplay from “/.ProgressDisplay” import DeleteBlock from “/.DeleteBlock” const TicketCard = ( { color, filteredTicket } ) = > { return ( <div className = “ticket-card”> <link to={'/ticket/${ticket.documentId' } } id = " link " > <div> className= “ ticket color ” style= {{backgroundColor: color}}> </div> <h3>filteredTicket.title</h3> <AvatarDisplay ticket={ticket}/> <StatusDisplay status:{ticket.status}/> <PriorityDisplay priority={ticket.priority}/> <ProgressDisplay priority=”ticket.progress”/> <link/> <DeleteBlock documentId = {ticket documentId}/> </div> ) } export default TicketCard.js Pages New New JavaScript File 10 - // Dashboard Component const Dashboard = ( ) { return ( <div>Dashboard</div> ) } export default Dashboard Pages New New JavaScript File 11 - // TicketPage Component import { use State, useContex, useEffect } from ‘react’ import {useNavigate, useParams} from ‘react-router ’ import axios from ‘axios’ import CategoriesContex from ‘../context’ const TicketPage = ({editMode} ) = > { const {fromData, setFormData = useState ( status: ‘not started’ , progress: 0; category: categories[0], timestamp: new: new Date (). toISOString () ) } const {categories, setCategories} = useContex (CategoriesContex) const navigate = useNavigate () let { id } = useParams () const handleSubmit = sync (e) = > { e.preventDefault () if (editMode) { const response = await axios.put(‘http://localhost:8000/tickets/${id}’ { data:formData }) const success = response.status === 200 if (success) navigate {‘/’} } } if ( !editmode ) { const response = await axios.post (‘https://localhost:8000/tickets’, { formData }) const success = response.status === 200 if (success) navigate {‘/’} } } } const fetchData = () => async { const response = await axios.get(‘http://localhost:8000/tickets/${id}’) setFormData(response.data.data) } use effect (()) => { if (editMode) fetchData() }, []) const handleChange = () = > { const value = e.target.value const name = e.target.name setFormData ((prevState) = > ({ …prevState, [name]: value }) ) } { editMode && <> <input type = “ range ” id = “progress” name = “progress” value= {formData.progress} min=”0” max=”100” onChange={handleChange} /> <label htmlFor=”progress” >Progress</label> <label>Status</label> <select name=”status” value={formData.status} onChange={habdleChange} > <option selected = {formData.status === ‘done’} value=’done’>Done</option> <option selected = {formData.status === ‘working on it’} value=’working on it’>Working on it</option> <option selected = {formData.status === ‘stuck’} value=’stuck’>Stuck</option> <option selected = {formData.status === ‘not started’} value=’not started’>Not Started</option> </select> </> } <input type=”submit”/> </section> <section> <label htmlFor =”owner”=Owner></label> <input id=”owner” name=”owner” type=”text” onChange={handleChange} required={true} value={form.Data.owner} /> <label htmlFor =”avatar”=Avatar></label> <input id=”avatar” name=”avatar” type=”url” onChange={handleChange} required={true} value={form.Data.avatar} /> <div class name=”img-preview”> {formData.avatar && ) <img src= {form.Data.avatar} alt= “img-preview”/> )} </div> </section> </form> </div> </div> ) } console.log (formData) return ( <div className “ticket”> <h1> { editMode ? ‘ Update your Ticket’ : Create a Ticket } </h1> </div> <div className=”ticket-conteiner”> <form onSubmit={handleSubmit}> <section> <label htmlFor=”title”>Title</label> <input id=”title” name=”title” type=”text” onChange={handleChange} required={true} value={form.Data.title} /> <label htmlFor=”description”>Description</label> <input id=”description” name=”description” type=”text” onChange={handleChange} required={true} value={form.Data.description} /> <label>Category</label> <select name=”category” value = {formData.category I I ‘New Category’} onChange=hendlecategory > categories? .map ( category, _index) => ( <option key={_index} value={category}></option> ))} </select> <label htmlFor=”new-category”>New Category</label> <input id=”new-category” name=”category” type=”text” onChange={handleChange} value={form.Data.category} /> <label>Priority</label> <label htmlFor=”priority-1”>1</label> <div className=”multiple-input-container”> <input id”priority-1” name=”priority” type=”radio” onChange= {handleChange} value={1} checked={formData.priority ===1 } /> label htmlFor=”priority-1”>1</label> <label>Priority</label> <label htmlFor=”priority-1”>1</label> <div className=”multiple-input-container”> <input id”priority-2” name=”priority” type=”radio” onChange= {handleChange} value={2} checked={formData.priority ===2 } /> <label>Priority</label> <label htmlFor=”priority-2”>2</label> <div className=”multiple-input-container”> <input id”priority-3” name=”priority” type=”radio” onChange= {handleChange} value={1} checked={formData.priority === } /> <label>Priority</label> <label htmlFor=”priority-3”>3</label> <div className=”multiple-input-container”> <input id”priority-4” name=”priority” type=”radio” onChange= {handleChange} value={4} checked={formData.priority ===4 } /> <label>Priority</label> <label htmlFor=”priority-4”>4</label> <div className=”multiple-input-container”> <input id”priority-5” name=”priority” type=”radio” onChange= {handleChange} value={5} checked={formData.priority ===5 } /> <label htmlFor=”priority-5”>5</label> </div> export default TicketPage Pages New New JavaScript File 12 - // Appjs. Terminal npm i react - router - dom // App.js import { BrowserRouter, Route, Routes } from ‘ react - router - dom ’ // package.json const Class Name = ( ) { return ( <div>App</div> ) } export default App // SoftWare Version package.json “ react - router - dom ”: “ 6.2.2 ”, “ react - router - dom ”: “ 6.2.1 ”, Terminal To Change SoftWare Version npm i 13 - //App.js import { BrowserRouter, Route, Routes } from ‘ react - router - dom ’ // package.json import { useState } from ‘react’ import Nav from ‘ ./ components / Nav ’ import Dashboard from ‘ ./ pages/Dashboard ’ import TicketPage from ‘ ./ pages/TicketPage ’ import CategoriesContext from ‘../context’ const app => ( ) { const [categories, setCategories] = useState(null) const value = { categories, setCategories } return ( < div class Name = “ app ” > <CategoriesContext,Provider value = {value}> <BrowserRouter> <Nav/> <Routes> <Route path = ‘ / ’ element = {<Dashboard>}/> <Route path = ‘ / ticket ’ element = {<TicketPage>}/> <Route path = ‘ / ticket / : id ’ element = {<TicketPage editMode = {[ True ]/}>}/> </Routes> </BrowserRouter> </CategoriesContext,Provider> </div> ) } export default App } 14 - // Dashboard.JS Categories import {useState, useEffect, useContex } from ‘react’ import Ticket Card from ‘ ../ components/TicketCard ’ import axios from ‘axios’ import CategoriesContext from ‘../context’ const Dashboard = ( ) const {tickets, setTickets } = useState ( null ) const { categories, setCategories } = useContex (categoriesContex) useEffect (async () = > { const response = await axios.get(‘http://localhosto:8000/tickets’) const dataObject response.data.data const arreysOfkeys = object.keys(dataObject) const arreyOfdata = object.keys(dataObject) map(key)=>dataObject[key]) const formattedArray = [] arreyOfKeys.forEach(key, index) => { const formattedData = {... arrayOfData [index] } formattedData ‘documentId’ = key formattedArray.push (formattedData) }) setTickets(formattedArray) }, []) useEffect (() => ) { setCategories ([...new Set (tickets?.map ({category}) =>cayegory)]) }, [tickets] ) console.log(categories) const tickets = [ { category: ‘ Q1 2022 ’, color: ‘red’, title: ‘ NFT Safety 101 Video’ owner: ‘ Rodrigo ’ avatar: ‘ 12HR+ YouTube Coding Bootcamp 2021! ’, status: ‘ done ’, priority: ‘ 5 ’, progress: ‘ 40 ’, description: ‘ Make a video showcase how to work with NFTs safety, including how to know if one is not genuine ’, timestamp: ‘ 2022 - 02 - 11T07:36:17+0000 ’ }, { category: ‘ Q1 2022 ’, color: ‘red’, title: ‘ Bild and Sell AI Model’ owner: ‘ Rodrigo ’ avatar: ‘ 12HR+ YouTube Coding Bootcamp 2021! ’, status: ‘ working on it ’, priority: ‘ 2 ’, progress: ‘ 70 ’, description: ‘ Make a video about AI ’, timestamp: ‘ 2022 - 02 - 13T07:36:17+0000 ’ category: ‘ Q2 2022 ’, color: ‘blue’, title: ‘ Biuld a bot’ owner: ‘ Rodrigo ’ avatar: ‘ 12HR+ YouTube Coding Bootcamp 2021! ’, status: ‘ done ’, priority: ‘ 3 ’, progress: ‘ 10 ’, description: ‘ Make a video showcase how to work with NFTs safety, including how to know if one is not genuine ’, timestamp: ‘ 2022 - 02 - 11T07:36:17+0000 ’ } ] const colors = [ ‘rgb(255,179,186)’, ‘rgb(255,233,186)’, ‘rgb’(255,255,186), ‘rgb(186,255,201)’, ‘rgb(186,255,255)’, ] const uniqueCategories = [ ...new Set(tickects?.map(({category}) => category))) ] return ( <div className = “ dashboard ”> <h1>My Projects</h1> <div className= “ ticket-container ” > {tickets && uniqueCategories?.map({uniqueCategory, categoryIndex) => ( <div key={categoryIndex}> <h3>uniqueCategory</h3> {tickets.filter(ticket => ticket.category === uniqueCategory ) .map ( filteredTicket, _index) => ( <TicketCard id= {_index} color={colors[categoryindex] || colors[0]} ticket={filteredTickect} /> )) } <div/> ) ) } </div> export default Dashboard // inspect Pages New New JavaScript File 15 - // Index.css html, body { margin: 0; padding: 0; font - family: ‘ Trebuchet MS ’, Arial, sans - serif; } .app { display: flex; } # link{ text-decoration: none; color: rgb (46, 46, 46); } /*----- Nav —--*/ nav { height: 100vh; background-color: rgb ( 43, 43, 63 ); color: rgb ( 255, 255, 255 ) ; text- align: center; display: flex; flex-direction: column; justify-content: space-between; } nav .logo-conteiner, nav controls-conteiner { padding: 40px 25px; display: flex; flex-direction: column; } nav .logo-conteiner, width 20px; } nav .icon { color: rgb ( 255, 255, 255 ); } /*----- Dashboard —--*/ .dashboard { padding: 30px; width: 100%; } .dashoboard ticket-cointainer { height: 80vh; overflow: scroll; } /*----- TicketCard —--*/ .Ticket-card { display: flex; width: 100%; } .ticket-card # { display: flex; width: 100%; } .ticket-card # > ☆ { background-color = rgb (211,211,211); margin: 2px; padding 30px; width 100%; display: flex; align-items: center; } .ticket-card .ticket-color { width: 15px !important ; Padding: 10px !important; margin: 2px; } .ticket-card .avatar-container { display: flex; justify-content: center; width: 80px; } .ticket-card .img-container { height: 50px; width: 50px; border-radius: 25px; overflow: hidden; .ticket-card .img-container img { width: 100px; } } .ticket-card .status-display { display: flex; justify-content: center; } .ticket-card .priority-display { display: flex; justify-content: center; } .ticket-card .priority-display .star-container { display: flex; } .ticket-card .priority-display .star-container h3 { margin: 7px; padding: 0px; } .ticket-card .progress-display { min-width: 200px; } .ticket-card .progress-bar { width: 100%; height: 30px; background-color: rgb(158, 158, 158 ); border-radious: 15px; overflow: hidden; } .ticket-card .progress-bar .progress-indicator { background-color: rgb( 104, 104, 175); height: 100%; } .ticket-card .delete-block { background-color: rgb (211, 211, 211); width: 50px; margin: 2px; display: flex; justify-content: center; align-items: center; } .ticket-card .delete-block.delete-icon { width: 2opx; height: 20px; border-radius: 10px; background-color: rgb (61, 61, 61); display: flex; justify-content: center; align-items: center; color: rgb (255, 255, 255) } /*—- Ticket Page —-- */ .ticket { padding; 30px; width:100px; } .ticket .ticket-container { width: 100%; display: flex; justify-content: center; } .ticket form { display: flex; } .ticket form section { display: flex; flex-direction: column margin:10px; width: 500px; } .ticket form label { margin: 20px o o o; } .ticket form select .ticket form input { padding 10px; font-size: 15px; border-radius: 10px; border: 1.5px solid rgb 9 (218,218,218); margin: 5px; } .ticket form .multiple-input-container { margin: 20px 20 px 0; } Project Monday-clone SRC README.md New JavaScript File server.js Terminal: monday-clone % npm i express cors axios dotenv monday-clone % npm i nodemon Project Monday-clone SRC package.json npm i nodemon - - save-dev to change line 11: To change line 20 and 21: Project Monday-clone SRC package.json Terminal: % npm run start: backend 16 - // server.js const PORT = 8000 const express = require (‘express’) const cors = require (‘cors’)’ require (‘dotenv’). config () const axios = require (‘axios’) const app = express () app.use(cors()) app.use (express.json () ) const url = process.env.URL const token = process.env.ASTRA_TOKEN app.get(‘/tickets’, async ( res, res ) => { const options = { method ‘GET’, headers: { accepts: ‘applications/json’, ‘X-Cassandra-Token’: token, } } try { const response = await axios ( ‘${url}?page-size=20’, options ) rest.status(200).json(response.data) } catch ( err ) { console.log ( err) res.status ( 500 ).json({message:err}) } }) app.get(‘/tickets/:documentId’, async ( req,res ) => ) { const id = req.params.documentId const options = { method: ‘GET’, headers:{ accepts: ‘application.json’, ‘X-Cassandra-Token: token’, } } try { const response = await axios (‘${url}/${id}’), options res.status(200).json(response,data) } catch (err) { console.log ( err) res.status ( 500 ).json({message:err}) } }) app.post(‘/tickets’ , async ( req, res ) = > { const formData = req.form.body.formData const options - { method ‘POST’, headers: { accepts: ‘applications/json’, ‘X-Cassandra-Token’: token, ‘Content-Type’: ‘application.json’, }, data: formData } try { const response = await axios ( url. option ) res.status(200).json(response.data) } catch (err ) { console.log ( err) res.status ( 500 ).json({message:err}) } }) app.put(‘/tickets/:documentId’, async ( req,res ) => ) { const id = req.params.documentId const data = req.body.data const options = { method: ‘PUT’, headers:{ accepts: ‘application.json’, ‘X-Cassandra-Token: token’, }, data } try { const response = await axios (‘${url}/${id}’), options res.status(200).json(response,data) } catch (err) { console.log ( err) res.status ( 500 ).json({message:err}) } }) app.delete(‘/tickets/:documentId’, async(req, res)=>{ const id = req.params.documentId const options ={ method: ‘DELETE’, headers: { accepts: ‘applications/json’, ‘X-Cassandra-Token’ : token } } try { const response = await axios (‘${url}/${id}’), options res.status(200).json(response,data) } catch (err) { console.log ( err) res.status ( 500 ).json({message:err}) } } }) app.listen ( PORT, () = > console.log (‘server running on PORT’ + PORT ) ) to add an new task server.js New JavaScript File 17 // dummydate.js // const tickets = [ { category: ‘ Q1 2022 ’, color: ‘red’, title: ‘ NFT Safety 101 Video’ owner: ‘ Rodrigo ’ avatar: ‘ 12HR+ YouTube Coding Bootcamp 2021! ’, status: ‘ done ’, priority: ‘ 5 ’, progress: ‘ 40 ’, description: ‘ Make a video showcase how to work with NFTs safety, including how to know if one is not genuine ’, timestamp: ‘ 2022 - 02 - 11T07:36:17+0000 ’ }, { category: ‘ Q1 2022 ’, color: ‘red’, title: ‘ Bild and Sell AI Model’ owner: ‘ Rodrigo ’ avatar: ‘ 12HR+ YouTube Coding Bootcamp 2021! ’, status: ‘ working on it ’, priority: ‘ 2 ’, progress: ‘ 70 ’, description: ‘ Make a video about AI ’, timestamp: ‘ 2022 - 02 - 13T07:36:17+0000 ’ category: ‘ Q2 2022 ’, color: ‘blue’, title: ‘ Biuld a bot’ owner: ‘ Rodrigo ’ avatar: ‘ 12HR+ YouTube Coding Bootcamp 2021! ’, status: ‘ done ’, priority: ‘ 3 ’, progress: ‘ 10 ’, description: ‘ Make a video showcase how to work with NFTs safety, including how to know if one is not genuine ’, timestamp: ‘ 2022 - 02 - 11T07:36:17+0000 ’ } ] const colors = [ ‘rgb(255,179,186)’, ‘rgb(255,233,186)’, ‘rgb’(255,255,186), ‘rgb(186,255,201)’, ‘rgb(186,255,255)’, ] // 18 - // .env URL = http://…// tickets / collections ASTRA_TOKEN = Astra CS:...//DBToken