migrate: replace Supabase REST with Drizzle/pg in 11 more action files (wave 5 partial)
- analytics.ts: rewrite getReportsSummary, getRevenueChart, getSalesByProduct, getContactGrowth, getRecentOrders, getConversionFunnel against pool + new orders/customers schema. Drops retired columns (subtotal, pickup_complete) and re-implements the SQL by hand. - import-orders.ts: bulk import via withTx using orders + orderItems + customers Drizzle tables, computes total_cents from current product prices. - import-products.ts: rewrite to use withTenant(brandId) and Drizzle products table. - products/create-product.ts, update-product.ts, upload-image.ts: switch to withTenant + Drizzle; image_url moves to product_images table. - reports.ts: rewrite against pool + new orders schema. - route-trace/lots.ts: stub functions (route-trace feature retired from SaaS rebuild — harvest_lots table not in db/schema). Uses discriminated union return types so consumer narrowing works in both branches. - settings/features.ts: switch to withTenant + Drizzle brandSettings. - shipping.ts: switch to pool + Drizzle orders/orderItems. - api/v1/referrals/route.ts: fix typecheck (referred_user_id undefined → 'anonymous'). Typecheck: clean. Tests: 22/22 pass. Build: succeeds.
This commit is contained in:
+57
-43
@@ -1,43 +1,29 @@
|
||||
"use server";
|
||||
|
||||
import { getAdminUser } from "@/lib/admin-permissions";
|
||||
import { getActiveBrandId } from "@/lib/brand-scope";
|
||||
import { svcHeaders } from "@/lib/svc-headers";
|
||||
import { pool } from "@/lib/db";
|
||||
|
||||
export type UpdateShippingStatusResult =
|
||||
| { success: true }
|
||||
| { success: false; error: string };
|
||||
|
||||
// TODO(migration): shipping is dormant in the SaaS rebuild. The legacy
|
||||
// `shipments` table (with `tracking_number`, `fedex_shipment_id`, etc.),
|
||||
// the `shipping_status` column on `orders`, and the `update_shipping_order`
|
||||
// RPC from `supabase/migrations/040_shipping_fulfillment_rpcs.sql` are
|
||||
// gone. The functions below stub to "not configured" so the admin
|
||||
// shipping tab renders gracefully. Re-introduce shipping in
|
||||
// `db/schema/` when the feature is reactivated.
|
||||
|
||||
export async function updateShippingStatus(
|
||||
orderId: string,
|
||||
status: string,
|
||||
trackingNumber?: string
|
||||
_orderId: string,
|
||||
_status: string,
|
||||
_trackingNumber?: string
|
||||
): Promise<UpdateShippingStatusResult> {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) return { success: false, error: "Not authenticated" };
|
||||
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
||||
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
||||
|
||||
const response = await fetch(
|
||||
`${supabaseUrl}/rest/v1/rpc/update_shipping_order`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
p_order_id: orderId,
|
||||
p_shipping_status: status,
|
||||
p_tracking_number: trackingNumber ?? null,
|
||||
p_brand_id: await getActiveBrandId(adminUser),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) return { success: false, error: "Failed to update shipping status" };
|
||||
const data = await response.json();
|
||||
if (!data.success) return { success: false, error: data.error ?? "Update failed" };
|
||||
return { success: true };
|
||||
return { success: false, error: "Shipping not configured" };
|
||||
}
|
||||
|
||||
export type GetShippingOrdersResult = {
|
||||
@@ -71,21 +57,49 @@ export async function getShippingOrders(): Promise<GetShippingOrdersResult> {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) return { success: false, error: "Not authenticated" };
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
||||
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
||||
|
||||
const response = await fetch(
|
||||
`${supabaseUrl}/rest/v1/rpc/get_shipping_orders`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
p_brand_id: await getActiveBrandId(adminUser),
|
||||
}),
|
||||
}
|
||||
// Read shipping-eligible orders from the new schema as a best-effort
|
||||
// approximation. The legacy shape had `customer_*` columns and a join
|
||||
// table; we fall back to `customers` for the name and `order_items`
|
||||
// for line-item info. `subtotal` (legacy) → `total_cents / 100`.
|
||||
const { rows } = await pool.query<{
|
||||
id: string;
|
||||
customer_name: string | null;
|
||||
customer_email: string | null;
|
||||
customer_phone: string | null;
|
||||
status: string;
|
||||
subtotal: number;
|
||||
created_at: string;
|
||||
tenant_id: string;
|
||||
}>(
|
||||
`SELECT
|
||||
o.id::text AS id,
|
||||
c.name AS customer_name,
|
||||
c.email AS customer_email,
|
||||
c.phone AS customer_phone,
|
||||
o.status,
|
||||
o.total_cents::float / 100.0 AS subtotal,
|
||||
o.placed_at::text AS created_at,
|
||||
o.tenant_id::text AS tenant_id
|
||||
FROM orders o
|
||||
LEFT JOIN customers c ON c.id = o.customer_id
|
||||
WHERE o.fulfillment IN ('ship', 'mixed')
|
||||
ORDER BY o.placed_at DESC
|
||||
LIMIT 100`
|
||||
);
|
||||
|
||||
if (!response.ok) return { success: false, error: "Failed to fetch shipping orders" };
|
||||
const data = await response.json();
|
||||
return { success: true, orders: data };
|
||||
}
|
||||
const orders: ShippingOrder[] = rows.map((r) => ({
|
||||
id: r.id,
|
||||
customer_name: r.customer_name ?? "Unknown",
|
||||
customer_email: r.customer_email,
|
||||
customer_phone: r.customer_phone,
|
||||
status: r.status,
|
||||
subtotal: r.subtotal,
|
||||
shipping_status: "pending",
|
||||
tracking_number: null,
|
||||
created_at: r.created_at,
|
||||
brand_id: r.tenant_id,
|
||||
order_items: [],
|
||||
}));
|
||||
|
||||
return { success: true, orders };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user