145 lines
3.5 KiB
TypeScript
145 lines
3.5 KiB
TypeScript
import "server-only";
|
|
|
|
export type OlympusRepairStatusHistoryItem = {
|
|
status: string;
|
|
status_label: string;
|
|
created_at: string;
|
|
};
|
|
|
|
export type OlympusEstimateItem = {
|
|
item_type: string;
|
|
title: string;
|
|
description: string | null;
|
|
quantity: string | number;
|
|
unit: string;
|
|
unit_price_cents: number;
|
|
total_cents: number;
|
|
};
|
|
|
|
export type OlympusEstimate = {
|
|
estimate_number: string;
|
|
status: "draft" | "sent" | "approved" | "declined" | "expired" | "cancelled" | string;
|
|
title: string;
|
|
customer_message: string | null;
|
|
subtotal_cents: number;
|
|
tax_cents: number;
|
|
total_cents: number;
|
|
currency: string;
|
|
valid_until: string | null;
|
|
items: OlympusEstimateItem[];
|
|
};
|
|
|
|
export type OlympusRepairStatus = {
|
|
repair_number: string;
|
|
public_status_label: string;
|
|
device_manufacturer: string;
|
|
device_model: string;
|
|
status_history_public: OlympusRepairStatusHistoryItem[];
|
|
updated_at: string;
|
|
estimate?: OlympusEstimate | null;
|
|
};
|
|
|
|
export type RepairStatusResult =
|
|
| { ok: true; status: OlympusRepairStatus }
|
|
| { ok: false; reason: "invalid" | "unavailable" };
|
|
|
|
export type EstimateAction = "approve" | "decline" | "question";
|
|
|
|
export type EstimateActionResult =
|
|
| { ok: true }
|
|
| { ok: false; reason: "invalid" | "unavailable" | "failed" };
|
|
|
|
function getStatusApiBaseUrl() {
|
|
return process.env.OLYMPUS_PUBLIC_STATUS_API_URL?.replace(/\/$/, "") ?? "";
|
|
}
|
|
|
|
function getStatusApiToken() {
|
|
return process.env.OLYMPUS_PUBLIC_STATUS_API_TOKEN ?? "";
|
|
}
|
|
|
|
export async function fetchOlympusRepairStatus(token: string): Promise<RepairStatusResult> {
|
|
const baseUrl = getStatusApiBaseUrl();
|
|
|
|
if (!baseUrl || !token.trim()) {
|
|
return { ok: false, reason: "unavailable" };
|
|
}
|
|
|
|
const headers: HeadersInit = {
|
|
Accept: "application/json",
|
|
};
|
|
const apiToken = getStatusApiToken();
|
|
|
|
if (apiToken) {
|
|
headers.Authorization = `Bearer ${apiToken}`;
|
|
}
|
|
|
|
let response: Response;
|
|
|
|
try {
|
|
response = await fetch(`${baseUrl}/public/repairs/status/${encodeURIComponent(token)}`, {
|
|
headers,
|
|
cache: "no-store",
|
|
});
|
|
} catch {
|
|
return { ok: false, reason: "unavailable" };
|
|
}
|
|
|
|
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
return { ok: false, reason: "invalid" };
|
|
}
|
|
|
|
if (!response.ok) {
|
|
return { ok: false, reason: "unavailable" };
|
|
}
|
|
|
|
try {
|
|
return { ok: true, status: await response.json() as OlympusRepairStatus };
|
|
} catch {
|
|
return { ok: false, reason: "unavailable" };
|
|
}
|
|
}
|
|
|
|
export async function submitOlympusEstimateAction(
|
|
token: string,
|
|
action: EstimateAction,
|
|
message?: string,
|
|
): Promise<EstimateActionResult> {
|
|
const baseUrl = getStatusApiBaseUrl();
|
|
|
|
if (!baseUrl || !token.trim()) {
|
|
return { ok: false, reason: "unavailable" };
|
|
}
|
|
|
|
const headers: HeadersInit = {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
};
|
|
const apiToken = getStatusApiToken();
|
|
|
|
if (apiToken) {
|
|
headers.Authorization = `Bearer ${apiToken}`;
|
|
}
|
|
|
|
let response: Response;
|
|
|
|
try {
|
|
response = await fetch(`${baseUrl}/public/repairs/status/${encodeURIComponent(token)}/estimate/${action}`, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({ message: message?.trim() || null }),
|
|
cache: "no-store",
|
|
});
|
|
} catch {
|
|
return { ok: false, reason: "unavailable" };
|
|
}
|
|
|
|
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
return { ok: false, reason: "invalid" };
|
|
}
|
|
|
|
if (!response.ok) {
|
|
return { ok: false, reason: "failed" };
|
|
}
|
|
|
|
return { ok: true };
|
|
}
|