diff --git a/ReOwn/Backend/models/favouriteModel.js b/ReOwn/Backend/models/favouriteModel.js
index 5273265d..e4e382df 100644
--- a/ReOwn/Backend/models/favouriteModel.js
+++ b/ReOwn/Backend/models/favouriteModel.js
@@ -4,4 +4,4 @@ const favouriteSchema = new mongoose.Schema({
productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product", required: true },
});
- export const Favourite = mongoose.model("Favourite", favouriteSchema);
+export const Favourite = mongoose.model("Favourite", favouriteSchema);
diff --git a/ReOwn/Backend/models/userModel.js b/ReOwn/Backend/models/userModel.js
index c39223f6..90eea3ba 100644
--- a/ReOwn/Backend/models/userModel.js
+++ b/ReOwn/Backend/models/userModel.js
@@ -50,11 +50,6 @@ required: true,
maxlength: 15,
},
- address: {
- type: String,
- minlength: 5,
- maxlength: 40,
- },
deleted_at: { type: Date, default: null },
image:{
type: String,
diff --git a/ReOwn/Frontend/TODO.md b/ReOwn/Frontend/TODO.md
new file mode 100644
index 00000000..47151c36
--- /dev/null
+++ b/ReOwn/Frontend/TODO.md
@@ -0,0 +1,7 @@
+# TODO: Turn on all components in project
+
+## Steps to Complete
+- [x] Add imports for AddAds, Checkout, All_Category, and ProductRecentlyAdded in src/App.jsx
+- [x] Add routes in the router: /add-ads for AddAds, /checkout for Checkout, /all-categories for All_Category, /recently-added for ProductRecentlyAdded
+- [ ] Run the development server to verify the routes work
+- [ ] Test navigation to the new routes
diff --git a/ReOwn/Frontend/package-lock.json b/ReOwn/Frontend/package-lock.json
index 46e01237..73a060ee 100644
--- a/ReOwn/Frontend/package-lock.json
+++ b/ReOwn/Frontend/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^7.1.0",
+ "@radix-ui/react-select": "^2.2.6",
"axios": "^1.13.2",
"flowbite": "^4.0.1",
"flowbite-react": "^0.12.10",
@@ -1475,6 +1476,502 @@
"url": "https://opencollective.com/popperjs"
}
},
+ "node_modules/@radix-ui/number": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
+ "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/primitive": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
+ "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
+ "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
+ "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-direction": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
+ "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
+ "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-escape-keydown": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
+ "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
+ "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
+ "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
+ "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-layout-effect": "1.1.1",
+ "@radix-ui/react-use-rect": "1.1.1",
+ "@radix-ui/react-use-size": "1.1.1",
+ "@radix-ui/rect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
+ "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
+ "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.1",
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-collection": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-direction": "1.1.1",
+ "@radix-ui/react-dismissable-layer": "1.1.11",
+ "@radix-ui/react-focus-guards": "1.1.3",
+ "@radix-ui/react-focus-scope": "1.1.7",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-popper": "1.2.8",
+ "@radix-ui/react-portal": "1.1.9",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-visually-hidden": "1.2.3",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.6.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
+ "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
+ "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-effect-event": "0.0.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-effect-event": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
+ "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
+ "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-callback-ref": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
+ "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
+ "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/rect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
+ "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
+ "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
+ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
+ "license": "MIT"
+ },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.47",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
@@ -2187,7 +2684,7 @@
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
@@ -2474,6 +2971,18 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/aria-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/array-timsort": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz",
@@ -2965,6 +3474,12 @@
"node": ">=8"
}
},
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
+ "license": "MIT"
+ },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -3757,6 +4272,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@@ -5075,6 +5599,53 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-remove-scroll": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.7",
+ "react-style-singleton": "^2.2.3",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.3",
+ "use-sidecar": "^1.1.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-style-singleton": "^2.2.2",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-router": {
"version": "7.9.6",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz",
@@ -5113,6 +5684,28 @@
"react-dom": ">=18"
}
},
+ "node_modules/react-style-singleton": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -5905,6 +6498,49 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-callback-ref": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/ReOwn/Frontend/package.json b/ReOwn/Frontend/package.json
index 515ef1c8..f2edfd89 100644
--- a/ReOwn/Frontend/package.json
+++ b/ReOwn/Frontend/package.json
@@ -10,6 +10,7 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^7.1.0",
+ "@radix-ui/react-select": "^2.2.6",
"axios": "^1.13.2",
"flowbite": "^4.0.1",
"flowbite-react": "^0.12.10",
diff --git a/ReOwn/Frontend/src/App.jsx b/ReOwn/Frontend/src/App.jsx
index f68551b5..39445acf 100644
--- a/ReOwn/Frontend/src/App.jsx
+++ b/ReOwn/Frontend/src/App.jsx
@@ -14,6 +14,10 @@ import ProductDetails from "./Components/Product_Details/ProductDetails";
import Footer from "./Components/Footer/Footer";
import Notfound from "./Components/Notfound/Notfound";
import FavouritePage from "./Components/Favourite/Favourite";
+import AddAds from "./Components/AddAds/AddAds";
+import Checkout from "./Components/Checkout/Checkout";
+import All_Category from "./Components/All_Category";
+import ProductRecentlyAdded from "./Components/ProductAddedRecently/ProductAddedRecently";
import { AuthProvider } from "./Context/AuthContext";
import { FavoritesProvider } from "./Components/Context/FavoritesContext";
@@ -33,6 +37,10 @@ const router = createBrowserRouter([
{ path: "product", element: },
{ path: "product/:id", element: },
{ path: "favourites", element: },
+ { path: "add-ads", element: },
+ { path: "checkout", element: },
+ { path: "all-categories", element: },
+ { path: "recently-added", element: },
{ path: "/chat/:productId", element: },
{ path: "*", element: },
],
diff --git a/ReOwn/Frontend/src/Components/AddAds/AddAds.jsx b/ReOwn/Frontend/src/Components/AddAds/AddAds.jsx
new file mode 100644
index 00000000..31c4510f
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/AddAds.jsx
@@ -0,0 +1,218 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Button } from "./button";
+import { Input } from "./input";
+import { Label } from "./label";
+import { Textarea } from "./Textarea";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "./select";
+import { addProduct } from "../mockProducts";
+
+const AddAds = () => {
+ const navigate = useNavigate();
+ const [formData, setFormData] = useState({
+ title: "",
+ description: "",
+ category: "",
+ price: "",
+ images: [],
+ });
+
+ const categories = [
+ { value: "home-kitchen", label: "Home & Kitchen tools" },
+ { value: "clothes", label: "Clothes & Accessories" },
+ { value: "electronics", label: "Electronics & Gadgets" },
+ { value: "books", label: "Books & Games" },
+ { value: "decor", label: "Home Decors & Gifts" },
+ { value: "kids", label: "Baby & Kids items" },
+ { value: "sports", label: "Sports & Hobbies" },
+ ];
+
+ const handleInputChange = (e) => {
+ const { name, value } = e.target;
+ setFormData((prev) => ({
+ ...prev,
+ [name]: value,
+ }));
+ };
+
+ const handleCategoryChange = (value) => {
+ setFormData((prev) => ({
+ ...prev,
+ category: value,
+ }));
+ };
+
+ const handleImageChange = (e) => {
+ const files = Array.from(e.target.files);
+ setFormData((prev) => ({
+ ...prev,
+ images: files,
+ }));
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ console.log("Form submitted:", formData);
+
+ // Map category to categoryId
+ const categoryMapping = {
+ "home-kitchen": 1,
+ "clothes": 2,
+ "electronics": 3,
+ "books": 4,
+ "decor": 5,
+ "kids": 6,
+ "sports": 7,
+ };
+ const categoryId = categoryMapping[formData.category] || 1;
+
+ // Create image URLs (placeholder for now)
+ const imageUrls = formData.images.length > 0
+ ? ["https://via.placeholder.com/400"] // Placeholder for uploaded images
+ : ["https://via.placeholder.com/400"];
+
+ // Create new product object
+ const newProduct = {
+ name: formData.title,
+ price: `${formData.price} EGP`,
+ condition: "New", // Default
+ description: formData.description,
+ images: imageUrls,
+ installment: "Use for 6 months", // Default
+ discount: "0% OFF", // Default
+ seller: {
+ id: 999, // Default
+ name: "Current User", // Default
+ avatar: "https://i.pravatar.cc/150?img=50",
+ location: "Cairo, Egypt", // Default
+ },
+ categoryId: categoryId,
+ };
+
+ // Add product to mock data
+ const addedProduct = addProduct(newProduct);
+
+ // Navigate to the new product's details page
+ navigate(`/product/${addedProduct.id}`, { state: { showAlert: true } });
+ };
+
+ return (
+
+ );
+};
+
+export default AddAds;
diff --git a/ReOwn/Frontend/src/Components/AddAds/Separator.jsx b/ReOwn/Frontend/src/Components/AddAds/Separator.jsx
new file mode 100644
index 00000000..de707a72
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/Separator.jsx
@@ -0,0 +1,23 @@
+import * as SeparatorPrimitive from "@radix-ui/react-separator";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Separator = React.forwardRef(
+ ({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
+
+ ),
+);
+
+Separator.displayName = SeparatorPrimitive.Root.displayName || "Separator";
+
+export { Separator };
\ No newline at end of file
diff --git a/ReOwn/Frontend/src/Components/AddAds/Textarea.jsx b/ReOwn/Frontend/src/Components/AddAds/Textarea.jsx
new file mode 100644
index 00000000..2055b633
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/Textarea.jsx
@@ -0,0 +1,17 @@
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Textarea = React.forwardRef(({ className, ...props }, ref) => (
+
+));
+
+Textarea.displayName = "Textarea";
+
+export { Textarea };
\ No newline at end of file
diff --git a/ReOwn/Frontend/src/Components/AddAds/button.jsx b/ReOwn/Frontend/src/Components/AddAds/button.jsx
new file mode 100644
index 00000000..bf2bb5b3
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/button.jsx
@@ -0,0 +1,50 @@
+import { Slot } from "@radix-ui/react-slot";
+import { cva } from "class-variance-authority";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
+);
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ return (
+
+ );
+ },
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/ReOwn/Frontend/src/Components/AddAds/input.jsx b/ReOwn/Frontend/src/Components/AddAds/input.jsx
new file mode 100644
index 00000000..e16ceae5
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/input.jsx
@@ -0,0 +1,21 @@
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Input.displayName = "Input";
+
+export { Input };
diff --git a/ReOwn/Frontend/src/Components/AddAds/label.jsx b/ReOwn/Frontend/src/Components/AddAds/label.jsx
new file mode 100644
index 00000000..e95be86d
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/label.jsx
@@ -0,0 +1,21 @@
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { cva } from "class-variance-authority";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+);
+
+const Label = React.forwardRef(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+Label.displayName = LabelPrimitive.Root.displayName;
+
+export { Label };
diff --git a/ReOwn/Frontend/src/Components/AddAds/select.jsx b/ReOwn/Frontend/src/Components/AddAds/select.jsx
new file mode 100644
index 00000000..070a9f51
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AddAds/select.jsx
@@ -0,0 +1,120 @@
+"use client";
+
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Select = SelectPrimitive.Root;
+const SelectGroup = SelectPrimitive.Group;
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName || "SelectTrigger";
+
+const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName || "SelectScrollUpButton";
+
+const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName || "SelectScrollDownButton";
+
+const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName || "SelectContent";
+
+const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName || "SelectLabel";
+
+const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName || "SelectItem";
+
+const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName || "SelectSeparator";
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton
+};
diff --git a/ReOwn/Frontend/src/Components/AllCategory.jsx b/ReOwn/Frontend/src/Components/AllCategory.jsx
new file mode 100644
index 00000000..6f76d6c9
--- /dev/null
+++ b/ReOwn/Frontend/src/Components/AllCategory.jsx
@@ -0,0 +1,412 @@
+import React, { useState, useRef } from "react";
+import { useNavigate } from "react-router-dom";
+import { Search, Bell, User, ChevronRight, ChevronLeft } from "lucide-react";
+import { FaHeart } from "react-icons/fa";
+import { FiHeart, FiPhone } from "react-icons/fi";
+import { BsChatSquareDots } from "react-icons/bs";
+import { useFavorites } from "../Context/FavoritesContext";
+
+function AllCategory() {
+ const navigate = useNavigate();
+ const { toggleFavorite, isFavorite } = useFavorites();
+ const [showMessage, setShowMessage] = useState({});
+ const carouselRefs = useRef({});
+
+ // Categories data with products
+ const categories = [
+ {
+ id: 1,
+ name: "Electronics",
+ products: [
+ {
+ id: 1,
+ name: "iPhone 13 Pro Max",
+ price: "30,500 EGP",
+ image:
+ "https://images.unsplash.com/photo-1632661674596-df8be070a5c5?w=400",
+ },
+ {
+ id: 2,
+ name: "Samsung Galaxy S21",
+ price: "25,000 EGP",
+ image:
+ "https://images.unsplash.com/photo-1610945415295-d9bbf067e59c?w=400&h=300&fit=crop",
+ },
+ {
+ id: 3,
+ name: "MacBook Pro 14",
+ price: "45,000 EGP",
+ image:
+ "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?w=400&h=300&fit=crop",
+ },
+ {
+ id: 4,
+ name: "iPad Air",
+ price: "20,000 EGP",
+ image:
+ "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=400&h=300&fit=crop",
+ },
+ {
+ id: 5,
+ name: "AirPods Pro",
+ price: "8,500 EGP",
+ image:
+ "https://images.unsplash.com/photo-1606841837239-c5a1a4a07af7?w=400&h=300&fit=crop",
+ },
+ {
+ id: 6,
+ name: "Apple Watch Series 7",
+ price: "12,000 EGP",
+ image:
+ "https://images.unsplash.com/photo-1579586337278-3befd40fd17a?w=400&h=300&fit=crop",
+ },
+ ],
+ },
+ {
+ id: 2,
+ name: "Fashion",
+ products: [
+ {
+ id: 7,
+ name: "Summer Dress Collection",
+ price: "1,200 EGP",
+ image:
+ "https://images.unsplash.com/photo-1595777457583-95e059d581b8?w=400&h=300&fit=crop",
+ },
+ {
+ id: 8,
+ name: "Casual Shirt Premium",
+ price: "800 EGP",
+ image:
+ "https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=400&h=300&fit=crop",
+ },
+ {
+ id: 9,
+ name: "Sport Sneakers Nike",
+ price: "2,500 EGP",
+ image:
+ "https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=400&h=300&fit=crop",
+ },
+ {
+ id: 10,
+ name: "Leather Handbag",
+ price: "1,800 EGP",
+ image:
+ "https://images.unsplash.com/photo-1584917865442-de89df76afd3?w=400&h=300&fit=crop",
+ },
+ {
+ id: 11,
+ name: "Classic Watch",
+ price: "3,200 EGP",
+ image:
+ "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=300&fit=crop",
+ },
+ ],
+ },
+ {
+ id: 3,
+ name: "Home & Kitchen",
+ products: [
+ {
+ id: 12,
+ name: "Coffee Maker Deluxe",
+ price: "1,500 EGP",
+ image:
+ "https://images.unsplash.com/photo-1517668808822-9ebb02f2a0e6?w=400&h=300&fit=crop",
+ },
+ {
+ id: 13,
+ name: "Professional Blender",
+ price: "900 EGP",
+ image:
+ "https://images.unsplash.com/photo-1570222094114-d054a817e56b?w=400&h=300&fit=crop",
+ },
+ {
+ id: 14,
+ name: "Air Fryer XL",
+ price: "2,200 EGP",
+ image:
+ "https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?w=400&h=300&fit=crop",
+ },
+ {
+ id: 15,
+ name: "Robot Vacuum Cleaner",
+ price: "3,500 EGP",
+ image:
+ "https://images.unsplash.com/photo-1558317374-067fb5f30001?w=400&h=300&fit=crop",
+ },
+ {
+ id: 16,
+ name: "Pressure Cooker",
+ price: "1,100 EGP",
+ image:
+ "https://images.unsplash.com/photo-1556909114-2a7b8e2b9b9a?w=400&h=300&fit=crop",
+ },
+ {
+ id: 17,
+ name: "Stand Mixer",
+ price: "2,800 EGP",
+ image:
+ "https://images.unsplash.com/photo-1556909114-2a7b8e2b9b9a?w=400&h=300&fit=crop",
+ },
+ ],
+ },
+ ];
+
+ const handleToggleLike = (product) => {
+
+ toggleFavorite(product);
+
+ const isNowFavorite = !isFavorite(product.id);
+ setShowMessage((prev) => ({
+ ...prev,
+ [product.id]: {
+ visible: true,
+ text: isNowFavorite ? "Added to favorites" : "Removed from favorites",
+ },
+ }));
+
+ setTimeout(() => {
+ setShowMessage((prev) => ({
+ ...prev,
+ [product.id]: { ...prev[product.id], visible: false },
+ }));
+ }, 2000);
+ };
+
+ const handleViewMore = (categoryId) => {
+ // Navigate to products page with category filter
+ navigate(`/product?category=${categoryId}`);
+
+ /*
+ const { search } = useLocation();
+ const params = new URLSearchParams(search);
+ const categoryId = params.get('category');
+
+ if (categoryId) {
+ const filtered = products.filter(p => p.categoryId === parseInt(categoryId));
+ }
+ */
+ };
+
+ const handleProductClick = (productId) => {
+ navigate(`/product/${productId}`);
+ };
+
+ const handleCall = () => {
+ alert("Calling seller...");
+ };
+
+ const handleChat = () => {
+ alert("Opening chat...");
+ };
+
+ const handleBack = () => {
+ navigate(-1);
+ };
+
+ const scroll = (categoryId, direction) => {
+ const container = carouselRefs.current[categoryId];
+ if (container) {
+ const scrollAmount = direction === "left" ? -300 : 300;
+ container.scrollBy({ left: scrollAmount, behavior: "smooth" });
+ }
+ };
+
+ return (
+
+
+ {/* Categories Content */}
+
+ {categories.map((category, index) => (
+
+ {/* Category Header */}
+
+
+ {category.name}
+
+
+
+
+ {/* Carousel Container */}
+
+ {/* Previous Button */}
+
+
+ {/* Products Carousel */}
+
(carouselRefs.current[category.id] = el)}
+ className="flex gap-5 pb-4 overflow-x-auto scrollbar-hide scroll-smooth"
+ style={{
+ scrollbarWidth: "none",
+ msOverflowStyle: "none",
+ }}
+ >
+ {category.products.map((product) => (
+
+
handleProductClick(product.id)}
+ className="relative flex flex-col h-full p-5 transition-all duration-300 bg-white shadow-md cursor-pointer rounded-xl hover:shadow-xl hover:-translate-y-1 group/card"
+ >
+ {/* Heart Icon */}
+
+
+ {showMessage[product.id]?.visible && (
+
+ {showMessage[product.id].text}
+
+ )}
+ {/* Product Image */}
+
+

+
+
+
+ {/* Product Info */}
+
+
+ {product.name}
+
+
+ {product.price}
+
+ {/* Buttons */}
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* Next Button */}
+
+
+
+ ))}
+
+
+
+
+ );
+}
+
+export default AllCategory;
diff --git a/ReOwn/Frontend/src/Components/Home/Home.jsx b/ReOwn/Frontend/src/Components/Home/Home.jsx
index 7a5585a5..26d79f0f 100644
--- a/ReOwn/Frontend/src/Components/Home/Home.jsx
+++ b/ReOwn/Frontend/src/Components/Home/Home.jsx
@@ -1,7 +1,6 @@
-import React, { useState, useEffect } from "react";
import CategoriesSection from "./CategoriesLink/CategoriesSection";
import Hero from "./Hero/Hero";
-import ProductAddedRecently from "./ProductAddedRecently/ProductAddedRecently";
+import ProductAddedRecently from "../ProductAddedRecently/ProductAddedRecently";
diff --git a/ReOwn/Frontend/src/Components/Home/ProductAddedRecently/ProductAddedRecently.jsx b/ReOwn/Frontend/src/Components/ProductAddedRecently/ProductAddedRecently.jsx
similarity index 97%
rename from ReOwn/Frontend/src/Components/Home/ProductAddedRecently/ProductAddedRecently.jsx
rename to ReOwn/Frontend/src/Components/ProductAddedRecently/ProductAddedRecently.jsx
index 565e42eb..511cd92e 100644
--- a/ReOwn/Frontend/src/Components/Home/ProductAddedRecently/ProductAddedRecently.jsx
+++ b/ReOwn/Frontend/src/Components/ProductAddedRecently/ProductAddedRecently.jsx
@@ -138,26 +138,13 @@ function ProductAddedRecently() {
const handleProductClick = (productId) => {
console.log("Product clicked:", productId);
};
-
- const handleCategoryClick = (categoryId) => {
- console.log("Category clicked:", categoryId);
- };
+
const handleViewAllProducts = () => {
console.log("View all products");
};
- const nextSlide = () => {
- if (currentIndex < products.length - itemsPerPage) {
- setCurrentIndex((prev) => prev + 1);
- }
- };
-
- const prevSlide = () => {
- if (currentIndex > 0) {
- setCurrentIndex((prev) => prev - 1);
- }
- };
+
const visibleProducts = products.slice(
currentIndex,
diff --git a/ReOwn/Frontend/src/Components/ProductRecentlyAdded/ProductRecentlyAdded.module.css b/ReOwn/Frontend/src/Components/ProductAddedRecently/ProductAddedRecently.module.css
similarity index 100%
rename from ReOwn/Frontend/src/Components/ProductRecentlyAdded/ProductRecentlyAdded.module.css
rename to ReOwn/Frontend/src/Components/ProductAddedRecently/ProductAddedRecently.module.css
diff --git a/ReOwn/Frontend/src/Components/ProductRecentlyAdded/ProductRecentlyAdded.jsx b/ReOwn/Frontend/src/Components/ProductRecentlyAdded/ProductRecentlyAdded.jsx
deleted file mode 100644
index f084ffd3..00000000
--- a/ReOwn/Frontend/src/Components/ProductRecentlyAdded/ProductRecentlyAdded.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react'
-import styles from './ProductRecentlyAdded.module.css'
-
-function ProductRecentlyAdded() {
- return (
-
-
ProductRecentlyAdded
-
- )
-}
-
-export default ProductRecentlyAdded;
diff --git a/ReOwn/Frontend/src/Components/Product_Details/ProductDetails.jsx b/ReOwn/Frontend/src/Components/Product_Details/ProductDetails.jsx
index 6626581e..378985b7 100644
--- a/ReOwn/Frontend/src/Components/Product_Details/ProductDetails.jsx
+++ b/ReOwn/Frontend/src/Components/Product_Details/ProductDetails.jsx
@@ -1,15 +1,16 @@
import React, { useState, useEffect } from "react";
-import { useParams, useNavigate } from "react-router-dom";
+import { useParams, useNavigate, useLocation } from "react-router-dom";
import { GoArrowLeft } from "react-icons/go";
import { ChevronLeft, ChevronRight, MapPin } from "lucide-react";
import { FaHeart } from "react-icons/fa";
import { FiHeart, FiPhone } from "react-icons/fi";
import { BsChatSquareDots } from "react-icons/bs";
-import { useFavorites } from "../context/FavoritesContext";
+import { useFavorites } from "../Context/FavoritesContext";
const ProductDetails = () => {
const { id } = useParams();
const navigate = useNavigate();
+ const location = useLocation();
const { toggleFavorite, isFavorite } = useFavorites();
const [product, setProduct] = useState(null);
const [relatedProducts, setRelatedProducts] = useState([]);
@@ -138,6 +139,12 @@ const ProductDetails = () => {
loadProductData();
}, [id]);
+ useEffect(() => {
+ if (location.state?.showAlert) {
+ alert("Ad posted successfully!");
+ }
+ }, [location.state]);
+
const loadProductData = async () => {
setLoading(true);
try {
diff --git a/ReOwn/Frontend/src/Components/mockProducts.js b/ReOwn/Frontend/src/Components/mockProducts.js
index 06aacdef..d2e2b8fa 100644
--- a/ReOwn/Frontend/src/Components/mockProducts.js
+++ b/ReOwn/Frontend/src/Components/mockProducts.js
@@ -1,213 +1,145 @@
-// // src/data/mockProducts.js
-// // هنا كل بيانات المنتجات المشتركة بين كل الصفحات
-// // لما الـ API يجهز، هتشيلي الملف ده وتستبدليه بـ API calls
-// export const MOCK_PRODUCTS = [
-// {
-// id: 1,
-// name: "iPhone 13 Pro Max - 256GB Gold Edition",
-// price: "30,500 EGP",
-// condition: "Used",
-// description:
-// "Used iPhone 13 Pro Max - Pink color, in excellent condition.\nUsed for 8 months only, battery health 90%.\nNo parts have been replaced and everything works perfectly.",
-// images: [
-// "https://images.unsplash.com/photo-1632661674596-df8be070a5c5?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1611472173362-3f53dbd65d80?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1632633173522-e83ff3e67c5c?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1678652197834-05d6d8b1a0e8?w=600&h=600&fit=crop",
-// ],
-// installment: "Use for 8 months",
-// discount: "40% OFF",
-// seller: {
-// id: 101,
-// name: "John Smith",
-// avatar: "https://i.pravatar.cc/150?img=12",
-// location: "Cairo, Egypt",
-// },
-// categoryId: 1,
-// },
-// {
-// id: 2,
-// name: "Samsung Galaxy S21 Ultra",
-// price: "25,000 EGP",
-// condition: "Used",
-// description:
-// "Samsung Galaxy S21 Ultra 5G - Phantom Black.\nUsed for 1 year, battery health 85%.\nIncludes original box and charger.\nNo scratches or dents.",
-// images: [
-// "https://images.unsplash.com/photo-1610945415295-d9bbf067e59c?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1591337676887-a217a6970a8a?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1598327105666-5b89351aff97?w=600&h=600&fit=crop",
-// ],
-// installment: "Use for 12 months",
-// discount: "35% OFF",
-// seller: {
-// id: 102,
-// name: "Sarah Ahmed",
-// avatar: "https://i.pravatar.cc/150?img=25",
-// location: "Alexandria, Egypt",
-// },
-// categoryId: 1,
-// },
-// {
-// id: 3,
-// name: "MacBook Pro 14 inch M1 Pro",
-// price: "45,000 EGP",
-// condition: "New",
-// description:
-// "Brand new MacBook Pro 14 with M1 Pro chip.\n16GB RAM, 512GB SSD.\nSpace Gray color.\nSealed box with full warranty.",
-// images: [
-// "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1611186871348-b1ce696e52c9?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=600&h=600&fit=crop",
-// ],
-// installment: "Use for 12 months",
-// discount: "20% OFF",
-// seller: {
-// id: 103,
-// name: "Mohamed Ali",
-// avatar: "https://i.pravatar.cc/150?img=33",
-// location: "Giza, Egypt",
-// },
-// categoryId: 1,
-// },
-// {
-// id: 4,
-// name: "iPad Air 5th Generation",
-// price: "20,000 EGP",
-// condition: "New",
-// description:
-// "iPad Air 2022 - 64GB WiFi.\nBlue color, brand new sealed.\nApple warranty included.\nPerfect for students and professionals.",
-// images: [
-// "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1561154464-82e9adf32764?w=600&h=600&fit=crop",
-// ],
-// installment: "Use for 6 months",
-// discount: "25% OFF",
-// seller: {
-// id: 104,
-// name: "Ahmed Hassan",
-// avatar: "https://i.pravatar.cc/150?img=15",
-// location: "Cairo, Egypt",
-// },
-// categoryId: 1,
-// },
-// {
-// id: 5,
-// name: "AirPods Pro 2nd Generation",
-// price: "8,500 EGP",
-// condition: "New",
-// description:
-// "AirPods Pro 2nd Gen with USB-C.\nActive Noise Cancellation.\nBrand new sealed box.\nOfficial Apple warranty.",
-// images: [
-// "https://images.unsplash.com/photo-1606841837239-c5a1a4a07af7?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1588423771073-b8903fbb85b5?w=600&h=600&fit=crop",
-// ],
-// installment: "Use for 4 months",
-// discount: "30% OFF",
-// seller: {
-// id: 105,
-// name: "Layla Mahmoud",
-// avatar: "https://i.pravatar.cc/150?img=45",
-// location: "Cairo, Egypt",
-// },
-// categoryId: 1,
-// },
-// {
-// id: 6,
-// name: "Apple Watch Series 7 GPS",
-// price: "12,000 EGP",
-// condition: "Used",
-// description:
-// "Apple Watch Series 7 GPS.\nGreen aluminum case.\nUsed for 6 months, excellent condition.\nIncludes original box and charger.",
-// images: [
-// "https://images.unsplash.com/photo-1579586337278-3befd40fd17a?w=600&h=600&fit=crop",
-// "https://images.unsplash.com/photo-1434493789847-2f02dc6ca35d?w=600&h=600&fit=crop",
-// ],
-// installment: "Use for 5 months",
-// discount: "28% OFF",
-// seller: {
-// id: 106,
-// name: "Omar Khaled",
-// avatar: "https://i.pravatar.cc/150?img=18",
-// location: "Cairo, Egypt",
-// },
-// categoryId: 1,
-// },
-// ];
-// // TODO: لما الـ API يجهز، استبدلي الـ functions دي
+export const MOCK_PRODUCTS = [
+ {
+ id: 1,
+ name: "iPhone 13 Pro Max - 256GB Gold Edition",
+ price: "30,500 EGP",
+ condition: "Used",
+ description:
+ "Used iPhone 13 Pro Max - Pink color, in excellent condition.\nUsed for 8 months only, battery health 90%.\nNo parts have been replaced and everything works perfectly.",
+ images: [
+ "https://images.unsplash.com/photo-1632661674596-df8be070a5c5?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1611472173362-3f53dbd65d80?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1632633173522-e83ff3e67c5c?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1678652197834-05d6d8b1a0e8?w=600&h=600&fit=crop",
+ ],
+ installment: "Use for 8 months",
+ discount: "40% OFF",
+ seller: {
+ id: 101,
+ name: "John Smith",
+ avatar: "https://i.pravatar.cc/150?img=12",
+ location: "Cairo, Egypt",
+ },
+ categoryId: 1,
+ },
+ {
+ id: 2,
+ name: "Samsung Galaxy S21 Ultra",
+ price: "25,000 EGP",
+ condition: "Used",
+ description:
+ "Samsung Galaxy S21 Ultra 5G - Phantom Black.\nUsed for 1 year, battery health 85%.\nIncludes original box and charger.\nNo scratches or dents.",
+ images: [
+ "https://images.unsplash.com/photo-1610945415295-d9bbf067e59c?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1591337676887-a217a6970a8a?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1598327105666-5b89351aff97?w=600&h=600&fit=crop",
+ ],
+ installment: "Use for 12 months",
+ discount: "35% OFF",
+ seller: {
+ id: 102,
+ name: "Sarah Ahmed",
+ avatar: "https://i.pravatar.cc/150?img=25",
+ location: "Alexandria, Egypt",
+ },
+ categoryId: 1,
+ },
+ {
+ id: 3,
+ name: "MacBook Pro 14 inch M1 Pro",
+ price: "45,000 EGP",
+ condition: "New",
+ description:
+ "Brand new MacBook Pro 14 with M1 Pro chip.\n16GB RAM, 512GB SSD.\nSpace Gray color.\nSealed box with full warranty.",
+ images: [
+ "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1611186871348-b1ce696e52c9?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=600&h=600&fit=crop",
+ ],
+ installment: "Use for 12 months",
+ discount: "20% OFF",
+ seller: {
+ id: 103,
+ name: "Mohamed Ali",
+ avatar: "https://i.pravatar.cc/150?img=33",
+ location: "Giza, Egypt",
+ },
+ categoryId: 1,
+ },
+ {
+ id: 4,
+ name: "iPad Air 5th Generation",
+ price: "20,000 EGP",
+ condition: "New",
+ description:
+ "iPad Air 2022 - 64GB WiFi.\nBlue color, brand new sealed.\nApple warranty included.\nPerfect for students and professionals.",
+ images: [
+ "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1561154464-82e9adf32764?w=600&h=600&fit=crop",
+ ],
+ installment: "Use for 6 months",
+ discount: "25% OFF",
+ seller: {
+ id: 104,
+ name: "Ahmed Hassan",
+ avatar: "https://i.pravatar.cc/150?img=15",
+ location: "Cairo, Egypt",
+ },
+ categoryId: 1,
+ },
+ {
+ id: 5,
+ name: "AirPods Pro 2nd Generation",
+ price: "8,500 EGP",
+ condition: "New",
+ description:
+ "AirPods Pro 2nd Gen with USB-C.\nActive Noise Cancellation.\nBrand new sealed box.\nOfficial Apple warranty.",
+ images: [
+ "https://images.unsplash.com/photo-1606841837239-c5a1a4a07af7?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1588423771073-b8903fbb85b5?w=600&h=600&fit=crop",
+ ],
+ installment: "Use for 4 months",
+ discount: "30% OFF",
+ seller: {
+ id: 105,
+ name: "Layla Mahmoud",
+ avatar: "https://i.pravatar.cc/150?img=45",
+ location: "Cairo, Egypt",
+ },
+ categoryId: 1,
+ },
+ {
+ id: 6,
+ name: "Apple Watch Series 7 GPS",
+ price: "12,000 EGP",
+ condition: "Used",
+ description:
+ "Apple Watch Series 7 GPS.\nGreen aluminum case.\nUsed for 6 months, excellent condition.\nIncludes original box and charger.",
+ images: [
+ "https://images.unsplash.com/photo-1579586337278-3befd40fd17a?w=600&h=600&fit=crop",
+ "https://images.unsplash.com/photo-1434493789847-2f02dc6ca35d?w=600&h=600&fit=crop",
+ ],
+ installment: "Use for 5 months",
+ discount: "28% OFF",
+ seller: {
+ id: 106,
+ name: "Omar Khaled",
+ avatar: "https://i.pravatar.cc/150?img=18",
+ location: "Cairo, Egypt",
+ },
+ categoryId: 1,
+ },
+];
-// // Function لجلب كل المنتجات
-// export const fetchAllProducts = async () => {
-// // Simulate API delay
-// return new Promise((resolve) => {
-// setTimeout(() => {
-// resolve(MOCK_PRODUCTS);
-// }, 300);
-// });
+// TODO: لما الـ API يجهز، استبدلي الـ functions دي
-// /*
-// لما الـ API يجهز، استبدلي بالكود ده:
-
-// try {
-// const response = await fetch('https://your-api.com/api/products');
-// const data = await response.json();
-// return data;
-// } catch (error) {
-// console.error('Error fetching products:', error);
-// throw error;
-// }
-// */
-// };
+// Function لإضافة منتج جديد
+export const addProduct = (newProduct) => {
+ const newId = MOCK_PRODUCTS.length > 0 ? Math.max(...MOCK_PRODUCTS.map(p => p.id)) + 1 : 1;
+ const productWithId = { ...newProduct, id: newId };
+ MOCK_PRODUCTS.push(productWithId);
+ return productWithId;
+};
-// // Function لجلب منتج واحد بالـ ID
-// export const fetchProductById = async (id) => {
-// return new Promise((resolve, reject) => {
-// setTimeout(() => {
-// const product = MOCK_PRODUCTS.find((p) => p.id === parseInt(id));
-// if (product) {
-// resolve(product);
-// } else {
-// reject(new Error("Product not found"));
-// }
-// }, 300);
-// });
-
-// /*
-// لما الـ API يجهز، استبدلي بالكود ده:
-
-// try {
-// const response = await fetch(`https://your-api.com/api/products/${id}`);
-// const data = await response.json();
-// return data;
-// } catch (error) {
-// console.error('Error fetching product:', error);
-// throw error;
-// }
-// */
-// };
-
-// // Function لجلب المنتجات المشابهة
-// export const fetchRelatedProducts = async (categoryId, excludeId) => {
-// return new Promise((resolve) => {
-// setTimeout(() => {
-// const related = MOCK_PRODUCTS.filter(
-// (p) => p.categoryId === categoryId && p.id !== excludeId
-// ).slice(0, 4);
-// resolve(related);
-// }, 300);
-// });
-
-// /*
-// لما الـ API يجهز، استبدلي بالكود ده:
-
-// try {
-// const response = await fetch(`https://your-api.com/api/products/${productId}/related`);
-// const data = await response.json();
-// return data;
-// } catch (error) {
-// console.error('Error fetching related products:', error);
-// throw error;
-// }
-// */
-// };
\ No newline at end of file
diff --git a/ReOwn/Frontend/src/Context/FavoritesContext.jsx b/ReOwn/Frontend/src/Context/FavoritesContext.jsx
new file mode 100644
index 00000000..1a39c21b
--- /dev/null
+++ b/ReOwn/Frontend/src/Context/FavoritesContext.jsx
@@ -0,0 +1,63 @@
+import React, { createContext, useContext, useState, useEffect } from "react";
+
+const FavoritesContext = createContext();
+
+export const FavoritesProvider = ({ children }) => {
+ const [favorites, setFavorites] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ const fetchFavorites = () => {
+ try {
+ const storedFavorites = localStorage.getItem('favorites');
+ if (storedFavorites) {
+ const parsed = JSON.parse(storedFavorites);
+ if (Array.isArray(parsed)) {
+ setFavorites(parsed);
+ } else {
+ setFavorites([]);
+ }
+ }
+ } catch (err) {
+ console.error('Error fetching favorites:', err);
+ setFavorites([]);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchFavorites();
+ }, []);
+
+ const toggleFavorite = (product) => {
+ const isFav = favorites.some(item => item.productId === product.id);
+ let newFavorites;
+ if (isFav) {
+ newFavorites = favorites.filter(item => item.productId !== product.id);
+ } else {
+ const favItem = {
+ _id: product.id,
+ productId: product.id,
+ productName: product.name,
+ price: product.price,
+ image: product.images[0],
+ condition: product.condition,
+ description: product.description,
+ seller: product.seller
+ };
+ newFavorites = [...favorites, favItem];
+ }
+ setFavorites(newFavorites);
+ localStorage.setItem('favorites', JSON.stringify(newFavorites));
+ };
+
+ const isFavorite = (id) => favorites.some(item => item.productId === id);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useFavorites = () => useContext(FavoritesContext);
diff --git a/ReOwn/Frontend/src/index.css b/ReOwn/Frontend/src/index.css
index 4e6bda4e..e219ab90 100644
--- a/ReOwn/Frontend/src/index.css
+++ b/ReOwn/Frontend/src/index.css
@@ -1,9 +1,9 @@
+@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');
+
@tailwind base;
@tailwind components;
@tailwind utilities;
-@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');
-
*{
margin: 0 ;
padding: 0;
diff --git a/ReOwn/Frontend/src/lib/utils.js b/ReOwn/Frontend/src/lib/utils.js
new file mode 100644
index 00000000..bbaf823a
--- /dev/null
+++ b/ReOwn/Frontend/src/lib/utils.js
@@ -0,0 +1,15 @@
+/**
+ * Utility function to merge class names
+ * @param {...(string | undefined | null | false)} classes
+ * @returns {string}
+ */
+export function cn(...classes) {
+ return classes
+ .filter(Boolean)
+ .join(" ")
+ .trim();
+}
+
+
+
+