diff --git a/README.md b/README.md index 76d6d581..9907c86d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository is for the Integrating With HubSpot I: Foundations course. This To read the full directions, please go to the [practicum instructions](https://app.hubspot.com/academy/l/tracks/1092124/1093824/5493?language=en). -**Put your HubSpot developer test account custom objects URL link here:** https://app.hubspot.com/contacts/l/objects/${custom-obj-number}/views/all/list +**Put your HubSpot developer test account custom objects URL link here:** https://app.hubspot.com/contacts/47275328/objects/2-34566618/views/all/list ___ ## Tips: diff --git a/index.js b/index.js index f337a32d..6ad12007 100644 --- a/index.js +++ b/index.js @@ -1,71 +1,65 @@ +require('dotenv').config(); const express = require('express'); const axios = require('axios'); +const path = require('path'); + const app = express(); +const PORT = 3000; +const HUBSPOT_API_URL = 'https://api.hubapi.com/crm/v3/objects'; +const CUSTOM_OBJECT_ID = '2-34566618'; app.set('view engine', 'pug'); -app.use(express.static(__dirname + '/public')); +app.set('views', path.join(__dirname, 'views')); + app.use(express.urlencoded({ extended: true })); app.use(express.json()); +app.use(express.static(path.join(__dirname, 'public'))); -// * Please DO NOT INCLUDE the private app access token in your repo. Don't do this practicum in your normal account. -const PRIVATE_APP_ACCESS = ''; - -// TODO: ROUTE 1 - Create a new app.get route for the homepage to call your custom object data. Pass this data along to the front-end and create a new pug template in the views folder. - -// * Code for Route 1 goes here - -// TODO: ROUTE 2 - Create a new app.get route for the form to create or update new custom object data. Send this data along in the next route. - -// * Code for Route 2 goes here - -// TODO: ROUTE 3 - Create a new app.post route for the custom objects form to create or update your custom object data. Once executed, redirect the user to the homepage. - -// * Code for Route 3 goes here - -/** -* * This is sample code to give you a reference for how you should structure your calls. - -* * App.get sample -app.get('/contacts', async (req, res) => { - const contacts = 'https://api.hubspot.com/crm/v3/objects/contacts'; - const headers = { - Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, - 'Content-Type': 'application/json' +const hubspotAxios = axios.create({ + headers: { + Authorization: `Bearer ${process.env.HUBSPOT_ACCESS_TOKEN}` } +}); + +app.get('/', async (req, res) => { try { - const resp = await axios.get(contacts, { headers }); - const data = resp.data.results; - res.render('contacts', { title: 'Contacts | HubSpot APIs', data }); + const response = await hubspotAxios.get(`${HUBSPOT_API_URL}/${CUSTOM_OBJECT_ID}`, { + params: { + properties: 'name,species,age' + } + }); + const customObjects = response.data.results; + res.render('homepage', { customObjects, title: 'Custom Object Records' }); } catch (error) { - console.error(error); + console.error('Error fetching custom objects:', error.response ? error.response.data : error.message); + res.status(500).send('Internal Server Error'); } }); -* * App.post sample -app.post('/update', async (req, res) => { - const update = { + +app.get('/update-cobj', (req, res) => { + res.render('updates', { title: 'Update Custom Object Form | Integrating With HubSpot I Practicum' }); +}); + +app.post('/update-cobj', async (req, res) => { + const { name, species, age } = req.body; + + const data = { properties: { - "favorite_book": req.body.newVal + name, + species, + age } - } - - const email = req.query.email; - const updateContact = `https://api.hubapi.com/crm/v3/objects/contacts/${email}?idProperty=email`; - const headers = { - Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, - 'Content-Type': 'application/json' }; - try { - await axios.patch(updateContact, update, { headers } ); - res.redirect('back'); - } catch(err) { - console.error(err); + try { + await hubspotAxios.post(`${HUBSPOT_API_URL}/${CUSTOM_OBJECT_ID}`, data); + res.redirect('/'); + } catch (error) { + console.error('Error creating custom object:', error.response ? error.response.data : error.message); + res.status(500).send('Internal Server Error'); } - }); -*/ - - -// * Localhost -app.listen(3000, () => console.log('Listening on http://localhost:3000')); \ No newline at end of file +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); diff --git a/public/css/style.css b/public/css/style.css index 85587bb4..7186211b 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,58 +1,77 @@ -@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); -body, * { - font-family: 'Roboto', sans-serif; - margin: 0; - padding: 0; +body, +* { + font-family: "Poppins", sans-serif; + margin: 0; + padding: 1rem; } h1 { - margin: 1rem; + color: #333; + padding-left: 0; } -.cards { - display: flex; - align-items: center; - justify-content: space-evenly; - flex-wrap: wrap; +table { + width: 100%; + border-collapse: collapse; } -.card { - flex-basis: 31%; - margin: 1rem 0.4rem; - padding: 1rem 0.4rem 2.5rem 0.4rem; - border: solid 1px lightgrey; - border-radius: 15px; - box-shadow: 3px 2px 6px lightgrey; +table, +th, +td { + border: 1px solid black; } -.card__email { - font-size: 1rem; +th, +td { + padding: 10px; + text-align: left; } -.form-wrapper { - font-size: 18px; - max-width: 768px; - margin: 2rem auto; - padding: 2rem; - border: solid 1px lightgrey; - border-radius: 15px; - box-shadow: 3px 2px 6px lightgrey; +form { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.1); + border-radius: 25px; + max-width: 25rem; + margin: 2rem auto; + display: flex; + flex-direction: column; } -label, input { - margin-top: 5px; - display: block; - font-size: inherit; +input { + border-radius: 15px; + border: 1px solid #bbbbbb; } -input[type="text"] { - padding: .25rem; +label{ + padding: 6px 0px; +} +button { + padding: 5px 15px; + cursor: pointer; + border-radius: 15px; + background-color: #000; + color: #fff; + transition: .3s; +} + +button:hover { + transition: .3s; + transform: scale(1.05); +} + +a { + margin-bottom: 1rem; + display: inline-block; + background-color: black; + color: #fff; + padding: 5px 10px; + border-radius: 15px; + text-decoration: none; + transition: all .3s; } -input[type="submit"] { - background-color: lightgrey; - border: none; - padding: .375rem 1rem; - margin-top: 10px; +a:hover { + transition: all .3s; + transform: scale(1.05); } \ No newline at end of file diff --git a/views/contacts.pug b/views/contacts.pug deleted file mode 100644 index c600ca01..00000000 --- a/views/contacts.pug +++ /dev/null @@ -1,15 +0,0 @@ -//- ** This is a sample of a pug template and how it uses the data passed to it from index.js. - -//- doctype html -//- html -//- head -//- title= `${title}` -//- meta(name="viewport" content="width=device-width, initial-scale=1") -//- link(rel="stylesheet", href="/css/style.css") -//- body -//- h1 Contacts -//- .cards -//- each contact in data -//- .card -//- h2.card__name #{contact.properties.firstname} #{contact.properties.lastname} -//- p.card__email #{contact.properties.email} \ No newline at end of file diff --git a/views/homepage.pug b/views/homepage.pug new file mode 100644 index 00000000..1c2c821b --- /dev/null +++ b/views/homepage.pug @@ -0,0 +1,22 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/css/style.css') + body + h1= title + a(href='/update-cobj') Add a new record + table + thead + tr + th Name + th Specie + th Age + tbody + each customObject in customObjects + tr + td= customObject.properties.name + td= customObject.properties.species + td= customObject.properties.age + if !customObjects.length + p There are no custom objects available. diff --git a/views/updates.pug b/views/updates.pug new file mode 100644 index 00000000..378ccc87 --- /dev/null +++ b/views/updates.pug @@ -0,0 +1,20 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/css/style.css') + body + h1= title + form(action='/update-cobj', method='POST') + label(for='name') Name: + input(type='text', id='name', name='name', required) + br + label(for='species') Species: + input(type='text', id='species', name='species', required) + br + label(for='age') Age: + input(type='number', id='age', name='age', required) + br + button(type='submit') Send + br + a(href='/') Return to the homepage