@@ -247,6 +247,46 @@ class OpenRocketsAPI {
247247 return await this . request ( `/api/search?${ params } ` ) ;
248248 }
249249
250+ // Community API Methods
251+ async getPosts ( filters = { } ) {
252+ const params = new URLSearchParams ( filters ) ;
253+ return await this . request ( `/api/community/posts?${ params } ` ) ;
254+ }
255+
256+ async createPost ( postData , imageFiles = [ ] ) {
257+ const formData = new FormData ( ) ;
258+ formData . append ( 'data' , JSON . stringify ( postData ) ) ;
259+
260+ imageFiles . forEach ( ( file , index ) => {
261+ formData . append ( `image_${ index } ` , file ) ;
262+ } ) ;
263+
264+ return await this . request ( '/api/community/posts' , {
265+ method : 'POST' ,
266+ body : formData ,
267+ headers : {
268+ 'Authorization' : this . token ? `Bearer ${ this . token } ` : ''
269+ }
270+ } ) ;
271+ }
272+
273+ async likePost ( postId ) {
274+ return await this . request ( `/api/community/posts/${ postId } /like` , {
275+ method : 'POST'
276+ } ) ;
277+ }
278+
279+ async getComments ( postId ) {
280+ return await this . request ( `/api/community/posts/${ postId } /comments` ) ;
281+ }
282+
283+ async addComment ( postId , content ) {
284+ return await this . request ( `/api/community/posts/${ postId } /comments` , {
285+ method : 'POST' ,
286+ body : JSON . stringify ( { content } )
287+ } ) ;
288+ }
289+
250290 // Utility methods
251291 isAuthenticated ( ) {
252292 return ! ! this . token && ! ! this . currentUser ;
@@ -298,6 +338,97 @@ class OpenRocketsAPI {
298338 return emailRegex . test ( email ) ;
299339 }
300340
341+ showNotification ( message , type = 'info' ) {
342+ console . log ( `[${ type . toUpperCase ( ) } ] ${ message } ` ) ;
343+
344+ // Create notification element
345+ const notification = document . createElement ( 'div' ) ;
346+ notification . className = `notification notification-${ type } ` ;
347+ notification . innerHTML = `
348+ <div class="notification-content">
349+ <i class="fas fa-${ type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle' } "></i>
350+ <span>${ message } </span>
351+ </div>
352+ <button class="notification-close" onclick="this.parentElement.remove()">
353+ <i class="fas fa-times"></i>
354+ </button>
355+ ` ;
356+
357+ // Style the notification
358+ Object . assign ( notification . style , {
359+ position : 'fixed' ,
360+ top : '20px' ,
361+ right : '20px' ,
362+ padding : '12px 16px' ,
363+ backgroundColor : this . getNotificationColor ( type ) ,
364+ color : 'white' ,
365+ borderRadius : '8px' ,
366+ boxShadow : '0 4px 12px rgba(0,0,0,0.15)' ,
367+ zIndex : '10000' ,
368+ display : 'flex' ,
369+ alignItems : 'center' ,
370+ gap : '12px' ,
371+ maxWidth : '400px' ,
372+ animation : 'slideInRight 0.3s ease' ,
373+ fontFamily : 'Inter, sans-serif'
374+ } ) ;
375+
376+ document . body . appendChild ( notification ) ;
377+
378+ // Auto-remove after 4 seconds
379+ setTimeout ( ( ) => {
380+ if ( notification . parentNode ) {
381+ notification . style . animation = 'slideOutRight 0.3s ease' ;
382+ setTimeout ( ( ) => notification . remove ( ) , 300 ) ;
383+ }
384+ } , 4000 ) ;
385+ }
386+
387+ getNotificationColor ( type ) {
388+ switch ( type ) {
389+ case 'success' : return '#10b981' ;
390+ case 'error' : return '#ef4444' ;
391+ case 'warning' : return '#f59e0b' ;
392+ default : return '#3b82f6' ;
393+ }
394+ }
395+
396+ showLoadingSpinner ( element , show = true ) {
397+ if ( ! element ) return ;
398+
399+ if ( show ) {
400+ element . style . position = 'relative' ;
401+ element . style . opacity = '0.6' ;
402+
403+ const spinner = document . createElement ( 'div' ) ;
404+ spinner . className = 'loading-spinner-overlay' ;
405+ spinner . innerHTML = `
406+ <div class="loading-spinner">
407+ <i class="fas fa-spinner fa-spin"></i>
408+ </div>
409+ ` ;
410+
411+ Object . assign ( spinner . style , {
412+ position : 'absolute' ,
413+ top : '0' ,
414+ left : '0' ,
415+ right : '0' ,
416+ bottom : '0' ,
417+ display : 'flex' ,
418+ alignItems : 'center' ,
419+ justifyContent : 'center' ,
420+ backgroundColor : 'rgba(255,255,255,0.8)' ,
421+ zIndex : '100'
422+ } ) ;
423+
424+ element . appendChild ( spinner ) ;
425+ } else {
426+ element . style . opacity = '' ;
427+ const spinner = element . querySelector ( '.loading-spinner-overlay' ) ;
428+ if ( spinner ) spinner . remove ( ) ;
429+ }
430+ }
431+
301432 validatePassword ( password ) {
302433 return password . length >= 6 ;
303434 }
0 commit comments