237 lines
6.1 KiB
TypeScript
237 lines
6.1 KiB
TypeScript
"use server";
|
|
|
|
import { getAdminUser } from "@/lib/admin-permissions";
|
|
import { svcHeaders } from "@/lib/svc-headers";
|
|
import { mockOrders, mockStops } from "@/lib/mock-data";
|
|
|
|
type AdminOrder = {
|
|
id: string;
|
|
customer_name: string;
|
|
customer_email: string | null;
|
|
customer_phone: string | null;
|
|
stop_id: string | null;
|
|
status: string;
|
|
subtotal: number;
|
|
pickup_complete: boolean;
|
|
pickup_completed_at: string | null;
|
|
pickup_completed_by: string | null;
|
|
created_at: string;
|
|
payment_processor: string | null;
|
|
// For AdminOrdersPanel: stops is { city, state, date }
|
|
// For DriverPickupPanel: stops is { id, city, state, date, brand_id }
|
|
// Both share city/state/date so we use the broader shape
|
|
stops: {
|
|
id?: string;
|
|
city: string;
|
|
state: string;
|
|
date: string;
|
|
brand_id?: string;
|
|
} | null;
|
|
order_items?: Array<{
|
|
id: string;
|
|
product_id: string;
|
|
quantity: number;
|
|
price: number;
|
|
fulfillment?: string;
|
|
products: { name: string } | null;
|
|
}>;
|
|
};
|
|
|
|
type AdminStop = {
|
|
id: string;
|
|
city: string;
|
|
state: string;
|
|
date: string;
|
|
location: string;
|
|
brand_id: string;
|
|
};
|
|
|
|
type AdminOrdersResult = {
|
|
orders: AdminOrder[];
|
|
stops: AdminStop[];
|
|
};
|
|
|
|
type AdminOrdersResponse =
|
|
| { success: true; orders: AdminOrder[]; stops: AdminStop[]; error: null }
|
|
| { success: false; orders: []; stops: []; error: string };
|
|
|
|
type AdminOrderDetail = {
|
|
id: string;
|
|
customer_name: string;
|
|
customer_email: string | null;
|
|
customer_phone: string | null;
|
|
stop_id: string | null;
|
|
status: string;
|
|
subtotal: number;
|
|
pickup_complete: boolean;
|
|
pickup_completed_at: string | null;
|
|
pickup_completed_by: string | null;
|
|
created_at: string;
|
|
discount_amount: number | null;
|
|
tax_amount: number | null;
|
|
tax_rate: number | null;
|
|
tax_location: string | null;
|
|
discount_reason: string | null;
|
|
internal_notes: string | null;
|
|
payment_processor: string | null;
|
|
payment_status: string | null;
|
|
payment_transaction_id: string | null;
|
|
refunded_amount: number | null;
|
|
refund_reason: string | null;
|
|
stops: {
|
|
id?: string;
|
|
city: string;
|
|
state: string;
|
|
date: string;
|
|
brand_id?: string;
|
|
location?: string;
|
|
} | null;
|
|
order_items?: Array<{
|
|
id: string;
|
|
product_id: string;
|
|
quantity: number;
|
|
price: number;
|
|
fulfillment?: string;
|
|
products: { name: string } | null;
|
|
}>;
|
|
refunds?: Array<{
|
|
id: string;
|
|
order_id: string;
|
|
amount: number;
|
|
reason: string | null;
|
|
processor: string | null;
|
|
processor_refund_id: string | null;
|
|
status: string;
|
|
created_at: string;
|
|
}>;
|
|
};
|
|
|
|
// Mock orders for UI review
|
|
const mockAdminOrders: AdminOrder[] = mockOrders.map((o: any) => ({
|
|
id: o.id,
|
|
customer_name: o.customer_name,
|
|
customer_email: o.customer_email,
|
|
customer_phone: o.customer_phone,
|
|
stop_id: o.stop_id,
|
|
status: o.status,
|
|
subtotal: o.subtotal,
|
|
pickup_complete: o.pickup_complete,
|
|
pickup_completed_at: o.pickup_completed_at,
|
|
pickup_completed_by: null,
|
|
created_at: o.created_at,
|
|
payment_processor: o.payment_processor,
|
|
stops: o.stops,
|
|
order_items: o.order_items,
|
|
}));
|
|
|
|
const mockAdminStops: AdminStop[] = mockStops.map((s: any) => ({
|
|
id: s.id,
|
|
city: s.city,
|
|
state: s.state,
|
|
date: s.date,
|
|
location: s.location,
|
|
brand_id: s.brand_id,
|
|
}));
|
|
|
|
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
|
|
|
|
export async function getAdminOrders(): Promise<AdminOrdersResponse> {
|
|
// Return mock data in mock mode
|
|
if (useMockData) {
|
|
return {
|
|
success: true,
|
|
orders: mockAdminOrders,
|
|
stops: mockAdminStops,
|
|
error: null,
|
|
};
|
|
}
|
|
|
|
const adminUser = await getAdminUser();
|
|
const brandId = adminUser?.brand_id ?? null;
|
|
|
|
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_admin_orders`,
|
|
{
|
|
method: "POST",
|
|
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json", Prefer: "return=representation" },
|
|
body: JSON.stringify({ p_brand_id: brandId }),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
return { success: false, orders: [], stops: [], error: `HTTP ${response.status}: ${response.statusText}` };
|
|
}
|
|
|
|
const data = await response.json();
|
|
return {
|
|
success: true,
|
|
orders: data?.orders ?? [],
|
|
stops: data?.stops ?? [],
|
|
error: null,
|
|
};
|
|
}
|
|
|
|
export async function getAdminStops(): Promise<AdminStop[]> {
|
|
const result = await getAdminOrders();
|
|
if (!result.success) return [];
|
|
return result.stops;
|
|
}
|
|
|
|
export async function getAdminPendingOrders(): Promise<AdminOrder[]> {
|
|
const result = await getAdminOrders();
|
|
if (!result.success) return [];
|
|
return result.orders.filter((o) => !o.pickup_complete);
|
|
}
|
|
|
|
export async function getAdminPickedUpOrders(): Promise<AdminOrder[]> {
|
|
const result = await getAdminOrders();
|
|
if (!result.success) return [];
|
|
return result.orders.filter((o) => o.pickup_complete);
|
|
}
|
|
|
|
export async function getAdminOrderDetail(orderId: string): Promise<AdminOrderDetail | null> {
|
|
// Return mock order detail in mock mode
|
|
if (useMockData) {
|
|
const order = mockAdminOrders.find((o) => o.id === orderId);
|
|
if (!order) return null;
|
|
return {
|
|
...order,
|
|
discount_amount: null,
|
|
tax_amount: order.subtotal * 0.08,
|
|
tax_rate: 0.08,
|
|
tax_location: "Colorado",
|
|
discount_reason: null,
|
|
internal_notes: null,
|
|
payment_status: "paid",
|
|
payment_transaction_id: `txn_mock_${orderId}`,
|
|
refunded_amount: 0,
|
|
refund_reason: null,
|
|
refunds: [],
|
|
};
|
|
}
|
|
|
|
const adminUser = await getAdminUser();
|
|
const brandId = adminUser?.brand_id ?? null;
|
|
|
|
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_admin_order_detail`,
|
|
{
|
|
method: "POST",
|
|
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
|
|
body: JSON.stringify({ p_order_id: orderId, p_brand_id: brandId }),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
return null;
|
|
}
|
|
|
|
const data = await response.json();
|
|
return data;
|
|
} |