Created
March 29, 2025 00:04
-
-
Save ibuilder/45ac5750e0aea0ae22f076af5207cc12 to your computer and use it in GitHub Desktop.
This HTML page provides a complete interface for interacting with the Procore API using Bootstrap for styling. Here's what it includes: Authentication Section Form to enter your Procore API credentials (Client ID and Secret) Implements OAuth 2.0 authentication flow Stores tokens securely in localStorage API Request Section Dropdown to select com…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Procore API Integration</title> | |
<!-- Bootstrap CSS --> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet"> | |
<style> | |
.card { | |
margin-bottom: 20px; | |
} | |
#apiResponseContainer { | |
max-height: 400px; | |
overflow-y: auto; | |
} | |
.loading { | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container mt-5"> | |
<h1 class="mb-4">Procore API Integration</h1> | |
<!-- Authentication Card --> | |
<div class="card"> | |
<div class="card-header bg-primary text-white"> | |
<h5 class="mb-0">Authentication</h5> | |
</div> | |
<div class="card-body"> | |
<form id="authForm"> | |
<div class="mb-3"> | |
<label for="clientId" class="form-label">Client ID</label> | |
<input type="text" class="form-control" id="clientId" required> | |
</div> | |
<div class="mb-3"> | |
<label for="clientSecret" class="form-label">Client Secret</label> | |
<input type="password" class="form-control" id="clientSecret" required> | |
</div> | |
<div class="mb-3"> | |
<label for="redirectUri" class="form-label">Redirect URI</label> | |
<input type="text" class="form-control" id="redirectUri" value="http://localhost:8000/callback" required> | |
</div> | |
<button type="submit" class="btn btn-primary">Authenticate</button> | |
</form> | |
</div> | |
</div> | |
<!-- API Request Card --> | |
<div class="card"> | |
<div class="card-header bg-success text-white"> | |
<h5 class="mb-0">API Request</h5> | |
</div> | |
<div class="card-body"> | |
<form id="apiRequestForm"> | |
<div class="mb-3"> | |
<label for="endpoint" class="form-label">API Endpoint</label> | |
<select class="form-select" id="endpoint" required> | |
<option value="">Select an endpoint</option> | |
<option value="/rest/v1.0/projects">List Projects</option> | |
<option value="/rest/v1.0/companies">List Companies</option> | |
<option value="/rest/v1.0/tasks">List Tasks</option> | |
<option value="/rest/v1.0/users">List Users</option> | |
<option value="/rest/v1.0/project_tools">List Project Tools</option> | |
</select> | |
</div> | |
<div class="mb-3"> | |
<label for="projectId" class="form-label">Project ID (if applicable)</label> | |
<input type="text" class="form-control" id="projectId"> | |
</div> | |
<div class="mb-3"> | |
<label for="method" class="form-label">HTTP Method</label> | |
<select class="form-select" id="method" required> | |
<option value="GET">GET</option> | |
<option value="POST">POST</option> | |
<option value="PUT">PUT</option> | |
<option value="DELETE">DELETE</option> | |
</select> | |
</div> | |
<div class="mb-3"> | |
<label for="requestBody" class="form-label">Request Body (JSON, if applicable)</label> | |
<textarea class="form-control" id="requestBody" rows="3"></textarea> | |
</div> | |
<button type="submit" class="btn btn-success" id="sendRequestBtn">Send Request</button> | |
<div class="spinner-border text-success loading mt-2" id="loadingSpinner" role="status"> | |
<span class="visually-hidden">Loading...</span> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- API Response Card --> | |
<div class="card"> | |
<div class="card-header bg-info text-white"> | |
<h5 class="mb-0">API Response</h5> | |
</div> | |
<div class="card-body"> | |
<div id="apiResponseContainer" class="bg-light p-3 rounded"> | |
<pre id="apiResponse">No response yet</pre> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Bootstrap and other JavaScript libraries --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script> | |
<script> | |
// Global access token variable | |
let accessToken = ''; | |
const BASE_URL = 'https://api.procore.com'; | |
// Check if we have a token in localStorage | |
document.addEventListener('DOMContentLoaded', () => { | |
accessToken = localStorage.getItem('procoreAccessToken'); | |
if (accessToken) { | |
displayMessage('Found existing access token.'); | |
} | |
}); | |
// Function to initiate OAuth flow | |
document.getElementById('authForm').addEventListener('submit', (e) => { | |
e.preventDefault(); | |
const clientId = document.getElementById('clientId').value; | |
const redirectUri = document.getElementById('redirectUri').value; | |
// Store client details for later use | |
localStorage.setItem('procoreClientId', clientId); | |
localStorage.setItem('procoreClientSecret', document.getElementById('clientSecret').value); | |
localStorage.setItem('procoreRedirectUri', redirectUri); | |
// Redirect to Procore authorization URL | |
const authUrl = `${BASE_URL}/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}`; | |
window.location.href = authUrl; | |
}); | |
// Function to check for authorization code in URL after redirect | |
function checkForAuthCode() { | |
const urlParams = new URLSearchParams(window.location.search); | |
const code = urlParams.get('code'); | |
if (code) { | |
// Exchange code for token | |
exchangeCodeForToken(code); | |
} | |
} | |
// Function to exchange authorization code for access token | |
async function exchangeCodeForToken(code) { | |
try { | |
const clientId = localStorage.getItem('procoreClientId'); | |
const clientSecret = localStorage.getItem('procoreClientSecret'); | |
const redirectUri = localStorage.getItem('procoreRedirectUri'); | |
const response = await fetch(`${BASE_URL}/oauth/token`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
grant_type: 'authorization_code', | |
code: code, | |
client_id: clientId, | |
client_secret: clientSecret, | |
redirect_uri: redirectUri | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error('Failed to exchange code for token'); | |
} | |
const data = await response.json(); | |
accessToken = data.access_token; | |
localStorage.setItem('procoreAccessToken', accessToken); | |
displayMessage('Successfully authenticated with Procore!'); | |
// Clear the URL to remove the authorization code | |
window.history.replaceState({}, document.title, window.location.pathname); | |
} catch (error) { | |
displayError('Authentication error: ' + error.message); | |
} | |
} | |
// Function to send API requests | |
document.getElementById('apiRequestForm').addEventListener('submit', async (e) => { | |
e.preventDefault(); | |
if (!accessToken) { | |
displayError('You need to authenticate first!'); | |
return; | |
} | |
const endpoint = document.getElementById('endpoint').value; | |
const method = document.getElementById('method').value; | |
const projectId = document.getElementById('projectId').value; | |
const requestBody = document.getElementById('requestBody').value; | |
// Show loading spinner | |
document.getElementById('loadingSpinner').style.display = 'inline-block'; | |
document.getElementById('sendRequestBtn').disabled = true; | |
try { | |
// Determine the URL based on whether a project ID is provided | |
let url = BASE_URL + endpoint; | |
if (projectId && endpoint.includes('projects')) { | |
url = `${BASE_URL}/rest/v1.0/projects/${projectId}` + endpoint.replace('/rest/v1.0/projects', ''); | |
} | |
const options = { | |
method: method, | |
headers: { | |
'Authorization': `Bearer ${accessToken}`, | |
'Content-Type': 'application/json' | |
} | |
}; | |
// Add request body for POST and PUT requests | |
if ((method === 'POST' || method === 'PUT') && requestBody) { | |
options.body = requestBody; | |
} | |
const response = await fetch(url, options); | |
const data = await response.json(); | |
// Display the response | |
document.getElementById('apiResponse').textContent = JSON.stringify(data, null, 2); | |
} catch (error) { | |
displayError('API request error: ' + error.message); | |
} finally { | |
// Hide loading spinner | |
document.getElementById('loadingSpinner').style.display = 'none'; | |
document.getElementById('sendRequestBtn').disabled = false; | |
} | |
}); | |
// Helper function to display messages | |
function displayMessage(message) { | |
document.getElementById('apiResponse').textContent = message; | |
} | |
// Helper function to display errors | |
function displayError(error) { | |
document.getElementById('apiResponse').textContent = `ERROR: ${error}`; | |
document.getElementById('apiResponse').style.color = 'red'; | |
} | |
// Check for auth code when page loads | |
checkForAuthCode(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment