Compare commits

..

1 Commits

Author SHA1 Message Date
Flatlogic Bot
f8eb73face 6 January 2026, commit 2026-01-06 10:04:24 +00:00
10 changed files with 83134 additions and 141 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

23
get_counts.php Normal file
View File

@ -0,0 +1,23 @@
<?php
require_once 'db/config.php';
try {
$db = db();
$tables = ['customers', 'products', 'shipments', 'lifecycle'];
$counts = [];
foreach ($tables as $table) {
$stmt = $db->query("SELECT COUNT(*) FROM `$table`");
$counts[$table] = $stmt->fetchColumn();
}
foreach ($counts as $table => $count) {
echo ucfirst($table) . ": $count rows
";
}
} catch (PDOException $e) {
echo "Error: " . $e->getMessage() . "
";
}
?>

22
get_schema.php Normal file
View File

@ -0,0 +1,22 @@
<?php
require_once 'db/config.php';
try {
$pdoconn = db();
echo "--- customers table ---\n";
$stmt = $pdoconn->query('DESCRIBE customers');
print_r($stmt->fetchAll(PDO::FETCH_ASSOC));
echo "--- lifecycle table ---\n";
$stmt = $pdoconn->query('DESCRIBE lifecycle');
print_r($stmt->fetchAll(PDO::FETCH_ASSOC));
echo "--- products table ---\n";
$stmt = $pdoconn->query('DESCRIBE products');
print_r($stmt->fetchAll(PDO::FETCH_ASSOC));
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}

354
import.php Normal file
View File

@ -0,0 +1,354 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
require_once 'db/config.php';
$messages = [];
$conn = db();
function create_lifecycle_table($conn) {
$messages = [];
try {
$sql_lifecycle = "
CREATE TABLE IF NOT EXISTS lifecycle (
id INT AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(255),
custcode VARCHAR(255),
first_ship_month VARCHAR(255),
last_ship_month VARCHAR(255),
total_ship INT,
avg_ship FLOAT,
maxstores INT,
mock_pct FLOAT,
store_count INT,
runrate FLOAT
);";
$conn->exec($sql_lifecycle);
$messages['success'][] = "Table 'lifecycle' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function create_customers_table($conn) {
$messages = [];
try {
$sql_customers = "
CREATE TABLE IF NOT EXISTS customers (
id INT AUTO_INCREMENT PRIMARY KEY,
custcode VARCHAR(255),
custname VARCHAR(255),
channel VARCHAR(255),
country VARCHAR(255),
maxstores INT
);";
$conn->exec($sql_customers);
$messages['success'][] = "Table 'customers' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function create_products_table($conn) {
$messages = [];
try {
$sql_products = "
CREATE TABLE IF NOT EXISTS products (
product_id VARCHAR(255) PRIMARY KEY,
prodname VARCHAR(255),
brand VARCHAR(255),
subbrand VARCHAR(255),
sub_brand_abbreviation VARCHAR(255),
listprice FLOAT
);";
$conn->exec($sql_products);
$messages['success'][] = "Table 'products' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function create_shipments_table($conn) {
$messages = [];
try {
$sql_shipments = "
CREATE TABLE IF NOT EXISTS shipments (
id INT AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(255),
month VARCHAR(255),
custcode VARCHAR(255),
ship INT
);";
$conn->exec($sql_shipments);
$messages['success'][] = "Table 'shipments' created or already exists.";
} catch (PDOException $e) {
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
}
return $messages;
}
function import_csv($conn, $filePath, $tableName, $fieldMap, &$messages) {
try {
$conn->exec("TRUNCATE TABLE `$tableName`");
} catch (PDOException $e) {
$messages['danger'][] = "Error truncating table $tableName: " . $e->getMessage();
return ['count' => 0, 'error' => "Error truncating table: " . $e->getMessage()];
}
if (!file_exists($filePath) || !is_readable($filePath)) {
return ['count' => 0, 'error' => "File not found or not readable: $filePath"];
}
$file = fopen($filePath, 'r');
if (!$file) {
return ['count' => 0, 'error' => "Failed to open file: $filePath"];
}
$header = fgetcsv($file);
if (!$header) {
return ['count' => 0, 'error' => "Failed to read header from file: $filePath"];
}
$indices = [];
foreach ($fieldMap as $dbField => $csvField) {
$index = array_search($csvField, $header);
if ($index === false) {
// Allow for flexibility with headers that have dots
$csvFieldWithDot = str_replace('_', '.', $csvField);
if (in_array($csvFieldWithDot, $header)) {
$index = array_search($csvFieldWithDot, $header);
} else {
return ['count' => 0, 'error' => "Column '$csvField' (or '$csvFieldWithDot') not found in $tableName CSV."];
}
}
$indices[$dbField] = $index;
}
$placeholders = implode(', ', array_fill(0, count($fieldMap), '?'));
$columns = implode(', ', array_keys($fieldMap));
if ($tableName === 'products') {
$sql = "INSERT IGNORE INTO $tableName ($columns) VALUES ($placeholders)";
} else {
$sql = "INSERT INTO $tableName ($columns) VALUES ($placeholders)";
}
$stmt = $conn->prepare($sql);
$count = 0;
$conn->beginTransaction();
while (($row = fgetcsv($file)) !== false) {
$params = [];
foreach ($fieldMap as $dbField => $csvField) {
$value = $row[$indices[$dbField]];
if ($tableName === 'lifecycle') {
if (in_array($dbField, ['total_ship', 'maxstores', 'store_count'])) {
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
} elseif (in_array($dbField, ['avg_ship', 'mock_pct', 'runrate'])) {
$params[] = (float)filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} else {
$params[] = trim($value);
}
} elseif ($tableName === 'customers') {
if (in_array($dbField, ['maxstores'])) {
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
} else {
$params[] = trim($value);
}
} elseif ($tableName === 'products') {
if (in_array($dbField, ['listprice'])) {
$params[] = (float)filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} else {
$params[] = trim($value);
}
} elseif ($tableName === 'shipments') {
if (in_array($dbField, ['ship'])) {
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
} else {
$params[] = trim($value);
}
} else {
$params[] = trim($value);
}
}
$stmt->execute($params);
$count++;
}
$conn->commit();
fclose($file);
return ['count' => $count, 'error' => null];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$messages = array_merge_recursive($messages, create_lifecycle_table($conn));
$messages = array_merge_recursive($messages, create_customers_table($conn));
$messages = array_merge_recursive($messages, create_products_table($conn));
$messages = array_merge_recursive($messages, create_shipments_table($conn));
$uploadDir = 'uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$files = [
'lifecycle' => ['lifecycle', [
'product_id' => 'product_id',
'custcode' => 'custcode',
'first_ship_month' => 'first_ship_month',
'last_ship_month' => 'last_ship_month',
'total_ship' => 'total_ship',
'avg_ship' => 'avg_ship',
'maxstores' => 'maxstores',
'mock_pct' => 'mock_pct',
'store_count' => 'store_count',
'runrate' => 'runrate'
]],
'customers' => ['customers', [
'custcode' => 'custcode',
'custname' => 'custname',
'channel' => 'channel',
'country' => 'country',
'maxstores' => 'maxstores'
]],
'products' => ['products', [
'product_id' => 'product_id',
'prodname' => 'prodname',
'brand' => 'brand',
'subbrand' => 'subbrand',
'sub_brand_abbreviation' => 'sub_brand.abbreviation',
'listprice' => 'listprice'
]],
'shipments' => ['shipments', [
'product_id' => 'product_id',
'month' => 'month',
'custcode' => 'custcode',
'ship' => 'ship'
]]
];
foreach ($files as $fileKey => $fileInfo) {
if (isset($_FILES[$fileKey]) && $_FILES[$fileKey]['error'] == UPLOAD_ERR_OK) {
$tableName = $fileInfo[0];
$fieldMap = $fileInfo[1];
$tmpName = $_FILES[$fileKey]['tmp_name'];
$filePath = $uploadDir . basename($_FILES[$fileKey]['name']);
if (move_uploaded_file($tmpName, $filePath)) {
$result = import_csv($conn, $filePath, $tableName, $fieldMap, $messages);
if ($result['error']) {
$messages['danger'][] = "Error importing $tableName: " . $result['error'];
} else {
$messages['success'][] = "Successfully imported " . $result['count'] . " records into '$tableName'.";
}
} else {
$messages['danger'][] = "Failed to move uploaded file for $fileKey.";
}
} elseif (isset($_FILES[$fileKey]) && !empty($_FILES[$fileKey]['name'])) {
$errorCode = $_FILES[$fileKey]['error'];
$errorMessage = "An unknown error occurred.";
if ($errorCode === UPLOAD_ERR_INI_SIZE) {
$maxSize = ini_get('upload_max_filesize');
$errorMessage = "The file is too large. The server's maximum upload size is {$maxSize}.";
}
$messages['warning'][] = "An error occurred with the '$fileKey' upload: " . $errorMessage;
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Importer</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">CRUD App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/import.php">Import Data</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card">
<div class="card-header">
<h1 class="card-title text-center">Data Importer</h1>
</div>
<div class="card-body">
<p class="card-text">Upload your CSV files to populate the database. The system will create the necessary tables and import the data.</p>
<?php if (!empty($messages)): ?>
<div class="mb-4">
<?php foreach ($messages as $type => $msgs): ?>
<?php foreach ($msgs as $msg): ?>
<div class="alert alert-<?php echo htmlspecialchars($type); ?>" role="alert">
<?php echo htmlspecialchars($msg); ?>
</div>
<?php endforeach; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="import.php" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="lifecycle" class="form-label">Lifecycle CSV File</label>
<input class="form-control" type="file" id="lifecycle" name="lifecycle">
<div class="form-text">Fields: product_id, custcode, first_ship_month, last_ship_month, total_ship, avg_ship, maxstores, mock_pct, store_count, runrate</div>
</div>
<div class="mb-3">
<label for="customers" class="form-label">Customers CSV File</label>
<input class="form-control" type="file" id="customers" name="customers">
<div class="form-text">Fields: custcode, custname, channel, country, maxstores</div>
</div>
<div class="mb-3">
<label for="products" class="form-label">Products CSV File</label>
<input class="form-control" type="file" id="products" name="products">
<div class="form-text">Fields: product_id, prodname, brand, subbrand, sub_brand.abbreviation, listprice</div>
</div>
<div class="mb-3">
<label for="shipments" class="form-label">Shipments CSV File</label>
<input class="form-control" type="file" id="shipments" name="shipments">
<div class="form-text">Fields: product_id, month, custcode, ship</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg"><i class="bi bi-cloud-upload-fill"></i> Preview & Import</button>
</div>
</form>
</div>
<div class="card-footer text-muted">
Ensure your CSV files have a header row that matches the specified fields.
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

214
index.php
View File

@ -1,150 +1,82 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'A CRUD application for managing shipments.';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shipments Admin</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Shipments Admin</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/import.php">Import Data</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/manage_lifecycle.php">Manage Lifecycle</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5">
<div class="p-5 mb-4 bg-light rounded-3">
<div class="container-fluid py-5">
<h1 class="display-5 fw-bold">Welcome to Shipments Admin</h1>
<p class="col-md-8 fs-4">This is your starting point for managing customers, products, and shipments. Use the navigation bar to get started.</p>
<a href="/import.php" class="btn btn-primary btn-lg" role="button">
<i class="bi bi-cloud-upload"></i> Start by Importing Data
</a>
</div>
</div>
<div class="row align-items-md-stretch">
<div class="col-md-6">
<div class="h-100 p-5 text-white bg-dark rounded-3">
<h2>View Data</h2>
<p>Once imported, you will be able to browse, search, and manage your data here.</p>
<button class="btn btn-outline-light" type="button" disabled>View Customers (Coming Soon)</button>
</div>
</div>
<div class="col-md-6">
<div class="h-100 p-5 bg-light border rounded-3">
<h2>Next Steps</h2>
<p>After importing your data, the next step will be to build the user interface for creating, reading, updating, and deleting records.</p>
<p>Let me know what functionality you'd like to see first!</p>
</div>
</div>
</div>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

104
manage_lifecycle.php Normal file
View File

@ -0,0 +1,104 @@
<?php
require_once 'db/config.php';
// Fetch all customers for the dropdown
try {
$pdoconn = db();
$stmt = $pdoconn->prepare('SELECT custcode, custname FROM customers ORDER BY custname ASC');
$stmt->execute();
$customers = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "Error fetching customers: " . $e->getMessage();
$customers = [];
}
$selected_custcode = $_GET['custcode'] ?? null;
$lifecycle_data = [];
if ($selected_custcode) {
try {
$stmt = $pdoconn->prepare('
SELECT l.id, l.product_id, p.prodname, DATE(l.first_ship_month) as first_ship_month, DATE(l.last_ship_month) as last_ship_month, l.store_count, l.runrate
FROM lifecycle l
JOIN products p ON l.product_id = p.product_id
WHERE l.custcode = :custcode
ORDER BY l.first_ship_month DESC
');
$stmt->execute(['custcode' => $selected_custcode]);
$lifecycle_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "Error fetching lifecycle data: " . $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Lifecycle Data</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.25/css/jquery.dataTables.min.css">
</head>
<body>
<div class="container mt-5">
<h1>Manage Lifecycle Data</h1>
<p>Select a customer to view their product lifecycle information.</p>
<form method="GET" action="manage_lifecycle.php" class="form-inline mb-4">
<div class="form-group">
<label for="custcode" class="mr-2">Select Customer:</label>
<select name="custcode" id="custcode" class="form-control" onchange="this.form.submit()">
<option value="">-- Select a Customer --</option>
<?php foreach ($customers as $customer): ?>
<option value="<?= htmlspecialchars($customer['custcode']) ?>" <?= $selected_custcode == $customer['custcode'] ? 'selected' : '' ?>>
<?= htmlspecialchars($customer['custname']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</form>
<?php if ($selected_custcode && !empty($lifecycle_data)): ?>
<h3>Lifecycle Records for <?= htmlspecialchars($customers[array_search($selected_custcode, array_column($customers, 'custcode'))]['custname']) ?></h3>
<table id="lifecycleTable" class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Product Name</th>
<th>First Ship Month</th>
<th>Last Ship Month</th>
<th>Store Count</th>
<th>Run Rate</th>
</tr>
</thead>
<tbody>
<?php foreach ($lifecycle_data as $row): ?>
<tr>
<td><?= htmlspecialchars($row['id']) ?></td>
<td><?= htmlspecialchars($row['prodname']) ?></td>
<td><?= htmlspecialchars($row['first_ship_month']) ?></td>
<td><?= htmlspecialchars($row['last_ship_month']) ?></td>
<td><?= htmlspecialchars($row['store_count']) ?></td>
<td><?= htmlspecialchars($row['runrate']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php elseif ($selected_custcode && empty($lifecycle_data)): ?>
<div class="alert alert-info">
No lifecycle records found for the selected customer.
</div>
<?php endif; ?>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function() {
$('#lifecycleTable').DataTable();
});
</script>
</body>
</html>

97
uploads/customers.csv Normal file
View File

@ -0,0 +1,97 @@
custcode,custname,channel,country,maxstores
100006,7 ELEVEN,Convenience,US,1
100021,COUCHE TARD,Convenience,Canada,1
100063,RACETRAC,Convenience,US,1
100083,WALLACE & CAREY INC,Distributor,Canada,1
6800327,Pioneer Distributing Inc,Distributor,US,1
6803002,RR DISTRIBUTORS,Distributor,US,1
100009,ARAMARK,Foodservice,US,1
100088,WENDYS INTERNATIONAL,Foodservice,US,1
100010,AVENDRA,Foodservice,US,1
100037,FOODBUY LLC,Foodservice,US,1
100060,PREMIER PURCHASING,Foodservice,US,1
100091,ASG CO-OP,Grocery Retail,US,1
100094,ASSOCIATED WSL INC,Grocery Retail,US,1
100098,WHOLE FOODS MARKET,Grocery Retail,US,1
100095,BTB PLANTS,Miscellaneous,US,1
100051,MCKESSON,Miscellaneous,US,1
100042,HILTON,Miscellaneous,US,1
100055,INTERCOMPANY,Miscellaneous,US,1
100043,HQ AAFES,Miscellaneous,US,1
6803538,Ontario Natural Food Company,Miscellaneous,Canada,1
100032,ESSENDANT CO,Miscellaneous,US,1
100059,PILOT FLYING J,Miscellaneous,US,1
100076,TRAVEL CENTERS,Miscellaneous,US,1
100066,WALMART,Grocery Retail,US,4593
100005,ALL OTHER RETAILERS,Grocery Retail,US,8000
100020,COSTCO,Club,US,639
100061,PUBLIX,Grocery Retail,US,1467
100028,DOLLAR TREE,Grocery Retail,US,9007
100081,WAKEFERN,Grocery Retail,US,372
100011,BJ'S,Club,US,278
100026,KROGER,Grocery Retail,US,1249
100007,ALBERTSONS,Grocery Retail,US,2271
100015,C&S,Grocery Retail,US,30
100041,HEB,Grocery Retail,US,344
100047,JETRO,Miscellaneous,US,12
100016,AHOLD,Grocery Retail,US,7659
100074,SYSCO,Foodservice,US,340
100025,DEMOULAS,Grocery Retail,US,95
100027,DOLLAR GENERAL,Grocery Retail,US,20388
100078,US FOODSERVICE,Foodservice,US,98
100033,FAMILY DOLLAR,Grocery Retail,US,7338
100030,EAST COAST FOODS DIST,Foodservice,US,4500
100073,SUPERVALU,Grocery Retail,US,43
100008,AMAZON,E-Commerce,US,58
100062,QUIK TRIP,Convenience,US,1195
100052,MCLANE,Distributor,US,110000
100075,TARGET,Grocery Retail,US,1995
100013,BOZZUTO'S,Grocery Retail,US,250
100019,COREMARK,Distributor,US,50000
100023,CVS,Grocery Retail,US,8987
100082,WALGREENS,Grocery Retail,US,8055
100093,ASSOCIATED WSL GROCERS,Grocery Retail,US,4000
100049,LOBLAWS INC,Grocery Retail,Canada,2000
100024,DECA,Miscellaneous,US,300000
100017,INDEPENDENTS,Grocery Retail,US,100000
100072,STAPLES,Miscellaneous,US,922
100039,GORDON FOOD SVC,Foodservice,US,199
100077,UNFI,Miscellaneous,US,30000
100080,VISTAR CORP,Miscellaneous,US,24
6803637,MICHAEL LEWIS,Distributor,US,3
100086,WEGMANS,Grocery Retail,US,114
100054,NATIONAL DCP LLC,Foodservice,US,10000
100084,WAWA,Convenience,US,1174
100053,MEIJER,Grocery Retail,US,274
100048,KRASDALE FOODS,Grocery Retail,US,2500
100069,SHEETZ,Convenience,US,815
100056,OFFICE DEPOT,Miscellaneous,US,827
100012,BK MILLER CO,Distributor,US,800
100092,ASSOCIATED GROCERS,Grocery Retail,US,166
100022,CUMBERLAND FARMS,Convenience,US,580
100045,J POLEP CHICOPEE,Distributor,US,4000
100057,PFG AFI,Foodservice,US,300000
100071,SPARTAN STORES,Grocery Retail,US,196
100067,OVERWAITEA FOOD GROUP,Grocery Retail,Canada,145
100031,EBY BROWN,Distributor,US,10000
100038,GENERAL TRADING,Grocery Retail,US,1500
6803974,FRESH DIRECT,E-Commerce,US,100
100085,WB MASON,Miscellaneous,US,60
100044,HT HACKNEY,Distributor,US,20000
100087,WEIS MARKETS,Grocery Retail,US,204
100034,SOBEYS INC,Grocery Retail,Canada,1500
6800114,Dairyland USA,Foodservice,US,10
100058,PIGGLY WIGGLY,Grocery Retail,US,498
100064,REINHART,Foodservice,US,42
6801041,Gold Star Foods,Foodservice,US,6
100035,FEDERATED CO-OP,Grocery Retail,Canada,10
100065,RITE AID,Grocery Retail,US,89
100014,BROOKSHIRE GROCER,Grocery Retail,US,109
100036,METRO RICHELIEU INC,Grocery Retail,Canada,953
100018,CIRCLE K,Convenience,US,6831
6801135,Industrial & Supply,Miscellaneous,US,100
100050,LONDON DRUGS LTD,Grocery Retail,Canada,80
100070,SODEXO,Foodservice,US,169
100079,Imperial Dade Inc,Foodservice,US,1
100046,J RABBA,Convenience,Canada,37
100089,WINCO,Grocery Retail,US,144
1 custcode custname channel country maxstores
2 100006 7 ELEVEN Convenience US 1
3 100021 COUCHE TARD Convenience Canada 1
4 100063 RACETRAC Convenience US 1
5 100083 WALLACE & CAREY INC Distributor Canada 1
6 6800327 Pioneer Distributing Inc Distributor US 1
7 6803002 RR DISTRIBUTORS Distributor US 1
8 100009 ARAMARK Foodservice US 1
9 100088 WENDYS INTERNATIONAL Foodservice US 1
10 100010 AVENDRA Foodservice US 1
11 100037 FOODBUY LLC Foodservice US 1
12 100060 PREMIER PURCHASING Foodservice US 1
13 100091 ASG CO-OP Grocery Retail US 1
14 100094 ASSOCIATED WSL INC Grocery Retail US 1
15 100098 WHOLE FOODS MARKET Grocery Retail US 1
16 100095 BTB PLANTS Miscellaneous US 1
17 100051 MCKESSON Miscellaneous US 1
18 100042 HILTON Miscellaneous US 1
19 100055 INTERCOMPANY Miscellaneous US 1
20 100043 HQ AAFES Miscellaneous US 1
21 6803538 Ontario Natural Food Company Miscellaneous Canada 1
22 100032 ESSENDANT CO Miscellaneous US 1
23 100059 PILOT FLYING J Miscellaneous US 1
24 100076 TRAVEL CENTERS Miscellaneous US 1
25 100066 WALMART Grocery Retail US 4593
26 100005 ALL OTHER RETAILERS Grocery Retail US 8000
27 100020 COSTCO Club US 639
28 100061 PUBLIX Grocery Retail US 1467
29 100028 DOLLAR TREE Grocery Retail US 9007
30 100081 WAKEFERN Grocery Retail US 372
31 100011 BJ'S Club US 278
32 100026 KROGER Grocery Retail US 1249
33 100007 ALBERTSONS Grocery Retail US 2271
34 100015 C&S Grocery Retail US 30
35 100041 HEB Grocery Retail US 344
36 100047 JETRO Miscellaneous US 12
37 100016 AHOLD Grocery Retail US 7659
38 100074 SYSCO Foodservice US 340
39 100025 DEMOULAS Grocery Retail US 95
40 100027 DOLLAR GENERAL Grocery Retail US 20388
41 100078 US FOODSERVICE Foodservice US 98
42 100033 FAMILY DOLLAR Grocery Retail US 7338
43 100030 EAST COAST FOODS DIST Foodservice US 4500
44 100073 SUPERVALU Grocery Retail US 43
45 100008 AMAZON E-Commerce US 58
46 100062 QUIK TRIP Convenience US 1195
47 100052 MCLANE Distributor US 110000
48 100075 TARGET Grocery Retail US 1995
49 100013 BOZZUTO'S Grocery Retail US 250
50 100019 COREMARK Distributor US 50000
51 100023 CVS Grocery Retail US 8987
52 100082 WALGREENS Grocery Retail US 8055
53 100093 ASSOCIATED WSL GROCERS Grocery Retail US 4000
54 100049 LOBLAWS INC Grocery Retail Canada 2000
55 100024 DECA Miscellaneous US 300000
56 100017 INDEPENDENTS Grocery Retail US 100000
57 100072 STAPLES Miscellaneous US 922
58 100039 GORDON FOOD SVC Foodservice US 199
59 100077 UNFI Miscellaneous US 30000
60 100080 VISTAR CORP Miscellaneous US 24
61 6803637 MICHAEL LEWIS Distributor US 3
62 100086 WEGMANS Grocery Retail US 114
63 100054 NATIONAL DCP LLC Foodservice US 10000
64 100084 WAWA Convenience US 1174
65 100053 MEIJER Grocery Retail US 274
66 100048 KRASDALE FOODS Grocery Retail US 2500
67 100069 SHEETZ Convenience US 815
68 100056 OFFICE DEPOT Miscellaneous US 827
69 100012 BK MILLER CO Distributor US 800
70 100092 ASSOCIATED GROCERS Grocery Retail US 166
71 100022 CUMBERLAND FARMS Convenience US 580
72 100045 J POLEP CHICOPEE Distributor US 4000
73 100057 PFG AFI Foodservice US 300000
74 100071 SPARTAN STORES Grocery Retail US 196
75 100067 OVERWAITEA FOOD GROUP Grocery Retail Canada 145
76 100031 EBY BROWN Distributor US 10000
77 100038 GENERAL TRADING Grocery Retail US 1500
78 6803974 FRESH DIRECT E-Commerce US 100
79 100085 WB MASON Miscellaneous US 60
80 100044 HT HACKNEY Distributor US 20000
81 100087 WEIS MARKETS Grocery Retail US 204
82 100034 SOBEYS INC Grocery Retail Canada 1500
83 6800114 Dairyland USA Foodservice US 10
84 100058 PIGGLY WIGGLY Grocery Retail US 498
85 100064 REINHART Foodservice US 42
86 6801041 Gold Star Foods Foodservice US 6
87 100035 FEDERATED CO-OP Grocery Retail Canada 10
88 100065 RITE AID Grocery Retail US 89
89 100014 BROOKSHIRE GROCER Grocery Retail US 109
90 100036 METRO RICHELIEU INC Grocery Retail Canada 953
91 100018 CIRCLE K Convenience US 6831
92 6801135 Industrial & Supply Miscellaneous US 100
93 100050 LONDON DRUGS LTD Grocery Retail Canada 80
94 100070 SODEXO Foodservice US 169
95 100079 Imperial Dade Inc Foodservice US 1
96 100046 J RABBA Convenience Canada 37
97 100089 WINCO Grocery Retail US 144

5850
uploads/lifecycle.csv Normal file

File diff suppressed because it is too large Load Diff

4612
uploads/products.csv Normal file

File diff suppressed because it is too large Load Diff

71999
uploads/shipments.csv Normal file

File diff suppressed because it is too large Load Diff