Build the Solaire inventory intelligence MVP
Some checks failed
CI / verify (push) Has been cancelled

Add the tenant-ready dashboard, freshness and pricing analytics, mock integration adapters, persistence schema, and verification tooling needed for the initial pilot.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-25 12:16:52 -07:00
parent bf336f8d15
commit 135be480e5
58 changed files with 7884 additions and 265 deletions

View File

@@ -0,0 +1,22 @@
import { expect, test } from "@playwright/test";
test("shows inventory intelligence overview", async ({ page }) => {
await page.goto("/");
await expect(page.getByRole("heading", { name: "Good morning, Nora" })).toBeVisible();
await expect(page.getByText("Inventory value")).toBeVisible();
await expect(page.getByText("Freshness watchlist")).toBeVisible();
});
test("moves from inventory to a product", async ({ page }) => {
await page.goto("/inventory");
await expect(page.getByRole("heading", { name: "Inventory" })).toBeVisible();
await page.getByRole("link", { name: "Organic Strawberries" }).first().click();
await expect(page.getByRole("heading", { name: "Organic Strawberries" })).toBeVisible();
});
test("reviews a recommendation", async ({ page }) => {
await page.goto("/recommendations");
await expect(page.getByRole("heading", { name: "Recommendations" })).toBeVisible();
await page.getByRole("button", { name: "Reviewed" }).first().click();
await expect(page.getByText("reviewed").first()).toBeVisible();
});

25
tests/freshness.test.ts Normal file
View File

@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import { calculateFreshness, freshnessStatus } from "@/domain/freshness";
describe("freshness scoring", () => {
const asOf = new Date("2026-07-25T12:00:00Z");
it("returns a fresh score for recently received stock", () => {
expect(calculateFreshness("2026-07-23T12:00:00Z", 10, asOf)).toMatchObject({
ageDays: 2,
remainingDays: 8,
score: 80,
status: "fresh",
});
});
it("marks the threshold states consistently", () => {
expect(freshnessStatus(45)).toBe("watch");
expect(freshnessStatus(20)).toBe("at_risk");
expect(freshnessStatus(0)).toBe("expired");
});
it("never emits a negative freshness score", () => {
expect(calculateFreshness("2026-07-01T12:00:00Z", 7, asOf).score).toBe(0);
});
});

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import { getDashboardData } from "@/domain/analytics";
import { runDemoSync } from "@/integrations/sync";
describe("mock integration sync", () => {
it("normalizes Odoo and Salesforce fixtures", async () => {
const [odoo, salesforce] = await Promise.all([
runDemoSync("odoo"),
runDemoSync("salesforce"),
]);
expect(odoo.status).toBe("succeeded");
expect(salesforce.status).toBe("succeeded");
expect(odoo.recordsWritten).toBeGreaterThan(0);
expect(salesforce.recordsWritten).toBeGreaterThan(odoo.recordsWritten);
});
it("keeps all demo data inside the pilot tenant", () => {
const data = getDashboardData();
expect(data.inventory.every((item) => item.organizationId === data.organization.id)).toBe(true);
});
it("is idempotent for repeated fixture reads", async () => {
const first = await runDemoSync("odoo");
const second = await runDemoSync("odoo");
expect(second.recordsWritten).toBe(first.recordsWritten);
});
});

View File

@@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";
import { getDashboardData } from "@/domain/analytics";
import { marginPercent } from "@/domain/recommendations";
describe("pricing recommendations", () => {
const data = getDashboardData();
it("protects every product margin floor", () => {
for (const recommendation of data.recommendations) {
const product = data.inventory.find((item) => item.productId === recommendation.productId)!.product;
expect(marginPercent(product, recommendation.recommendedLow))
.toBeGreaterThanOrEqual(product.marginFloorPercent - 0.01);
}
});
it("provides explanations and bounded ranges", () => {
for (const recommendation of data.recommendations) {
expect(recommendation.reason.length).toBeGreaterThan(20);
expect(recommendation.recommendedHigh).toBeGreaterThanOrEqual(recommendation.recommendedLow);
expect(recommendation.recommendedHigh).toBeLessThanOrEqual(recommendation.currentPrice);
}
});
it("contains actionable aging-stock recommendations", () => {
expect(data.recommendations.some((item) => item.type === "markdown")).toBe(true);
expect(data.recommendations.some((item) => item.type === "promotion")).toBe(true);
});
});