diff --git a/README.md b/README.md index 76d6d581..e9b7a12c 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-na2.hubspot.com/contacts/243065647/objects/2-169705155/views/all/list ___ ## Tips: diff --git a/index.js b/index.js index f337a32d..23ba2eb9 100644 --- a/index.js +++ b/index.js @@ -1,26 +1,84 @@ const express = require('express'); const axios = require('axios'); +const path = require('path'); +require('dotenv').config(); + const app = express(); +const PORT = process.env.PORT || 3000; app.set('view engine', 'pug'); +app.set('views', path.join(__dirname, 'views')); app.use(express.static(__dirname + '/public')); app.use(express.urlencoded({ extended: true })); + app.use(express.json()); +const HUBSPOT_API_URL = process.env.HUBSPOT_API_URL; +const OBJECT_TYPE_ID = process.env.OBJECT_TYPE_ID; + // * 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.HUBSPOT_ACCESS_TOKEN; // 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) => { + try { + const response = await axios.get(`${HUBSPOT_API_URL}/crm/v3/objects/${OBJECT_TYPE_ID}`, { + headers: { + Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, + 'Content-Type' : 'application/json' + }, + params: { + properties: 'pet_name, pet_breed, pet_favorite_toy', + limit: 100, + }, + }); + const records = response.data.results; + res.render('homepage', { + title: 'Custom Object Records | Practicum', + records, + }); + } catch(error) { + console.error('Error fetching custom object: ', error.response?.data || error.message); + res.status(500).send('Error fetching custom object'); + } +}); // 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 +// POST form submission +app.post('/update-cobj', async (req, res) => { + const { pet_name, pet_breed, pet_favorite_toy } = req.body; + console.log('Form submitted: ', req.body); + + try { + const response = await axios.post(`${HUBSPOT_API_URL}/crm/v3/objects/${OBJECT_TYPE_ID}`, { + properties: { + pet_name, + pet_breed, + pet_favorite_toy, + }, + }, { + headers: { + Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, + 'Content-Type': 'application/json', + }, + }); + console.log("Record created:", response.data); + res.redirect('/'); + } catch(err) { + console.error('Error creating object:', err.response?.data || err.message); + res.status(500).send('Failed to create record'); + } +}); /** * * 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..1da5e679 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,9 @@ "author": "HubSpot Academy learner", "license": "ISC", "dependencies": { - "axios": "^1.3.5", - "express": "^4.18.2", - "pug": "^3.0.2" + "axios": "^1.10.0", + "dotenv": "^16.5.0", + "express": "4.21.2", + "pug": "^3.0.3" } } diff --git a/public/css/style.css b/public/css/style.css index 85587bb4..a5624872 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -55,4 +55,22 @@ input[type="submit"] { border: none; padding: .375rem 1rem; margin-top: 10px; +} + +body { + font-family: Arial, sans-serif; + padding: 20px; +} +table { + border-collapse: collapse; + width: 100%; +} +th, td { + border: 1px solid #CCC; + padding: 8px; + text-align: left; +} +a { + display: inline-block; + margin-bottom: 10px; } \ No newline at end of file diff --git a/views/homepage.pug b/views/homepage.pug new file mode 100644 index 00000000..5700a1de --- /dev/null +++ b/views/homepage.pug @@ -0,0 +1,26 @@ +doctype html +html(lang="en") + head + meta(charset="UTF-8") + meta(name="viewport", content="width=device-width, initial-scale=1.0") + title= title + link(rel="stylesheet", href="/css/style.css") + body + h1= title + a(href="/update-cobj") Add to this table + table + thead + tr + th Pet Name + th Pet Breed + th Favorite Toy + tbody + if records.length + each record in records + tr + td= record.properties.pet_name + td= record.properties.pet_breed + td= record.properties.pet_favorite_toy + else + tr + td(colspan="3") No record found \ No newline at end of file diff --git a/views/updates.pug b/views/updates.pug new file mode 100644 index 00000000..e41e0ce2 --- /dev/null +++ b/views/updates.pug @@ -0,0 +1,27 @@ +doctype html +html(lang="en") + head + meta(charset="UTF-8") + meta(name="viewport", content="width=device-width, initial-scale=1.0") + title= title + link(rel="stylesheet", href="/css/style.css") + body + h1= title + form(method="POST", action="/update-cobj") + label(for="pet_name") Pet Name: + input(type="text", name="pet_name", required) + br + + label(for="pet_breed") Pet Breed: + input(type="text", name="pet_breed") + br + + label(for="pet_favorite_toy") Pet Favorite Toy: + input(type="text", name="pet_favorite_toy") + br + + button(type="submit") Submit + + br + a(href="/") Return to the homepage + \ No newline at end of file