diff --git a/frontend/api/sublet/amenitiesApi.ts b/frontend/api/sublet/amenitiesApi.ts new file mode 100644 index 00000000..af0cbb40 --- /dev/null +++ b/frontend/api/sublet/amenitiesApi.ts @@ -0,0 +1,14 @@ +// /api/amenitiesApi.ts + +import { doApiRequest } from '@/utils/fetch' + +const BASE_URL = 'api/sublet' + +export const getAmenities = async (): Promise => { + const response = await doApiRequest(`${BASE_URL}/listAmenities`) + if (!response.ok) { + console.error('Failed to fetch amenities') + return [] + } + return response.json() +} diff --git a/frontend/api/sublet/offersApi.ts b/frontend/api/sublet/offersApi.ts new file mode 100644 index 00000000..9c237602 --- /dev/null +++ b/frontend/api/sublet/offersApi.ts @@ -0,0 +1,44 @@ +// /api/offersApi.ts +import { CreateOfferRequest, OfferResponse } from '@/interfaces/sublet/Offer' +import { doApiRequest } from '@/utils/fetch' + +const BASE_URL = 'api/sublet' + +// Get all offers for a specific sublet +export const getOffers = async (subletId: string): Promise => { + const response = await doApiRequest(`${BASE_URL}/listOffers/${subletId}`) + if (!response.ok) { + console.error('Failed to fetch offers') + return [] + } + return response.json() +} + +// Create a new offer for a sublet +export const createOffer = async ( + subletId: string, + data: CreateOfferRequest +): Promise => { + const response = await doApiRequest(`${BASE_URL}/createOffer/${subletId}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + }) + if (!response.ok) { + console.error('Failed to create offer') + return false + } + return true +} + +// Delete an offer by sublet ID (no request body required) +export const deleteOffer = async (subletId: string): Promise => { + const response = await doApiRequest(`${BASE_URL}/destroyOffer/${subletId}`, { + method: 'DELETE', + }) + if (!response.ok) { + console.error('Failed to delete offer') + return false + } + return true +} diff --git a/frontend/api/sublet/subletsApi.ts b/frontend/api/sublet/subletsApi.ts new file mode 100644 index 00000000..3e5cecd6 --- /dev/null +++ b/frontend/api/sublet/subletsApi.ts @@ -0,0 +1,109 @@ +// /api/subletsApi.ts +import { + CreateSubletImageRequest, + CreateSubletRequest, + Sublet, + SubletResponse, + UpdateSubletRequest, +} from '@/interfaces/sublet/Sublet' +import { doApiRequest } from '@/utils/fetch' + +const BASE_URL = 'api/sublet' + +export const getSublets = async (): Promise => { + const response = await doApiRequest(`${BASE_URL}/listSublets`) + if (!response.ok) { + console.error('Failed to fetch sublets') + return [] + } + return response.json() +} + +export const getSubletById = async (id: number): Promise => { + const response = await doApiRequest( + `${BASE_URL}/retrieveSubletSerializerRead/${id}` + ) + if (!response.ok) { + console.error('Failed to fetch sublet') + return null + } + return response.json() +} + +export const createSublet = async ( + data: CreateSubletRequest +): Promise => { + const response = await doApiRequest(`${BASE_URL}/createSublet`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + }) + if (!response.ok) { + console.error('Failed to create sublet') + return null + } + return response.json() +} + +export const updateSublet = async ( + id: string, + data: UpdateSubletRequest +): Promise => { + const response = await doApiRequest(`${BASE_URL}/updateSublet/${id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + }) + if (!response.ok) { + console.error('Failed to update sublet') + return null + } + return response.json() +} + +export const deleteSublet = async (id: number): Promise => { + const response = await doApiRequest(`${BASE_URL}/destroySublet/${id}`, { + method: 'DELETE', + }) + if (!response.ok) { + console.error('Failed to delete sublet') + return false + } + return true +} + +export const uploadSubletImage = async ( + subletId: string, + data: CreateSubletImageRequest +): Promise => { + const formData = new FormData() + formData.append('sublet', data.sublet.toString()) + if (data.image) formData.append('image', data.image) + + const response = await doApiRequest( + `${BASE_URL}/createSubletImage/${subletId}`, + { + method: 'POST', + body: formData, + } + ) + if (!response.ok) { + console.error('Failed to upload sublet image') + return false + } + return true +} + +export const deleteSubletImage = async (imageId: number): Promise => { + const response = await doApiRequest( + `${BASE_URL}/destroySubletImage/${imageId}`, + { + method: 'DELETE', + } + ) + if (!response.ok) { + console.error('Failed to delete sublet image') + return false + } + return true +} diff --git a/frontend/interfaces/sublet/Offer.ts b/frontend/interfaces/sublet/Offer.ts new file mode 100644 index 00000000..a14134bd --- /dev/null +++ b/frontend/interfaces/sublet/Offer.ts @@ -0,0 +1,18 @@ +// /interfaces/CreateOfferRequest.ts +export interface CreateOfferRequest { + phone_number: string // Required phone number + email?: string | null // Optional email, max length 255 characters + message: string // Message, max length 255 characters + sublet: number // Sublet ID +} + +// /interfaces/OfferResponse.ts +export interface OfferResponse { + id: number // Offer ID + phone_number: string // Phone number of the user making the offer + email?: string | null // Optional email + message: string // Message content + created_date: string // ISO datetime string + user: string // User ID or name + sublet: number // Sublet ID associated with the offer +} diff --git a/frontend/interfaces/sublet/Sublet.ts b/frontend/interfaces/sublet/Sublet.ts new file mode 100644 index 00000000..35458bf3 --- /dev/null +++ b/frontend/interfaces/sublet/Sublet.ts @@ -0,0 +1,74 @@ +// SubletListing.ts + +export interface Sublet { + id: number + subletter: string // Name or ID of the subletter + amenities: string[] // Array of amenities as strings + title: string // Required, max length 255 characters + address?: string | null // Optional, max length 255 characters + beds?: number | null // Optional, integer + baths?: string | null // Optional, can be a decimal, e.g., "1.5" + description?: string | null // Optional description + external_link?: string | null // Optional external URL, must match a valid URL pattern + price: number // Required, integer value + negotiable: boolean // Indicates if the price is negotiable + start_date: string // Required, ISO 8601 date string + end_date: string // Required, ISO 8601 date string + expires_at: string // Required, ISO 8601 datetime string +} + +// /interfaces/SubletResponse.ts +export interface SubletResponse { + id: number + subletter: string // Subletter’s name or ID + amenities: string[] // List of amenities + title: string + address?: string | null // Optional or nullable + beds?: number | null // Optional number of beds + baths?: string | null // Optional, can be a decimal + description?: string | null // Optional description + external_link?: string | null // Optional external URL + price: number + negotiable: boolean // Is price negotiable? + start_date: string // ISO date string + end_date: string // ISO date string + expires_at: string // ISO datetime string +} + +// /interfaces/CreateSubletRequest.ts +export interface CreateSubletRequest { + amenities: string[] + title: string + address?: string | null + beds?: number | null + baths?: string | null + description?: string | null + external_link?: string | null + price: number + negotiable: boolean + start_date: string + end_date: string + expires_at: string +} + +// /interfaces/UpdateSubletRequest.ts +export interface UpdateSubletRequest { + amenities: string[] + title: string + address?: string | null + beds?: number | null + baths?: string | null + description?: string | null + external_link?: string | null + price: number + negotiable: boolean + start_date: string + end_date: string + expires_at: string +} + +// /interfaces/CreateSubletImageRequest.ts +export interface CreateSubletImageRequest { + sublet: number // Sublet ID + image: File | null // Binary image file +}