Initial commit - Route Commerce platform
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* import-woo-to-route.ts - 2026 Season Import
|
||||
*
|
||||
* Reads Tuxedo_Corn_2026_Tour_Schedule-2.xlsx (Full Tour Schedule sheet) and produces:
|
||||
* products.csv - 1 core pickup product
|
||||
* stops.csv - 164 active stops
|
||||
*
|
||||
* Usage:
|
||||
* npx tsx scripts/import-woo-to-route.ts \
|
||||
* --xlsx "/path/to/Tuxedo_Corn_2026_Tour_Schedule-2.xlsx" \
|
||||
* --brand 64294306-5f42-463d-a5e8-2ad6c81a96de \
|
||||
* --output ./import-output
|
||||
*/
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { parseArgs } from "util";
|
||||
import ExcelJS from "exceljs";
|
||||
|
||||
// Types
|
||||
|
||||
interface ProductOutput {
|
||||
name: string;
|
||||
price: number;
|
||||
type: "Pickup" | "Shipping" | "Pickup & Shipping";
|
||||
description: string;
|
||||
active: boolean;
|
||||
image_url: string;
|
||||
is_taxable: boolean;
|
||||
}
|
||||
|
||||
interface StopOutput {
|
||||
city: string;
|
||||
state: string;
|
||||
location: string;
|
||||
date: string;
|
||||
time: string;
|
||||
address: string;
|
||||
notes: string;
|
||||
}
|
||||
|
||||
function slugify(s: string): string {
|
||||
return s
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs({
|
||||
options: {
|
||||
xlsx: { type: "string" },
|
||||
brand: { type: "string" },
|
||||
output: { type: "string", default: "./import-output" },
|
||||
},
|
||||
});
|
||||
|
||||
const xlsxPath = args.values.xlsx as string;
|
||||
const brandId = args.values.brand as string;
|
||||
const outputDir = args.values.output as string;
|
||||
|
||||
if (!xlsxPath || !brandId) {
|
||||
console.error(
|
||||
"Usage: npx tsx scripts/import-woo-to-route.ts --xlsx <path> --brand <uuid> [--output <dir>]"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (!fs.existsSync(xlsxPath)) {
|
||||
console.error("File not found: " + xlsxPath);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read Excel
|
||||
// Row 0=title, Row1=subtitle, Row2=color-key, Row3=column-headers, Row4+=data
|
||||
const wb = new ExcelJS.Workbook();
|
||||
await wb.xlsx.readFile(xlsxPath);
|
||||
const sheet = wb.getWorksheet(1);
|
||||
if (!sheet) {
|
||||
console.error("No worksheet found in file");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const allRows: (string | null | undefined)[][] = [];
|
||||
sheet.eachRow((row) => {
|
||||
allRows.push(row.values as (string | null | undefined)[]);
|
||||
});
|
||||
|
||||
const colHeaders = (allRows[3] as string[]).map((h: unknown) => String(h ?? "").trim());
|
||||
console.log("Column headers:", colHeaders);
|
||||
|
||||
const ci = (col: string) => colHeaders.indexOf(col);
|
||||
const wkIdx = ci("Wk"), typeIdx = ci("Type"), datesIdx = ci("Dates"), dayIdx = ci("Day");
|
||||
const cityIdx = ci("City / Location"), hostIdx = ci("Host / Venue");
|
||||
const timeIdx = ci("Time"), truckIdx = ci("Truck"), statusIdx = ci("Status"), notesIdx = ci("Notes");
|
||||
|
||||
interface ScheduleRow {
|
||||
wk: number; type: string; dates: string; day: string;
|
||||
city: string; host: string; time: string; truck: string; status: string; notes: string;
|
||||
}
|
||||
|
||||
const scheduleRows: ScheduleRow[] = [];
|
||||
let currentWeek: { wk: number; type: string; dates: string; day: string } | null = null;
|
||||
|
||||
for (let i = 4; i < allRows.length; i++) {
|
||||
const row = allRows[i] as (string | null | undefined)[];
|
||||
if (!row) continue;
|
||||
|
||||
const wkVal = row[wkIdx];
|
||||
const Day = String(row[dayIdx] ?? "").trim();
|
||||
const City_Location = String(row[cityIdx] ?? "").trim();
|
||||
const Status = String(row[statusIdx] ?? "").trim();
|
||||
|
||||
if (wkVal !== null && wkVal !== undefined && String(wkVal).trim() !== "") {
|
||||
currentWeek = {
|
||||
wk: Number(wkVal),
|
||||
type: String(row[typeIdx] ?? "").trim(),
|
||||
dates: String(row[datesIdx] ?? "").trim(),
|
||||
day: Day,
|
||||
};
|
||||
}
|
||||
|
||||
if (!currentWeek) continue;
|
||||
if (Status === "Off day") continue;
|
||||
if (!City_Location) continue;
|
||||
|
||||
scheduleRows.push({
|
||||
wk: currentWeek.wk, type: currentWeek.type, dates: currentWeek.dates,
|
||||
day: Day || currentWeek.day,
|
||||
city: City_Location,
|
||||
host: String(row[hostIdx] ?? "").trim(),
|
||||
time: String(row[timeIdx] ?? "").trim(),
|
||||
truck: String(row[truckIdx] ?? "").trim(),
|
||||
status: Status,
|
||||
notes: String(row[notesIdx] ?? "").trim(),
|
||||
});
|
||||
}
|
||||
|
||||
const ACTIVE_STATUSES = ["✓ Confirmed", "🔴 Pre-sale live"];
|
||||
const activeRows = scheduleRows.filter((r) => ACTIVE_STATUSES.includes(r.status));
|
||||
console.log("\nSchedule rows: " + scheduleRows.length + " total | " + activeRows.length + " active\n");
|
||||
|
||||
// PRODUCTS -- one core product for all stops
|
||||
const products: ProductOutput[] = [
|
||||
{
|
||||
name: "Olathe Sweet Corn -- 24 Ear Box",
|
||||
price: 25,
|
||||
type: "Pickup",
|
||||
description:
|
||||
"Fresh Olathe Sweet Corn pickup box. Add to cart, then choose your pickup stop at checkout. " +
|
||||
"Available July-September 2026 at stops across Colorado, Wyoming, and New Mexico. " +
|
||||
"See tuxedocorn.com for full schedule.",
|
||||
active: true,
|
||||
image_url:
|
||||
"https://tuxedocorn.com/wp-content/uploads/2024/06/110260317_3494904527187819_6983832261179080791_n-1.jpg",
|
||||
is_taxable: true,
|
||||
},
|
||||
];
|
||||
|
||||
console.log("[products] 1 core product: \"" + products[0].name + "\" at $" + products[0].price + "\n");
|
||||
|
||||
// STOPS -- one row per active schedule entry
|
||||
const stops: StopOutput[] = [];
|
||||
const seenStopKeys = new Set<string>();
|
||||
|
||||
for (const row of activeRows) {
|
||||
if (!row.city) continue;
|
||||
|
||||
// Parse "City ST" or "City, ST" -> city + state
|
||||
const cityRaw = row.city.replace(/--/g, "-").trim();
|
||||
let city = cityRaw, state = "CO";
|
||||
|
||||
if (cityRaw.includes(",")) {
|
||||
const parts = cityRaw.split(",").map((s: string) => s.trim());
|
||||
const lastPart = parts[parts.length - 1] ?? "";
|
||||
if (/^[A-Z]{2}$/.test(lastPart)) { city = parts.slice(0, -1).join(", ").trim(); state = lastPart; }
|
||||
else city = parts.join(", ");
|
||||
} else {
|
||||
const tokens = cityRaw.split(/\s+/);
|
||||
const lastToken = tokens[tokens.length - 1] ?? "";
|
||||
if (/^[A-Z]{2}$/.test(lastToken)) { state = lastToken; city = tokens.slice(0, -1).join(" "); }
|
||||
}
|
||||
|
||||
const stopKey = slugify(city + "-" + state + "-" + row.dates + "-" + row.time);
|
||||
if (seenStopKeys.has(stopKey)) continue;
|
||||
seenStopKeys.add(stopKey);
|
||||
|
||||
// Human-readable stop label = "Store (Time) | Truck T1"
|
||||
const stopName = row.host + " (" + row.time + ")" + (row.truck ? " | Truck " + row.truck : "");
|
||||
|
||||
stops.push({
|
||||
city,
|
||||
state,
|
||||
location: stopName,
|
||||
date: row.dates, // week range -- set actual date in /admin/stops after import
|
||||
time: row.time,
|
||||
address: row.city + " | " + row.host,
|
||||
notes: row.day + (row.notes ? " | " + row.notes : ""),
|
||||
});
|
||||
}
|
||||
|
||||
console.log("[stops] " + stops.length + " stops written\n");
|
||||
|
||||
// Show sample by week
|
||||
const byWeek: Record<string, typeof stops> = {};
|
||||
for (const s of stops) {
|
||||
if (!byWeek[s.date]) byWeek[s.date] = [];
|
||||
byWeek[s.date].push(s);
|
||||
}
|
||||
const weeks = Object.keys(byWeek).slice(0, 4);
|
||||
for (const week of weeks) {
|
||||
const ws = byWeek[week];
|
||||
console.log(" " + week + " (" + ws.length + " stops):");
|
||||
for (const s of ws.slice(0, 3)) {
|
||||
console.log(" " + s.city + ", " + s.state + " | \"" + s.location + "\" | " + s.time);
|
||||
}
|
||||
}
|
||||
|
||||
// Write output CSVs
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
function writeCSV(filename: string, header: string, rows: string[][]) {
|
||||
const lines = rows.map((r) =>
|
||||
r.map((c) => "\"" + String(c ?? "").replace(/\"/g, "\"\"") + "\"").join(",")
|
||||
);
|
||||
fs.writeFileSync(path.join(outputDir, filename), header + lines.join("\n"));
|
||||
}
|
||||
|
||||
writeCSV(
|
||||
"products.csv",
|
||||
"name,price,type,description,active,image_url,is_taxable\n",
|
||||
products.map((p) => [
|
||||
p.name, String(p.price), p.type, p.description,
|
||||
String(p.active), p.image_url, String(p.is_taxable),
|
||||
])
|
||||
);
|
||||
|
||||
writeCSV(
|
||||
"stops.csv",
|
||||
"city,state,location,date,time,address,notes\n",
|
||||
stops.map((s) => [s.city, s.state, s.location, s.date, s.time, s.address, s.notes])
|
||||
);
|
||||
|
||||
// Summary
|
||||
console.log("\n============================================================");
|
||||
console.log("IMPORT READY");
|
||||
console.log("============================================================");
|
||||
console.log("\nproducts.csv: 1 product (\"" + products[0].name + "\" @ $" + products[0].price + " Pickup)");
|
||||
console.log("stops.csv: " + stops.length + " stops (confirmed + pre-sale only)");
|
||||
console.log("\nOutput: " + outputDir + "/");
|
||||
console.log("");
|
||||
console.log(">>> NOTE: stops.csv 'date' field is a WEEK RANGE (e.g. Jul 22-28).");
|
||||
console.log("After importing stops, visit /admin/stops to set the actual date for each stop.");
|
||||
console.log("");
|
||||
console.log("NEXT STEPS:");
|
||||
console.log(" 1. Upload " + outputDir + "/products.csv via /admin/import -> type: products");
|
||||
console.log(" 2. Upload " + outputDir + "/stops.csv via /admin/import -> type: stops");
|
||||
console.log(" 3. Visit /admin/stops -- set each stop's actual date, verify address/time");
|
||||
console.log(" 4. Visit /tuxedo -- verify product and stops appear\n");
|
||||
console.log("Re-run: npx tsx scripts/import-woo-to-route.ts --xlsx \"" + xlsxPath + "\" --brand " + brandId + " --output " + outputDir + "\n");
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
#!/bin/bash
|
||||
# Seed script for Route Commerce Platform
|
||||
# Run with: bash scripts/seed.sh
|
||||
|
||||
set -e
|
||||
|
||||
# Use service role key for write operations during seeding
|
||||
SUPABASE_URL="${SUPABASE_URL:-https://your-project.supabase.co}"
|
||||
SERVICE_KEY="${SUPABASE_SERVICE_ROLE_KEY}"
|
||||
ANON_KEY="${ANON_KEY:-$NEXT_PUBLIC_SUPABASE_ANON_KEY}"
|
||||
|
||||
AUTH_HEADER="apikey: $SERVICE_KEY"
|
||||
CONTENT_TYPE="Content-Type: application/json"
|
||||
|
||||
echo "Fetching brands..."
|
||||
BRANDS=$(curl -s "$SUPABASE_URL/rest/v1/brands?select=id,name" \
|
||||
-H "$AUTH_HEADER")
|
||||
echo "Brands: $BRANDS"
|
||||
|
||||
TUXEDO_ID=$(echo "$BRANDS" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
IRD_ID=$(echo "$BRANDS" | grep -o '"id":"[^"]*"' | tail -1 | cut -d'"' -f4)
|
||||
|
||||
echo "Tuxedo: $TUXEDO_ID"
|
||||
echo "IRD: $IRD_ID"
|
||||
|
||||
echo ""
|
||||
echo "Creating stops..."
|
||||
|
||||
STOP1=$(curl -s -X POST "$SUPABASE_URL/rest/v1/stops" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$TUXEDO_ID"'",
|
||||
"city": "Burlington",
|
||||
"state": "NC",
|
||||
"date": "2026-05-15",
|
||||
"time": "8:00 AM – 2:00 PM",
|
||||
"location": "102 W Front St, Burlington, NC",
|
||||
"slug": "burlington-nc-2026-05-15",
|
||||
"active": true
|
||||
}')
|
||||
echo "Stop 1: $STOP1"
|
||||
|
||||
STOP2=$(curl -s -X POST "$SUPABASE_URL/rest/v1/stops" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$TUXEDO_ID"'",
|
||||
"city": "Greensboro",
|
||||
"state": "NC",
|
||||
"date": "2026-05-16",
|
||||
"time": "8:00 AM – 2:00 PM",
|
||||
"location": "200 S Elm St, Greensboro, NC",
|
||||
"slug": "greensboro-nc-2026-05-16",
|
||||
"active": true
|
||||
}')
|
||||
echo "Stop 2: $STOP2"
|
||||
|
||||
STOP3=$(curl -s -X POST "$SUPABASE_URL/rest/v1/stops" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$IRD_ID"'",
|
||||
"city": "Fort Pierce",
|
||||
"state": "FL",
|
||||
"date": "2026-05-15",
|
||||
"time": "7:00 AM – 1:00 PM",
|
||||
"location": "1224 N US-1, Fort Pierce, FL",
|
||||
"slug": "fort-pierce-fl-2026-05-15",
|
||||
"active": true
|
||||
}')
|
||||
echo "Stop 3: $STOP3"
|
||||
|
||||
echo ""
|
||||
echo "Creating products..."
|
||||
|
||||
PROD1=$(curl -s -X POST "$SUPABASE_URL/rest/v1/products" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$TUXEDO_ID"'",
|
||||
"name": "Sweet Corn",
|
||||
"description": "Fresh picked white sweet corn, 8 ears per bag",
|
||||
"price": 12,
|
||||
"type": "Sweet Corn",
|
||||
"active": true
|
||||
}')
|
||||
echo "Prod 1: $PROD1"
|
||||
|
||||
PROD2=$(curl -s -X POST "$SUPABASE_URL/rest/v1/products" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$TUXEDO_ID"'",
|
||||
"name": "Peaches",
|
||||
"description": "Just-peeled freestone peaches, pint jar",
|
||||
"price": 8,
|
||||
"type": "Fruit",
|
||||
"active": true
|
||||
}')
|
||||
echo "Prod 2: $PROD2"
|
||||
|
||||
PROD3=$(curl -s -X POST "$SUPABASE_URL/rest/v1/products" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$TUXEDO_ID"'",
|
||||
"name": "Corn & Peach Bundle",
|
||||
"description": "Sweet corn + peaches combo, save $2",
|
||||
"price": 18,
|
||||
"type": "Bundle",
|
||||
"active": true
|
||||
}')
|
||||
echo "Prod 3: $PROD3"
|
||||
|
||||
PROD4=$(curl -s -X POST "$SUPABASE_URL/rest/v1/products" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$IRD_ID"'",
|
||||
"name": "Navel Oranges",
|
||||
"description": "Premium navel oranges, 4 lb bag",
|
||||
"price": 15,
|
||||
"type": "Citrus",
|
||||
"active": true
|
||||
}')
|
||||
echo "Prod 4: $PROD4"
|
||||
|
||||
PROD5=$(curl -s -X POST "$SUPABASE_URL/rest/v1/products" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$IRD_ID"'",
|
||||
"name": "Grapefruit",
|
||||
"description": "Ruby red grapefruit, 3 lb bag",
|
||||
"price": 12,
|
||||
"type": "Citrus",
|
||||
"active": true
|
||||
}')
|
||||
echo "Prod 5: $PROD5"
|
||||
|
||||
PROD6=$(curl -s -X POST "$SUPABASE_URL/rest/v1/products" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"brand_id": "'"$IRD_ID"'",
|
||||
"name": "Citrus Sampler",
|
||||
"description": "Oranges, grapefruit & tangelos, 6 lb mix",
|
||||
"price": 22,
|
||||
"type": "Bundle",
|
||||
"active": true
|
||||
}')
|
||||
echo "Prod 6: $PROD6"
|
||||
|
||||
P1_ID=$(echo "$PROD1" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
P2_ID=$(echo "$PROD2" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
P3_ID=$(echo "$PROD3" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
P4_ID=$(echo "$PROD4" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
P5_ID=$(echo "$PROD5" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
P6_ID=$(echo "$PROD6" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
|
||||
S1_ID=$(echo "$STOP1" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
S2_ID=$(echo "$STOP2" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
S3_ID=$(echo "$STOP3" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
|
||||
echo ""
|
||||
echo "Assigning products to stops..."
|
||||
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S1_ID"'", "product_id": "'"$P1_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S1_ID"'", "product_id": "'"$P2_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S1_ID"'", "product_id": "'"$P3_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S2_ID"'", "product_id": "'"$P1_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S2_ID"'", "product_id": "'"$P3_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S3_ID"'", "product_id": "'"$P4_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S3_ID"'", "product_id": "'"$P5_ID"'"}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/product_stops" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"stop_id": "'"$S3_ID"'", "product_id": "'"$P6_ID"'"}' > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "Creating orders..."
|
||||
|
||||
ORDER1=$(curl -s -X POST "$SUPABASE_URL/rest/v1/orders" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"customer_name": "Sarah Mitchell",
|
||||
"customer_email": "sarah.m@email.com",
|
||||
"customer_phone": "(336) 555-0142",
|
||||
"stop_id": "'"$S1_ID"'",
|
||||
"status": "pending",
|
||||
"subtotal": 32,
|
||||
"discount_amount": 0,
|
||||
"pickup_complete": false,
|
||||
"payment_processor": "manual",
|
||||
"payment_status": "paid"
|
||||
}')
|
||||
echo "Order 1: $ORDER1"
|
||||
|
||||
ORDER2=$(curl -s -X POST "$SUPABASE_URL/rest/v1/orders" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"customer_name": "James Rivera",
|
||||
"customer_email": "jrivera@email.com",
|
||||
"customer_phone": "(336) 555-0298",
|
||||
"stop_id": "'"$S1_ID"'",
|
||||
"status": "confirmed",
|
||||
"subtotal": 22,
|
||||
"discount_amount": 2,
|
||||
"discount_reason": "First-time customer",
|
||||
"pickup_complete": true,
|
||||
"payment_processor": "manual",
|
||||
"payment_status": "paid"
|
||||
}')
|
||||
echo "Order 2: $ORDER2"
|
||||
|
||||
ORDER3=$(curl -s -X POST "$SUPABASE_URL/rest/v1/orders" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"customer_name": "Lisa Chen",
|
||||
"customer_phone": "(919) 555-0763",
|
||||
"stop_id": "'"$S2_ID"'",
|
||||
"status": "pending",
|
||||
"subtotal": 12,
|
||||
"pickup_complete": false,
|
||||
"payment_processor": "manual",
|
||||
"payment_status": "pending"
|
||||
}')
|
||||
echo "Order 3: $ORDER3"
|
||||
|
||||
ORDER4=$(curl -s -X POST "$SUPABASE_URL/rest/v1/orders" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"customer_name": "Marcus Thompson",
|
||||
"customer_email": "mthompson@email.com",
|
||||
"customer_phone": "(772) 555-0119",
|
||||
"stop_id": "'"$S3_ID"'",
|
||||
"status": "confirmed",
|
||||
"subtotal": 37,
|
||||
"pickup_complete": false,
|
||||
"payment_processor": "venmo",
|
||||
"payment_status": "paid",
|
||||
"payment_transaction_id": "VEN-88420"
|
||||
}')
|
||||
echo "Order 4: $ORDER4"
|
||||
|
||||
ORDER5=$(curl -s -X POST "$SUPABASE_URL/rest/v1/orders" \
|
||||
-H "$AUTH_HEADER" \
|
||||
-H "$CONTENT_TYPE" \
|
||||
-H "Prefer: return=representation" \
|
||||
-d '{
|
||||
"customer_name": "Amanda Foster",
|
||||
"customer_email": "afoster@email.com",
|
||||
"stop_id": "'"$S3_ID"'",
|
||||
"status": "cancelled",
|
||||
"subtotal": 15,
|
||||
"pickup_complete": false,
|
||||
"internal_notes": "Customer cancelled after hours, no show next day",
|
||||
"payment_processor": "manual",
|
||||
"payment_status": "refunded",
|
||||
"refunded_amount": 15,
|
||||
"refund_reason": "Customer cancelled"
|
||||
}')
|
||||
echo "Order 5: $ORDER5"
|
||||
|
||||
O1_ID=$(echo "$ORDER1" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
O2_ID=$(echo "$ORDER2" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
O3_ID=$(echo "$ORDER3" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
O4_ID=$(echo "$ORDER4" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
O5_ID=$(echo "$ORDER5" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
|
||||
echo ""
|
||||
echo "Creating order items..."
|
||||
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O1_ID"'", "product_id": "'"$P1_ID"'", "quantity": 2, "price": 12}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O1_ID"'", "product_id": "'"$P2_ID"'", "quantity": 1, "price": 8}' > /dev/null
|
||||
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O2_ID"'", "product_id": "'"$P3_ID"'", "quantity": 1, "price": 18}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O2_ID"'", "product_id": "'"$P1_ID"'", "quantity": 2, "price": 12}' > /dev/null
|
||||
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O3_ID"'", "product_id": "'"$P1_ID"'", "quantity": 1, "price": 12}' > /dev/null
|
||||
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O4_ID"'", "product_id": "'"$P4_ID"'", "quantity": 2, "price": 15}' > /dev/null
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O4_ID"'", "product_id": "'"$P5_ID"'", "quantity": 1, "price": 12}' > /dev/null
|
||||
|
||||
curl -s -X POST "$SUPABASE_URL/rest/v1/order_items" \
|
||||
-H "$AUTH_HEADER" -H "$CONTENT_TYPE" \
|
||||
-d '{"order_id": "'"$O5_ID"'", "product_id": "'"$P4_ID"'", "quantity": 1, "price": 15}' > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "Done! Seed data created:"
|
||||
echo " 3 stops (Burlington NC, Greensboro NC, Fort Pierce FL)"
|
||||
echo " 6 products (3 Tuxedo Corn, 3 Indian River Direct)"
|
||||
echo " 5 orders with items"
|
||||
echo ""
|
||||
echo "Quick test URLs:"
|
||||
echo " /admin/orders"
|
||||
echo " /admin/products"
|
||||
echo " /admin/stops"
|
||||
@@ -0,0 +1,74 @@
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
import { readFileSync } from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
|
||||
// Load .env.local manually
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const envPath = path.join(__dirname, "..", ".env.local");
|
||||
const envContent = readFileSync(envPath, "utf8");
|
||||
envContent.split("\n").forEach((line) => {
|
||||
const [key, ...rest] = line.split("=");
|
||||
if (key && rest.length && key.trim() && !key.trim().startsWith("#")) {
|
||||
process.env[key.trim()] = rest.join("=").trim().replace(/^"|"$/g, "");
|
||||
}
|
||||
});
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
||||
|
||||
if (!supabaseUrl || !supabaseServiceKey) {
|
||||
console.error("Missing env vars");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
||||
|
||||
async function uploadTuxedoHeroVideo() {
|
||||
const filePath = "/Users/kylemartinez/Downloads/062624TuxedoCorn 001.mp4";
|
||||
const fileBuffer = readFileSync(filePath);
|
||||
const fileSizeMB = (fileBuffer.byteLength / 1024 / 1024).toFixed(2);
|
||||
console.log(`File: ${filePath} (${fileSizeMB} MB)`);
|
||||
|
||||
// Ensure public videos bucket exists
|
||||
const { data: buckets, error: listErr } = await supabase.storage.listBuckets();
|
||||
if (listErr) { console.error("List buckets error:", listErr.message); process.exit(1); }
|
||||
|
||||
const existing = buckets?.find((b) => b.name === "videos");
|
||||
if (!existing) {
|
||||
console.log("Creating 'videos' bucket (public)...");
|
||||
const { error: createErr } = await supabase.storage.createBucket("videos", { public: true });
|
||||
if (createErr) { console.error("Create bucket error:", createErr.message); process.exit(1); }
|
||||
console.log("Bucket 'videos' created.");
|
||||
} else {
|
||||
console.log(`'videos' bucket exists (public: ${existing.public}).`);
|
||||
if (!existing.public) {
|
||||
const { error: updErr } = await supabase.storage.updateBucket("videos", { public: true });
|
||||
if (updErr) console.warn("Could not update bucket to public:", updErr.message);
|
||||
else console.log("Bucket updated to public.");
|
||||
}
|
||||
}
|
||||
|
||||
// Upload
|
||||
console.log("Uploading tuxedo-hero.mp4...");
|
||||
const { data: uploadData, error: uploadErr } = await supabase.storage
|
||||
.from("videos")
|
||||
.upload("tuxedo-hero.mp4", fileBuffer, {
|
||||
contentType: "video/mp4",
|
||||
upsert: true,
|
||||
});
|
||||
|
||||
if (uploadErr) {
|
||||
console.error("Upload error:", uploadErr.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Upload OK:", JSON.stringify(uploadData));
|
||||
|
||||
// Public URL
|
||||
const { data: urlData } = supabase.storage.from("videos").getPublicUrl("tuxedo-hero.mp4");
|
||||
const url = urlData.publicUrl;
|
||||
console.log(`\n✅ Public URL:\n${url}`);
|
||||
}
|
||||
|
||||
uploadTuxedoHeroVideo().catch((e) => { console.error(e); process.exit(1); });
|
||||
Reference in New Issue
Block a user