feat: reconcile application vertical slices
Backfill the completed acquisitions, sales, dashboard, pricing, search, public detail, worker, migration, contracts, validation, tests, and implementation-status changes into one verified application slice.
This commit is contained in:
@@ -1,24 +1,28 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, {
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
...options,
|
||||
headers: { ...(options.body ? { 'content-type': 'application/json' } : {}), ...options.headers },
|
||||
headers: {
|
||||
...(options.body ? { "content-type": "application/json" } : {}),
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Acquisition request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Acquisition request failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export function listAdminAcquisitions() {
|
||||
return request('/admin/acquisitions');
|
||||
return request("/admin/acquisitions");
|
||||
}
|
||||
|
||||
export function createAdminAcquisition(input, csrfToken) {
|
||||
return request('/admin/acquisitions', {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
return request("/admin/acquisitions", {
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
export async function getAdminDashboard() {
|
||||
const response = await fetch(`${apiBaseUrl}/admin/dashboard`, { credentials: 'include' });
|
||||
const response = await fetch(`${apiBaseUrl}/admin/dashboard`, {
|
||||
credentials: "include",
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Dashboard request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Dashboard request failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, {
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
...options,
|
||||
headers: { ...(options.body ? { 'content-type': 'application/json' } : {}), ...options.headers },
|
||||
headers: {
|
||||
...(options.body ? { "content-type": "application/json" } : {}),
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Job request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Job request failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export function listAdminPricingJobs() {
|
||||
return request('/admin/jobs');
|
||||
return request("/admin/jobs");
|
||||
}
|
||||
|
||||
export function retryAdminPricingJob(jobId, csrfToken) {
|
||||
return request(`/admin/jobs/${encodeURIComponent(jobId)}/retry`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,14 +1,40 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const headers = { ...(options.body ? { 'content-type': 'application/json' } : {}), ...options.headers };
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, { credentials: 'include', ...options, headers });
|
||||
const headers = {
|
||||
...(options.body ? { "content-type": "application/json" } : {}),
|
||||
...options.headers,
|
||||
};
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, {
|
||||
credentials: "include",
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Listing request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Listing request failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export function listAdminListings() { return request('/admin/listings'); }
|
||||
export function createAdminListing(input, csrfToken) { return request('/admin/listings', { method: 'POST', headers: { 'x-csrf-token': csrfToken }, body: JSON.stringify(input) }); }
|
||||
export function publishAdminListing(id, csrfToken) { return request(`/admin/listings/${encodeURIComponent(id)}/publish`, { method: 'POST', headers: { 'x-csrf-token': csrfToken } }); }
|
||||
export function markListingPending(id, csrfToken) { return request(`/admin/listings/${encodeURIComponent(id)}/pending`, { method: 'POST', headers: { 'x-csrf-token': csrfToken } }); }
|
||||
export function listAdminListings() {
|
||||
return request("/admin/listings");
|
||||
}
|
||||
export function createAdminListing(input, csrfToken) {
|
||||
return request("/admin/listings", {
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
export function publishAdminListing(id, csrfToken) {
|
||||
return request(`/admin/listings/${encodeURIComponent(id)}/publish`, {
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
});
|
||||
}
|
||||
export function markListingPending(id, csrfToken) {
|
||||
return request(`/admin/listings/${encodeURIComponent(id)}/pending`, {
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,45 +1,58 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, {
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
...options,
|
||||
headers: { ...(options.body ? { 'content-type': 'application/json' } : {}), ...options.headers },
|
||||
headers: {
|
||||
...(options.body ? { "content-type": "application/json" } : {}),
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Pricing request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Pricing request failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export function addAdminPricingObservation(input, csrfToken) {
|
||||
return request('/admin/pricing/observations', {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
return request("/admin/pricing/observations", {
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
export function calculateAdminValuation(inventoryItemId, csrfToken) {
|
||||
return request(`/admin/inventory/${encodeURIComponent(inventoryItemId)}/valuation`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
});
|
||||
return request(
|
||||
`/admin/inventory/${encodeURIComponent(inventoryItemId)}/valuation`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function overrideAdminValuation(inventoryItemId, input, csrfToken) {
|
||||
return request(`/admin/inventory/${encodeURIComponent(inventoryItemId)}/valuation/override`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
return request(
|
||||
`/admin/inventory/${encodeURIComponent(inventoryItemId)}/valuation/override`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function enqueueAdminPricingRefresh(inventoryItemId, input, csrfToken) {
|
||||
return request(`/admin/inventory/${encodeURIComponent(inventoryItemId)}/pricing/refresh`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
return request(
|
||||
`/admin/inventory/${encodeURIComponent(inventoryItemId)}/pricing/refresh`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function getAdminPricingJob(jobId) {
|
||||
|
||||
@@ -1,24 +1,35 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, {
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
...options,
|
||||
headers: { ...(options.body ? { 'content-type': 'application/json' } : {}), ...options.headers },
|
||||
headers: {
|
||||
...(options.body ? { "content-type": "application/json" } : {}),
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Provider mapping request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(
|
||||
payload?.error?.message ?? "Provider mapping request failed.",
|
||||
);
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export function listAdminProviderMappings(productId) {
|
||||
return request(`/admin/products/${encodeURIComponent(productId)}/provider-mappings`);
|
||||
return request(
|
||||
`/admin/products/${encodeURIComponent(productId)}/provider-mappings`,
|
||||
);
|
||||
}
|
||||
|
||||
export function createAdminProviderMapping(productId, input, csrfToken) {
|
||||
return request(`/admin/products/${encodeURIComponent(productId)}/provider-mappings`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-csrf-token': csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
return request(
|
||||
`/admin/products/${encodeURIComponent(productId)}/provider-mappings`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "x-csrf-token": csrfToken },
|
||||
body: JSON.stringify(input),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,46 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
export async function createAdminSale(input, csrfToken) {
|
||||
const response = await fetch(`${apiBaseUrl}/admin/sales`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'x-csrf-token': csrfToken,
|
||||
"content-type": "application/json",
|
||||
"x-csrf-token": csrfToken,
|
||||
},
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Sale request failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Sale request failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export async function listAdminSales() {
|
||||
const response = await fetch(`${apiBaseUrl}/admin/sales`, { credentials: 'include' });
|
||||
const response = await fetch(`${apiBaseUrl}/admin/sales`, {
|
||||
credentials: "include",
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Sale history is unavailable.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Sale history is unavailable.");
|
||||
return payload?.data ?? [];
|
||||
}
|
||||
|
||||
export async function voidAdminSale(id, reason, csrfToken) {
|
||||
const response = await fetch(`${apiBaseUrl}/admin/sales/${encodeURIComponent(id)}/void`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'x-csrf-token': csrfToken,
|
||||
const response = await fetch(
|
||||
`${apiBaseUrl}/admin/sales/${encodeURIComponent(id)}/void`,
|
||||
{
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-csrf-token": csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ reason }),
|
||||
},
|
||||
body: JSON.stringify({ reason }),
|
||||
});
|
||||
);
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(payload?.error?.message ?? 'Sale correction failed.');
|
||||
if (!response.ok)
|
||||
throw new Error(payload?.error?.message ?? "Sale correction failed.");
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const apiBaseUrl = import.meta.env?.VITE_API_BASE_URL ?? "/api/v1";
|
||||
|
||||
export async function searchCatalog({ query = '', category = '', conditionId = '', minPriceMinor, maxPriceMinor, sort = 'recent', page = 1, limit = 20 } = {}) {
|
||||
const params = new URLSearchParams({ query, page: String(page), limit: String(limit) });
|
||||
if (sort !== 'recent') params.set('sort', sort);
|
||||
if (category) params.set('category', category);
|
||||
if (conditionId) params.set('conditionId', conditionId);
|
||||
if (typeof minPriceMinor === 'number') params.set('minPriceMinor', String(minPriceMinor));
|
||||
if (typeof maxPriceMinor === 'number') params.set('maxPriceMinor', String(maxPriceMinor));
|
||||
export async function searchCatalog({
|
||||
query = "",
|
||||
category = "",
|
||||
conditionId = "",
|
||||
minPriceMinor,
|
||||
maxPriceMinor,
|
||||
sort = "recent",
|
||||
page = 1,
|
||||
limit = 20,
|
||||
} = {}) {
|
||||
const params = new URLSearchParams({
|
||||
query,
|
||||
page: String(page),
|
||||
limit: String(limit),
|
||||
});
|
||||
if (sort !== "recent") params.set("sort", sort);
|
||||
if (category) params.set("category", category);
|
||||
if (conditionId) params.set("conditionId", conditionId);
|
||||
if (typeof minPriceMinor === "number")
|
||||
params.set("minPriceMinor", String(minPriceMinor));
|
||||
if (typeof maxPriceMinor === "number")
|
||||
params.set("maxPriceMinor", String(maxPriceMinor));
|
||||
const response = await fetch(`${apiBaseUrl}/public/search?${params}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Catalog search is unavailable.');
|
||||
throw new Error("Catalog search is unavailable.");
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
@@ -19,21 +34,27 @@ export async function searchCatalog({ query = '', category = '', conditionId = '
|
||||
|
||||
export async function listPublicCategories() {
|
||||
const response = await fetch(`${apiBaseUrl}/public/lookups/categories`);
|
||||
if (!response.ok) throw new Error('Categories are unavailable.');
|
||||
if (!response.ok) throw new Error("Categories are unavailable.");
|
||||
return (await response.json()).data;
|
||||
}
|
||||
|
||||
export async function listPublicConditions() {
|
||||
const response = await fetch(`${apiBaseUrl}/public/lookups/conditions`);
|
||||
if (!response.ok) throw new Error('Conditions are unavailable.');
|
||||
if (!response.ok) throw new Error("Conditions are unavailable.");
|
||||
return (await response.json()).data;
|
||||
}
|
||||
|
||||
export async function getPublicListing(id) {
|
||||
const response = await fetch(`${apiBaseUrl}/public/listings/${encodeURIComponent(id)}`);
|
||||
const response = await fetch(
|
||||
`${apiBaseUrl}/public/listings/${encodeURIComponent(id)}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(response.status === 404 ? 'Listing not found.' : 'Listing is unavailable.');
|
||||
throw new Error(
|
||||
response.status === 404
|
||||
? "Listing not found."
|
||||
: "Listing is unavailable.",
|
||||
);
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
@@ -41,12 +62,18 @@ export async function getPublicListing(id) {
|
||||
}
|
||||
|
||||
export async function getPublicProduct(slug) {
|
||||
const response = await fetch(`${apiBaseUrl}/public/products/${encodeURIComponent(slug)}`);
|
||||
const response = await fetch(
|
||||
`${apiBaseUrl}/public/products/${encodeURIComponent(slug)}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(response.status === 404 ? 'Product not found.' : 'Product is unavailable.');
|
||||
throw new Error(
|
||||
response.status === 404
|
||||
? "Product not found."
|
||||
: "Product is unavailable.",
|
||||
);
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
return payload.data;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user