diff --git a/src/App.tsx b/src/App.tsx index d803a2b..72529c3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,8 +2,10 @@ 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 DealPage from "./pages/DealDetailsPage" + + +import CreateDealPage from "./pages/CreateDealPage" import AccountSettingsPage from "./pages/AccountSettingsPage" import ProfilePage from "./pages/ProfilePage" import SearchPage from "./pages/SearchPage" @@ -33,7 +35,8 @@ export default function App() { element={ setShowLoginModal(true)} />} /> - } /> + + } /> } /> } /> diff --git a/src/adapters/requests/dealCreateAdapter.ts b/src/adapters/requests/dealCreateAdapter.ts new file mode 100644 index 0000000..aadf95b --- /dev/null +++ b/src/adapters/requests/dealCreateAdapter.ts @@ -0,0 +1,18 @@ + +import type { DealDraft } from "../../models/DealDraft" +import type { CreateDealRequest } from "../../api/deal/types" + +export function mapDealDraftToCreateRequest( + draft: DealDraft +): CreateDealRequest { + return { + title: draft.title, + description: draft.description, + price: draft.price, + imageUrl: draft.imageUrl, + + url: draft.url || undefined, + + sellerName:draft.seller.name + } +} diff --git a/src/adapters/requests/sellerFromLookupAdapter.ts b/src/adapters/requests/sellerFromLookupAdapter.ts new file mode 100644 index 0000000..dee7319 --- /dev/null +++ b/src/adapters/requests/sellerFromLookupAdapter.ts @@ -0,0 +1,8 @@ +import type { Seller } from "../../models/seller/Seller"; +import type { SellerFromLookupRequest } from "../../api/seller/types"; + +export function mapSellerFromLookupRequest(seller:Seller):SellerFromLookupRequest{ + return{ + url:seller.url + } +} \ No newline at end of file diff --git a/src/adapters/responses/dealCardAdapter.ts b/src/adapters/responses/dealCardAdapter.ts new file mode 100644 index 0000000..c41a67f --- /dev/null +++ b/src/adapters/responses/dealCardAdapter.ts @@ -0,0 +1,35 @@ +// src/adapters/dealCardAdapter.ts +import type { DealCard } from "../../models/deal/DealCard" +import type { DealCardResponse } from "../../api/deal/types" + +export function mapDealCardResponseToDeal( + api: DealCardResponse +): DealCard{ + return { + id: api.id, + title: api.title, + description: api.description, + price: api.price ?? undefined, + + score: api.score, + commentsCount:api.commentsCount, + status: api.status, + saleType: api.saleType, + affiliateType: api.affiliateType, + + createdAt: api.createdAt, + updatedAt:api.updatedAt, + user: { + id: api.user.id, + username: api.user.username, + avatarUrl: api.user.avatarUrl, + }, + + seller:{ + name:api.seller.name, + url:api.seller.url + }, + + imageUrl:api.imageUrl, + } +} diff --git a/src/adapters/responses/dealDetailAdapter.ts b/src/adapters/responses/dealDetailAdapter.ts new file mode 100644 index 0000000..1e8e614 --- /dev/null +++ b/src/adapters/responses/dealDetailAdapter.ts @@ -0,0 +1,41 @@ +import type { DealDetailResponse } from "../../api/deal/types" +import type { DealDetail } from "../../models/deal/DealDetail" + +export function mapDealDetailResponseToDealDetail( + api: DealDetailResponse +): DealDetail { + return { + id: api.id, + title: api.title, + description: api.description, + + url: api.url ?? undefined, + price: api.price ?? undefined, + score: api.score, + commentsCount: api.commentsCount, + + status: api.status, + saleType: api.saleType, + affiliateType: api.affiliateType, + + createdAt: api.createdAt, + updatedAt: api.updatedAt, + + user: api.user, + seller:{ + name:api.seller.name, + url:api.seller.url + }, + images: api.images.map((img) => ({ + url: img.imageUrl, + order: img.order, + })), + + comments: api.comments.map((c) => ({ + id: c.id, + text: c.text, + createdAt: c.createdAt, + user: c.user, + })), + } +} diff --git a/src/adapters/responses/sellerFromLookupAdapter.ts b/src/adapters/responses/sellerFromLookupAdapter.ts new file mode 100644 index 0000000..49aebd0 --- /dev/null +++ b/src/adapters/responses/sellerFromLookupAdapter.ts @@ -0,0 +1,12 @@ +import type { SellerFromLookupResponse } from "../../api/seller/types" +import type { Seller } from "../../models/seller/Seller" + +export function mapSellerFromLookupResponse( + api: SellerFromLookupResponse +): Seller { + return{ + id:api.id, + name:api.name, + url:null +} +} \ No newline at end of file diff --git a/src/api/deal/getDeal.ts b/src/api/deal/getDeal.ts index 90ea19a..f050ec6 100644 --- a/src/api/deal/getDeal.ts +++ b/src/api/deal/getDeal.ts @@ -1,22 +1,17 @@ // src/api/deal/dealApi.ts import instance from "../axiosInstance" +import type { DealCardResponse,DealDetailResponse} from "./types" -export async function getDeals(page = 1) { - try { - const { data } = await instance.get(`/deals?page=${page}`) - return data.results - } catch (error) { - console.error("Deal listesi hatası:", error) - throw error - } +export async function getDeals( + page = 1 +): Promise { + const { data } = await instance.get(`/deals?page=${page}`) + return data.results } -export async function getDeal(id: number) { - try { - const { data } = await instance.get(`/deals/${id}`) - return data - } catch (error) { - console.error("Deal alma hatası:", error) - throw error - } +export async function getDealDetail( + id: number +): Promise { + const { data } = await instance.get(`/deals/${id}`) + return data } diff --git a/src/api/deal/newDeal.ts b/src/api/deal/newDeal.ts index 2eb421f..b8addc8 100644 --- a/src/api/deal/newDeal.ts +++ b/src/api/deal/newDeal.ts @@ -6,6 +6,7 @@ type DealData = { description?: string url?: string imageUrl?: string + customCompany?: string price?: number } diff --git a/src/api/deal/types.ts b/src/api/deal/types.ts new file mode 100644 index 0000000..73c2906 --- /dev/null +++ b/src/api/deal/types.ts @@ -0,0 +1,97 @@ +// src/api/deal/types.ts + +export type DealCardResponse = { + id: number + title: string + description: string // DB null → backend "" yapar + price: number | null // fiyat yoksa bilinçli null + + score: number + status: "PENDING" | "ACTIVE" | "EXPIRED" | "REJECTED" + saleType: "ONLINE" | "OFFLINE" | "CODE" + affiliateType: "AFFILIATE" | "NON_AFFILIATE" | "USER_AFFILIATE" + + createdAt: string // ISO string + updatedAt: string + /* ---------- YAYINLAYAN KULLANICI (profil için yeterli) ---------- */ + user: { + id: number // 🔑 profil sayfası için + username: string + avatarUrl: string | null + } + + /* ---------- SATICI (company + customCompany sadeleştirilmiş) ---------- */ + seller: { + name: string + url:string|null + } + + /* ---------- GÖRSELLER ---------- */ + imageUrl:string + + /* ---------- UX İÇİN TÜRETİLMİŞ ALANLAR ---------- */ + commentsCount: number + userVote?: "UP" | "DOWN" | null +} + +export type DealDetailResponse = { + id: number + title: string + description: string + + url: string | null + price: number | null + score: number + commentsCount: number + + status: "PENDING" | "ACTIVE" | "EXPIRED" | "REJECTED" + saleType: "ONLINE" | "OFFLINE" | "CODE" + affiliateType: "AFFILIATE" | "NON_AFFILIATE" | "USER_AFFILIATE" + + createdAt: string + updatedAt: string + + user: { + id: number + username: string + avatarUrl: string | null + } + + seller: { + name: string + url:string|null + } + + images: { + id: number + imageUrl: string + order: number + }[] + + comments: { + id: number + text: string + createdAt: string + user: { + id: number + username: string + avatarUrl: string | null + } + }[] +} + + + +export type CreateDealRequest = { + title: string + description?: string + price?: number + imageUrl: string + + // online deal + url?: string + + // seller (known or custom) + sellerName: string +} + diff --git a/src/api/seller/from-lookup.ts b/src/api/seller/from-lookup.ts new file mode 100644 index 0000000..bcfb377 --- /dev/null +++ b/src/api/seller/from-lookup.ts @@ -0,0 +1,13 @@ +import type { SellerFromLookupResponse,SellerFromLookupRequest } from "./types" +import instance from "../axiosInstance" + + + +export async function lookupSellerFromLink(seller:SellerFromLookupRequest) :Promise{ + const { data } = await instance.post( + "/seller/from-link", + { seller } + ) + + return data +} diff --git a/src/api/seller/types.ts b/src/api/seller/types.ts new file mode 100644 index 0000000..3b992e7 --- /dev/null +++ b/src/api/seller/types.ts @@ -0,0 +1,9 @@ +export type SellerFromLookupResponse= { + id:number + name:string +} + + +export type SellerFromLookupRequest={ + url:string|null +} \ No newline at end of file diff --git a/src/components/CreateDeal/DealDetailsStep.tsx b/src/components/CreateDeal/DealDetailsStep.tsx new file mode 100644 index 0000000..e60d6e6 --- /dev/null +++ b/src/components/CreateDeal/DealDetailsStep.tsx @@ -0,0 +1,175 @@ +import type { DealDraft } from "../../models/DealDraft" + +type Props = { + data: DealDraft + onChange: (data: DealDraft) => void + onBack: () => void + onSubmit: () => void +} + +export default function DealDetailsStep({ + data, + onChange, + onBack, + onSubmit, +}: Props) { + const hasDetectedCompany = Boolean(data.sellerId) + + return ( +
{ + e.preventDefault() + onSubmit() + }} + className="max-w-2xl mx-auto flex flex-col gap-6" + > + {/* BAŞLIK */} +
+

+ Fırsat Detayları +

+

+ Fırsata ait temel bilgileri aşağıdaki alanlara giriniz. +

+
+ + {/* BAŞLIK */} +
+ + + onChange({ ...data, title: e.target.value }) + } + className="border rounded-md px-3 py-2" + required + /> +

+ Kullanıcının ilk göreceği kısa ve net başlık. +

+
+ + {/* AÇIKLAMA */} +
+ +