@@ -11,10 +11,6 @@ export class DatabaseService {
1111 ) ;
1212 }
1313
14- getSupabaseClient ( ) {
15- return this . supabase ;
16- }
17-
1814 async requestLiveDemo ( email : string ) {
1915 return await this . supabase . from ( "live_demo_requests" ) . insert ( { email } ) ;
2016 }
@@ -220,171 +216,6 @@ export class DatabaseService {
220216 return await this . supabase . from ( "tenders" ) . select ( "*" ) ;
221217 }
222218
223- async getTendersPaginated ( params : {
224- offset : number ;
225- limit : number ;
226- search : string ;
227- sortBy : string ;
228- sortOrder : string ;
229- filters : {
230- status ?: string ;
231- category ?: string ;
232- region ?: string ;
233- entity ?: string ;
234- } ;
235- } ) {
236- let query = this . supabase . from ( "tenders" ) . select ( "*" , { count : "exact" } ) ;
237-
238- // Apply search filter
239- if ( params . search ) {
240- query = query . or (
241- `title.ilike.%${ params . search } %,description.ilike.%${ params . search } %,contracting_entity_name.ilike.%${ params . search } %`
242- ) ;
243- }
244-
245- // Apply status filter
246- if ( params . filters . status ) {
247- query = query . eq ( "status" , params . filters . status ) ;
248- }
249-
250- // Apply category filter
251- if ( params . filters . category ) {
252- query = query . eq ( "category_primary" , params . filters . category ) ;
253- }
254-
255- // Apply region filter
256- if ( params . filters . region ) {
257- query = query . or (
258- `contracting_entity_province.ilike.%${ params . filters . region } %,contracting_entity_city.ilike.%${ params . filters . region } %,delivery_location.ilike.%${ params . filters . region } %`
259- ) ;
260- }
261-
262- // Apply entity filter
263- if ( params . filters . entity ) {
264- query = query . ilike (
265- "contracting_entity_name" ,
266- `%${ params . filters . entity } %`
267- ) ;
268- }
269-
270- // Apply sorting
271- query = query . order ( params . sortBy , {
272- ascending : params . sortOrder === "asc" ,
273- } ) ;
274-
275- // Apply pagination
276- query = query . range ( params . offset , params . offset + params . limit - 1 ) ;
277-
278- const { data, error, count } = await query ;
279-
280- if ( error ) {
281- return { data : null , error } ;
282- }
283-
284- return {
285- data : {
286- data : data || [ ] ,
287- total : count || 0 ,
288- } ,
289- error : null ,
290- } ;
291- }
292-
293- async getTenderStatistics ( ) {
294- try {
295- // Get total counts by source
296- const { data : sourceStats , error : sourceError } = await this . supabase
297- . from ( "tenders" )
298- . select ( "source" )
299- . then ( async ( { data, error } ) => {
300- if ( error ) return { data : null , error } ;
301-
302- // Group by source and count
303- const sourceGroups =
304- data ?. reduce ( ( acc : Record < string , number > , tender ) => {
305- const source = tender . source || "unknown" ;
306- acc [ source ] = ( acc [ source ] || 0 ) + 1 ;
307- return acc ;
308- } , { } ) || { } ;
309-
310- return { data : sourceGroups , error : null } ;
311- } ) ;
312-
313- if ( sourceError ) {
314- return { data : null , error : sourceError } ;
315- }
316-
317- // Get tenders added in last 24 hours by source
318- const yesterday = new Date ( ) ;
319- yesterday . setDate ( yesterday . getDate ( ) - 1 ) ;
320-
321- const { data : recentStats , error : recentError } = await this . supabase
322- . from ( "tenders" )
323- . select ( "source, last_scraped_at" )
324- . gte ( "last_scraped_at" , yesterday . toISOString ( ) )
325- . then ( async ( { data, error } ) => {
326- if ( error ) return { data : null , error } ;
327-
328- // Group by source and count recent additions
329- const recentGroups =
330- data ?. reduce ( ( acc : Record < string , number > , tender ) => {
331- const source = tender . source || "unknown" ;
332- acc [ source ] = ( acc [ source ] || 0 ) + 1 ;
333- return acc ;
334- } , { } ) || { } ;
335-
336- return { data : recentGroups , error : null } ;
337- } ) ;
338-
339- if ( recentError ) {
340- return { data : null , error : recentError } ;
341- }
342-
343- // Map sources to display names
344- const sourceMapping : Record < string , string > = {
345- canadian : "Government of Canada" ,
346- ontario : "Ontario Province" ,
347- toronto : "City of Toronto" ,
348- quebec : "Quebec Province" ,
349- mississauga : "City of Mississauga" ,
350- brampton : "City of Brampton" ,
351- hamilton : "City of Hamilton" ,
352- london : "City of London" ,
353- unknown : "Other Sources" ,
354- } ;
355-
356- // Build statistics array from all sources that have data
357- const allSources = new Set ( [
358- ...Object . keys ( sourceStats || { } ) ,
359- ...Object . keys ( recentStats || { } ) ,
360- ] ) ;
361-
362- const statistics = Array . from ( allSources )
363- . map ( ( sourceKey ) => {
364- const displayName =
365- sourceMapping [ sourceKey ] ||
366- sourceKey . charAt ( 0 ) . toUpperCase ( ) + sourceKey . slice ( 1 ) ;
367- const totalCount = sourceStats ?. [ sourceKey ] || 0 ;
368- const recentCount = recentStats ?. [ sourceKey ] || 0 ;
369-
370- return {
371- source : displayName ,
372- numberOfTendersAddedDaily : recentCount ,
373- numberOfTendersAvailable : totalCount ,
374- } ;
375- } )
376- . filter ( ( stat ) => stat . numberOfTendersAvailable > 0 ) // Only show sources with data
377- . sort (
378- ( a , b ) => b . numberOfTendersAvailable - a . numberOfTendersAvailable
379- ) ; // Sort by total count descending
380-
381- return { data : statistics , error : null } ;
382- } catch ( error ) {
383- console . error ( "Error calculating tender statistics:" , error ) ;
384- return { data : null , error } ;
385- }
386- }
387-
388219 async getTendersForAiFiltering ( limit : number = 5 ) {
389220 return await this . supabase
390221 . from ( "tenders" )
@@ -522,56 +353,6 @@ export class DatabaseService {
522353 }
523354 }
524355
525- async changeUserPassword ( accessToken : string , currentPassword : string , newPassword : string ) {
526- try {
527- // Set the session with the access token first
528- const { data : sessionData , error : sessionError } =
529- await this . supabase . auth . setSession ( {
530- access_token : accessToken ,
531- refresh_token : "" , // Will be handled by Supabase
532- } ) ;
533-
534- if ( sessionError ) {
535- console . error ( "Error setting session for password change:" , sessionError ) ;
536- throw sessionError ;
537- }
538-
539- // Get the current user
540- const { data : user , error : userError } = await this . supabase . auth . getUser ( ) ;
541-
542- if ( userError || ! user . user ) {
543- console . error ( "Error getting user:" , userError ) ;
544- throw new Error ( "User not found or session invalid" ) ;
545- }
546-
547- // Verify current password by attempting to sign in with it
548- const { error : verifyError } = await this . supabase . auth . signInWithPassword ( {
549- email : user . user . email ! ,
550- password : currentPassword ,
551- } ) ;
552-
553- if ( verifyError ) {
554- console . error ( "Current password verification failed:" , verifyError ) ;
555- throw new Error ( "Current password is incorrect" ) ;
556- }
557-
558- // Update the password
559- const { data, error } = await this . supabase . auth . updateUser ( {
560- password : newPassword ,
561- } ) ;
562-
563- if ( error ) {
564- console . error ( "Error changing password:" , error ) ;
565- throw error ;
566- }
567-
568- return data ;
569- } catch ( error ) {
570- console . error ( "Failed to change password:" , error ) ;
571- throw error ;
572- }
573- }
574-
575356 // Profile methods
576357 async createOrUpdateProfile (
577358 profileData :
0 commit comments