diff --git a/src/components/UI/global/SearchResultCard.jsx b/src/components/UI/global/SearchResultCard.jsx
index f579a23f..30b7d6a8 100644
--- a/src/components/UI/global/SearchResultCard.jsx
+++ b/src/components/UI/global/SearchResultCard.jsx
@@ -1,21 +1,21 @@
+import Link from 'next/link'
export default function SearchResultCard({ item }) {
console.log(item)
const { name, dpp_class, manufactured_by, created_at } = item
return (
-
-
-
-
{name}
-
- {/* {created_at && created_at.creation_time && (
- {created_at.creation_time}
- )} */}
- {manufactured_by && manufactured_by.owner_name && (
- {manufactured_by.owner_name}
- )}
-
+
+
+
+
+
{name}
+
+ {manufactured_by && manufactured_by.owner_name && (
+ {manufactured_by.owner_name}
+ )}
+
+
-
+
)
}
diff --git a/src/models/Product.js b/src/models/Product.js
index 791f35b8..041b1552 100644
--- a/src/models/Product.js
+++ b/src/models/Product.js
@@ -2,11 +2,19 @@ import mongoose from 'mongoose'
const Schema = mongoose.Schema
-const eventSchema = new Schema(
+const eventSchema = new Schema({
+ id: Number,
+ dpp_class: String,
+ creation_time: Date, // Assuming this should be a Date type
+ action: String,
+})
+
+const componentSchema = new Schema(
{
id: Number,
+ name: String,
dpp_class: String,
- creation_time: Date, // Assuming this should be a Date type
+ _id: Schema.Types.ObjectId,
},
{ _id: false }
)
@@ -23,20 +31,25 @@ const productSchema = new Schema(
privacy: String,
},
created_at: {
- creation_time: Date, // Assuming this should be a Date type
+ creation_time: String, // Assuming this should be a Date type
privacy: String,
},
- has_carbon_footprint: {
+ carbon_footprint: {
id: Number,
dpp_class: String,
privacy: String,
+ effect: String,
+ },
+ key_components: {
+ privacy: String,
+ components: [componentSchema],
},
- main_component: {
+ crm: {
id: Number,
dpp_class: String,
privacy: String,
},
- has_event_trail: {
+ event_trail: {
privacy: String,
events: [eventSchema], // Define a separate schema for events
},
diff --git a/src/models/ProductCollection.js b/src/models/ProductCollection.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/pages/api/v1/admin/products/index.js b/src/pages/api/v1/admin/products/index.js
index e69de29b..66747d8a 100644
--- a/src/pages/api/v1/admin/products/index.js
+++ b/src/pages/api/v1/admin/products/index.js
@@ -0,0 +1,62 @@
+import { defaultHandler } from '@/utils/server/api-helpers'
+import Product from '@/models/Product' // Ensure you have a Product model similar to Role
+
+const getAllProducts = async (req, res) => {
+ try {
+ const page = parseInt(req.query.page) || 1
+ const pageSize = parseInt(req.query.pageSize) || 25
+ const skip = (page - 1) * pageSize
+
+ const products = await Product.find({}).skip(skip).limit(pageSize)
+ const totalProducts = await Product.countDocuments()
+
+ res.status(200).json({ products, totalProducts })
+ } catch (error) {
+ console.error('Error fetching products:', error)
+ res
+ .status(500)
+ .json({ message: 'Internal server error', error: error.message })
+ }
+}
+
+const createProduct = async (req, res) => {
+ try {
+ // Add product-specific validation here
+ // Example: if (!req.body.name) { return res.status(400).json({ message: 'Missing product name' }) }
+
+ const product = await Product.create(req.body)
+ res.status(201).json({ product })
+ } catch (error) {
+ console.error('Error creating product:', error)
+
+ if (error.name === 'ValidationError') {
+ return res
+ .status(400)
+ .json({ message: 'Validation error', error: error.message })
+ }
+
+ if (error.code === 11000) {
+ return res.status(409).json({ message: 'Product already exists' })
+ }
+
+ res
+ .status(500)
+ .json({ message: 'Internal server error', error: error.message })
+ }
+}
+
+const handler = async (req, res) =>
+ defaultHandler(
+ req,
+ res,
+ {
+ GET: getAllProducts,
+ POST: createProduct,
+ },
+ {
+ requiresAuth: false,
+ requiresAdmin: true,
+ }
+ )
+
+export default handler
diff --git a/src/pages/api/v1/product/[productID].js b/src/pages/api/v1/product/[productID].js
deleted file mode 100644
index 5f8f1a31..00000000
--- a/src/pages/api/v1/product/[productID].js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { defaultHandler } from '@/utils/server/api-helpers'
-import { ObjectId } from 'mongodb'
-import Product from '@/models/Product'
-
-const getProductByID = async (req, res) => {
- try {
- const { productID } = req.query
-
- if (!ObjectId.isValid(productID)) {
- return res.status(400).json({ message: 'Invalid ID' })
- }
-
- // Use 'new' to create a new instance of ObjectId
- const productData = await Product.findOne({ _id: new ObjectId(productID) })
-
- if (!productData) {
- return res.status(404).json({ message: 'Product not found' })
- }
-
- res.status(200).json(productData)
- } catch (error) {
- console.error('Error fetching product:', error)
- return res
- .status(500)
- .json({ message: 'Internal server error', error: error.message })
- }
-}
-
-const handler = async (req, res) =>
- defaultHandler(
- req,
- res,
- {
- GET: getProductByID,
- },
- {
- requiresAuth: false,
- requiresAdmin: false,
- }
- )
-
-export default handler
diff --git a/src/pages/api/v1/product/[productId].js b/src/pages/api/v1/product/[productId].js
new file mode 100644
index 00000000..8b3b9edc
--- /dev/null
+++ b/src/pages/api/v1/product/[productId].js
@@ -0,0 +1,81 @@
+import { defaultHandler } from '@/utils/server/api-helpers'
+import { ObjectId } from 'mongodb'
+import Product from '@/models/Product'
+
+const getProductById = async (req, res) => {
+ console.log('heres the req', req.query)
+ try {
+ const { productId } = req.query
+ console.log('heres the productid', productId)
+ if (!ObjectId.isValid(productId)) {
+ return res.status(400).json({ message: 'Invalid ID' })
+ }
+
+ const productData = await Product.findOne({ _id: new ObjectId(productId) })
+
+ if (!productData) {
+ return res.status(404).json({ message: 'Product not found' })
+ }
+
+ res.status(200).json(productData)
+ } catch (error) {
+ console.error('Error fetching product:', error)
+ return res
+ .status(500)
+ .json({ message: 'Internal server error', error: error.message })
+ }
+}
+
+const addEventToProduct = async (req, res) => {
+ const { productId } = req.query
+ const event = req.body // The incoming event data
+ console.log('api prodid', productId)
+
+ // Validate productId
+ if (!ObjectId.isValid(productId)) {
+ return res.status(400).json({ message: 'Invalid ID' })
+ }
+
+ // Ensure the event has a creation_time as Date object
+ if (event.creation_time) {
+ event.creation_time = new Date(event.creation_time)
+ } else {
+ // Optionally set creation_time to now if not provided
+ event.creation_time = new Date()
+ }
+
+ try {
+ const updatedProduct = await Product.findOneAndUpdate(
+ { _id: productId },
+ { $push: { 'event_trail.events': event } },
+ { new: true } // Option to return the document after update
+ )
+
+ if (!updatedProduct) {
+ return res.status(404).json({ message: 'Product not found' })
+ }
+
+ res.status(200).json(updatedProduct)
+ } catch (error) {
+ console.error('Error adding event to product:', error)
+ return res
+ .status(500)
+ .json({ message: 'Internal server error', error: error.message })
+ }
+}
+
+const handler = async (req, res) =>
+ defaultHandler(
+ req,
+ res,
+ {
+ GET: getProductById,
+ PUT: addEventToProduct,
+ },
+ {
+ requiresAuth: false,
+ requiresAdmin: false,
+ }
+ )
+
+export default handler
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index 5b674c1f..7b7d1d9f 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -1,9 +1,23 @@
import Image from 'next/image'
import { Inter } from 'next/font/google'
import LayoutGlobal from '@/components/Layout/LayoutGlobal'
+import SearchBar from '@/components/Global/SearchBar'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
- return
+ return (
+
+
+
Project D0020E
+
Digital Product Passport & Key Services
+
+
+ Want to know more about a product?
+
+
+
+
+
+ )
}
diff --git a/src/pages/product/[productId].js b/src/pages/product/[productId].js
index 0f841ff1..c8778fd1 100644
--- a/src/pages/product/[productId].js
+++ b/src/pages/product/[productId].js
@@ -2,17 +2,22 @@ import { useRouter } from 'next/router'
import LayoutGlobal from '@/components/Layout/LayoutGlobal'
import { Container } from '@/components/utils/Container'
import { useEffect, useState } from 'react'
+import React from 'react'
+import { formatDate } from '@/utils/server/helpers'
+import Link from 'next/link'
+import TextField from '@/components/UI/Forms/TextField'
+import { set } from 'mongoose'
export async function getServerSideProps(context) {
- const { productID } = context.params
+ const { productId } = context.params
- const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL //REDO THIS LATER WE CANT HAVE BASE URL SHOULD WORK TO JUST DO /api/v1/product/${productID}
+ const fetchUrl = `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/v1/product/${productId}`
try {
- const res = await fetch(`${baseUrl}/api/v1/product/${productID}`) // Use backticks for template literal
+ const res = await fetch(fetchUrl)
if (!res.ok) {
- throw new Error(`Failed to fetch product, status: ${res.status}`) // Template literal
+ throw new Error(`Failed to fetch product, status: ${res.status}`)
}
const productData = await res.json()
@@ -28,125 +33,103 @@ export async function getServerSideProps(context) {
}
}
-export default function ProductPage({ product }) {
- // Function to recursively render product properties, including nested objects/arrays
- const renderProductData = data => {
- if (Array.isArray(data)) {
- return (
-
- {data.map((item, index) => (
- - {renderProductData(item)}
- ))}
-
- )
- } else if (typeof data === 'object' && data !== null) {
- return (
-
- {Object.entries(data).map(([key, value]) => (
- -
- {key}: {renderProductData(value)}
-
- ))}
-
- )
- } else {
- return data.toString()
+function ProductDetails({ product }) {
+ const [eventAction, setEventAction] = useState('')
+ const [error, setError] = useState('')
+
+ const handleInputChange = e => {
+ setEventAction(e.target.value)
+ if (error) {
+ setError('')
}
}
- return (
-
-
Product Details
- {renderProductData(product)}
-
- )
-}
-
-/* function ProductDetails() {
- const router = useRouter()
- const ProductId = router.query.productId
- const [productData, setProductData] = useState({
- _id,
- id,
- name,
- dpp_class,
- creation_time,
- privacy,
- })
+ const handleSubmit = async e => {
+ if (!eventAction) {
+ setError('Please enter an event action')
+ return
+ }
- const fetchproduct = async () => {
try {
- // Make a GET request to the API route
- const response = await fetch('/api/v1/product/export-product')
-
- // Check if the response is successful (status code 200)
- if (response.ok) {
- // Parse the response body as JSON
- const result = await response.json()
+ const response = await fetch(`/api/v1/product/${product._id}`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ productId: product._id,
+ action: eventAction,
+ }),
+ })
- // Update the component state with the fetched data
- setData(result)
- } else {
- // Handle non-successful responses (e.g., log an error)
- console.error(
- 'Error fetching data:',
- response.status,
- response.statusText
- )
+ if (!response.ok) {
+ throw new Error(`Failed to submit event, status: ${response.status}`)
}
+
+ const data = await response.json()
+ console.log('Event submitted:', data)
+ setEventAction('')
} catch (error) {
- // Handle network errors or other exceptions
- console.error('Error fetching data:', error.message)
+ setError(error.message)
+ console.error('Error submitting event:', error)
}
}
- fetchproduct()
-
return (
-
-
-
-
-
- Battery
+
+
+
+
+
+ You are qualified to add events
+
+
+ {
+ "If you've performed something that's altered the product please state it and submit the form"
+ }
+
+
+
+ {/*
+ This event will be added to {product.name} 's event trail
+
*/}
+
+
+
+
+
+
+
+ {product.name}
- Text.
+ ProductID: {product.id}
-
-
-
- ProductID:
+
+
-
+ Manufactuter:
- -
- 198454
-
-
-
-
-
- Main Component:
-
- -
- Lithium
-
-
-
-
-
- MaterialID:
-
- -
- 223411
-
-
-
-
-
- Owner
-
- -
- Scania
+
-
+ {product.manufactured_by.owner_name}
@@ -154,18 +137,76 @@ export default function ProductPage({ product }) {
Creation time
-
- aug 2023
+ {formatDate(product.created_at.creation_time)}
-
-
-
-
-
-
-
-
- - Weight
+ {product.carbon_footprint.effect != null && (
+
+
-
+ Carbon footprint
+
+ -
+ {product.carbon_footprint.effect}
+
+
+ )}
+ {product.crm !== undefined && (
+
+
-
+ Crm
+
+ -
+ Scania
+
+
+ )}
+
+ {product.event_trail.events.length > 0 && (
+
+
+ Events
+
+
+ {product.event_trail.events.map((event, index) => (
+
+
ID: {event.id}
+
+ Creation Time: {formatDate(event.creation_time)}
+
+
Action: {event.action}
+
+
+ ))}
+
+
+ )}
+ {product.key_components.components.length > 0 && (
+
+
+ Key components
+
+
+ {product.key_components.components.map(
+ (component, index) => (
+
+
ID: {component.id}
+
DPP Class: {component.dpp_class}
+
+ Name:
+
+ {component.name}
+
+
+
+ )
+ )}
+
+
+ )}
+
@@ -176,4 +217,3 @@ export default function ProductPage({ product }) {
}
export default ProductDetails
- */
diff --git a/src/pages/product/index.jsx b/src/pages/product/index.jsx
deleted file mode 100644
index 244c8ad4..00000000
--- a/src/pages/product/index.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-
-function Product() {
-
- return
hello
-}
-
-export default Product
\ No newline at end of file