initial commit
This commit is contained in:
4
.env.dist
Normal file
4
.env.dist
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
VAULT_ENCRYTPION_KEY=a1234567890
|
||||||
|
VAULT_USER=user1
|
||||||
|
VAULT_PASS=userpassword
|
||||||
|
VAULT_PATH=secretname
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
vendor/
|
||||||
26
composer.json
Normal file
26
composer.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "bobs/dropshipemail",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"slim/slim": "^4.14",
|
||||||
|
"slim/psr7": "^1.7",
|
||||||
|
"hpz937/phpvaultclient": "^1.0",
|
||||||
|
"vlucas/phpdotenv": "^5.6"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Bobs\\Dropshipemail\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Brad Cimbura",
|
||||||
|
"email": "brad@bobscycle.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repositories": [{
|
||||||
|
"type": "composer",
|
||||||
|
"url": "https://git.hpz.pw/api/packages/hpz937/composer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1202
composer.lock
generated
Normal file
1202
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
59
public/index.html
Normal file
59
public/index.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Order Tracker</title>
|
||||||
|
<!-- Tailwind CSS CDN link -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100">
|
||||||
|
<div class="container mx-auto p-6">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Order Tracker</h1>
|
||||||
|
|
||||||
|
<!-- Form for entering the order number -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="order-number" class="block text-sm font-medium text-gray-700">Enter Order Number:</label>
|
||||||
|
<input type="text" id="order-number" class="mt-1 block w-full p-2 border-gray-300 rounded-md">
|
||||||
|
<button id="fetch-order" class="mt-2 p-2 bg-blue-500 text-white rounded">Fetch Order</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section to display order details -->
|
||||||
|
<div id="order-details" class="hidden bg-white p-4 rounded shadow-md">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Order Details</h2>
|
||||||
|
<p id="customer-name"></p>
|
||||||
|
<p id="address"></p>
|
||||||
|
<p id="email"></p>
|
||||||
|
<p id="phone"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section to display tracking information -->
|
||||||
|
<div id="tracking-details" class="hidden bg-white p-4 mt-4 rounded shadow-md">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Tracking Information</h2>
|
||||||
|
<ul id="tracking-list"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section for crafting the email -->
|
||||||
|
<div id="email-section" class="hidden bg-white p-4 mt-4 rounded shadow-md">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Email Information</h2>
|
||||||
|
|
||||||
|
<!-- Email subject line -->
|
||||||
|
<label for="email-subject" class="block text-sm font-medium text-gray-700">Subject Line:</label>
|
||||||
|
<input type="text" id="email-subject" class="mt-1 block w-full p-2 border-gray-300 rounded-md">
|
||||||
|
<button id="copy-subject" class="mt-2 p-2 bg-green-500 text-white rounded">Copy Subject</button>
|
||||||
|
|
||||||
|
<!-- Customer email -->
|
||||||
|
<label for="customer-email" class="block text-sm font-medium text-gray-700 mt-4">Customer Email:</label>
|
||||||
|
<input type="text" id="customer-email" class="mt-1 block w-full p-2 border-gray-300 rounded-md">
|
||||||
|
<button id="copy-email-address" class="mt-2 p-2 bg-green-500 text-white rounded">Copy Email Address</button>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold mt-6">Email Preview</h2>
|
||||||
|
<textarea id="email-content" class="w-full p-2 border-gray-300 rounded-md" rows="6"></textarea>
|
||||||
|
<button id="copy-email-content" class="mt-2 p-2 bg-green-500 text-white rounded">Copy Email</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
18
public/index.php
Normal file
18
public/index.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Slim\Factory\AppFactory;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$app = AppFactory::create();
|
||||||
|
|
||||||
|
// Include the routes
|
||||||
|
require __DIR__ . '/../src/routes.php';
|
||||||
|
|
||||||
|
$app->run();
|
||||||
|
|
||||||
121
public/js/main.js
Normal file
121
public/js/main.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
document.getElementById('fetch-order').addEventListener('click', async function () {
|
||||||
|
const orderNumber = document.getElementById('order-number').value;
|
||||||
|
if (!orderNumber) {
|
||||||
|
alert('Please enter an order number.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch order details
|
||||||
|
const orderResponse = await fetch(`/api/orders/${orderNumber}`);
|
||||||
|
if (orderResponse.ok) {
|
||||||
|
const orderData = await orderResponse.json();
|
||||||
|
displayOrderDetails(orderData);
|
||||||
|
|
||||||
|
// Fetch tracking details only after successfully getting order data
|
||||||
|
const trackingResponse = await fetch(`/api/tracking/${orderNumber}`);
|
||||||
|
if (trackingResponse.ok) {
|
||||||
|
const trackingData = await trackingResponse.json();
|
||||||
|
displayTrackingDetails(trackingData);
|
||||||
|
generateEmailContent(orderData, trackingData);
|
||||||
|
} else {
|
||||||
|
throw new Error('Tracking information not found');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Order not found');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function displayOrderDetails(orderData) {
|
||||||
|
document.getElementById('customer-name').textContent = `Customer: ${orderData.customer_name}`;
|
||||||
|
document.getElementById('address').textContent = `Address: ${orderData.shipping_address.address1}, ${orderData.shipping_address.city}, ${orderData.shipping_address.state} ${orderData.shipping_address.zip}`;
|
||||||
|
document.getElementById('email').textContent = `Email: ${orderData.email}`;
|
||||||
|
document.getElementById('phone').textContent = `Phone: ${orderData.phone}`;
|
||||||
|
document.getElementById('customer-email').value = orderData.email;
|
||||||
|
document.getElementById('order-details').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayTrackingDetails(trackingData) {
|
||||||
|
const trackingList = document.getElementById('tracking-list');
|
||||||
|
trackingList.innerHTML = ''; // Clear previous tracking details
|
||||||
|
trackingData.tracking_details.forEach((detail, index) => {
|
||||||
|
const listItem = document.createElement('li');
|
||||||
|
listItem.innerHTML = `Package #${index + 1}: <a href="${getTrackingUrl(detail)}" target="_blank">${detail.tracking_number}</a> (${detail.carrier})`;
|
||||||
|
trackingList.appendChild(listItem);
|
||||||
|
});
|
||||||
|
document.getElementById('tracking-details').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTrackingUrl(detail) {
|
||||||
|
const carrierUrls = {
|
||||||
|
'Speedee': 'https://www.speedee.delivery/track?trackingNumber=',
|
||||||
|
'UPS': 'https://www.ups.com/track?loc=en_US&tracknum=',
|
||||||
|
'FedEx': 'https://www.fedex.com/apps/fedextrack/?tracknumbers=',
|
||||||
|
'USPS': 'https://tools.usps.com/go/TrackConfirmAction_input?qtc_tLabels1=',
|
||||||
|
'GLS': 'https://gls-group.eu/EU/en/parcel-tracking?match=',
|
||||||
|
'OnTrac': 'https://www.ontrac.com/trackingres.asp?tracking_number='
|
||||||
|
};
|
||||||
|
return carrierUrls[detail.carrier] + detail.tracking_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateEmailContent(orderData, trackingData) {
|
||||||
|
const customerFirstName = orderData.customer_name.split(' ')[0];
|
||||||
|
const orderNumber = trackingData.order_number;
|
||||||
|
|
||||||
|
let emailContent = `Hello ${customerFirstName},\n\nWe are pleased to inform you that your order <b>#${orderNumber}</b> has been drop-shipped directly from our distributor to your address.\n\n`;
|
||||||
|
|
||||||
|
emailContent += `<b>Here is your tracking information for the shipment(s):</b>\n`;
|
||||||
|
|
||||||
|
trackingData.tracking_details.forEach((detail, index) => {
|
||||||
|
emailContent += `Package #${index + 1}: <a href="${getTrackingUrl(detail)}">${detail.tracking_number}</a>\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
emailContent += `\n<i>Please allow up to 24 hours for tracking information to update online.</i>\n\n`;
|
||||||
|
emailContent += `Thank you for choosing us. We truly appreciate your business and are here if you have any questions.`;
|
||||||
|
|
||||||
|
document.getElementById('email-content').value = emailContent;
|
||||||
|
document.getElementById('email-subject').value = `Order #${orderNumber} Drop Shipped`;
|
||||||
|
document.getElementById('email-section').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy subject line
|
||||||
|
document.getElementById('copy-subject').addEventListener('click', function () {
|
||||||
|
const subject = document.getElementById('email-subject');
|
||||||
|
subject.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
// alert('Subject copied to clipboard');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy customer email address
|
||||||
|
document.getElementById('copy-email-address').addEventListener('click', function () {
|
||||||
|
const email = document.getElementById('customer-email');
|
||||||
|
email.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
// alert('Customer email copied to clipboard');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy email content to clipboard
|
||||||
|
document.getElementById('copy-email-content').addEventListener('click', function () {
|
||||||
|
const emailContent = document.getElementById('email-content').value;
|
||||||
|
|
||||||
|
// Convert the email content to an HTML string
|
||||||
|
const htmlContent = emailContent
|
||||||
|
.replace(/(?:\r\n|\r|\n)/g, '<br>') // Replace newlines with <br> tags
|
||||||
|
.replace(/<a href="(.+?)">(.+?)<\/a>/g, (match, url, text) => {
|
||||||
|
return `<a href="${url}" target="_blank">${text}</a>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const blob = new Blob([htmlContent], { type: 'text/html' });
|
||||||
|
const data = [new ClipboardItem({ 'text/html': blob })];
|
||||||
|
|
||||||
|
navigator.clipboard.write(data).then(function () {
|
||||||
|
//alert('Email content copied to clipboard as HTML');
|
||||||
|
}).catch(function (error) {
|
||||||
|
alert('Failed to copy email content');
|
||||||
|
console.error('Error copying email content:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
54
src/Counterpoint.php
Normal file
54
src/Counterpoint.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bobs\Dropshipemail;
|
||||||
|
|
||||||
|
use Hpz937\Phpvaultclient\PhpVaultClient;
|
||||||
|
|
||||||
|
class Counterpoint
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$vault = new PhpVaultClient();
|
||||||
|
$vault->login($_ENV['VAULT_USER'], $_ENV['VAULT_PASS']);
|
||||||
|
|
||||||
|
$dbCredentials = $vault->getSecret($_ENV['VAULT_PATH'], $_ENV['VAULT_ENCRYTPION_KEY']);
|
||||||
|
|
||||||
|
$this->db = sqlsrv_connect($dbCredentials['server'], [
|
||||||
|
'Database' => $dbCredentials['database'],
|
||||||
|
'UID' => $dbCredentials['username'],
|
||||||
|
'PWD' => $dbCredentials['password'],
|
||||||
|
'Encrypt' => true,
|
||||||
|
'TrustServerCertificate' => true,
|
||||||
|
]);
|
||||||
|
if ($this->db === false) {
|
||||||
|
echo "Failed to connect to CP database.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrder($orderNumber)
|
||||||
|
{
|
||||||
|
$sql = 'select TKT_NO,BILL_EMAIL_ADRS_1,BILL_PHONE_1,BILL_NAM,SHIP_ADRS_1,SHIP_ADRS_2,SHIP_CITY,SHIP_STATE,SHIP_ZIP_COD from VI_PS_DOC_HDR where TKT_NO=?';
|
||||||
|
$stmt = sqlsrv_query($this->db, $sql, [$orderNumber]);
|
||||||
|
$order = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTracking($orderNumber)
|
||||||
|
{
|
||||||
|
$sql = 'select h.TKT_NO,PKG_TRK_NO from PS_DOC_PKG_TRK_NO p join PS_DOC_HDR h on p.DOC_ID=h.DOC_ID where h.TKT_NO=?';
|
||||||
|
$stmt = sqlsrv_query($this->db, $sql, [$orderNumber]);
|
||||||
|
//get all tracking numbers for the order
|
||||||
|
$tracking = [];
|
||||||
|
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||||
|
$tracking[] = $row['PKG_TRK_NO'];
|
||||||
|
}
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
|
||||||
|
return $tracking;
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/routes.php
Normal file
79
src/routes.php
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Serve index.html for root route
|
||||||
|
$app->get('/', function ($request, $response) {
|
||||||
|
$file = __DIR__ . '/../public/index.html';
|
||||||
|
$response->getBody()->write(file_get_contents($file));
|
||||||
|
return $response->withHeader('Content-Type', 'text/html');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch order details
|
||||||
|
$app->get('/api/orders/{order_number}', function ($request, $response, array $args) {
|
||||||
|
$orderNumber = $args['order_number'];
|
||||||
|
$counterPoint = new Bobs\Dropshipemail\Counterpoint();
|
||||||
|
$order = $counterPoint->getOrder($orderNumber);
|
||||||
|
if ($order) {
|
||||||
|
$data['order_number'] = $order['TKT_NO'];
|
||||||
|
$data['customer_name'] = $order['BILL_NAM'];
|
||||||
|
$data['email'] = $order['BILL_EMAIL_ADRS_1'];
|
||||||
|
$data['phone'] = $order['BILL_PHONE_1'];
|
||||||
|
$data['shipping_address'] = [
|
||||||
|
'address1' => $order['SHIP_ADRS_1'],
|
||||||
|
'address2' => $order['SHIP_ADRS_2'],
|
||||||
|
'city' => $order['SHIP_CITY'],
|
||||||
|
'state' => $order['SHIP_STATE'],
|
||||||
|
'zip' => $order['SHIP_ZIP_COD']
|
||||||
|
];
|
||||||
|
|
||||||
|
$response->getBody()->write(json_encode($data));
|
||||||
|
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
|
||||||
|
} else {
|
||||||
|
$response->getBody()->write(json_encode(['error' => 'Order not found']));
|
||||||
|
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/api/tracking/{order_number}', function ($request, $response, array $args) {
|
||||||
|
$orderNumber = $args['order_number'];
|
||||||
|
$counterPoint = new Bobs\Dropshipemail\Counterpoint();
|
||||||
|
$tracking = $counterPoint->getTracking($orderNumber);
|
||||||
|
if ($tracking) {
|
||||||
|
$trackingDetails = [];
|
||||||
|
|
||||||
|
foreach ($tracking as $trackingNumber) {
|
||||||
|
$trackingDetails[] = [
|
||||||
|
'tracking_number' => $trackingNumber,
|
||||||
|
'carrier' => determineCarrier($trackingNumber)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->getBody()->write(json_encode([
|
||||||
|
'order_number' => $orderNumber,
|
||||||
|
'tracking_details' => $trackingDetails
|
||||||
|
]));
|
||||||
|
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
|
||||||
|
} else {
|
||||||
|
$response->getBody()->write(json_encode(['error' => 'Tracking information not found']));
|
||||||
|
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to determine the carrier based on the tracking number
|
||||||
|
function determineCarrier($trackingNumber) {
|
||||||
|
if (strpos($trackingNumber, 'SP') === 0) {
|
||||||
|
return 'Speedee';
|
||||||
|
} elseif (strpos($trackingNumber, '1Z') === 0) {
|
||||||
|
return 'UPS';
|
||||||
|
} elseif (preg_match('/^\d{12}$/', $trackingNumber)) {
|
||||||
|
return 'FedEx';
|
||||||
|
} elseif (preg_match('/^(94|93|92|95)\d{20}$/', $trackingNumber) || strlen($trackingNumber) >= 20) {
|
||||||
|
return 'USPS';
|
||||||
|
} elseif (preg_match('/^\d{10}$/', $trackingNumber)) {
|
||||||
|
return 'GLS';
|
||||||
|
} elseif (preg_match('/^C\d+$/', $trackingNumber)) {
|
||||||
|
return 'OnTrac';
|
||||||
|
} else {
|
||||||
|
return 'Unknown Carrier';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user