added listings
This commit is contained in:
240
src/listing/data.rs
Normal file
240
src/listing/data.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
use axum::{
|
||||
extract::{Path, State, Extension},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use crate::auth::structs::{AppState, User};
|
||||
use crate::company::structs::Company;
|
||||
use crate::listing::structs::{Listing, CreateListingRequest, UpdateListingRequest};
|
||||
use serde_json::json;
|
||||
|
||||
pub async fn get_listings(
|
||||
State(app_state): State<AppState>,
|
||||
Extension(user): Extension<User>,
|
||||
) -> Result<Json<Vec<Listing>>, (StatusCode, Json<serde_json::Value>)> {
|
||||
match sqlx::query_as::<_, Listing>(
|
||||
"SELECT id, company_name, is_active, price_per_gallon, price_per_gallon_cash, note, minimum_order, service, bio_percent, phone, online_ordering, county_id, user_id, last_edited FROM listings WHERE user_id = $1 ORDER BY id DESC"
|
||||
)
|
||||
.bind(user.id)
|
||||
.fetch_all(&*app_state.db)
|
||||
.await
|
||||
{
|
||||
Ok(listings) => Ok(Json(listings)),
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": format!("Failed to fetch listings: {}", e)}))
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_listing_by_id(
|
||||
State(app_state): State<AppState>,
|
||||
Path(listing_id): Path<i32>,
|
||||
Extension(user): Extension<User>,
|
||||
) -> Result<Json<Listing>, (StatusCode, Json<serde_json::Value>)> {
|
||||
match sqlx::query_as::<_, Listing>(
|
||||
"SELECT id, company_name, is_active, price_per_gallon, price_per_gallon_cash, note, minimum_order, service, bio_percent, phone, online_ordering, county_id, user_id, last_edited FROM listings WHERE id = $1 AND user_id = $2"
|
||||
)
|
||||
.bind(listing_id)
|
||||
.bind(user.id)
|
||||
.fetch_optional(&*app_state.db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(listing)) => Ok(Json(listing)),
|
||||
Ok(None) => Err((
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(json!({"error": "Listing not found"}))
|
||||
)),
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": format!("Failed to fetch listing: {}", e)}))
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_listing(
|
||||
State(app_state): State<AppState>,
|
||||
Extension(user): Extension<User>,
|
||||
Json(payload): Json<CreateListingRequest>,
|
||||
) -> Result<Json<Listing>, (StatusCode, Json<serde_json::Value>)> {
|
||||
eprintln!("DEBUG: Starting create_listing for user_id: {}", user.id);
|
||||
eprintln!("DEBUG: Payload: {:?}", payload);
|
||||
|
||||
// Create the listing directly without company validation
|
||||
match sqlx::query_as::<_, Listing>(
|
||||
"INSERT INTO listings (company_name, is_active, price_per_gallon, price_per_gallon_cash, note, minimum_order, service, bio_percent, phone, online_ordering, county_id, user_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id, company_name, is_active, price_per_gallon, price_per_gallon_cash, note, minimum_order, service, bio_percent, phone, online_ordering, county_id, user_id, last_edited"
|
||||
)
|
||||
.bind(&payload.company_name)
|
||||
.bind(payload.is_active)
|
||||
.bind(payload.price_per_gallon)
|
||||
.bind(payload.price_per_gallon_cash)
|
||||
.bind(&payload.note)
|
||||
.bind(payload.minimum_order)
|
||||
.bind(payload.service)
|
||||
.bind(payload.bio_percent)
|
||||
.bind(&payload.phone)
|
||||
.bind(&payload.online_ordering)
|
||||
.bind(payload.county_id)
|
||||
.bind(user.id)
|
||||
.fetch_one(&*app_state.db)
|
||||
.await
|
||||
{
|
||||
Ok(listing) => {
|
||||
eprintln!("DEBUG: Successfully created listing: {:?}", listing);
|
||||
Ok(Json(listing))
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("DEBUG: Error creating listing: {:?}", e);
|
||||
Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": format!("Failed to create listing: {}", e)}))
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_listing(
|
||||
State(app_state): State<AppState>,
|
||||
Path(listing_id): Path<i32>,
|
||||
Extension(user): Extension<User>,
|
||||
Json(payload): Json<UpdateListingRequest>,
|
||||
) -> Result<Json<Listing>, (StatusCode, Json<serde_json::Value>)> {
|
||||
// Build dynamic update query
|
||||
let mut query = "UPDATE listings SET ".to_string();
|
||||
let mut params: Vec<String> = Vec::new();
|
||||
let mut param_count = 1;
|
||||
|
||||
if let Some(company_name) = &payload.company_name {
|
||||
params.push(format!("company_name = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(is_active) = payload.is_active {
|
||||
params.push(format!("is_active = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(price_per_gallon) = payload.price_per_gallon {
|
||||
params.push(format!("price_per_gallon = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(price_per_gallon_cash) = payload.price_per_gallon_cash {
|
||||
params.push(format!("price_per_gallon_cash = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(note) = &payload.note {
|
||||
params.push(format!("note = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(minimum_order) = payload.minimum_order {
|
||||
params.push(format!("minimum_order = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(service) = payload.service {
|
||||
params.push(format!("service = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(bio_percent) = payload.bio_percent {
|
||||
params.push(format!("bio_percent = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(phone) = &payload.phone {
|
||||
params.push(format!("phone = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(online_ordering) = &payload.online_ordering {
|
||||
params.push(format!("online_ordering = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
if let Some(county_id) = payload.county_id {
|
||||
params.push(format!("county_id = ${}", param_count));
|
||||
param_count += 1;
|
||||
}
|
||||
|
||||
if params.is_empty() {
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": "No fields to update"}))
|
||||
));
|
||||
}
|
||||
|
||||
query.push_str(¶ms.join(", "));
|
||||
query.push_str(&format!(" WHERE id = ${} AND user_id = ${} RETURNING *", param_count, param_count + 1));
|
||||
|
||||
// This is a simplified version - in production, you'd want to build the query more safely
|
||||
// For now, let's use a simpler approach
|
||||
match sqlx::query_as::<_, Listing>(
|
||||
"UPDATE listings SET company_name = COALESCE($1, company_name), is_active = COALESCE($2, is_active), price_per_gallon = COALESCE($3, price_per_gallon), price_per_gallon_cash = COALESCE($4, price_per_gallon_cash), note = COALESCE($5, note), minimum_order = COALESCE($6, minimum_order), service = COALESCE($7, service), bio_percent = COALESCE($8, bio_percent), phone = COALESCE($9, phone), online_ordering = COALESCE($10, online_ordering), county_id = COALESCE($11, county_id), last_edited = CURRENT_TIMESTAMP WHERE id = $12 AND user_id = $13 RETURNING id, company_name, is_active, price_per_gallon, price_per_gallon_cash, note, minimum_order, service, bio_percent, phone, online_ordering, county_id, user_id, last_edited"
|
||||
)
|
||||
.bind(&payload.company_name)
|
||||
.bind(payload.is_active)
|
||||
.bind(payload.price_per_gallon)
|
||||
.bind(payload.price_per_gallon_cash)
|
||||
.bind(&payload.note)
|
||||
.bind(payload.minimum_order)
|
||||
.bind(payload.service)
|
||||
.bind(payload.bio_percent)
|
||||
.bind(&payload.phone)
|
||||
.bind(&payload.online_ordering)
|
||||
.bind(payload.county_id)
|
||||
.bind(listing_id)
|
||||
.bind(user.id)
|
||||
.fetch_optional(&*app_state.db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(listing)) => Ok(Json(listing)),
|
||||
Ok(None) => Err((
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(json!({"error": "Listing not found"}))
|
||||
)),
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": format!("Failed to update listing: {}", e)}))
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_listings_by_county(
|
||||
State(app_state): State<AppState>,
|
||||
Path(county_id): Path<i32>,
|
||||
) -> Result<Json<Vec<Listing>>, (StatusCode, Json<serde_json::Value>)> {
|
||||
match sqlx::query_as::<_, Listing>(
|
||||
"SELECT id, company_name, is_active, price_per_gallon, price_per_gallon_cash, note, minimum_order, service, bio_percent, phone, online_ordering, county_id, user_id, last_edited FROM listings WHERE county_id = $1 AND is_active = true ORDER BY last_edited DESC"
|
||||
)
|
||||
.bind(county_id)
|
||||
.fetch_all(&*app_state.db)
|
||||
.await
|
||||
{
|
||||
Ok(listings) => Ok(Json(listings)),
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": format!("Failed to fetch listings: {}", e)}))
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_listing(
|
||||
State(app_state): State<AppState>,
|
||||
Path(listing_id): Path<i32>,
|
||||
Extension(user): Extension<User>,
|
||||
) -> Result<Json<serde_json::Value>, (StatusCode, Json<serde_json::Value>)> {
|
||||
match sqlx::query("DELETE FROM listings WHERE id = $1 AND user_id = $2")
|
||||
.bind(listing_id)
|
||||
.bind(user.id)
|
||||
.execute(&*app_state.db)
|
||||
.await
|
||||
{
|
||||
Ok(result) => {
|
||||
if result.rows_affected() == 0 {
|
||||
Err((
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(json!({"error": "Listing not found"}))
|
||||
))
|
||||
} else {
|
||||
Ok(Json(json!({"success": true, "message": "Listing deleted"})))
|
||||
}
|
||||
}
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": format!("Failed to delete listing: {}", e)}))
|
||||
)),
|
||||
}
|
||||
}
|
||||
2
src/listing/mod.rs
Normal file
2
src/listing/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod structs;
|
||||
pub mod data;
|
||||
52
src/listing/structs.rs
Normal file
52
src/listing/structs.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Listing {
|
||||
pub id: i32,
|
||||
pub company_name: String,
|
||||
pub is_active: bool,
|
||||
pub price_per_gallon: f64,
|
||||
pub price_per_gallon_cash: Option<f64>,
|
||||
pub note: Option<String>,
|
||||
pub minimum_order: Option<i32>,
|
||||
pub service: bool,
|
||||
pub bio_percent: i32,
|
||||
pub phone: Option<String>,
|
||||
pub online_ordering: String,
|
||||
pub county_id: i32,
|
||||
pub user_id: i32,
|
||||
pub last_edited: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateListingRequest {
|
||||
pub company_name: String,
|
||||
pub is_active: bool,
|
||||
pub price_per_gallon: f64,
|
||||
pub price_per_gallon_cash: Option<f64>,
|
||||
pub note: Option<String>,
|
||||
pub minimum_order: Option<i32>,
|
||||
pub service: bool,
|
||||
pub bio_percent: i32,
|
||||
pub phone: Option<String>,
|
||||
pub online_ordering: String,
|
||||
pub county_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateListingRequest {
|
||||
pub company_name: Option<String>,
|
||||
pub is_active: Option<bool>,
|
||||
pub price_per_gallon: Option<f64>,
|
||||
pub price_per_gallon_cash: Option<f64>,
|
||||
pub note: Option<String>,
|
||||
pub minimum_order: Option<i32>,
|
||||
pub service: Option<bool>,
|
||||
pub bio_percent: Option<i32>,
|
||||
pub phone: Option<String>,
|
||||
pub online_ordering: Option<String>,
|
||||
pub county_id: Option<i32>,
|
||||
}
|
||||
Reference in New Issue
Block a user