v1.0.0 - Initial backup

This commit is contained in:
2026-05-16 23:00:37 -05:00
commit bf8b6225a3
114 changed files with 21120 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* Tom's Java Jive - Generate PWA Icons
* Creates basic SVG icons for PWA manifest
*/
$sizes = [72, 96, 128, 144, 152, 192, 384, 512];
$outputDir = __DIR__ . '/../assets/icons/';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
// Generate a simple coffee cup icon as SVG
$svgTemplate = '<?xml version="1.0" encoding="UTF-8"?>
<svg width="{SIZE}" height="{SIZE}" viewBox="0 0 {SIZE} {SIZE}" xmlns="http://www.w3.org/2000/svg">
<rect width="{SIZE}" height="{SIZE}" rx="{RADIUS}" fill="#FF5E1A"/>
<g transform="translate({OFFSET}, {OFFSET}) scale({SCALE})">
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
</g>
</svg>';
foreach ($sizes as $size) {
$radius = floor($size * 0.15);
$offset = floor($size * 0.1);
$scale = $size / 100;
$svg = str_replace(
['{SIZE}', '{RADIUS}', '{OFFSET}', '{SCALE}'],
[$size, $radius, $offset, $scale],
$svgTemplate
);
// Save as SVG
file_put_contents($outputDir . "icon-{$size}.svg", $svg);
echo "Generated icon-{$size}.svg\n";
}
// Also create badge icon
$badgeSvg = '<?xml version="1.0" encoding="UTF-8"?>
<svg width="72" height="72" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
<circle cx="36" cy="36" r="36" fill="#FF5E1A"/>
<text x="36" y="45" font-family="Arial, sans-serif" font-size="28" font-weight="bold" fill="white" text-anchor="middle">TJ</text>
</svg>';
file_put_contents($outputDir . 'badge-72.svg', $badgeSvg);
echo "Generated badge-72.svg\n";
echo "\nDone! Icons generated in: $outputDir\n";
echo "Note: For production, convert SVGs to PNGs using an image tool.\n";
+435
View File
@@ -0,0 +1,435 @@
<?php
/**
* Tom's Java Jive - MongoDB to MySQL Migration Script
*
* This script migrates data from the existing MongoDB database to MySQL.
* Run this script once after setting up the MySQL database with schema.sql
*
* Usage: php migrate_from_mongodb.php [mongodb_url] [mongodb_dbname]
*
* Prerequisites:
* - PHP MongoDB extension installed
* - MySQL database created and schema.sql executed
* - database.php configured with MySQL credentials
*/
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../includes/db.php';
// Configuration
$mongoUrl = $argv[1] ?? getenv('MONGO_URL') ?: 'mongodb://localhost:27017';
$mongoDbName = $argv[2] ?? getenv('MONGO_DB_NAME') ?: 'tomsjavajive';
echo "==============================================\n";
echo "Tom's Java Jive - MongoDB to MySQL Migration\n";
echo "==============================================\n\n";
// Check for MongoDB extension
if (!extension_loaded('mongodb')) {
echo "ERROR: MongoDB PHP extension is not installed.\n";
echo "Install it with: pecl install mongodb\n";
echo "Or: apt-get install php-mongodb\n\n";
echo "Alternatively, export data from MongoDB using mongodump/mongoexport\n";
echo "and import it manually.\n";
exit(1);
}
try {
// Connect to MongoDB
echo "Connecting to MongoDB at: $mongoUrl\n";
$mongoClient = new MongoDB\Client($mongoUrl);
$mongodb = $mongoClient->selectDatabase($mongoDbName);
echo "Connected to MongoDB database: $mongoDbName\n\n";
// Test MySQL connection
$mysql = db();
echo "Connected to MySQL database: " . DB_NAME . "\n\n";
} catch (Exception $e) {
echo "ERROR: " . $e->getMessage() . "\n";
exit(1);
}
/**
* Helper function to convert MongoDB document to array
*/
function docToArray($doc) {
if ($doc instanceof MongoDB\Model\BSONDocument || $doc instanceof MongoDB\Model\BSONArray) {
return json_decode(json_encode($doc), true);
}
return $doc;
}
/**
* Helper function to safely get nested value
*/
function safeGet($array, $key, $default = null) {
return isset($array[$key]) ? $array[$key] : $default;
}
/**
* Migrate Admin Users
*/
function migrateAdminUsers($mongodb, $mysql) {
echo "Migrating admin users...\n";
$cursor = $mongodb->admin_users->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
try {
$mysql->insert('admin_users', [
'user_id' => safeGet($doc, 'user_id') ?: safeGet($doc, 'admin_id'),
'email' => safeGet($doc, 'email'),
'password_hash' => safeGet($doc, 'password_hash'),
'name' => safeGet($doc, 'name'),
'picture' => safeGet($doc, 'picture'),
'is_admin' => 1,
'is_master' => safeGet($doc, 'is_master') ? 1 : 0,
'permissions' => json_encode(safeGet($doc, 'permissions', [])),
'created_at' => safeGet($doc, 'created_at') ?: date('Y-m-d H:i:s')
]);
$count++;
} catch (Exception $e) {
echo " Warning: Could not migrate admin {$doc['email']}: " . $e->getMessage() . "\n";
}
}
echo " Migrated $count admin users\n\n";
}
/**
* Migrate Customers
*/
function migrateCustomers($mongodb, $mysql) {
echo "Migrating customers...\n";
$cursor = $mongodb->customers->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
try {
$mysql->insert('customers', [
'customer_id' => safeGet($doc, 'customer_id'),
'email' => safeGet($doc, 'email'),
'password_hash' => safeGet($doc, 'password_hash'),
'name' => safeGet($doc, 'name'),
'phone' => safeGet($doc, 'phone'),
'shipping_address' => json_encode(safeGet($doc, 'shipping_address')),
'billing_address' => json_encode(safeGet($doc, 'billing_address')),
'wallet_balance' => safeGet($doc, 'account_balance', 0),
'reward_points' => safeGet($doc, 'loyalty_points', 0),
'is_guest' => safeGet($doc, 'is_guest') ? 1 : 0,
'created_via' => safeGet($doc, 'created_via', 'web'),
'created_at' => safeGet($doc, 'created_at') ?: date('Y-m-d H:i:s')
]);
// Migrate wallet transactions
$transactions = safeGet($doc, 'account_transactions', []);
foreach ($transactions as $txn) {
try {
$mysql->insert('wallet_transactions', [
'transaction_id' => safeGet($txn, 'transaction_id') ?: 'txn_' . bin2hex(random_bytes(6)),
'customer_id' => $doc['customer_id'],
'amount' => safeGet($txn, 'amount', 0),
'balance_after' => safeGet($txn, 'balance_after', 0),
'type' => mapTransactionType(safeGet($txn, 'type')),
'description' => safeGet($txn, 'description'),
'order_id' => safeGet($txn, 'order_id'),
'created_at' => safeGet($txn, 'date') ?: date('Y-m-d H:i:s')
]);
} catch (Exception $e) {
// Skip transaction errors
}
}
$count++;
} catch (Exception $e) {
echo " Warning: Could not migrate customer {$doc['email']}: " . $e->getMessage() . "\n";
}
}
echo " Migrated $count customers\n\n";
}
function mapTransactionType($type) {
$map = [
'deposit' => 'deposit',
'payment' => 'withdrawal',
'gift_card_purchase' => 'purchase',
'admin_deposit' => 'deposit',
'pos_payment' => 'withdrawal',
'refund' => 'refund'
];
return isset($map[$type]) ? $map[$type] : 'deposit';
}
/**
* Migrate Products
*/
function migrateProducts($mongodb, $mysql) {
echo "Migrating products...\n";
$cursor = $mongodb->products->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
// Map blend_type to category
$category = safeGet($doc, 'blend_type') ?: safeGet($doc, 'category') ?: 'coffee';
try {
$mysql->insert('products', [
'product_id' => safeGet($doc, 'product_id'),
'name' => safeGet($doc, 'name'),
'description' => safeGet($doc, 'description'),
'price' => safeGet($doc, 'price', 0),
'sale_price' => safeGet($doc, 'sale_price'),
'sku' => safeGet($doc, 'sku'),
'barcode' => safeGet($doc, 'barcode'),
'category' => $category,
'tags' => json_encode([safeGet($doc, 'roast_level'), safeGet($doc, 'product_type')]),
'images' => json_encode([safeGet($doc, 'image_url')]),
'stock' => safeGet($doc, 'stock_quantity', 100),
'low_stock_threshold' => safeGet($doc, 'low_stock_threshold', 10),
'weight' => safeGet($doc, 'weight'),
'is_active' => safeGet($doc, 'in_stock', true) ? 1 : 0,
'is_featured' => safeGet($doc, 'on_sale', false) ? 1 : 0,
'created_at' => safeGet($doc, 'created_at') ?: date('Y-m-d H:i:s')
]);
$count++;
} catch (Exception $e) {
echo " Warning: Could not migrate product {$doc['name']}: " . $e->getMessage() . "\n";
}
}
echo " Migrated $count products\n\n";
}
/**
* Migrate Orders
*/
function migrateOrders($mongodb, $mysql) {
echo "Migrating orders...\n";
$cursor = $mongodb->orders->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
try {
$mysql->insert('orders', [
'order_id' => safeGet($doc, 'order_id'),
'order_number' => safeGet($doc, 'order_number'),
'customer_id' => safeGet($doc, 'customer_id'),
'customer_email' => safeGet($doc, 'customer_email'),
'customer_name' => safeGet($doc, 'customer_name'),
'items' => json_encode(safeGet($doc, 'items', [])),
'subtotal' => safeGet($doc, 'subtotal', 0),
'shipping_cost' => safeGet($doc, 'shipping_cost', 0),
'tax' => safeGet($doc, 'tax', 0),
'discount' => safeGet($doc, 'discount_amount', 0),
'total' => safeGet($doc, 'total', 0),
'shipping_address' => json_encode(safeGet($doc, 'shipping_address')),
'shipping_method' => safeGet($doc, 'shipping_method'),
'payment_method' => safeGet($doc, 'payment_method'),
'payment_status' => safeGet($doc, 'payment_status', 'pending'),
'order_status' => safeGet($doc, 'order_status', 'pending'),
'stripe_payment_intent' => safeGet($doc, 'checkout_session_id'),
'tracking_number' => safeGet($doc, 'tracking_number'),
'notes' => safeGet($doc, 'pos_notes'),
'is_pos_order' => safeGet($doc, 'pos_order') ? 1 : 0,
'created_at' => safeGet($doc, 'created_at') ?: date('Y-m-d H:i:s')
]);
// Also insert into order_items for reporting
$items = safeGet($doc, 'items', []);
foreach ($items as $item) {
try {
$mysql->insert('order_items', [
'order_id' => $doc['order_id'],
'product_id' => safeGet($item, 'product_id'),
'name' => safeGet($item, 'name'),
'price' => safeGet($item, 'price', 0),
'quantity' => safeGet($item, 'quantity', 1),
'total' => safeGet($item, 'total', 0)
]);
} catch (Exception $e) {
// Skip item errors
}
}
$count++;
} catch (Exception $e) {
echo " Warning: Could not migrate order {$doc['order_number']}: " . $e->getMessage() . "\n";
}
}
echo " Migrated $count orders\n\n";
}
/**
* Migrate Gift Cards
*/
function migrateGiftCards($mongodb, $mysql) {
echo "Migrating gift cards...\n";
$cursor = $mongodb->gift_cards->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
try {
$mysql->insert('gift_cards', [
'gift_card_id' => safeGet($doc, 'gift_card_id'),
'code' => safeGet($doc, 'code'),
'initial_balance' => safeGet($doc, 'initial_balance', 0),
'current_balance' => safeGet($doc, 'current_balance', 0),
'purchaser_email' => safeGet($doc, 'purchased_by_email'),
'recipient_email' => safeGet($doc, 'recipient_email'),
'recipient_name' => safeGet($doc, 'recipient_name'),
'message' => safeGet($doc, 'message'),
'is_active' => safeGet($doc, 'status') !== 'disabled' ? 1 : 0,
'created_at' => safeGet($doc, 'created_at') ?: date('Y-m-d H:i:s')
]);
$count++;
} catch (Exception $e) {
echo " Warning: Could not migrate gift card {$doc['code']}: " . $e->getMessage() . "\n";
}
}
echo " Migrated $count gift cards\n\n";
}
/**
* Migrate Reviews
*/
function migrateReviews($mongodb, $mysql) {
echo "Migrating reviews...\n";
$cursor = $mongodb->product_reviews->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
try {
$mysql->insert('reviews', [
'review_id' => safeGet($doc, 'review_id'),
'product_id' => safeGet($doc, 'product_id'),
'customer_id' => safeGet($doc, 'customer_id'),
'customer_name' => safeGet($doc, 'customer_name'),
'customer_email' => safeGet($doc, 'customer_email'),
'rating' => safeGet($doc, 'rating', 5),
'title' => safeGet($doc, 'title'),
'comment' => safeGet($doc, 'comment'),
'is_verified_purchase' => safeGet($doc, 'verified_purchase') ? 1 : 0,
'is_approved' => safeGet($doc, 'status') === 'approved' ? 1 : 0,
'created_at' => safeGet($doc, 'created_at') ?: date('Y-m-d H:i:s')
]);
$count++;
} catch (Exception $e) {
echo " Warning: Could not migrate review: " . $e->getMessage() . "\n";
}
}
echo " Migrated $count reviews\n\n";
}
/**
* Migrate Email Subscribers
*/
function migrateSubscribers($mongodb, $mysql) {
echo "Migrating email subscribers...\n";
$cursor = $mongodb->email_subscribers->find();
$count = 0;
foreach ($cursor as $doc) {
$doc = docToArray($doc);
try {
$mysql->insert('email_subscribers', [
'email' => safeGet($doc, 'email'),
'name' => safeGet($doc, 'name'),
'is_active' => safeGet($doc, 'status') === 'active' ? 1 : 0,
'source' => safeGet($doc, 'source', 'website'),
'created_at' => safeGet($doc, 'subscribed_at') ?: date('Y-m-d H:i:s')
]);
$count++;
} catch (Exception $e) {
// Skip duplicate emails
}
}
echo " Migrated $count subscribers\n\n";
}
/**
* Migrate Settings
*/
function migrateSettings($mongodb, $mysql) {
echo "Migrating settings...\n";
// Shipping settings
$shipping = $mongodb->shipping_settings->findOne(['settings_id' => 'shipping_settings']);
if ($shipping) {
$shipping = docToArray($shipping);
$mysql->query(
"UPDATE settings SET setting_value = :val WHERE setting_key = 'shipping'",
['val' => json_encode([
'flat_rate_enabled' => safeGet($shipping, 'flat_rate_enabled', true),
'flat_rate_amount' => safeGet($shipping, 'flat_rate_amount', 5.99),
'free_shipping_threshold' => safeGet($shipping, 'free_shipping_threshold', 50),
'weight_based_enabled' => safeGet($shipping, 'weight_based_enabled', false)
])]
);
echo " Migrated shipping settings\n";
}
// Payment settings
$payment = $mongodb->payment_settings->findOne(['settings_id' => 'payment_settings']);
if ($payment) {
$payment = docToArray($payment);
$mysql->query(
"UPDATE settings SET setting_value = :val WHERE setting_key = 'payment'",
['val' => json_encode([
'stripe_enabled' => safeGet($payment, 'stripe_enabled', true),
'paypal_enabled' => safeGet($payment, 'paypal_enabled', false),
'cod_enabled' => false
])]
);
echo " Migrated payment settings\n";
}
echo "\n";
}
// Run migrations
echo "Starting migration...\n\n";
try {
migrateAdminUsers($mongodb, $mysql);
migrateCustomers($mongodb, $mysql);
migrateProducts($mongodb, $mysql);
migrateOrders($mongodb, $mysql);
migrateGiftCards($mongodb, $mysql);
migrateReviews($mongodb, $mysql);
migrateSubscribers($mongodb, $mysql);
migrateSettings($mongodb, $mysql);
echo "==============================================\n";
echo "Migration completed successfully!\n";
echo "==============================================\n";
} catch (Exception $e) {
echo "\nMIGRATION ERROR: " . $e->getMessage() . "\n";
exit(1);
}
+37
View File
@@ -0,0 +1,37 @@
-- Migration: Add wishlist table and addresses column to customers
-- Run this in phpMyAdmin to add the new features
-- Add addresses column to customers table
ALTER TABLE `customers` ADD COLUMN `addresses` JSON DEFAULT NULL AFTER `billing_address`;
-- Add preferences column to customers table
ALTER TABLE `customers` ADD COLUMN `preferences` JSON DEFAULT NULL AFTER `addresses`;
-- Add is_active column to customers table if not exists
ALTER TABLE `customers` ADD COLUMN `is_active` TINYINT(1) DEFAULT 1 AFTER `preferences`;
-- Create wishlist table
CREATE TABLE IF NOT EXISTS `wishlist` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_id` VARCHAR(50) NOT NULL,
`product_id` VARCHAR(50) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `unique_wishlist` (`customer_id`, `product_id`),
INDEX `idx_customer` (`customer_id`),
INDEX `idx_product` (`product_id`),
FOREIGN KEY (`customer_id`) REFERENCES `customers`(`customer_id`) ON DELETE CASCADE,
FOREIGN KEY (`product_id`) REFERENCES `products`(`product_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Add reorder_level column to products if not exists
ALTER TABLE `products` ADD COLUMN `reorder_level` INT DEFAULT 10 AFTER `low_stock_threshold`;
-- Add slug column to products if not exists
ALTER TABLE `products` ADD COLUMN `slug` VARCHAR(255) DEFAULT NULL AFTER `name`;
ALTER TABLE `products` ADD INDEX `idx_slug` (`slug`);
-- Update existing products to have slugs based on name
UPDATE `products` SET `slug` = LOWER(REPLACE(REPLACE(REPLACE(`name`, ' ', '-'), "'", ''), '"', '')) WHERE `slug` IS NULL;
-- Add is_pos_order to orders table
ALTER TABLE `orders` ADD COLUMN `is_pos_order` TINYINT(1) DEFAULT 0 AFTER `notes`;
+96
View File
@@ -0,0 +1,96 @@
-- Migration: Add tables for push notifications, loyalty program, and integration settings
-- Run this in phpMyAdmin
-- Push notification subscriptions
CREATE TABLE IF NOT EXISTS `push_subscriptions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_id` VARCHAR(50) DEFAULT NULL,
`endpoint` TEXT NOT NULL,
`p256dh_key` VARCHAR(255) NOT NULL,
`auth_key` VARCHAR(255) NOT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_customer` (`customer_id`),
INDEX `idx_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Push notifications queue
CREATE TABLE IF NOT EXISTS `push_notifications` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`notification_id` VARCHAR(50) NOT NULL UNIQUE,
`subscription_endpoint` TEXT NOT NULL,
`payload` TEXT NOT NULL,
`status` ENUM('pending', 'sent', 'failed') DEFAULT 'pending',
`error_message` TEXT DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`sent_at` TIMESTAMP NULL DEFAULT NULL,
INDEX `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Loyalty transactions history
CREATE TABLE IF NOT EXISTS `loyalty_transactions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`transaction_id` VARCHAR(50) NOT NULL UNIQUE,
`customer_id` VARCHAR(50) NOT NULL,
`points` INT NOT NULL,
`type` ENUM('earn', 'redeem', 'tier_upgrade', 'birthday_bonus', 'referral_bonus', 'referral_welcome', 'adjustment', 'expiry') NOT NULL,
`description` VARCHAR(255) DEFAULT NULL,
`reference_amount` DECIMAL(10,2) DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_customer` (`customer_id`),
INDEX `idx_type` (`type`),
INDEX `idx_created` (`created_at`),
FOREIGN KEY (`customer_id`) REFERENCES `customers`(`customer_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Add lifetime_points and loyalty_tier to customers
ALTER TABLE `customers`
ADD COLUMN IF NOT EXISTS `lifetime_points` INT DEFAULT 0 AFTER `reward_points`,
ADD COLUMN IF NOT EXISTS `loyalty_tier` ENUM('bronze', 'silver', 'gold', 'platinum') DEFAULT 'bronze' AFTER `lifetime_points`,
ADD COLUMN IF NOT EXISTS `birthday` DATE DEFAULT NULL AFTER `loyalty_tier`,
ADD COLUMN IF NOT EXISTS `referral_code` VARCHAR(20) DEFAULT NULL AFTER `birthday`,
ADD COLUMN IF NOT EXISTS `referred_by` VARCHAR(50) DEFAULT NULL AFTER `referral_code`;
-- Add unique index on referral_code
ALTER TABLE `customers` ADD UNIQUE INDEX IF NOT EXISTS `idx_referral_code` (`referral_code`);
-- Update settings table with integration keys (INSERT IGNORE to not overwrite existing)
INSERT IGNORE INTO `settings` (`setting_key`, `setting_value`, `updated_at`) VALUES
('sendgrid_api_key', '', NOW()),
('sendgrid_from_email', 'noreply@tomsjavajive.com', NOW()),
('sendgrid_from_name', 'Tom''s Java Jive', NOW()),
('twilio_account_sid', '', NOW()),
('twilio_auth_token', '', NOW()),
('twilio_phone_number', '', NOW()),
('vapid_public_key', '', NOW()),
('vapid_private_key', '', NOW()),
('loyalty_enabled', '1', NOW()),
('email_notifications_enabled', '1', NOW()),
('sms_notifications_enabled', '0', NOW()),
('push_notifications_enabled', '1', NOW());
-- Add Stripe checkout session column to orders
ALTER TABLE `orders` ADD COLUMN IF NOT EXISTS `stripe_checkout_session` VARCHAR(255) DEFAULT NULL AFTER `stripe_payment_intent`;
-- Payment transactions table for tracking payment attempts
CREATE TABLE IF NOT EXISTS `payment_transactions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`transaction_id` VARCHAR(50) NOT NULL UNIQUE,
`order_id` VARCHAR(50) NOT NULL,
`customer_id` VARCHAR(50) DEFAULT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`currency` VARCHAR(3) DEFAULT 'USD',
`payment_method` VARCHAR(50) DEFAULT 'stripe',
`stripe_session_id` VARCHAR(255) DEFAULT NULL,
`stripe_payment_intent` VARCHAR(255) DEFAULT NULL,
`status` ENUM('initiated', 'pending', 'processing', 'succeeded', 'failed', 'cancelled', 'refunded') DEFAULT 'initiated',
`metadata` JSON DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_order` (`order_id`),
INDEX `idx_customer` (`customer_id`),
INDEX `idx_status` (`status`),
INDEX `idx_stripe_session` (`stripe_session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+402
View File
@@ -0,0 +1,402 @@
-- Tom's Java Jive - MySQL Database Schema
-- Version: 1.0
-- Compatible with MySQL 8.0+
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
-- --------------------------------------------------------
-- Database Schema for Tom's Java Jive
-- NOTE: Database must already exist in cPanel
-- Select your database in phpMyAdmin before importing
-- --------------------------------------------------------
-- --------------------------------------------------------
-- Table: settings
-- --------------------------------------------------------
CREATE TABLE `settings` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`setting_key` VARCHAR(100) NOT NULL UNIQUE,
`setting_value` JSON,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: admin_users
-- --------------------------------------------------------
CREATE TABLE `admin_users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` VARCHAR(50) NOT NULL UNIQUE,
`email` VARCHAR(255) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) DEFAULT NULL,
`name` VARCHAR(255) DEFAULT NULL,
`picture` VARCHAR(500) DEFAULT NULL,
`is_admin` TINYINT(1) DEFAULT 1,
`is_master` TINYINT(1) DEFAULT 0,
`permissions` JSON DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`last_login` TIMESTAMP NULL,
INDEX `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: customers
-- --------------------------------------------------------
CREATE TABLE `customers` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_id` VARCHAR(50) NOT NULL UNIQUE,
`email` VARCHAR(255) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) DEFAULT NULL,
`name` VARCHAR(255) DEFAULT NULL,
`phone` VARCHAR(50) DEFAULT NULL,
`shipping_address` JSON DEFAULT NULL,
`billing_address` JSON DEFAULT NULL,
`wallet_balance` DECIMAL(10,2) DEFAULT 0.00,
`reward_points` INT DEFAULT 0,
`is_guest` TINYINT(1) DEFAULT 0,
`created_via` VARCHAR(50) DEFAULT 'web',
`email_verified` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_email` (`email`),
INDEX `idx_customer_id` (`customer_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: products
-- --------------------------------------------------------
CREATE TABLE `products` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`product_id` VARCHAR(50) NOT NULL UNIQUE,
`name` VARCHAR(255) NOT NULL,
`description` TEXT,
`price` DECIMAL(10,2) NOT NULL,
`sale_price` DECIMAL(10,2) DEFAULT NULL,
`cost_price` DECIMAL(10,2) DEFAULT NULL,
`sku` VARCHAR(100) DEFAULT NULL,
`barcode` VARCHAR(100) DEFAULT NULL,
`category` VARCHAR(100) DEFAULT NULL,
`tags` JSON DEFAULT NULL,
`images` JSON DEFAULT NULL,
`stock` INT DEFAULT 0,
`low_stock_threshold` INT DEFAULT 10,
`weight` DECIMAL(10,2) DEFAULT NULL,
`dimensions` JSON DEFAULT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`is_featured` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_product_id` (`product_id`),
INDEX `idx_category` (`category`),
INDEX `idx_is_active` (`is_active`),
FULLTEXT INDEX `idx_search` (`name`, `description`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: orders
-- --------------------------------------------------------
CREATE TABLE `orders` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`order_id` VARCHAR(50) NOT NULL UNIQUE,
`order_number` VARCHAR(20) NOT NULL UNIQUE,
`customer_id` VARCHAR(50) DEFAULT NULL,
`customer_email` VARCHAR(255) NOT NULL,
`customer_name` VARCHAR(255) DEFAULT NULL,
`customer_phone` VARCHAR(50) DEFAULT NULL,
`items` JSON NOT NULL,
`subtotal` DECIMAL(10,2) NOT NULL,
`shipping_cost` DECIMAL(10,2) DEFAULT 0.00,
`tax` DECIMAL(10,2) DEFAULT 0.00,
`discount` DECIMAL(10,2) DEFAULT 0.00,
`gift_card_discount` DECIMAL(10,2) DEFAULT 0.00,
`wallet_amount_used` DECIMAL(10,2) DEFAULT 0.00,
`total` DECIMAL(10,2) NOT NULL,
`shipping_address` JSON DEFAULT NULL,
`billing_address` JSON DEFAULT NULL,
`shipping_method` VARCHAR(50) DEFAULT NULL,
`payment_method` VARCHAR(50) DEFAULT NULL,
`payment_status` ENUM('pending', 'paid', 'failed', 'refunded', 'partially_refunded') DEFAULT 'pending',
`order_status` ENUM('pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded') DEFAULT 'pending',
`stripe_session_id` VARCHAR(255) DEFAULT NULL,
`stripe_payment_intent` VARCHAR(255) DEFAULT NULL,
`tracking_number` VARCHAR(100) DEFAULT NULL,
`tracking_url` VARCHAR(500) DEFAULT NULL,
`notes` TEXT DEFAULT NULL,
`is_pos_order` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_order_id` (`order_id`),
INDEX `idx_customer_id` (`customer_id`),
INDEX `idx_customer_email` (`customer_email`),
INDEX `idx_order_status` (`order_status`),
INDEX `idx_payment_status` (`payment_status`),
INDEX `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: order_items (normalized for reporting)
-- --------------------------------------------------------
CREATE TABLE `order_items` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`order_id` VARCHAR(50) NOT NULL,
`product_id` VARCHAR(50) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`price` DECIMAL(10,2) NOT NULL,
`quantity` INT NOT NULL,
`total` DECIMAL(10,2) NOT NULL,
INDEX `idx_order_id` (`order_id`),
INDEX `idx_product_id` (`product_id`),
FOREIGN KEY (`order_id`) REFERENCES `orders`(`order_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: gift_cards
-- --------------------------------------------------------
CREATE TABLE `gift_cards` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`gift_card_id` VARCHAR(50) NOT NULL UNIQUE,
`code` VARCHAR(20) NOT NULL UNIQUE,
`initial_balance` DECIMAL(10,2) NOT NULL,
`current_balance` DECIMAL(10,2) NOT NULL,
`purchaser_email` VARCHAR(255) DEFAULT NULL,
`recipient_email` VARCHAR(255) DEFAULT NULL,
`recipient_name` VARCHAR(255) DEFAULT NULL,
`message` TEXT DEFAULT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`expires_at` TIMESTAMP NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_code` (`code`),
INDEX `idx_is_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: gift_card_transactions
-- --------------------------------------------------------
CREATE TABLE `gift_card_transactions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`gift_card_id` VARCHAR(50) NOT NULL,
`order_id` VARCHAR(50) DEFAULT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`balance_after` DECIMAL(10,2) NOT NULL,
`type` ENUM('purchase', 'redemption', 'refund') NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_gift_card_id` (`gift_card_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: wallet_transactions
-- --------------------------------------------------------
CREATE TABLE `wallet_transactions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`transaction_id` VARCHAR(50) NOT NULL UNIQUE,
`customer_id` VARCHAR(50) NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`balance_after` DECIMAL(10,2) NOT NULL,
`type` ENUM('deposit', 'withdrawal', 'purchase', 'refund', 'reward') NOT NULL,
`description` VARCHAR(255) DEFAULT NULL,
`order_id` VARCHAR(50) DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_customer_id` (`customer_id`),
INDEX `idx_type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: reviews
-- --------------------------------------------------------
CREATE TABLE `reviews` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`review_id` VARCHAR(50) NOT NULL UNIQUE,
`product_id` VARCHAR(50) NOT NULL,
`customer_id` VARCHAR(50) DEFAULT NULL,
`customer_name` VARCHAR(255) NOT NULL,
`customer_email` VARCHAR(255) NOT NULL,
`rating` INT NOT NULL CHECK (rating >= 1 AND rating <= 5),
`title` VARCHAR(255) DEFAULT NULL,
`comment` TEXT,
`is_verified_purchase` TINYINT(1) DEFAULT 0,
`is_approved` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_product_id` (`product_id`),
INDEX `idx_is_approved` (`is_approved`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: email_campaigns
-- --------------------------------------------------------
CREATE TABLE `email_campaigns` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`campaign_id` VARCHAR(50) NOT NULL UNIQUE,
`name` VARCHAR(255) NOT NULL,
`subject` VARCHAR(255) NOT NULL,
`content` TEXT NOT NULL,
`recipient_type` ENUM('all', 'customers_only', 'subscribers_only') DEFAULT 'all',
`status` ENUM('draft', 'scheduled', 'sent', 'cancelled') DEFAULT 'draft',
`scheduled_at` TIMESTAMP NULL,
`sent_at` TIMESTAMP NULL,
`recipients_count` INT DEFAULT 0,
`opened_count` INT DEFAULT 0,
`clicked_count` INT DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: email_subscribers
-- --------------------------------------------------------
CREATE TABLE `email_subscribers` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`email` VARCHAR(255) NOT NULL UNIQUE,
`name` VARCHAR(255) DEFAULT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`source` VARCHAR(50) DEFAULT 'website',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_is_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: abandoned_carts
-- --------------------------------------------------------
CREATE TABLE `abandoned_carts` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`cart_id` VARCHAR(50) NOT NULL UNIQUE,
`customer_id` VARCHAR(50) DEFAULT NULL,
`customer_email` VARCHAR(255) DEFAULT NULL,
`items` JSON NOT NULL,
`subtotal` DECIMAL(10,2) NOT NULL,
`recovery_email_sent` TINYINT(1) DEFAULT 0,
`recovered` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_customer_email` (`customer_email`),
INDEX `idx_recovered` (`recovered`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: referrals
-- --------------------------------------------------------
CREATE TABLE `referrals` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`referral_id` VARCHAR(50) NOT NULL UNIQUE,
`referrer_customer_id` VARCHAR(50) NOT NULL,
`referral_code` VARCHAR(20) NOT NULL UNIQUE,
`referred_customer_id` VARCHAR(50) DEFAULT NULL,
`referred_email` VARCHAR(255) DEFAULT NULL,
`status` ENUM('pending', 'completed', 'expired') DEFAULT 'pending',
`reward_amount` DECIMAL(10,2) DEFAULT 5.00,
`reward_given` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_referral_code` (`referral_code`),
INDEX `idx_referrer` (`referrer_customer_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: visitor_sessions
-- --------------------------------------------------------
CREATE TABLE `visitor_sessions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`session_id` VARCHAR(100) NOT NULL UNIQUE,
`visitor_id` VARCHAR(50) NOT NULL,
`ip_address` VARCHAR(45) DEFAULT NULL,
`user_agent` TEXT DEFAULT NULL,
`current_page` VARCHAR(500) DEFAULT NULL,
`referrer` VARCHAR(500) DEFAULT NULL,
`country` VARCHAR(100) DEFAULT NULL,
`city` VARCHAR(100) DEFAULT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`page_views` INT DEFAULT 1,
`started_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`last_activity` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_is_active` (`is_active`),
INDEX `idx_last_activity` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: categories
-- --------------------------------------------------------
CREATE TABLE `categories` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`category_id` VARCHAR(50) NOT NULL UNIQUE,
`name` VARCHAR(255) NOT NULL,
`slug` VARCHAR(255) NOT NULL UNIQUE,
`description` TEXT DEFAULT NULL,
`image` VARCHAR(500) DEFAULT NULL,
`parent_id` VARCHAR(50) DEFAULT NULL,
`sort_order` INT DEFAULT 0,
`is_active` TINYINT(1) DEFAULT 1,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_slug` (`slug`),
INDEX `idx_is_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: coupons
-- --------------------------------------------------------
CREATE TABLE `coupons` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`coupon_id` VARCHAR(50) NOT NULL UNIQUE,
`code` VARCHAR(50) NOT NULL UNIQUE,
`discount_type` ENUM('percentage', 'fixed') NOT NULL DEFAULT 'percentage',
`discount_value` DECIMAL(10,2) NOT NULL,
`min_order_amount` DECIMAL(10,2) DEFAULT NULL,
`max_uses` INT DEFAULT NULL,
`times_used` INT DEFAULT 0,
`is_active` TINYINT(1) DEFAULT 1,
`starts_at` TIMESTAMP NULL,
`expires_at` TIMESTAMP NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_code` (`code`),
INDEX `idx_is_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: password_reset_tokens
-- --------------------------------------------------------
CREATE TABLE `password_reset_tokens` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`email` VARCHAR(255) NOT NULL,
`token` VARCHAR(255) NOT NULL,
`user_type` ENUM('admin', 'customer') NOT NULL,
`expires_at` TIMESTAMP NOT NULL,
`used` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_token` (`token`),
INDEX `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table: sessions
-- --------------------------------------------------------
CREATE TABLE `sessions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`session_id` VARCHAR(128) NOT NULL UNIQUE,
`user_id` VARCHAR(50) DEFAULT NULL,
`user_type` ENUM('admin', 'customer') DEFAULT NULL,
`data` TEXT,
`ip_address` VARCHAR(45) DEFAULT NULL,
`user_agent` VARCHAR(255) DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`expires_at` TIMESTAMP NOT NULL,
INDEX `idx_session_id` (`session_id`),
INDEX `idx_expires_at` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Insert default settings
-- --------------------------------------------------------
INSERT INTO `settings` (`setting_key`, `setting_value`) VALUES
('store_name', '"Tom\'s Java Jive"'),
('store_email', '"support@tomsjavajive.com"'),
('store_phone', '""'),
('store_address', '""'),
('currency', '"USD"'),
('currency_symbol', '"$"'),
('tax_rate', '0'),
('shipping', '{"flat_rate_enabled": true, "flat_rate_amount": 5.99, "free_shipping_threshold": 50, "weight_based_enabled": false}'),
('payment', '{"stripe_enabled": true, "paypal_enabled": false, "cod_enabled": false}'),
('email', '{"sendgrid_api_key": "", "sender_email": "noreply@tomsjavajive.com", "sender_name": "Tom\'s Java Jive"}');
COMMIT;