Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sublet): add FE apis and interfaces #312

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions frontend/api/sublet/amenitiesApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// /api/amenitiesApi.ts

import { doApiRequest } from '@/utils/fetch'

const BASE_URL = 'api/sublet'

export const getAmenities = async (): Promise<string[]> => {
const response = await doApiRequest(`${BASE_URL}/listAmenities`)
if (!response.ok) {
throw new Error('Failed to fetch amenities')
}
return response.json()
}
39 changes: 39 additions & 0 deletions frontend/api/sublet/offersApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// /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<OfferResponse[]> => {
const response = await doApiRequest(`${BASE_URL}/listOffers/${subletId}`)
if (!response.ok) {
throw new Error('Failed to fetch offers')
}
return response.json()
}

// Create a new offer for a sublet
export const createOffer = async (
subletId: string,
data: CreateOfferRequest
): Promise<void> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the response shouldn't be void? We should return the response so we can check it and maybe display an error or sucess message based off of it.

In general is it nescecary to throw after each call (i have no preference here but just wondering). Why not just return the response and have the caller inspect it? It seems natural that for most responses we might want to handle the happy case and the bad case

const response = await doApiRequest(`${BASE_URL}/createOffer/${subletId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to create offer')
}
}

// Delete an offer by sublet ID (no request body required)
export const deleteOffer = async (subletId: string): Promise<void> => {
const response = await doApiRequest(`${BASE_URL}/destroyOffer/${subletId}`, {
method: 'DELETE',
})
if (!response.ok) {
throw new Error('Failed to delete offer')
}
}
99 changes: 99 additions & 0 deletions frontend/api/sublet/subletsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// /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<SubletResponse[]> => {
const response = await doApiRequest(`${BASE_URL}/listSublets`)
if (!response.ok) {
throw new Error('Failed to fetch sublets')
}
return response.json()
}

export const getSubletById = async (id: number): Promise<Sublet> => {
const response = await doApiRequest(
`${BASE_URL}/retrieveSubletSerializerRead/${id}`
)
if (!response.ok) {
throw new Error('Failed to fetch sublet')
}
return response.json()
}

export const createSublet = async (
data: CreateSubletRequest
): Promise<SubletResponse> => {
const response = await doApiRequest(`${BASE_URL}/createSublet`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to create sublet')
}
return response.json()
}

export const updateSublet = async (
id: string,
data: UpdateSubletRequest
): Promise<SubletResponse> => {
const response = await doApiRequest(`${BASE_URL}/updateSublet/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to update sublet')
}
return response.json()
}

export const deleteSublet = async (id: number): Promise<void> => {
const response = await doApiRequest(`${BASE_URL}/destroySublet/${id}`, {
method: 'DELETE',
})
if (!response.ok) {
throw new Error('Failed to delete sublet')
}
}

export const uploadSubletImage = async (
subletId: string,
data: CreateSubletImageRequest
): Promise<void> => {
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) {
throw new Error('Failed to upload sublet image')
}
}

export const deleteSubletImage = async (imageId: number): Promise<void> => {
const response = await doApiRequest(
`${BASE_URL}/destroySubletImage/${imageId}`,
{
method: 'DELETE',
}
)
if (!response.ok) {
throw new Error('Failed to delete sublet image')
}
}
18 changes: 18 additions & 0 deletions frontend/interfaces/sublet/Offer.ts
Original file line number Diff line number Diff line change
@@ -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
}
74 changes: 74 additions & 0 deletions frontend/interfaces/sublet/Sublet.ts
Original file line number Diff line number Diff line change
@@ -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
}
Loading