diff --git a/packages/front-end/components/D3Tree.vue b/packages/front-end/components/D3Tree.vue index 3f12342..c6afb81 100644 --- a/packages/front-end/components/D3Tree.vue +++ b/packages/front-end/components/D3Tree.vue @@ -25,6 +25,14 @@ export default { default: 40, // Default size of the image node }, }, + watch: { + data: { + handler() { + this.drawTree(); + }, + deep: true, // Watch nested properties for changes + }, + }, mounted() { this.drawTree(); }, diff --git a/packages/front-end/nuxt.config.ts b/packages/front-end/nuxt.config.ts index 4dc8303..739f529 100644 --- a/packages/front-end/nuxt.config.ts +++ b/packages/front-end/nuxt.config.ts @@ -14,7 +14,7 @@ export default defineNuxtConfig({ public: { baseUrl: process.env.BACKEND_URL ? process.env.BACKEND_URL : globalBaseUrl, // Supply Graph AI configuration - supplyGraphAiUrl: process.env.SUPPLY_GRAPH_AI_URL || 'http://localhost:8081' + supplyGraphAiUrl: process.env.SUPPLY_GRAPH_AI_URL || 'http://localhost:8001' // apiBase: '/api' } }, diff --git a/packages/front-end/pages/products/[id]/supplyTree.vue b/packages/front-end/pages/products/[id]/supplyTree.vue index d1cf251..accc50d 100644 --- a/packages/front-end/pages/products/[id]/supplyTree.vue +++ b/packages/front-end/pages/products/[id]/supplyTree.vue @@ -24,7 +24,8 @@ const apiBaseUrl = ref( const supplyGraphApiUrl = ref( import.meta.env.VITE_SUPPLY_GRAPH_AI_URL || "http://localhost:8001" ); -const supplyGraphApiEndpoint = ref("/v1/match"); // Path to the versioned supply tree creation endpoint +const supplyGraphApiEndpoint = ref("/v1/api/match"); // Path to the versioned supply tree creation endpoint + const sendToSupplyGraphAI = async (o: any) => { if (!o) { @@ -54,37 +55,11 @@ const sendToSupplyGraphAI = async (o: any) => { try { const originalData = okhItem.originalData || okhItem; + console.log("Preparing to send OKH item to Supply Graph AI:", okhItem); + + // WARNING! TODO: This is hard coding a recipe. We instatn want this to be the OKH! const payload = { - okh_reference: - okhItem.id?.toString() || - originalData.fname || - originalData.id || - "unknown", - required_quantity: 1, - deadline: null, - metadata: { - name: okhItem.name || originalData.name || originalData.title, - shortDescription: - okhItem.shortDescription || - originalData.description || - originalData.summary, - keywords: - okhItem.keywords || originalData.keywords || originalData.tags || [], - maker: - okhItem.maker || - originalData.maker || - originalData.author || - originalData.creator, - whereToFind: - okhItem.whereToFind || - originalData.whereToFind || - originalData.source, - source: "project-data-platform-ts", - image: okhItem.image || originalData.image || originalData.imageUrl, - originalId: originalData.id || originalData.fname, - dataSource: originalData.dataSource || "project-data-platform", - ...originalData, - }, + recipe_id: "8f14e3c4-09f2-4a5e-8bd9-4b5bb5d0a9cd", }; console.log("Enhanced payload for Supply Graph AI (port 8001):", payload); @@ -134,87 +109,57 @@ const sendToSupplyGraphAI = async (o: any) => { // Parse the response from supply graph AI const supplyTreeResponse = await response.json(); - console.log("Supply Graph AI Response:", supplyTreeResponse); - - // Enhanced response processing to handle various response formats - supplyTreeData.value = { - rootItem: selectedOkh.value, - relatedComponents: - supplyTreeResponse.components || - supplyTreeResponse.related_items || - supplyTreeResponse.dependencies || - [], - supplyChainDepth: - supplyTreeResponse.depth || supplyTreeResponse.chain_depth || 1, - analysisTimestamp: - supplyTreeResponse.creation_time || - supplyTreeResponse.timestamp || - new Date().toISOString(), - analysisMethod: "supply-graph-ai-port-8001", - apiResponse: supplyTreeResponse, // Keep full response for debugging - requestPayload: payload, // Keep request for debugging - }; - console.log("Final Supply Tree Data:", supplyTreeData.value); + + console.log("Supply Graph AI Response2:", supplyTreeResponse); + + // Clear previous solutions + const formattedSolutions: any[] = []; + + supplyTreeResponse.data.solutions.forEach((solution: any) => { + + const children = solution.tree.capabilities_used.map((capability: string) => ({ + name: capability, + image: "/OKP_icon.png", + })); + + + const formattedSolution = { + name: solution.facility_name, + image: "/okw_maker.png", + class: "test", + children: children, + }; + + formattedSolutions.push(formattedSolution); + }); + + console.log("formattedSolutions:", formattedSolutions); + + // Update treeData.value to trigger re-render in D3Tree component + treeData.value = { + name: selectedOkh.value?.name || "Supply Tree", + children: [ + { + image: "/okh.png", + children: formattedSolutions, + }, + ], + }; } catch (err) { console.error("Error generating supply tree:", err); - error.value = `Failed to generate supply tree: ${err.message}`; + error.value = `Failed to generate supply tree: ${err instanceof Error ? err.message : String(err)}`; } finally { loading.value = false; } }; -const treeData = reactive({ - name: "Oatmeal raisin cookie", - +const treeData = ref({ + name: "Chocolate Chip Cookies", children: [ { - // name: "OKW", - image: "/okw.png", - children: [ - { - name: "cinnamon", - class: "test", - children: [ - { - image: "/okw.png", - children: [ - { - name: "testA", - children: [ - { - image: "/okw.png", - children: [ - { - name: "test1", - }, - ], - }, - { image: "/okw.png" }, - { image: "/okw.png" }, - { image: "/okw.png" }, - ], - }, - ], - }, - { image: "/okw.png", children: [{ name: "testB" }] }, - { image: "/okw.png" }, - { image: "/okw.png" }, - ], - }, - { name: "vanilla extract", children: [{ image: "/okw.png" }] }, - { name: "white sugar", children: [{ image: "/okw.png" }] }, - { name: "raisin", children: [{ image: "/okw.png" }] }, - { name: "egg", children: [{ image: "/okw.png" }] }, - { name: "shipping supply", children: [{ image: "/okw.png" }] }, - { name: "butter", children: [{ image: "/okw.png" }] }, - { name: "oats", children: [{ image: "/okw.png" }] }, - { - name: "all purpose flour", - children: [{ image: "/okw.png" }], - }, - { name: "brown sugar", children: [{ image: "/okw.png" }] }, - ], + image: "/okh.png", + children: [], }, ], }); diff --git a/packages/front-end/pages/supply-graph-api.vue b/packages/front-end/pages/supply-graph-api.vue index 1066d21..b42babf 100644 --- a/packages/front-end/pages/supply-graph-api.vue +++ b/packages/front-end/pages/supply-graph-api.vue @@ -94,23 +94,27 @@ const sendToSupplyGraphAI = async (okhItem) => { console.log('Processing OKH item for Supply Graph AI:', okhItem); const originalData = okhItem.originalData || okhItem; - const payload = { - okh_reference: okhItem.id?.toString() || originalData.fname || originalData.id || 'unknown', - required_quantity: 1, - deadline: null, - metadata: { - name: okhItem.name || originalData.name || originalData.title, - shortDescription: okhItem.shortDescription || originalData.description || originalData.summary, - keywords: okhItem.keywords || originalData.keywords || originalData.tags || [], - maker: okhItem.maker || originalData.maker || originalData.author || originalData.creator, - whereToFind: okhItem.whereToFind || originalData.whereToFind || originalData.source, - source: "project-data-platform-ts", - image: okhItem.image || originalData.image || originalData.imageUrl, - originalId: originalData.id || originalData.fname, - dataSource: originalData.dataSource || 'project-data-platform', - ...originalData - } - }; + // const payload = { + // okh_reference: okhItem.id?.toString() || originalData.fname || originalData.id || 'unknown', + // required_quantity: 1, + // deadline: null, + // metadata: { + // name: okhItem.name || originalData.name || originalData.title, + // shortDescription: okhItem.shortDescription || originalData.description || originalData.summary, + // keywords: okhItem.keywords || originalData.keywords || originalData.tags || [], + // maker: okhItem.maker || originalData.maker || originalData.author || originalData.creator, + // whereToFind: okhItem.whereToFind || originalData.whereToFind || originalData.source, + // source: "project-data-platform-ts", + // image: okhItem.image || originalData.image || originalData.imageUrl, + // originalId: originalData.id || originalData.fname, + // dataSource: originalData.dataSource || 'project-data-platform', + // ...originalData + // } + // }; + + const payload = { + "recipe_id": "8f14e3c4-09f2-4a5e-8bd9-4b5bb5d0a9cd" +} console.log('Enhanced payload for Supply Graph AI (port 8001):', payload); console.log(`Sending request to: ${supplyGraphApiUrl.value}${supplyGraphApiEndpoint.value}`); diff --git a/packages/front-end/public/OKP_icon.png b/packages/front-end/public/OKP_icon.png new file mode 100644 index 0000000..59ce796 Binary files /dev/null and b/packages/front-end/public/OKP_icon.png differ diff --git a/packages/front-end/public/okh.png b/packages/front-end/public/okh.png new file mode 100644 index 0000000..5dfbc63 Binary files /dev/null and b/packages/front-end/public/okh.png differ diff --git a/packages/front-end/public/okw_maker.png b/packages/front-end/public/okw_maker.png new file mode 100644 index 0000000..375b40c Binary files /dev/null and b/packages/front-end/public/okw_maker.png differ diff --git a/packages/mock-api/README.md b/packages/mock-api/README.md new file mode 100644 index 0000000..9036378 --- /dev/null +++ b/packages/mock-api/README.md @@ -0,0 +1,68 @@ +# Mock API (project-data-platform-ts) + +Lightweight Express mock API used for local development and for testing integration with the frontend and supply-graph services. + +- Port: 8081 (default) +- Purpose: return deterministic mock supply-tree responses and basic health checks. + +## Quick start + +1. Install dependencies + +```powershell +npm install +``` + +2. Start dev server + +```powershell +npm run dev +``` + +3. Server will be listening on: + +``` +http://localhost:8081 +``` + +(If you want to run without nodemon, use `node index.js`.) + +## Available endpoints + +- GET / + - Basic welcome text. + +- POST /v1/match + - Accepts a JSON body and returns a mock supply-tree response. + - Useful for testing supply-graph AI integration. + +- GET /health + - Returns a simple health JSON: `{ "status": "OK", "message": "Server is healthy!" }`. + +## Example cURL + +POST to the match endpoint: + +```bash +curl -X POST http://localhost:8081/v1/match \ + -H "Content-Type: application/json" \ + -d '{"product": {"id":"Q15026","desc":"chair"}}' +``` + +Response will be the mock supply-tree JSON defined in `mock-data.js`. + +## Files + +- `index.js` — Express server and endpoints. +- `mock-data.js` — Contains the mock response object(s) including `supplyTreeMockResponse`. +- `package.json` — Scripts and dependencies. + +## Notes + +- This mock API is intentionally simple and synchronous to make frontend integration straightforward. +- Replace or extend the mock response in `mock-data.js` to suit tests or demos. +- The repo uses `type: "module"`, so Node should be v14+ (prefer v18+). + +## License + +Project license follows the parent repository. See top-level LICENSE for details. diff --git a/packages/mock-api/index.js b/packages/mock-api/index.js new file mode 100644 index 0000000..f18a583 --- /dev/null +++ b/packages/mock-api/index.js @@ -0,0 +1,32 @@ +import express from "express"; +import morgan from "morgan"; +import { supplyTreeMockResponse } from "./mock-data.js"; + +const app = express(); + +const PORT = 8001; + +// Middleware to parse JSON +app.use(express.json()); + +//middleware for logging +app.use(morgan('tiny')); + +// Basic route +app.get("/", (req, res) => { + res.send("Hello from your first Node.js backend!"); +}); + +app.post("/v1/match", (req, res) => { + console.log("Received data:", req.body); + res.status(200).json({ status: "OK", data: supplyTreeMockResponse }); +}); + +app.get("/health", (req, res) => { + res.status(200).json({ status: "OK", message: "Server is healthy!" }); +}); + +// Start the server +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); diff --git a/packages/mock-api/mock-data.js b/packages/mock-api/mock-data.js new file mode 100644 index 0000000..0922884 --- /dev/null +++ b/packages/mock-api/mock-data.js @@ -0,0 +1,107 @@ +export const supplyTreeMockResponse = { + "product": { + "id": "Q15026", + "desc": "chair" + }, + "type": "made", + "party": "Devhawk Engineering", + "design": "Funky Chair Design", + "bom": [ + { + "product": { + "id": "QH100", + "desc": "chair leg" + }, + "type": "supplied", + "party": "Robert's Chair Parts" + }, + { + "product": { + "id": "QH108", + "desc": "nails" + }, + "type": "supplied", + "party": "raw supplies" + }, + { + "product": { + "id": "QH102", + "desc": "chair back" + }, + "type": "supplied", + "party": "Robert's Chair Parts" + }, + { + "product": { + "id": "QH100", + "desc": "chair leg" + }, + "type": "made", + "party": "Devhawk Engineering", + "design": "Leg Design", + "bom": [ + { + "product": { + "id": "QH104", + "desc": "wood" + }, + "type": "supplied", + "party": "raw supplies" + } + ] + }, + { + "product": { + "id": "QH101", + "desc": "chair seat" + }, + "type": "made", + "party": "James Maker Space", + "design": "Seat Design 2", + "bom": [ + { + "product": { + "id": "QH107", + "desc": "frame" + }, + "type": "supplied", + "party": "raw supplies" + }, + { + "product": { + "id": "QH106", + "desc": "upholstery" + }, + "type": "supplied", + "party": "raw supplies" + }, + { + "product": { + "id": "QH105", + "desc": "stuffing" + }, + "type": "missing" + } + ] + }, + { + "product": { + "id": "QH101", + "desc": "chair seat" + }, + "type": "made", + "party": "Devhawk Engineering", + "design": "Seat Design 1", + "bom": [ + { + "product": { + "id": "QH103", + "desc": "fabric" + }, + "type": "supplied", + "party": "raw supplies" + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/mock-api/package.json b/packages/mock-api/package.json new file mode 100644 index 0000000..5ee7218 --- /dev/null +++ b/packages/mock-api/package.json @@ -0,0 +1,19 @@ +{ + "name": "mock-api", + "version": "1.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "dev": "nodemon index.js", + "start" : "node index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^5.1.0", + "morgan": "^1.10.1" + } +}