Codificar tu aplicación
Comencemos a codificar la aplicación.
La lógica del backend de tu aplicación se codificará en el archivo main.py presente en el directorio LeadManager/.
El front end de tu código se almacenará de la siguiente manera:
-
En el directorio LeadManager/templates/, crea los siguientes archivos:
- index.html: Contiene el código HTML para el front-end de la aplicación.
- login.html: Contiene el código HTML para habilitar Embedded Authentication para la aplicación.
-
En el directorio LeadManager/static/, crea los siguientes archivos:
- main.css: Contiene los elementos de estilo necesarios para la aplicación
- main.js: Contiene el código JavaScript que conecta el front-end y el backend.
Esta es la estructura final de carpetas del proyecto LeadManager:

Codificarás los archivos main.py, index.html, login.html, main.css y main.js. También actualizarás el archivo app-config.json.
Copia el código a continuación y pégalo en los archivos respectivos de tu proyecto usando un IDE y guarda los archivos.
import sys
sys.path.insert(0, './myenv/lib/python3.9/site-packages')
from flask import Flask, request, jsonify, make_response, redirect
import os
import json
import logging
import requests
from flask.templating import render_template
import zcatalyst_sdk
app = Flask(name, static_url_path=’/’, static_folder=‘static’, template_folder=‘templates’)
API_BASE = “https://www.zohoapis.com/crm/v2"
LEADS_URL = f”{API_BASE}/Leads"
TOKEN_URL = “https://accounts.zoho.com/oauth/v2/token"
CLIENT_ID = os.environ.get(‘ZOHO_CLIENT_ID’)
CLIENT_SECRET = os.environ.get(‘ZOHO_CLIENT_SECRET’)
logger = logging.getLogger()
def is_valid_record_id(record_id):
return isinstance(record_id, str) and record_id.isdigit()
@app.route(’/’)
def index():
return render_template(‘index.html’)
@app.route(’/index.html’)
def indexhtml():
return render_template(‘index.html’)
@app.route(’/login.html’)
def login():
return render_template(’login.html’)
@app.route(’/generateToken’, methods=[‘GET’])
def generateToken():
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
user_management_service = catalyst_app.authentication()
datastore = catalyst_app.datastore()
code = request.args.get(‘code’)
user_details = user_management_service.get_current_user()
user_id = user_details[“user_id”]
is_local = os.getenv(‘X_ZOHO_CATALYST_IS_LOCAL’) == ’true’
protocol = ‘http’ if is_local else ‘https’
host = request.headers[‘host’] if is_local else request.headers[‘host’].split(’:’)[0]
domain = f”{protocol}://{host}"
logger.info(f’domain:{domain}’)
tab = datastore.table(‘2113000000078743’) #reemplaza con tu Table ID
refresh_token = getRefreshToken(code, domain)
logger.info(‘refresh_token retrieved successfully’) # nunca registres el valor del token
row = {“refresh_token”: refresh_token, “userId”: user_id}
tab.insert_row(row)
redirect_uri = f"{domain}/index.html"
return redirect(redirect_uri)
except Exception as err:
logger.error(f’Error on generateToken: {err}’)
return make_response({
“message”: “Internal Server Error. Please try again after sometime.”
}, 500)
@app.route(’/getUserDetails’, methods=[‘GET’])
def userDetails():
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
user_details = getUserId(catalyst_app)
if len(user_details) != 0:
user_id = user_details[0][“Token”][“userId”]
else:
user_id = None
return make_response({“userId”: user_id}, 200)
except Exception as err:
logger.error(f’Error on getUserDetails: {err}’)
return make_response({
“message”: “Internal Server Error in getting user details. Please try again after sometime.”
}, 500)
@app.route(’/crmData’, methods=[‘GET’])
def crmData():
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
user_details = getUserId(catalyst_app)
access_token = getAccessToken(user_details, catalyst_app)
headers = {
‘Authorization’: f’Zoho-oauthtoken {access_token}’,
“Content-Type”: “application/json”
}
api_response = requests.get(LEADS_URL, headers=headers)
return make_response(jsonify(api_response.json()), 200)
except Exception as err:
logger.error(err)
return make_response({
“message”: “Internal Server Error. Please try again after sometime.”
}, 500)
@app.route(’/crmData’, methods=[‘POST’])
def postcrmData():
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
data = {“data”: [json.loads(request.data)]}
user_details = getUserId(catalyst_app)
access_token = getAccessToken(user_details, catalyst_app)
headers = {
‘Authorization’: f’Zoho-oauthtoken {access_token}’,
“Content-Type”: “application/json”
}
api_response = requests.post(LEADS_URL, headers=headers, json=data)
if api_response.status_code in (200, 201):
return make_response(api_response.json(), 200)
logger.error(api_response.json())
return make_response(api_response.json(), 500)
except Exception as err:
logger.error(err)
return make_response({
“message”: “Internal Server Error. Please try again after sometime.”
}, 500)
@app.route(’/crmData/<id>’, methods=[‘GET’])
def getCRMData(id):
if not is_valid_record_id(id):
return make_response({“message”: “Invalid record ID”}, 400)
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
user_details = getUserId(catalyst_app)
access_token = getAccessToken(user_details, catalyst_app)
headers = {
‘Authorization’: f’Zoho-oauthtoken {access_token}’,
“Content-Type”: “application/json”
}
api_response = requests.get(f"{LEADS_URL}/{id}", headers=headers)
return make_response(api_response.json(), 200)
except Exception as err:
logger.error(err)
return make_response({
“message”: “Internal Server Error. Please try again after sometime.”
}, 500)
@app.route(’/crmData/<id>’, methods=[‘PUT’])
def putcrmData(id):
if not is_valid_record_id(id):
return make_response({“message”: “Invalid record ID”}, 400)
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
req_data = request.get_json()
if req_data is None:
return make_response({“message”: “Update data not found”}, 400)
data = {“data”: [req_data]}
user_details = getUserId(catalyst_app)
access_token = getAccessToken(user_details, catalyst_app)
headers = {
‘Authorization’: f’Zoho-oauthtoken {access_token}’,
“Content-Type”: “application/json”
}
api_response = requests.put(f"{LEADS_URL}/{id}", headers=headers, json=data)
return make_response(api_response.json(), 200)
except Exception as err:
logger.error(err)
return make_response({
“message”: “Internal Server Error. Please try again after sometime.”
}, 500)
@app.route(’/crmData/<id>’, methods=[‘DELETE’])
def deletelead(id):
if not is_valid_record_id(id):
return make_response({“message”: “Invalid record ID”}, 400)
try:
catalyst_app = zcatalyst_sdk.initialize(req=request)
user_details = getUserId(catalyst_app)
access_token = getAccessToken(user_details, catalyst_app)
headers = {
‘Authorization’: f’Zoho-oauthtoken {access_token}’,
“Content-Type”: “application/json”
}
api_response = requests.delete(f"{LEADS_URL}/{id}", headers=headers)
return make_response(api_response.json(), 200)
except Exception as err:
logger.error(err)
return make_response({
“message”: “Internal Server Error. Please try again after sometime.”
}, 500)
def getRefreshToken(code, domain):
if not code:
raise ValueError(“Authorization ‘code’ is missing from the request”)
if not CLIENT_ID or not CLIENT_SECRET:
raise ValueError(“ZOHO_CLIENT_ID / ZOHO_CLIENT_SECRET environment variables are not set”)
params = {
‘code’: code,
‘client_id’: CLIENT_ID,
‘client_secret’: CLIENT_SECRET,
‘grant_type’: ‘authorization_code’,
‘redirect_uri’: f"{domain}/generateToken"
}
response = requests.post(TOKEN_URL, params=params)
try:
data = response.json()
except ValueError:
logger.error(f’Token endpoint returned non-JSON (status {response.status_code}): {response.text}’)
raise
if ‘refresh_token’ not in data:
logger.error(f’Token exchange failed: {data}’)
raise ValueError(data.get(’error’, ‘refresh_token not returned by token endpoint’))
return data[‘refresh_token’]
def getAccessToken(userdetails, catalyst_app):
user_id = userdetails[0][‘Token’][‘userId’]
zcql = catalyst_app.zcql()
query = f"Select refresh_token FROM Token where userId={user_id}"
response = zcql.execute_query(query)
refresh_token = response[0][‘Token’][‘refresh_token’]
args = {
“client_id”: CLIENT_ID,
“client_secret”: CLIENT_SECRET,
“auth_url”: TOKEN_URL,
“refresh_url”: TOKEN_URL,
“refresh_token”: refresh_token,
}
connector_service = catalyst_app.connection({user_id: args})
access_token = connector_service.get_connector(user_id).get_access_token()
return access_token
def getUserId(catalyst_app):
user = catalyst_app.authentication().get_current_user()
user_id = user[“user_id”]
return catalyst_app.zcql().execute_query(
f"Select * from Token where userId = {user_id}"
)
if name == ‘main’:
port = int(os.environ.get(‘X_ZOHO_CATALYST_LISTEN_PORT’, 9000))
app.run(host=‘0.0.0.0’, port=port)
-
Los valores de Client ID y Client Secret obtenidos después de registrar el cliente en la consola de API de Zoho se leen desde las variables de entorno ZOHO_CLIENT_ID y ZOHO_CLIENT_SECRET. Configura estas variables de entorno para tu aplicación de AppSail en lugar de incluirlas directamente en el archivo de código fuente.
-
Agrega el Table ID de la tabla que creaste en el Data Store en la línea 57.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>CRM Lead Manager</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.1/css/mdb.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous">
</script>
<script src="main.js"> </script>
<script src="https://static.zohocdn.com/catalyst/sdk/js/4.6.2/catalystWebSDK.js"> </script>
<script src="/__catalyst/sdk/init.js"> </script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<style>
#connect {
height: 260px;
width: 500px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
</style>
</head>
<body onload="getUserDetails()">
<br>
<center>
<h1>CRM Lead Manager</h1> <br>
</center>
<div id="connect">
<center>
<p>Click here to connect to Zoho CRM</p>
<button onclick='navigate()'>
<img src="https://www.zohowebstatic.com/sites/default/files/styles/product-home-page/public/icon-crm_blue.png"
style="width: 180px;height: 130px;">
</button>
</center>
</div>
<div style="width: 200px;float: right; margin-right: 30px;"> <label for="logoutbtn"
class="btn btn-success btn-block btn-outlined">Logout</label>
<button id="logoutbtn" onclick="logout()" style="display: none;"> </button>
</div>
<div id="main">
<div class="container">
<ul class="nav nav-tabs nav-justified mb-3" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="check-tab" data-toggle="tab" href="#check" role="tab"
aria-controls="check" aria-selected="true">Add a Lead</a>
</li>
<li class="nav-item">
<a class="nav-link" id="report-tab" data-toggle="tab" href="#report" onclick="getLeads()" role="tab"
aria-controls="report" aria-selected="false">Manage Leads</a>
</li>
</ul> <br>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="check" role="tabpanel" aria-labelledby="check-tab">
<div class="tab-pane fade show active" id="ex3-tabs-1" role="tabpanel" aria-labelledby="ex3-tab-1">
<div style="margin-left: 100px; margin-right:100px;">
<form id="leads">
<!-- 2 column grid layout with text inputs for the first and last names -->
<div class="row mb-4">
<div class="col">
<div class="form-outline">
<label class="form-label" for="form6Example1">First name</label>
<input type="text" id="firstName" class="form-control" />
</div>
</div>
<div class="col">
<div class="form-outline">
<label class="form-label" for="form6Example2">Last name</label>
<input type="text" id="lastName" class="form-control" required />
</div>
</div>
</div>
<!-- Text input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example3">Company name</label>
<input type="text" id="companyName" class="form-control" />
</div>
<!-- Email input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example5">Email</label>
<input type="email" id="email" class="form-control" />
</div>
<!-- Text input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example4">State</label>
<input type="text" id="state" class="form-control" />
</div>
<!-- Number input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example6">Phone</label>
<input type="number" id="phone" class="form-control" />
</div>
<!-- Number input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example6">Lead Source</label>
<input type="text" id="leadSource" class="form-control" />
</div>
<!-- Submit button -->
<center> <button type="submit" onclick="createLead();return false;"
class="btn btn-primary btn-block mb-4" style="width: 100px;">Add Lead</button>
<div id="loader" style="display: none;">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</center>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="report" role="tabpanel" aria-labelledby="report-tab">
<center>
<div id="loaders" style="display: none; padding-top: 200px;">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</center>
<p id="showData"> </p>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<center>
<h4 class="modal-title" id="myModalLabel"> </h4>
<div class="modal-body" id="message"> </div>
<div class="modal-footer">
<button type="button" style="background-color: #007bff;color: white;"
class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</center>
</div>
</div>
</div>
</div>
<div class="modal fade" id="editForm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div style="margin-left: 80px; margin-right:80px;">
<br>
<center>
<h2>Edit Lead</h2>
</center>
<br>
<form id="editLeads">
<!-- 2 column grid layout with text inputs for the first and last names -->
<div class="row mb-4">
<div class="col">
<div class="form-outline">
<label class="form-label" for="form6Example1">First name</label>
<input type="text" id="editfirstName" class="form-control" />
</div>
</div>
<div class="col">
<div class="form-outline">
<label class="form-label" for="form6Example2">Last name</label>
<input type="text" id="editlastName" class="form-control" required />
</div>
</div>
</div>
<!-- Text input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example3">Company name</label>
<input type="text" id="editcompanyName" class="form-control" />
</div>
<!-- Email input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example5">Email</label>
<input type="email" id="editemail" class="form-control" />
</div>
<!-- Text input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example4">State</label>
<input type="text" id="editstate" class="form-control" />
</div>
<!-- Number input -->
<div class="form-outline mb-4">
<label class="form-label" for="form6Example6">Phone</label>
<input type="number" id="editphone" class="form-control" />
</div>
<!-- Submit button -->
<center> <button type="submit" id="editBtn" onclick="editLead();return false;"
class="btn btn-primary btn-block mb-4" style="width: 100px;">Edit</button>
<div id="loader" style="display: none;">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</center>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade right" id="ModalDanger" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-notify modal-danger modal-side modal-top-right" role="document">
<!--Content-->
<div class="modal-content">
<!--Header-->
<div class="modal-header">
<p class="heading">Lead Deletion</p>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true" class="white-text">×</span>
</button>
</div>
<!--Body-->
<div class="modal-body">
<div class="row">
<div class="col-9">
<p>Are you sure you want to delete the Lead?</p>
</div>
</div>
</div>
<!--Footer-->
<div class="modal-footer justify-content-center">
<a type="button" class="btn btn-danger" id="delete-btn" onclick="deleteLead()">Delete</a>
<a type="button" class="btn btn-outline-danger waves-effect" data-dismiss="modal">Cancel</a>
</div>
</div>
<!--/.Content-->
</div>
</div>
</div>
</body>
<script>
//Agrega tu Client ID
function navigate() {
window.location.href = "https://accounts.zoho.com/oauth/v2/auth?scope=ZohoCRM.modules.ALL&client_id={{client_id}}&response_type=code&access_type=offline&redirect_uri=" + location.protocol + '//' + location.host + "/generateToken";
}
</script>
</html>
<html>
</html>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>CRM Lead Manager</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
<script src="main.js"> </script>
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://static.zohocdn.com/catalyst/sdk/js/4.6.2/catalystWebSDK.js"> </script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous">
</script>
<script src="https://static.zohocdn.com/catalyst/sdk/js/4.6.2/catalystWebSDK.js"> </script>
<script src="/__catalyst/sdk/init.js"> </script>
<script>
catalyst.auth.signIn("login");
</script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<style>
#login {
height: 100%;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
</style>
<body>
<br>
<center>
<h1>CRM Lead Manager</h1>
</center>
<div id="login"> </div>
</body>
</html>
#dataTable {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
}
#dataTable td, #dataTable th {
border: 1px solid #ddd;
padding: 8px;
}
#dataTable tr:nth-child(even){background-color: #f2f2f2;}
#dataTable tr:hover {background-color: #ddd;}
#dataTable th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
background-color: #007bff;
color: white;
}
.vertical-alignment-helper {
display: table;
height: 100%;
width: 100%;
pointer-events: none;
}
.vertical-align-center {
display: table-cell;
vertical-align: middle;
pointer-events: none;
}
.modal-content {
width: inherit;
max-width: inherit;
height: inherit;
margin: 0 auto;
pointer-events: all;
}
function createLead() {
debugger;
$("#loader").show();
var firstName = $("#firstName").val();
var lastName = $("#lastName").val();
if (lastName == "") {
alert("Kindly Enter the Last Name");
$("#loader").hide();
return;
}
var companyName = $("#companyName").val();
var email = $("#email").val();
var state = $("#state").val();
var phone = $("#phone").val();
var leadSource = $("#leadSource").val();
$.ajax({
url: "/crmData",
type: "post",
contentType: "application/json",
data: JSON.stringify({
"First_Name": firstName,
"Last_Name": lastName,
"Company": companyName,
"Email": email,
"State": state,
"Phone": phone,
"Lead_Source": leadSource,
}),
success: function (data) {
debugger;
$('#leads').trigger("reset");
$("#myModalLabel").html("Success");
$("#message").html("Lead Created Successfully");
$("#loader").hide();
$('#myModal').modal('show');
},
error: function (error) {
$('#leads').trigger("reset");
$("#myModalLabel").html("Failure");
$("#message").html("Please try again after Sometime");
$("#loader").hide();
$('#myModal').modal('show');
}
});
}
function logout() {
var redirectURL = "/login.html"; //Agrega el dominio de tu aplicación
var auth = catalyst.auth;
auth.signOut(redirectURL);
}
function getUserDetails() {
$("#main").hide();
$("#connect").hide();
catalyst.auth.isUserAuthenticated().then(result => {
console.log(result)
$("#loader").show();
$.ajax({
url: "/getUserDetails",
type: "get",
success: function (data) {
$("#loader").hide();
if (data.userId) {
$("#main").show();
$("#connect").hide();
} else {
$("#connect").show();
$("#main").hide();
}
},
error: function (error) {
$("#myModalLabel").html("Failure");
$("#message").html("Please try again after Sometime");
$("#loader").hide();
$('#myModal').modal('show');
}
});
}).catch(err => {
document.body.innerHTML = 'You are not logged in. Please log in to continue. Redirecting you to the login page..';
setTimeout(function () {
window.location.href = "login.html";
}, 3000);
});
}
function getLeads() {
debugger;
var tableContainer = document.getElementById("showData");
tableContainer.innerHTML = "";
$("#loaders").show();
$.ajax({
url: "/crmData",
type: "get",
success: function (data) {
debugger;
var reqData = getRequiredData(data.data);
$("#loaders").hide();
renderTable(reqData);
},
error: function (error) {
$("#myModalLabel").html("Failure");
$("#message").html("Please try again after Sometime");
$("#loader").hide();
$('#myModal').modal('show');
}
});
}
function showDeletePopup(leadID) {
$('#ModalDanger').modal('show');
var deleteBtn = document.getElementById("delete-btn");
deleteBtn.value = leadID;
}
function deleteLead() {
var leadID = document.getElementById('delete-btn').value;
$.ajax({
url: "/crmData/" + leadID,
type: "delete",
success: function (data) {
debugger;
$('#ModalDanger').modal('toggle');
$("#myModalLabel").html("Success");
$("#message").html("Lead Deleted Successfully");
$('#myModal').modal('show');
setTimeout(function () {
location.reload();
}, 3000);
},
error: function (error) {
$("#myModalLabel").html("Failure");
$("#message").html("Please try again after Sometime");
$("#loader").hide();
$('#myModal').modal('show');
}
});
}
function showEditPopup(leadID) {
$.ajax({
url: "/crmData/" + leadID,
type: "get",
success: function (data) {
debugger;
var respData = data.data;
$('#editfirstName').val(respData[0].First_Name);
$('#editlastName').val(respData[0].Last_Name);
$('#editcompanyName').val(respData[0].Company);
$('#editemail').val(respData[0].Email);
$('#editstate').val(respData[0].State);
$('#editphone').val(respData[0].Phone);
$('#editleadSource').val(respData[0].Lead_Source);
$('#editBtn').val(respData[0].id);
$('#editfirstName').html(respData[0].First_Name);
$('#editlastName').html(respData[0].Last_Name);
$('#editcompanyName').html(respData[0].Company);
$('#editemail').html(respData[0].Email);
$('#editstate').html(respData[0].State);
$('#editphone').html(respData[0].Phone);
$('#editleadSource').html(respData[0].Lead_Source);
$('#editForm').modal('show');
},
error: function (error) {
$("#myModalLabel").html("Failure");
$("#message").html("Please try again after Sometime");
$("#loader").hide();
$('#myModal').modal('show');
}
});
}
function editLead() {
var firstName = $("#editfirstName").val();
var lastName = $("#editlastName").val();
if (lastName == "") {
alert("Kindly Enter the Last Name");
return;
}
var companyName = $("#editcompanyName").val();
var email = $("#editemail").val();
var state = $("#editstate").val();
var phone = $("#editphone").val();
var leadSource = $("#editleadSource").val();
var leadID = $("#editBtn").val();
$.ajax({
url: "/crmData/" + leadID,
type: "put",
contentType: "application/json",
data: JSON.stringify({
"First_Name": firstName,
"Last_Name": lastName,
"Company": companyName,
"Email": email,
"State": state,
"Phone": phone,
"Lead_Source": leadSource
}),
success: function (data) {
debugger;
$('#editForm').modal('toggle');
$("#myModalLabel").html("Success");
$("#message").html("Lead Edited Successfully");
$('#myModal').modal('show');
setTimeout(function () {
location.reload();
}, 3000);
},
error: function (error) {
$('#leads').trigger("reset");
$("#myModalLabel").html("Failure");
$("#message").html("Please try again after Sometime");
$("#loader").hide();
$('#myModal').modal('show');
}
});
}
function getRequiredData(data) {
var i;
var resp = [];
for (i = 0; i < data.length; i++) {
var gulp = {
"First Name": data[i].First_Name,
"Last Name": data[i].Last_Name,
"Phone": data[i].Phone,
"Email": data[i].Email,
"Company": data[i].Company,
"Edit": '<center><a href="javascript:showEditPopup(\'' + data[i].id + '\')">✎︎</a></center>',
"Delete": '<center><a href="javascript:showDeletePopup(\'' + data[i].id + '\')">🗑︎</a></center>'
}
resp.push(gulp);
}
console.log(resp);
return resp;
}
function renderTable(respData) {
var col = [];
for (var i = 0; i < respData.length; i++) {
for (var key in respData[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
var table = document.createElement("table");
table.id = "dataTable";
var tr = table.insertRow(-1);
for (var i = 0; i < col.length; i++) {
var th = document.createElement("th");
th.innerHTML = col[i];
tr.appendChild(th);
}
for (var i = 0; i < respData.length; i++) {
tr = table.insertRow(-1);
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = respData[i][col[j]];
}
}
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
}
{
"command": "python3 -u main.py",
"build_path": "/",
"stack": "python_3_9",
"env_variables": {},
"memory": 256,
"scripts": {},
"raw": {},
"catalyst_auth": true,
"login_redirect": "/index.html"
}
Ahora repasemos la lógica y funcionalidad del código de la aplicación.
-
El archivo login.html en el componente cliente habilita el inicio de sesión con Catalyst Embedded Authentication, permitiéndote iniciar sesión usando el inicio de sesión social de Zoho.
-
El archivo index.html contiene el código para el front-end del cliente. Define una función navigate() que habilita la conexión a la cuenta de CRM pasando la información de autorización requerida como el Client ID y el scope.
-
La lógica del backend de la aplicación define las siguientes APIs que realizan diversas acciones:
- /generateToken: Obtiene el Refresh Token llamando a la función getRefreshToken(), y lo inserta junto con el userID del usuario actual en la tabla Token en el Data Store. Luego redirige a la página index del cliente.
- /getUserDetails: Obtiene los detalles del usuario llamando a la función getUserDetails(). Si no hay un registro en la tabla, se envía una respuesta JSON con solo el userID. Si el registro existe en la tabla, se envía junto con el userID.
- /crmData: Esta ruta define la ruta de todos los leads en el módulo de Leads. La función Node ejecuta las operaciones HTTP GET y POST para obtener todos los leads o crear un nuevo lead usando esta ruta, respectivamente.
- /crmData/:id: Esta ruta define la ruta de un lead en particular en el módulo de Leads. El id representa el ID único del lead en CRM. La función ejecuta las operaciones HTTP PUT y DELETE para editar o eliminar un lead, respectivamente, usando esta ruta.
-
La lógica del backend se implementa usando las siguientes funciones que son llamadas por las APIs:
- getRefreshToken(): Obtiene el Refresh Token pasando el Client ID, Client Secret, Redirect URI y otros valores requeridos como parámetros de cadena de consulta.
- getAccessToken(): Obtiene el Access Token pasando el Refresh Token consultado de la tabla Token usando ZCQL, y otros parámetros requeridos. El Access Token se obtiene cada vez que se necesita realizar una operación en el módulo de CRM, como agregar un lead o editar un lead.
- getResponse(): Pasa el Access Token obtenido para conseguir la autorización necesaria para realizar cada acción en el módulo de CRM. Esta función se llama con cada API /crmData y /crmData/:id para autorizar la solicitud.
- getUserDetails(): Obtiene el registro de la tabla Token que contiene el Refresh Token, pasando el userID.
-
main.js: Es la función JavaScript en el componente cliente que analiza las respuestas JSON obtenidas de la función y las renderiza en la página index.html. Esta función define las acciones para todos los elementos de la interfaz de usuario en la aplicación cliente. Por ejemplo, getLeads() obtiene todos los leads de la respuesta de la función y los renderiza en el cliente, y showEditPopup() define las acciones para editar un lead.
Última actualización 2026-09-02 11:52:26 +0530 IST