diff --git a/src/lib/api/client.ts b/src/lib/api/client.ts index 7cdef85..7480d13 100644 --- a/src/lib/api/client.ts +++ b/src/lib/api/client.ts @@ -12,6 +12,7 @@ import type { Listing, CreateListingRequest, UpdateListingRequest, + OilPrice, County, ServiceCategory } from './types'; @@ -265,6 +266,18 @@ export const listingsApi = { } }; +/** + * Oil Prices API methods (scraped market data, public) + */ +export const oilPricesApi = { + /** + * Get oil prices for a county (public) + */ + async getByCounty(countyId: number): Promise> { + return request(`/oil-prices/county/${countyId}`); + } +}; + /** * State/County API methods */ @@ -304,6 +317,7 @@ export const api = { company: companyApi, listing: listingApi, listings: listingsApi, + oilPrices: oilPricesApi, state: stateApi, categories: categoriesApi }; diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 2e2c872..710342a 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -1,3 +1,3 @@ // API Client exports -export { api, authApi, companyApi, listingApi, listingsApi, stateApi, categoriesApi } from './client'; +export { api, authApi, companyApi, listingApi, listingsApi, oilPricesApi, stateApi, categoriesApi } from './client'; export * from './types'; diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts index 57a9265..20cb421 100644 --- a/src/lib/api/types.ts +++ b/src/lib/api/types.ts @@ -99,6 +99,18 @@ export interface CreateListingRequest { export type UpdateListingRequest = Partial; +// Oil Price Types (scraped market data) +export interface OilPrice { + id: number; + state: string | null; + zone: number | null; + name: string | null; + price: number | null; + date: string | null; + scrapetimestamp: string | null; + county_id: number | null; +} + // State/County Types export interface County { id: number; diff --git a/src/routes/(app)/[stateSlug]/[countySlug]/+page.svelte b/src/routes/(app)/[stateSlug]/[countySlug]/+page.svelte index a5e6df7..38bc59c 100644 --- a/src/routes/(app)/[stateSlug]/[countySlug]/+page.svelte +++ b/src/routes/(app)/[stateSlug]/[countySlug]/+page.svelte @@ -4,11 +4,12 @@ import { onMount } from 'svelte'; import { newEnglandStates } from '../../../../lib/states'; import { api } from '$lib/api'; - import type { Listing, County } from '$lib/api'; + import type { Listing, OilPrice, County } from '$lib/api'; const { stateSlug, countySlug } = $page.params as { stateSlug: string; countySlug: string }; let countyData: County | null = null; let listings: Listing[] = []; + let oilPrices: OilPrice[] = []; let loading = true; let listingsLoading = false; let error: string | null = null; @@ -18,34 +19,40 @@ onMount(async () => { const result = await api.state.getCounty(stateSlug, countySlug); - + if (result.error) { error = result.error; countyData = null; } else { countyData = result.data; - // Fetch listings for this county - await fetchListings(); + // Fetch both listings and oil prices in parallel + await fetchAllPrices(); } - + loading = false; }); - async function fetchListings() { + async function fetchAllPrices() { if (!countyData) return; listingsLoading = true; listingsError = null; - - const result = await api.listings.getByCounty(countyData.id); - - if (result.error) { + + const [listingsResult, oilPricesResult] = await Promise.all([ + api.listings.getByCounty(countyData.id), + api.oilPrices.getByCounty(countyData.id) + ]); + + if (listingsResult.error) { listingsError = 'Failed to load listings'; } else { - listings = result.data || []; + listings = listingsResult.data || []; sortListings(); } - + + // Oil prices failure is non-critical - just show empty + oilPrices = oilPricesResult.data || []; + listingsLoading = false; } @@ -113,114 +120,175 @@ Back to {getStateName(countyData.state)} - -
- {#if listingsLoading} -
- -
- {:else if listingsError} -
- {listingsError} -
- {:else if listings.length === 0} -
-

No active listings found for this county.

-
- {:else} - -
- - -
+ {#if listingsLoading} +
+ +
+ {:else if listingsError} +
+ {listingsError} +
+ {:else} + +
+

Premium Dealers

- - + + +
+ {#each listings as listing} +
+
+

+ {listing.company_name} + {#if listing.town} +
{listing.town} + {/if} +

+
+
+ Card Price:
+ ${listing.price_per_gallon.toFixed(2)} +
+
+ Cash Price:
+ {listing.price_per_gallon_cash ? `$${listing.price_per_gallon_cash.toFixed(2)}` : 'N/A'} +
+
+ Bio %:
+ {listing.bio_percent}% +
+
+ Service:
+ {#if listing.service} + Yes + {:else} + No + {/if} +
+
+ Contact:
+ {#if listing.phone} + {listing.phone}
+ {:else} + N/A
+ {/if} + + Online: {#if listing.online_ordering === 'none'} No {:else if listing.online_ordering === 'online_only'} @@ -228,77 +296,73 @@ {:else if listing.online_ordering === 'both'} Both {/if} -
+ +
+
+ Last Updated:
+ {listing.last_edited ? new Date(listing.last_edited).toLocaleDateString() : 'N/A'}
- - {listing.last_edited ? new Date(listing.last_edited).toLocaleDateString() : 'N/A'} - - {/each} - - -
- - -
- {#each listings as listing} -
-
-

- {listing.company_name} - {#if listing.town} -
{listing.town} - {/if} -

-
-
- Card Price:
- ${listing.price_per_gallon.toFixed(2)} -
-
- Cash Price:
- {listing.price_per_gallon_cash ? `$${listing.price_per_gallon_cash.toFixed(2)}` : 'N/A'} -
-
- Bio %:
- {listing.bio_percent}% -
-
- Service:
- {#if listing.service} - Yes - {:else} - No - {/if} -
-
- Contact:
- {#if listing.phone} - {listing.phone}
- {:else} - N/A
- {/if} - - Online: - {#if listing.online_ordering === 'none'} - No - {:else if listing.online_ordering === 'online_only'} - Online Only - {:else if listing.online_ordering === 'both'} - Both - {/if} - -
-
- Last Updated:
- {listing.last_edited ? new Date(listing.last_edited).toLocaleDateString() : 'N/A'}
-
- {/each} + {/each} +
+ {/if} +
+ + + {#if oilPrices.length > 0} +
+

Market Prices

+ + + + + +
+ {#each oilPrices as op} +
+
+
+ {op.name || 'Unknown'} + {op.price != null ? `$${op.price.toFixed(2)}` : 'N/A'} +
+
+ Posted: {op.date || 'N/A'} +
+
+
+ {/each} +
{/if} -
+ + + {#if listings.length === 0 && oilPrices.length === 0} +
+

No pricing data available for this county yet.

+
+ {/if} + {/if} {:else if error}

County Not Found