From 187047e5f355abcbb8535f4e4d9d1f2cfacf04e7 Mon Sep 17 00:00:00 2001 From: Javier Gonell Date: Fri, 28 Mar 2025 13:28:33 +0100 Subject: [PATCH 1/4] First commit to my Integrating With HubSpot I: Foundations practicum repository. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76d6d581..ead7f604 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-eu1.hubspot.com/contacts/145876913/objects/2-140871415/views/all/list ___ ## Tips: From 5e8c1bcc2ce8cdf92113821ebb3d053c9608a8bd Mon Sep 17 00:00:00 2001 From: Javier Gonell Date: Fri, 28 Mar 2025 14:26:27 +0100 Subject: [PATCH 2/4] Final practicum submission - Pets custom object --- index.js | 52 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 1 + views/homepage.pug | 26 +++++++++++++++++++++++ views/updates.pug | 19 +++++++++++++++++ 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 views/homepage.pug create mode 100644 views/updates.pug diff --git a/index.js b/index.js index f337a32d..badb9d48 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ const express = require('express'); const axios = require('axios'); +require('dotenv').config(); const app = express(); app.set('view engine', 'pug'); @@ -8,19 +9,62 @@ app.use(express.urlencoded({ extended: true })); app.use(express.json()); // * 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 = ''; +const PRIVATE_APP_ACCESS = process.env.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 +app.get('/', async (req, res) => { + const url = 'https://api.hubapi.com/crm/v3/objects/pets'; + + const headers = { + Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, + 'Content-Type': 'application/json' + }; + + try { + const response = await axios.get(url, { headers }); + const pets = response.data.results; + res.render('homepage', { title: 'Pets Table | HubSpot Practicum', pets }); + } catch (err) { + console.error(err); + res.status(500).send('Error retrieving pets'); + } +}); // 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 +app.get('/update-cobj', (req, res) => { + res.render('updates', { + title: 'Update Custom Object Form | Integrating With HubSpot I Practicum' + }); +}); // 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 +app.post('/update-cobj', async (req, res) => { + const petData = { + properties: { + name: req.body.name, + type: req.body.type, + age__years_: req.body.age__years_ + } + }; + + const url = 'https://api.hubapi.com/crm/v3/objects/pets'; + + const headers = { + Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, + 'Content-Type': 'application/json' + }; + + try { + await axios.post(url, petData, { headers }); + res.redirect('/'); + } catch (err) { + console.error(err); + res.status(500).send('Error creating pet'); + } +}); /** * * This is sample code to give you a reference for how you should structure your calls. diff --git a/package.json b/package.json index 62db37aa..cffbd16c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.3.5", + "dotenv": "^16.4.7", "express": "^4.18.2", "pug": "^3.0.2" } diff --git a/views/homepage.pug b/views/homepage.pug new file mode 100644 index 00000000..b00da466 --- /dev/null +++ b/views/homepage.pug @@ -0,0 +1,26 @@ +doctype html +html + head + title= title + body + h1= title + + a(href='/update-cobj') Add to this table + br + br + + if pets.length + table(border='1') + thead + tr + th Name + th Type + th Age (Years) + tbody + each pet in pets + tr + td= pet.properties.name + td= pet.properties.type + td= pet.properties.age__years_ + else + p No pets found. diff --git a/views/updates.pug b/views/updates.pug new file mode 100644 index 00000000..0c23f3a2 --- /dev/null +++ b/views/updates.pug @@ -0,0 +1,19 @@ +doctype html +html + head + title= title + body + h1= title + form(method='POST', action='/update-cobj') + label(for='name') Name: + input(type='text', name='name', required=true) + br + label(for='type') Type: + input(type='text', name='type', required=true) + br + label(for='age__years_') Age (Years): + input(type='number', name='age__years_', required=true) + br + button(type='submit') Submit + br + a(href='/') Return to the homepage From fad5293ada0fd5bacf0d6298584ed42d877ac25f Mon Sep 17 00:00:00 2001 From: Javier Gonell Date: Mon, 31 Mar 2025 11:17:02 +0200 Subject: [PATCH 3/4] Final polished version with correct property handling and styling --- index.js | 65 ++++++++-------------------------------------- views/homepage.pug | 49 +++++++++++++++++++++++++++++----- views/updates.pug | 46 ++++++++++++++++++++++++++++---- 3 files changed, 95 insertions(+), 65 deletions(-) diff --git a/index.js b/index.js index badb9d48..d4ced4d2 100644 --- a/index.js +++ b/index.js @@ -8,13 +8,12 @@ app.use(express.static(__dirname + '/public')); app.use(express.urlencoded({ extended: true })); app.use(express.json()); -// * Please DO NOT INCLUDE the private app access token in your repo. Don't do this practicum in your normal account. +// Keep your token in a .env file const PRIVATE_APP_ACCESS = process.env.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. - +// ROUTE 1 - Homepage: Display custom object data app.get('/', async (req, res) => { - const url = 'https://api.hubapi.com/crm/v3/objects/pets'; + const url = 'https://api.hubapi.com/crm/v3/objects/pets?properties=name,type,age__years_'; const headers = { Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, @@ -24,23 +23,24 @@ app.get('/', async (req, res) => { try { const response = await axios.get(url, { headers }); const pets = response.data.results; - res.render('homepage', { title: 'Pets Table | HubSpot Practicum', pets }); + res.render('homepage', { + title: 'Custom Object Table', + pets + }); } catch (err) { console.error(err); res.status(500).send('Error retrieving pets'); } }); -// 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. - +// ROUTE 2 - Show form to create/update object app.get('/update-cobj', (req, res) => { res.render('updates', { title: 'Update Custom Object Form | Integrating With HubSpot I Practicum' }); }); -// 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. - +// ROUTE 3 - Handle form submission to create object app.post('/update-cobj', async (req, res) => { const petData = { properties: { @@ -66,50 +66,7 @@ app.post('/update-cobj', async (req, res) => { } }); -/** -* * 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' - } - try { - const resp = await axios.get(contacts, { headers }); - const data = resp.data.results; - res.render('contacts', { title: 'Contacts | HubSpot APIs', data }); - } catch (error) { - console.error(error); - } -}); - -* * App.post sample -app.post('/update', async (req, res) => { - const update = { - properties: { - "favorite_book": req.body.newVal - } - } - - 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); - } - -}); -*/ +// Start server +app.listen(3000, () => console.log('Listening on http://localhost:3000')); -// * Localhost -app.listen(3000, () => console.log('Listening on http://localhost:3000')); \ No newline at end of file diff --git a/views/homepage.pug b/views/homepage.pug index b00da466..c26c2b61 100644 --- a/views/homepage.pug +++ b/views/homepage.pug @@ -2,15 +2,49 @@ doctype html html head title= title + style. + body { + font-family: 'Helvetica Neue', sans-serif; + text-align: center; + background: #f7f7f7; + padding: 50px; + } + h1 { + font-size: 2em; + margin-bottom: 0; + } + h2 { + margin-top: 0; + color: #777; + font-weight: normal; + } + table { + margin: 20px auto; + border-collapse: collapse; + background: white; + box-shadow: 0 2px 6px rgba(0,0,0,0.1); + width: 400px; + } + th, td { + padding: 12px 15px; + border: 1px solid #ccc; + text-align: left; + } + th { + background: #eee; + } + a { + display: inline-block; + margin-top: 20px; + text-decoration: none; + color: #007bff; + } body - h1= title - - a(href='/update-cobj') Add to this table - br - br + h1 Custom Object Table + h2 Integrating With HubSpot I Practicum if pets.length - table(border='1') + table thead tr th Name @@ -24,3 +58,6 @@ html td= pet.properties.age__years_ else p No pets found. + + a(href='/update-cobj') Add / Update this table + diff --git a/views/updates.pug b/views/updates.pug index 0c23f3a2..ff26bb26 100644 --- a/views/updates.pug +++ b/views/updates.pug @@ -2,18 +2,54 @@ doctype html html head title= title + style. + body { + font-family: 'Helvetica Neue', sans-serif; + text-align: center; + background: #f7f7f7; + padding: 50px; + } + form { + background: white; + padding: 20px; + display: inline-block; + box-shadow: 0 2px 6px rgba(0,0,0,0.1); + border-radius: 10px; + } + input { + display: block; + margin: 10px auto; + padding: 8px; + width: 200px; + border: 1px solid #ccc; + border-radius: 5px; + } + button { + background: #28a745; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + } + a { + display: block; + margin-top: 15px; + color: #007bff; + text-decoration: none; + } body h1= title form(method='POST', action='/update-cobj') label(for='name') Name: input(type='text', name='name', required=true) - br + label(for='type') Type: input(type='text', name='type', required=true) - br + label(for='age__years_') Age (Years): input(type='number', name='age__years_', required=true) - br + button(type='submit') Submit - br - a(href='/') Return to the homepage + + a(href='/') Go back to the table From 832bf7f98f271e4e27710d9f103efd2fdb120822 Mon Sep 17 00:00:00 2001 From: Javier Gonell Date: Mon, 31 Mar 2025 11:42:14 +0200 Subject: [PATCH 4/4] Final practicum submission - Pets custom object # Conflicts: # index.js # views/homepage.pug # views/updates.pug --- index.js | 1 + views/homepage.pug | 1 - views/updates.pug | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index d4ced4d2..b863e4d1 100644 --- a/index.js +++ b/index.js @@ -70,3 +70,4 @@ app.post('/update-cobj', async (req, res) => { app.listen(3000, () => console.log('Listening on http://localhost:3000')); + diff --git a/views/homepage.pug b/views/homepage.pug index c26c2b61..c7d155f9 100644 --- a/views/homepage.pug +++ b/views/homepage.pug @@ -60,4 +60,3 @@ html p No pets found. a(href='/update-cobj') Add / Update this table - diff --git a/views/updates.pug b/views/updates.pug index ff26bb26..5ff77705 100644 --- a/views/updates.pug +++ b/views/updates.pug @@ -53,3 +53,4 @@ html button(type='submit') Submit a(href='/') Go back to the table +