diff --git a/package-lock.json b/package-lock.json
index 6c2319d..4c9e5b2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@tailwindcss/vite": "^4.1.16",
+ "axios": "^1.13.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.9.4"
@@ -2048,6 +2049,12 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
"node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
@@ -2086,6 +2093,17 @@
"postcss": "^8.1.0"
}
},
+ "node_modules/axios": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.1.tgz",
+ "integrity": "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.4",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -2161,6 +2179,19 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2229,6 +2260,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/commander": {
"version": "14.0.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
@@ -2309,6 +2352,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dependency-cruiser": {
"version": "17.2.0",
"resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-17.2.0.tgz",
@@ -2418,6 +2470,20 @@
"node": ">=8"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.241",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.241.tgz",
@@ -2438,6 +2504,51 @@
"node": ">=10.13.0"
}
},
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/esbuild": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
@@ -2821,6 +2932,42 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -2853,7 +3000,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2869,6 +3015,43 @@
"node": ">=6.9.0"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2911,6 +3094,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2934,11 +3129,37 @@
"node": ">=8"
}
},
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -3491,6 +3712,15 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/memoize": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/memoize/-/memoize-10.2.0.tgz",
@@ -3531,6 +3761,27 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/mimic-function": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
@@ -3784,6 +4035,12 @@
"node": ">= 6"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
diff --git a/package.json b/package.json
index c02b254..2d2104c 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"@tailwindcss/vite": "^4.1.16",
+ "axios": "^1.13.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.9.4"
diff --git a/public/placeholders/placeholder-profile.png b/public/placeholders/placeholder-profile.png
new file mode 100644
index 0000000..7d7bb39
Binary files /dev/null and b/public/placeholders/placeholder-profile.png differ
diff --git a/src/App.tsx b/src/App.tsx
index 55437f9..d803a2b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,19 +1,43 @@
-import { useState } from 'react'
-import './App.css'
-import Navbar from './components/Layout/Navbar/Navbar'
+import { useState } from "react"
+import { BrowserRouter, Routes, Route } from "react-router-dom"
+import HomePage from "./pages/HomePage"
+import DealPage from "./pages/DealPage"
+import SubmitDealPage from "./pages/SubmitDealPage"
+import AccountSettingsPage from "./pages/AccountSettingsPage"
+import ProfilePage from "./pages/ProfilePage"
+import SearchPage from "./pages/SearchPage"
+
+import LoginModal from "./components/Auth/LoginModal"
+
+export default function App() {
+ const [showLoginModal, setShowLoginModal] = useState(false)
-function App() {
- const [count, setCount] = useState(0)
return (
- <>
+
+ {showLoginModal && (
+ setShowLoginModal(false)}
+ />
+ )}
-
+
+ setShowLoginModal(true)} />} />
+
+ setShowLoginModal(true)} />}
+ />
+ setShowLoginModal(true)} />}
+ />
+ } />
+ } />
+ } />
-
- >
+
+
)
}
-
-export default App
diff --git a/src/api/account/uploadAvatar.ts b/src/api/account/uploadAvatar.ts
index 20e3ac0..a6115d3 100644
--- a/src/api/account/uploadAvatar.ts
+++ b/src/api/account/uploadAvatar.ts
@@ -1,17 +1,21 @@
-const API_URL = "http://localhost:3000/api"
+// src/api/account/uploadAvatar.ts
+import instance from "../axiosInstance"
-export async function uploadAvatar(token: string, file: File) {
- const formData = new FormData()
- formData.append("file", file)
+export async function uploadAvatar(file: File) {
+ try {
+ const formData = new FormData()
+ formData.append("file", file)
- const res = await fetch(`${API_URL}/account/avatar`, {
- method: "POST",
- headers: {
- Authorization: `Bearer ${token}`,
- },
- body: formData,
- })
+ const { data } = await instance.post("/account/avatar", formData, {
+ headers: {
+ "Content-Type": "multipart/form-data",
+ },
+ })
- if (!res.ok) throw new Error("Avatar yükleme hatası")
- return res.json()
+ return data
+ } catch (error: any) {
+ const message = error.response?.data?.error || "Avatar yükleme hatası"
+ console.error("Avatar yükleme hatası:", message)
+ throw new Error(message)
+ }
}
diff --git a/src/api/auth/login.ts b/src/api/auth/login.ts
index 713ab42..e58402e 100644
--- a/src/api/auth/login.ts
+++ b/src/api/auth/login.ts
@@ -1,14 +1,13 @@
+// src/api/auth/login.ts
+import instance from "../axiosInstance"
+
export async function login(email: string, password: string) {
- const res = await fetch("http://localhost:3000/api/auth/login", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ email, password }),
- })
-
- if (!res.ok) {
- const data = await res.json()
- throw new Error(data.message || "Giriş başarısız")
+ try {
+ const { data } = await instance.post("/auth/login", { email, password })
+ return data // { token, user }
+ } catch (error: any) {
+ const message = error.response?.data?.message || "Giriş başarısız"
+ console.error("Login hatası:", message)
+ throw new Error(message)
}
-
- return res.json() // { token, user }
}
diff --git a/src/api/auth/me.ts b/src/api/auth/me.ts
new file mode 100644
index 0000000..57188b2
--- /dev/null
+++ b/src/api/auth/me.ts
@@ -0,0 +1,13 @@
+// src/api/auth/me.ts
+import instance from "../axiosInstance"
+
+export async function me() {
+ try {
+ const { data } = await instance.get("/auth/me")
+ return data
+ } catch (error: any) {
+ const message = error.response?.data?.error || "Kullanıcı bilgisi alınamadı"
+ console.error("Kullanıcı bilgisi alma hatası:", message)
+ throw new Error(message)
+ }
+}
diff --git a/src/api/auth/register.ts b/src/api/auth/register.ts
index fb75b4e..b29ffba 100644
--- a/src/api/auth/register.ts
+++ b/src/api/auth/register.ts
@@ -1,14 +1,17 @@
+// src/api/auth/register.ts
+import instance from "../axiosInstance"
+
export async function register(username: string, email: string, password: string) {
- const res = await fetch("http://localhost:3000/api/auth/register", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ username, email, password }),
- })
-
- if (!res.ok) {
- const data = await res.json()
- throw new Error(data.message || "Kayıt başarısız")
+ try {
+ const { data } = await instance.post("/auth/register", {
+ username,
+ email,
+ password,
+ })
+ return data // { token, user }
+ } catch (error: any) {
+ const message = error.response?.data?.message || "Kayıt başarısız"
+ console.error("Register hatası:", message)
+ throw new Error(message)
}
-
- return res.json() // { token, user }
}
diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts
new file mode 100644
index 0000000..58a5bbe
--- /dev/null
+++ b/src/api/axiosInstance.ts
@@ -0,0 +1,27 @@
+import axios from "axios";
+
+const instance = axios.create({
+ baseURL: "http://localhost:3000/api",
+ headers: {
+ "Content-Type": "application/json",
+ },
+});
+
+instance.interceptors.request.use((config) => {
+ const token = localStorage.getItem("token");
+ if (token) config.headers.Authorization = `Bearer ${token}`;
+ return config;
+});
+
+instance.interceptors.response.use(
+ (response) => response,
+ (error) => {
+ if (error.response?.status === 401) {
+ localStorage.clear();
+ window.location.href = "/" // anasayfaya yönlendir
+ }
+ return Promise.reject(error);
+ }
+);
+
+export default instance;
diff --git a/src/api/deal/commentDeal.ts b/src/api/deal/commentDeal.ts
index 7ac3f52..d877ce9 100644
--- a/src/api/deal/commentDeal.ts
+++ b/src/api/deal/commentDeal.ts
@@ -1,24 +1,24 @@
-const API_URL = "http://localhost:3000/api/comments"
-
+// src/api/deal/commentApi.ts
+import instance from "../axiosInstance"
export async function getComments(dealId: number) {
- const res = await fetch(`${API_URL}/${dealId}`)
- const data = await res.json()
- if (!res.ok) throw new Error(data.error || "Yorumlar alınamadı")
- return data
+ try {
+ const { data } = await instance.get(`/comments/${dealId}`)
+ return data
+ } catch (error: any) {
+ const message = error.response?.data?.error || "Yorumlar alınamadı"
+ console.error("Yorumları alma hatası:", message)
+ throw new Error(message)
+ }
}
-export async function postComment(token: string, dealId: number, text: string) {
- const res = await fetch(API_URL, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${token}`,
- },
- body: JSON.stringify({ dealId, text }),
- })
-
- const data = await res.json()
- if (!res.ok) throw new Error(data.error || "Yorum gönderilemedi")
- return data
+export async function postComment(dealId: number, text: string) {
+ try {
+ const { data } = await instance.post("/comments", { dealId, text })
+ return data
+ } catch (error: any) {
+ const message = error.response?.data?.error || "Yorum gönderilemedi"
+ console.error("Yorum gönderme hatası:", message)
+ throw new Error(message)
+ }
}
diff --git a/src/api/deal/getDeal.ts b/src/api/deal/getDeal.ts
index cd2043e..90ea19a 100644
--- a/src/api/deal/getDeal.ts
+++ b/src/api/deal/getDeal.ts
@@ -1,22 +1,22 @@
-const API_URL = "http://localhost:3000/api"
+// src/api/deal/dealApi.ts
+import instance from "../axiosInstance"
export async function getDeals(page = 1) {
try {
- const res = await fetch(`http://localhost:3000/api/deals?page=${page}`)
- if (!res.ok) throw new Error("Deal listesi alınamadı")
-
- const data = await res.json()
- // Sadece results dizisini döndür
+ const { data } = await instance.get(`/deals?page=${page}`)
return data.results
- } catch (err) {
- console.error("Deal listesi hatası:", err)
- throw err
+ } catch (error) {
+ console.error("Deal listesi hatası:", error)
+ throw error
}
}
-
export async function getDeal(id: number) {
- const res = await fetch(`${API_URL}/deals/${id}`)
- if (!res.ok) throw new Error("Deal alınamadı")
- return res.json()
-}
\ No newline at end of file
+ try {
+ const { data } = await instance.get(`/deals/${id}`)
+ return data
+ } catch (error) {
+ console.error("Deal alma hatası:", error)
+ throw error
+ }
+}
diff --git a/src/api/deal/newDeal.ts b/src/api/deal/newDeal.ts
index b103c9b..2eb421f 100644
--- a/src/api/deal/newDeal.ts
+++ b/src/api/deal/newDeal.ts
@@ -1,22 +1,21 @@
-const API_URL = "http://localhost:3000/api"
+// src/api/deal/createDeal.ts
+import instance from "../axiosInstance"
-export async function createDeal(token: string, dealData: {
+type DealData = {
title: string
description?: string
url?: string
imageUrl?: string
price?: number
-}) {
- const res = await fetch(`${API_URL}/deals`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${token}`,
- },
- body: JSON.stringify(dealData),
- })
-
- const data = await res.json()
- if (!res.ok) throw new Error(data.error || "Fırsat eklenemedi")
- return data
+}
+
+export async function createDeal(dealData: DealData) {
+ try {
+ const { data } = await instance.post("/deals", dealData)
+ return data
+ } catch (error: any) {
+ const message = error.response?.data?.error || "Fırsat eklenemedi"
+ console.error("Fırsat oluşturma hatası:", message)
+ throw new Error(message)
+ }
}
diff --git a/src/api/deal/searchDeal.ts b/src/api/deal/searchDeal.ts
new file mode 100644
index 0000000..4d0f1cc
--- /dev/null
+++ b/src/api/deal/searchDeal.ts
@@ -0,0 +1,14 @@
+import instance from "../axiosInstance"
+
+export async function searchDeals(query: string, page = 1) {
+ try {
+ const { data } = await instance.get(`/deals/search`, {
+ params: { q: query, page },
+ })
+ // backend response { results, total, totalPages } formatındaysa:
+ return data.results
+ } catch (error) {
+ console.error("Deal arama hatası:", error)
+ throw error
+ }
+}
diff --git a/src/api/deal/voteDeal.ts b/src/api/deal/voteDeal.ts
index ec589a7..167ee5f 100644
--- a/src/api/deal/voteDeal.ts
+++ b/src/api/deal/voteDeal.ts
@@ -1,14 +1,15 @@
-const API_URL = "http://localhost:3000/api"
+// src/api/deal/voteDeal.ts
+import instance from "../axiosInstance"
-export async function voteDeal(token: string, dealId: number, type: "UP" | "DOWN") {
- const res = await fetch(`${API_URL}/deal-votes`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${token}`,
- },
- body: JSON.stringify({ dealId, voteType: type }),
- })
- if (!res.ok) throw new Error("Vote hatası")
- return res.json()
+export async function voteDeal(dealId: number, type: "UP" | "DOWN") {
+ try {
+ const { data } = await instance.post("/deal-votes", {
+ dealId,
+ voteType: type,
+ })
+ return data
+ } catch (error) {
+ console.error("Vote hatası:", error)
+ throw error
+ }
}
diff --git a/src/api/user/getUser.ts b/src/api/user/getUser.ts
new file mode 100644
index 0000000..11231aa
--- /dev/null
+++ b/src/api/user/getUser.ts
@@ -0,0 +1,12 @@
+// src/api/user/getUser.ts
+import instance from "../axiosInstance"
+
+export async function getUser(userName: string) {
+ try {
+ const res = await instance.get(`/user/${userName}`)
+ return res.data // { user, deals, comments }
+ } catch (err: any) {
+ console.error("Kullanıcı bilgileri alınamadı:", err)
+ throw new Error(err.response?.data?.message || "Kullanıcı bilgileri alınamadı")
+ }
+}
diff --git a/src/components/Auth/LoginModal.tsx b/src/components/Auth/LoginModal.tsx
index e854b97..62b296a 100644
--- a/src/components/Auth/LoginModal.tsx
+++ b/src/components/Auth/LoginModal.tsx
@@ -1,31 +1,38 @@
-import { useState } from "react";
+import { useState } from "react"
+import { useAuth } from "../../context/AuthContext"
+import { login as loginApi } from "../../api/auth/login"
+import { register as registerApi } from "../../api/auth/register"
type LoginModalProps = {
- onClose: () => void;
- onLogin: (email: string, password: string) => void;
- onRegister: (username: string, email: string, password: string) => void;
-};
+ onClose: () => void
+}
-export default function LoginModal({ onClose, onLogin, onRegister }: LoginModalProps) {
- const [isRegister, setIsRegister] = useState(false);
- const [username, setUsername] =useState("");
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
+export default function LoginModal({ onClose }: LoginModalProps) {
+ const [isRegister, setIsRegister] = useState(false)
+ const [username, setUsername] = useState("")
+ const [email, setEmail] = useState("")
+ const [password, setPassword] = useState("")
+ const { login } = useAuth() // global auth fonksiyonu
- const handleSubmit = () => {
- if (isRegister) {
- if (!username || !email || !password) return;
- onRegister(username, email, password);
- } else {
- if (!email || !password) return;
- onLogin(email, password);
+ const handleSubmit = async () => {
+ try {
+ if (isRegister) {
+ const data = await registerApi(username, email, password)
+ login(data.user, data.token)
+ } else {
+ const data = await loginApi(email, password)
+ login(data.user, data.token)
+ }
+ onClose()
+ } catch (err) {
+ alert("Giriş/Kayıt başarısız")
}
- };
+ }
return (
-
-
+
+
{isRegister ? "Kayıt Ol" : "Giriş Yap"}
@@ -36,7 +43,7 @@ export default function LoginModal({ onClose, onLogin, onRegister }: LoginModalP
placeholder="Kullanıcı adı"
value={username}
onChange={(e) => setUsername(e.target.value)}
- className="w-full mb-3 p-3 rounded bg-surface border border-neutral-700 text-text placeholder-text-muted"
+ className="w-full mb-3 p-3 rounded bg-surface border"
/>
)}
@@ -45,27 +52,23 @@ export default function LoginModal({ onClose, onLogin, onRegister }: LoginModalP
placeholder="E-posta"
value={email}
onChange={(e) => setEmail(e.target.value)}
- className="w-full mb-3 p-3 rounded bg-surface border border-neutral-700 text-text placeholder-text-muted"
+ className="w-full mb-3 p-3 rounded bg-surface border"
/>
-
setPassword(e.target.value)}
- className="w-full mb-5 p-3 rounded bg-surface border border-neutral-700 text-text placeholder-text-muted"
+ className="w-full mb-5 p-3 rounded bg-surface border"
/>
-
-
+
{isRegister ? "Zaten hesabın var mı?" : "Hesabın yok mu?"}
setIsRegister(!isRegister)}
@@ -76,5 +79,5 @@ export default function LoginModal({ onClose, onLogin, onRegister }: LoginModalP
- );
+ )
}
diff --git a/src/components/DealScreen/DealComments.tsx b/src/components/DealScreen/DealComments.tsx
index 4198318..5f854bd 100644
--- a/src/components/DealScreen/DealComments.tsx
+++ b/src/components/DealScreen/DealComments.tsx
@@ -1,25 +1,21 @@
import React, { useEffect, useState } from "react"
-import { getComments,postComment } from "../../api/deal/commentDeal"
-
-
-type Comment = {
- id: number
- user: { username: string }
- text: string
- createdAt: string
-}
+import { Link } from "react-router-dom"
+import { getComments, postComment } from "../../api/deal/commentDeal"
+import { useAuth } from "../../context/AuthContext"
+import { timeAgo } from "../../utils/timeAgo"
+import type { Comment } from "../../models/Comment"
type DealCommentsProps = {
dealId: number
+ onRequireLogin: () => void
}
-export default function DealComments({ dealId }: DealCommentsProps) {
+export default function DealComments({ dealId, onRequireLogin }: DealCommentsProps) {
const [comments, setComments] = useState
([])
const [newComment, setNewComment] = useState("")
const [loading, setLoading] = useState(false)
- const token = localStorage.getItem("token")
+ const { isAuthenticated } = useAuth()
- // Yorumları yükle
useEffect(() => {
async function loadComments() {
try {
@@ -35,17 +31,15 @@ export default function DealComments({ dealId }: DealCommentsProps) {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!newComment.trim()) return
- if (!token) {
- alert("Giriş yapmalısın.")
- return
- }
+ if (!isAuthenticated) return onRequireLogin()
setLoading(true)
try {
- const added = await postComment(token, dealId, newComment)
- setComments((prev) => [added, ...prev]) // yeni yorumu ekle
+ const added = await postComment(dealId, newComment)
+ setComments((prev) => [added, ...prev])
setNewComment("")
} catch (err: any) {
+ console.error(err)
alert(err.message || "Sunucu hatası")
} finally {
setLoading(false)
@@ -53,56 +47,85 @@ export default function DealComments({ dealId }: DealCommentsProps) {
}
return (
-
-
Yorumlar
+
+
Yorumlar
-
) : (
-
- Yorum yazmak için{" "}
-
- giriş yap
- {" "}
- veya{" "}
-
- kayıt ol
-
- .
-
+
+ Yorum yazmak için giriş yap veya kayıt ol
+
)}
)
diff --git a/src/components/DealScreen/DealDescription.tsx b/src/components/DealScreen/DealDescription.tsx
index ce986a5..5f76c0f 100644
--- a/src/components/DealScreen/DealDescription.tsx
+++ b/src/components/DealScreen/DealDescription.tsx
@@ -1,5 +1,3 @@
-import React from "react"
-
type DealDescriptionProps = {
description: string
}
diff --git a/src/components/Layout/Navbar/Navbar.tsx b/src/components/Layout/Navbar/Navbar.tsx
index ed7490e..e32d01f 100644
--- a/src/components/Layout/Navbar/Navbar.tsx
+++ b/src/components/Layout/Navbar/Navbar.tsx
@@ -1,12 +1,13 @@
import UserInfo from "./UserInfo"
import { Link } from "react-router-dom"
+import SearchBar from "../Navbar/SearchBar"
export default function Navbar() {
return (