1- "use client"
2- import { useState , useEffect , useRef } from "react"
3- import { RefreshCw , AlertTriangle , Loader2 , Lock } from "lucide-react"
4- import type { ApiResponse , SizeAvailability } from "@/types"
1+ "use client" ;
2+ import { useState , useEffect , useRef } from "react" ;
3+ import { RefreshCw , AlertTriangle , Loader2 , Lock } from "lucide-react" ;
4+ import type { ApiResponse , SizeAvailability } from "@/types" ;
55
66// API endpoint URL - use our proxy API route
7- const API_URL = "/api/tshirt-data"
7+ const API_URL =
8+ "https://script.google.com/macros/s/AKfycbz8j2mEAYbOlsLHEIFwunY1ygxR0xqJeL0EPHdUYUpG_RcJYtXKe4_MyaOyBhB5LJFE/exec" ;
89
910// Constants for determining stock status
10- const LOW_STOCK_THRESHOLD = 5
11- const FETCH_INTERVAL = 25000 // 25 Seconds
11+ const LOW_STOCK_THRESHOLD = 5 ;
12+ const FETCH_INTERVAL = 25000 ; // 25 Seconds
1213
1314// Set this to true to enable fetching by default, false to disable
14- const ENABLE_FETCHING = true
15+ const ENABLE_FETCHING = true ;
1516
1617export default function TShirtAvailability ( ) {
17- const [ sizes , setSizes ] = useState < SizeAvailability [ ] > ( [ ] )
18- const [ lastUpdated , setLastUpdated ] = useState < string > ( "Never" )
19- const [ isLoading , setIsLoading ] = useState < boolean > ( true )
20- const [ error , setError ] = useState < string | null > ( null )
21- const [ fetchCount , setFetchCount ] = useState ( 0 ) // Add this to force re-renders
22- const [ isMockData , setIsMockData ] = useState ( false )
23- const intervalRef = useRef < NodeJS . Timeout | null > ( null )
18+ const [ sizes , setSizes ] = useState < SizeAvailability [ ] > ( [ ] ) ;
19+ const [ lastUpdated , setLastUpdated ] = useState < string > ( "Never" ) ;
20+ const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
21+ const [ error , setError ] = useState < string | null > ( null ) ;
22+ const [ fetchCount , setFetchCount ] = useState ( 0 ) ; // Add this to force re-renders
23+ const [ isMockData , setIsMockData ] = useState ( false ) ;
24+ const intervalRef = useRef < NodeJS . Timeout | null > ( null ) ;
2425
2526 // Function to transform API response to our format
2627 const transformApiData = ( data : ApiResponse ) : SizeAvailability [ ] => {
2728 return Object . entries ( data )
2829 . map ( ( [ size , sizeData ] ) => {
2930 // Determine status based on availability
30- let status : "available" | "low" | "out" = "out"
31+ let status : "available" | "low" | "out" = "out" ;
3132 if ( sizeData . availability > 0 ) {
32- status = sizeData . availability <= LOW_STOCK_THRESHOLD ? "low" : "available"
33+ status =
34+ sizeData . availability <= LOW_STOCK_THRESHOLD ? "low" : "available" ;
3335 }
3436
3537 return {
3638 size,
3739 total : sizeData . stockReceived ,
3840 available : sizeData . availability ,
3941 status,
40- }
42+ } ;
4143 } )
4244 . sort ( ( a , b ) => {
4345 // Custom sort order for sizes
44- const sizeOrder = [ "3XS" , "2XS" , "XS" , "S" , "M" , "L" , "XL" , "2XL" , "3XL" ]
45- return sizeOrder . indexOf ( a . size ) - sizeOrder . indexOf ( b . size )
46- } )
47- }
46+ const sizeOrder = [
47+ "3XS" ,
48+ "2XS" ,
49+ "XS" ,
50+ "S" ,
51+ "M" ,
52+ "L" ,
53+ "XL" ,
54+ "2XL" ,
55+ "3XL" ,
56+ ] ;
57+ return sizeOrder . indexOf ( a . size ) - sizeOrder . indexOf ( b . size ) ;
58+ } ) ;
59+ } ;
4860
4961 // Function to fetch data from API
5062 const fetchData = async ( ) => {
5163 try {
52- setIsLoading ( true )
53- setError ( null )
54- setIsMockData ( false )
64+ setIsLoading ( true ) ;
65+ setError ( null ) ;
66+ setIsMockData ( false ) ;
5567
56- console . log ( "Fetching data from:" , API_URL ) // Debug log
68+ console . log ( "Fetching data from:" , API_URL ) ; // Debug log
5769
58- // Fetch from our server-side API route
70+ // Fetch directly from the external API for real-time data
5971 const response = await fetch ( API_URL , {
6072 method : "GET" ,
6173 headers : {
6274 "Content-Type" : "application/json" ,
6375 } ,
6476 cache : "no-store" ,
65- } )
77+ } ) ;
6678
67- console . log ( "Response status:" , response . status ) // Debug log
79+ console . log ( "Response status:" , response . status ) ; // Debug log
6880
6981 if ( ! response . ok ) {
70- throw new Error ( `HTTP error! Status: ${ response . status } ` )
82+ throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
7183 }
7284
7385 // Check if we're getting mock data
74- const isMock = response . headers . get ( "X-Mock-Data" ) === "true"
75- setIsMockData ( isMock )
86+ const isMock = response . headers . get ( "X-Mock-Data" ) === "true" ;
87+ setIsMockData ( isMock ) ;
7688
7789 if ( isMock ) {
78- const errorMessage = response . headers . get ( "X-Error-Message" )
79- console . warn ( "Using mock data:" , errorMessage )
80- setError ( `Using demo data: ${ errorMessage || "API unavailable" } ` )
90+ const errorMessage = response . headers . get ( "X-Error-Message" ) ;
91+ console . warn ( "Using mock data:" , errorMessage ) ;
92+ setError ( `Using demo data: ${ errorMessage || "API unavailable" } ` ) ;
8193 }
8294
83- const data = await response . json ( )
84- console . log ( "Received data:" , data ) // Debug log
95+ const data = await response . json ( ) ;
96+ console . log ( "Received data:" , data ) ; // Debug log
8597
8698 // Check if data is empty or has the expected structure
8799 if ( ! data || Object . keys ( data ) . length === 0 ) {
88- throw new Error ( "No data available" )
100+ throw new Error ( "No data available" ) ;
89101 }
90102
91103 // Check if we got an authentication error
92104 if ( data . error && data . error . includes ( "authentication" ) ) {
93- throw new Error ( "Authentication required. Please sign in to access the data." )
105+ throw new Error (
106+ "Authentication required. Please sign in to access the data."
107+ ) ;
94108 }
95109
96- const transformedData = transformApiData ( data )
97- setSizes ( transformedData )
98- setLastUpdated ( new Date ( ) . toLocaleString ( ) + ( isMock ? " (demo data)" : "" ) )
99- setFetchCount ( ( prev ) => prev + 1 ) // Increment to force re-render
110+ const transformedData = transformApiData ( data ) ;
111+ setSizes ( transformedData ) ;
112+ setLastUpdated (
113+ new Date ( ) . toLocaleString ( ) + ( isMock ? " (demo data)" : "" )
114+ ) ;
115+ setFetchCount ( ( prev ) => prev + 1 ) ; // Increment to force re-render
100116 } catch ( err : any ) {
101- console . error ( "Error fetching data:" , err )
117+ console . error ( "Error fetching data:" , err ) ;
102118
103119 // Use mock data for demonstration
104120 const mockData : ApiResponse = {
@@ -111,33 +127,36 @@ export default function TShirtAvailability() {
111127 XL : { distributed : 40 , stockReceived : 50 , availability : 10 } ,
112128 "2XL" : { distributed : 20 , stockReceived : 30 , availability : 10 } ,
113129 "3XL" : { distributed : 5 , stockReceived : 10 , availability : 5 } ,
114- }
130+ } ;
115131
116- setSizes ( transformApiData ( mockData ) )
117- setLastUpdated ( new Date ( ) . toLocaleString ( ) + " (demo data)" )
118- setIsMockData ( true )
132+ setSizes ( transformApiData ( mockData ) ) ;
133+ setLastUpdated ( new Date ( ) . toLocaleString ( ) + " (demo data)" ) ;
134+ setIsMockData ( true ) ;
119135
120136 // Set error message
121137 if ( err . message . includes ( "authentication" ) ) {
122- setError ( "Authentication required. Please sign in to access the data." )
138+ setError ( "Authentication required. Please sign in to access the data." ) ;
123139 } else if ( err . message . includes ( "No data available" ) ) {
124- setError ( "No T-shirt data available. Please check back later." )
125- } else if ( err . name === "TypeError" && err . message . includes ( "Failed to fetch" ) ) {
126- setError ( "Network error. Please check your connection and try again." )
140+ setError ( "No T-shirt data available. Please check back later." ) ;
141+ } else if (
142+ err . name === "TypeError" &&
143+ err . message . includes ( "Failed to fetch" )
144+ ) {
145+ setError ( "Network error. Please check your connection and try again." ) ;
127146 } else {
128- setError ( `Using demo data: ${ err . message } ` )
147+ setError ( `Using demo data: ${ err . message } ` ) ;
129148 }
130149 } finally {
131- setIsLoading ( false )
150+ setIsLoading ( false ) ;
132151 }
133- }
152+ } ;
134153
135154 // Set up auto-fetch interval
136155 useEffect ( ( ) => {
137156 // Only set up the interval if ENABLE_FETCHING is true
138157 if ( ENABLE_FETCHING ) {
139- fetchData ( ) // Fetch immediately when component mounts
140- intervalRef . current = setInterval ( fetchData , FETCH_INTERVAL )
158+ fetchData ( ) ; // Fetch immediately when component mounts
159+ intervalRef . current = setInterval ( fetchData , FETCH_INTERVAL ) ;
141160 } else {
142161 // If fetching is disabled, use mock data
143162 const mockData : ApiResponse = {
@@ -150,24 +169,27 @@ export default function TShirtAvailability() {
150169 XL : { distributed : 40 , stockReceived : 50 , availability : 10 } ,
151170 "2XL" : { distributed : 20 , stockReceived : 30 , availability : 10 } ,
152171 "3XL" : { distributed : 5 , stockReceived : 10 , availability : 5 } ,
153- }
172+ } ;
154173
155- setSizes ( transformApiData ( mockData ) )
156- setLastUpdated ( "Using demo data (fetching disabled)" )
157- setIsLoading ( false )
158- setIsMockData ( true )
174+ setSizes ( transformApiData ( mockData ) ) ;
175+ setLastUpdated ( "Using demo data (fetching disabled)" ) ;
176+ setIsLoading ( false ) ;
177+ setIsMockData ( true ) ;
159178 }
160179
161180 // Clean up the interval when the component unmounts
162181 return ( ) => {
163182 if ( intervalRef . current ) {
164- clearInterval ( intervalRef . current )
183+ clearInterval ( intervalRef . current ) ;
165184 }
166- }
167- } , [ ] )
185+ } ;
186+ } , [ ] ) ;
168187
169188 return (
170- < section className = "py-12 bg-gradient-to-b from-black to-black/95" id = "availability" >
189+ < section
190+ className = "py-12 bg-gradient-to-b from-black to-black/95"
191+ id = "availability"
192+ >
171193 < div className = "container mx-auto px-4" >
172194 < div className = "max-w-4xl mx-auto" >
173195 < div className = "mb-10 text-center" >
@@ -188,7 +210,9 @@ export default function TShirtAvailability() {
188210 ) : (
189211 < RefreshCw className = "w-4 h-4 text-white/40 mr-2" />
190212 ) }
191- < span className = "text-sm text-white/60" > Last updated: { lastUpdated } </ span >
213+ < span className = "text-sm text-white/60" >
214+ Last updated: { lastUpdated }
215+ </ span >
192216 </ div >
193217
194218 { /* Error message */ }
@@ -201,7 +225,9 @@ export default function TShirtAvailability() {
201225 < AlertTriangle className = "w-5 h-5 text-yellow-500" />
202226 ) }
203227 < span className = "font-medium text-yellow-500" >
204- { error . includes ( "Authentication" ) ? "Authentication Required" : "Using Demo Data" }
228+ { error . includes ( "Authentication" )
229+ ? "Authentication Required"
230+ : "Using Demo Data" }
205231 </ span >
206232 </ div >
207233 < p className = "text-white/70" > { error } </ p >
@@ -220,7 +246,10 @@ export default function TShirtAvailability() {
220246 { sizes . length > 0 && (
221247 < div className = "grid grid-cols-2 md:grid-cols-3 gap-6 mt-6" >
222248 { sizes . map ( ( sizeItem ) => (
223- < SizeCard key = { `${ sizeItem . size } -${ fetchCount } ` } sizeItem = { sizeItem } />
249+ < SizeCard
250+ key = { `${ sizeItem . size } -${ fetchCount } ` }
251+ sizeItem = { sizeItem }
252+ />
224253 ) ) }
225254 </ div >
226255 ) }
@@ -231,7 +260,8 @@ export default function TShirtAvailability() {
231260 < AlertTriangle className = "w-10 h-10 text-yellow-500 mx-auto mb-4" />
232261 < h3 className = "text-xl font-medium mb-2" > No Data Available</ h3 >
233262 < p className = "text-white/60" >
234- We couldn't find any T-shirt availability data. Please check back later.
263+ We couldn't find any T-shirt availability data. Please check
264+ back later.
235265 </ p >
236266 </ div >
237267 ) }
@@ -267,36 +297,53 @@ export default function TShirtAvailability() {
267297 </ div >
268298 </ div >
269299 </ section >
270- )
300+ ) ;
271301}
272302
273303type SizeCardProps = {
274- sizeItem : SizeAvailability
275- }
304+ sizeItem : SizeAvailability ;
305+ } ;
276306
277307function SizeCard ( { sizeItem } : SizeCardProps ) {
278- const percentage = Math . round ( ( sizeItem . available / sizeItem . total ) * 100 ) || 0
308+ const percentage =
309+ Math . round ( ( sizeItem . available / sizeItem . total ) * 100 ) || 0 ;
279310
280311 // Determine status color
281312 const statusColor =
282- sizeItem . status === "available" ? "bg-green-500" : sizeItem . status === "low" ? "bg-yellow-500" : "bg-red-500"
313+ sizeItem . status === "available"
314+ ? "bg-green-500"
315+ : sizeItem . status === "low"
316+ ? "bg-yellow-500"
317+ : "bg-red-500" ;
283318
284319 const textColor =
285- sizeItem . status === "available" ? "text-green-500" : sizeItem . status === "low" ? "text-yellow-500" : "text-red-500"
320+ sizeItem . status === "available"
321+ ? "text-green-500"
322+ : sizeItem . status === "low"
323+ ? "text-yellow-500"
324+ : "text-red-500" ;
286325
287326 const borderColor =
288327 sizeItem . status === "available"
289328 ? "border-green-500/30"
290329 : sizeItem . status === "low"
291- ? "border-yellow-500/30"
292- : "border-red-500/30"
330+ ? "border-yellow-500/30"
331+ : "border-red-500/30" ;
293332
294333 const bgColor =
295- sizeItem . status === "available" ? "bg-green-500/5" : sizeItem . status === "low" ? "bg-yellow-500/5" : "bg-red-500/5"
334+ sizeItem . status === "available"
335+ ? "bg-green-500/5"
336+ : sizeItem . status === "low"
337+ ? "bg-yellow-500/5"
338+ : "bg-red-500/5" ;
296339
297340 // Determine status text
298341 const statusText =
299- sizeItem . status === "available" ? "In Stock" : sizeItem . status === "low" ? "Low Stock" : "Out of Stock"
342+ sizeItem . status === "available"
343+ ? "In Stock"
344+ : sizeItem . status === "low"
345+ ? "Low Stock"
346+ : "Out of Stock" ;
300347
301348 return (
302349 < div className = { `p-5 rounded-lg border ${ borderColor } ${ bgColor } ` } >
@@ -306,7 +353,9 @@ function SizeCard({ sizeItem }: SizeCardProps) {
306353 </ div >
307354
308355 { /* Status text */ }
309- < div className = { `text-sm font-medium mb-3 ${ textColor } ` } > { statusText } </ div >
356+ < div className = { `text-sm font-medium mb-3 ${ textColor } ` } >
357+ { statusText }
358+ </ div >
310359
311360 { /* Progress bar */ }
312361 < div className = "h-3 bg-white/10 rounded-full overflow-hidden" >
@@ -318,5 +367,5 @@ function SizeCard({ sizeItem }: SizeCardProps) {
318367 > </ div >
319368 </ div >
320369 </ div >
321- )
370+ ) ;
322371}
0 commit comments