index.js
481 lines
| 1 | import { useState } from 'react'; |
| 2 | import { Card, Label, Notice, Spinner, Pagination, Select, Table, Button, PeriodSelector, Modal } from '@givewp/components'; |
| 3 | import API, { useLogFetcher, getEndpoint } from './api'; |
| 4 | |
| 5 | import styles from './styles.module.scss'; |
| 6 | |
| 7 | const { __ } = wp.i18n; |
| 8 | |
| 9 | const Logs = () => { |
| 10 | const [ state, setState ] = useState( { |
| 11 | initialLoad: false, |
| 12 | currentPage: 1, |
| 13 | currentStatus: '', // log type |
| 14 | currentSource: '', |
| 15 | currentCategory: '', |
| 16 | sortColumn: '', |
| 17 | sortDirection: '', |
| 18 | startDate: null, |
| 19 | endDate: null, |
| 20 | pages: 0, |
| 21 | statuses: [], |
| 22 | sources: [], |
| 23 | categories: [], |
| 24 | isSorting: false, |
| 25 | } ); |
| 26 | |
| 27 | const [ logModal, setLogModal ] = useState( { |
| 28 | visible: false, |
| 29 | } ); |
| 30 | |
| 31 | const [ logFlushModal, setLogFlushModal ] = useState( { |
| 32 | visible: false, |
| 33 | } ); |
| 34 | |
| 35 | const parameters = { |
| 36 | page: state.currentPage, |
| 37 | sort: state.sortColumn, |
| 38 | direction: state.sortDirection, |
| 39 | type: state.currentStatus, |
| 40 | source: state.currentSource, |
| 41 | category: state.currentCategory, |
| 42 | start: state.startDate ? state.startDate.format( 'YYYY-MM-DD' ) : '', |
| 43 | end: state.endDate ? state.endDate.format( 'YYYY-MM-DD' ) : '', |
| 44 | }; |
| 45 | |
| 46 | const { data, isLoading, isError } = useLogFetcher( getEndpoint( '/get-logs', parameters ), { |
| 47 | onSuccess: ( { response } ) => { |
| 48 | setState( ( previousState ) => { |
| 49 | return { |
| 50 | ...previousState, |
| 51 | initialLoad: true, |
| 52 | pages: response.pages, |
| 53 | statuses: response.statuses, |
| 54 | categories: response.categories, |
| 55 | sources: response.sources, |
| 56 | currentPage: state.currentPage > response.pages ? 1 : state.currentPage, |
| 57 | isSorting: false, |
| 58 | }; |
| 59 | } ); |
| 60 | }, |
| 61 | } ); |
| 62 | |
| 63 | const openLogModal = ( log ) => { |
| 64 | setLogModal( { |
| 65 | visible: true, |
| 66 | id: log.id, |
| 67 | type: log.log_type, |
| 68 | category: log.category, |
| 69 | source: log.source, |
| 70 | description: log.description, |
| 71 | date: log.date, |
| 72 | message: log.message, |
| 73 | context: log.context, |
| 74 | } ); |
| 75 | }; |
| 76 | |
| 77 | const closeLogModal = () => { |
| 78 | setLogModal( { visible: false } ); |
| 79 | }; |
| 80 | |
| 81 | const openLogFlushModal = ( e ) => { |
| 82 | e.preventDefault(); |
| 83 | setLogFlushModal( { visible: true } ); |
| 84 | }; |
| 85 | |
| 86 | const closeLogFlushModal = () => { |
| 87 | setLogFlushModal( { visible: false } ); |
| 88 | }; |
| 89 | |
| 90 | const flushLogs = () => { |
| 91 | setLogFlushModal( { |
| 92 | visible: true, |
| 93 | flushing: true, |
| 94 | } ); |
| 95 | |
| 96 | API.delete( '/flush-logs' ) |
| 97 | .then( () => { |
| 98 | window.location.reload(); |
| 99 | } ) |
| 100 | .catch( () => { |
| 101 | setLogFlushModal( ( previousState ) => { |
| 102 | return { |
| 103 | ...previousState, |
| 104 | type: 'error', |
| 105 | error: true, |
| 106 | }; |
| 107 | } ); |
| 108 | } ); |
| 109 | }; |
| 110 | |
| 111 | const setSortDirectionForColumn = ( column, direction ) => { |
| 112 | setState( ( previousState ) => { |
| 113 | return { |
| 114 | ...previousState, |
| 115 | sortColumn: column, |
| 116 | sortDirection: direction, |
| 117 | isSorting: true, |
| 118 | }; |
| 119 | } ); |
| 120 | }; |
| 121 | |
| 122 | const setCurrentPage = ( currentPage ) => { |
| 123 | setState( ( previousState ) => { |
| 124 | return { |
| 125 | ...previousState, |
| 126 | currentPage, |
| 127 | }; |
| 128 | } ); |
| 129 | }; |
| 130 | |
| 131 | const setCurrentCategory = ( e ) => { |
| 132 | const category = e.target.value; |
| 133 | setState( ( previousState ) => { |
| 134 | return { |
| 135 | ...previousState, |
| 136 | currentCategory: category, |
| 137 | }; |
| 138 | } ); |
| 139 | }; |
| 140 | |
| 141 | const setCurrentStatus = ( e ) => { |
| 142 | const status = e.target.value; |
| 143 | setState( ( previousState ) => { |
| 144 | return { |
| 145 | ...previousState, |
| 146 | currentStatus: status, |
| 147 | }; |
| 148 | } ); |
| 149 | }; |
| 150 | |
| 151 | const setCurrentSource = ( e ) => { |
| 152 | const source = e.target.value; |
| 153 | setState( ( previousState ) => { |
| 154 | return { |
| 155 | ...previousState, |
| 156 | currentSource: source, |
| 157 | }; |
| 158 | } ); |
| 159 | }; |
| 160 | |
| 161 | const setDates = ( startDate, endDate ) => { |
| 162 | setState( ( previousState ) => { |
| 163 | return { |
| 164 | ...previousState, |
| 165 | startDate, |
| 166 | endDate, |
| 167 | }; |
| 168 | } ); |
| 169 | }; |
| 170 | |
| 171 | const getCategories = () => { |
| 172 | const defaultCategory = { |
| 173 | value: '', |
| 174 | label: __( 'All categories', 'give' ), |
| 175 | }; |
| 176 | |
| 177 | const categories = Object.values( state.categories ).map( ( label ) => { |
| 178 | return { |
| 179 | label, |
| 180 | value: label, |
| 181 | }; |
| 182 | } ); |
| 183 | |
| 184 | return [ defaultCategory, ...categories ]; |
| 185 | }; |
| 186 | |
| 187 | const getStatuses = () => { |
| 188 | const defaultStatus = { |
| 189 | value: '', |
| 190 | label: __( 'All statuses', 'give' ), |
| 191 | }; |
| 192 | |
| 193 | const statuses = Object.entries( state.statuses ).map( ( [ value, label ] ) => { |
| 194 | return { |
| 195 | label, |
| 196 | value, |
| 197 | }; |
| 198 | } ); |
| 199 | |
| 200 | return [ defaultStatus, ...statuses ]; |
| 201 | }; |
| 202 | |
| 203 | const getSources = () => { |
| 204 | const defaultSource = { |
| 205 | value: '', |
| 206 | label: __( 'All sources', 'give' ), |
| 207 | }; |
| 208 | |
| 209 | const sources = Object.values( state.sources ).map( ( label ) => { |
| 210 | return { |
| 211 | label, |
| 212 | value: label, |
| 213 | }; |
| 214 | } ); |
| 215 | |
| 216 | return [ defaultSource, ...sources ]; |
| 217 | }; |
| 218 | |
| 219 | const getLogModal = () => { |
| 220 | return ( |
| 221 | <Modal visible={ logModal.visible } type={ logModal.type } handleClose={ closeLogModal } data-givewp-test="log-modal"> |
| 222 | <Modal.Title> |
| 223 | <Label type={ logModal.type } text={ getLogTypeText( logModal.type ) } /> |
| 224 | |
| 225 | <strong style={ { marginLeft: 20 } }> |
| 226 | { __( 'Log ID', 'give' ) }: { logModal.id } |
| 227 | </strong> |
| 228 | |
| 229 | <Modal.CloseIcon onClick={ closeLogModal } data-givewp-test="log-modal-close" /> |
| 230 | </Modal.Title> |
| 231 | |
| 232 | <Modal.Section title={ __( 'Description', 'give' ) } content={ logModal.message } /> |
| 233 | <Modal.Section title={ __( 'Category', 'give' ) } content={ logModal.category } /> |
| 234 | <Modal.Section title={ __( 'Source', 'give' ) } content={ logModal.source } /> |
| 235 | <Modal.Section title={ __( 'Date & Time', 'give' ) } content={ logModal.date } /> |
| 236 | |
| 237 | <Modal.AdditionalContext type={ logModal.type } context={ logModal.context } /> |
| 238 | </Modal> |
| 239 | ); |
| 240 | }; |
| 241 | |
| 242 | const getLogFlushConfirmationModal = () => { |
| 243 | return ( |
| 244 | <Modal visible={ logFlushModal.visible } type={ logFlushModal.type } handleClose={ closeLogFlushModal }> |
| 245 | { logFlushModal.flushing ? ( |
| 246 | <Modal.Content align="center"> |
| 247 | { logFlushModal.error ? ( |
| 248 | <> |
| 249 | <h2>{ __( 'Something went wrong!', 'give' ) }</h2> |
| 250 | <div> |
| 251 | Try to <a onClick={ () => window.location.reload() } href="#">reload</a> the browser |
| 252 | </div> |
| 253 | </> |
| 254 | ) : ( |
| 255 | <> |
| 256 | <Spinner /> |
| 257 | <div style={ { marginTop: 20 } }> |
| 258 | { __( 'Flushing logs', 'give' ) } |
| 259 | </div> |
| 260 | </> |
| 261 | ) } |
| 262 | </Modal.Content> |
| 263 | ) : ( |
| 264 | <> |
| 265 | <Modal.Title> |
| 266 | { __( 'Flush all logs', 'give' ) } |
| 267 | </Modal.Title> |
| 268 | |
| 269 | <Modal.Content> |
| 270 | { __( 'Do you want to flush all logs?', 'give' ) } |
| 271 | </Modal.Content> |
| 272 | |
| 273 | <Modal.Content> |
| 274 | <button style={ { marginRight: 20 } } className="button button-primary" onClick={ flushLogs } data-givewp-test="flush-logs-confirm-btn"> |
| 275 | { __( 'Confirm', 'give' ) } |
| 276 | </button> |
| 277 | <button className="button" onClick={ closeLogFlushModal }> |
| 278 | { __( 'Cancel', 'give' ) } |
| 279 | </button> |
| 280 | </Modal.Content> |
| 281 | </> |
| 282 | ) } |
| 283 | </Modal> |
| 284 | ); |
| 285 | }; |
| 286 | |
| 287 | const getLogTypeText = ( type ) => { |
| 288 | if ( type in window.GiveLogs.logTypes ) { |
| 289 | return window.GiveLogs.logTypes[ type ]; |
| 290 | } |
| 291 | return type; |
| 292 | }; |
| 293 | |
| 294 | const resetQueryParameters = ( e ) => { |
| 295 | e.preventDefault(); |
| 296 | |
| 297 | // Reset table sort state |
| 298 | Table.resetSortState(); |
| 299 | |
| 300 | setState( ( previousState ) => { |
| 301 | return { |
| 302 | ...previousState, |
| 303 | currentPage: 1, |
| 304 | currentStatus: '', |
| 305 | currentSource: '', |
| 306 | currentCategory: '', |
| 307 | sortColumn: '', |
| 308 | sortDirection: '', |
| 309 | startDate: null, |
| 310 | endDate: null, |
| 311 | }; |
| 312 | } ); |
| 313 | }; |
| 314 | |
| 315 | const columns = [ |
| 316 | { |
| 317 | key: 'log_type', |
| 318 | label: __( 'Status', 'give' ), |
| 319 | sort: true, |
| 320 | sortCallback: ( direction ) => setSortDirectionForColumn( 'log_type', direction ), |
| 321 | }, |
| 322 | { |
| 323 | key: 'category', |
| 324 | label: __( 'Category', 'give' ), |
| 325 | sort: true, |
| 326 | sortCallback: ( direction ) => setSortDirectionForColumn( 'category', direction ), |
| 327 | }, |
| 328 | { |
| 329 | key: 'source', |
| 330 | label: __( 'Source', 'give' ), |
| 331 | sort: true, |
| 332 | sortCallback: ( direction ) => setSortDirectionForColumn( 'source', direction ), |
| 333 | }, |
| 334 | { |
| 335 | key: 'date', |
| 336 | label: __( 'Date/Time', 'give' ), |
| 337 | sort: true, |
| 338 | sortCallback: ( direction ) => setSortDirectionForColumn( 'date', direction ), |
| 339 | }, |
| 340 | { |
| 341 | key: 'message', |
| 342 | label: __( 'Description', 'give' ), |
| 343 | }, |
| 344 | { |
| 345 | key: 'details', |
| 346 | label: __( 'Details', 'give' ), |
| 347 | append: true, |
| 348 | styles: { |
| 349 | maxWidth: 100, |
| 350 | textAlign: 'center', |
| 351 | justifyContent: 'center', |
| 352 | }, |
| 353 | }, |
| 354 | ]; |
| 355 | |
| 356 | const columnFilters = { |
| 357 | log_type: ( type ) => <Label type={ type } text={ getLogTypeText( type ) } />, |
| 358 | details: ( value, log ) => { |
| 359 | return ( |
| 360 | <Button |
| 361 | data-givewp-test="view-log" |
| 362 | onClick={ ( e ) => { |
| 363 | e.preventDefault(); |
| 364 | openLogModal( log ); |
| 365 | } } |
| 366 | icon={ true }> |
| 367 | <span className="dashicons dashicons-visibility" /> |
| 368 | </Button> |
| 369 | ); |
| 370 | }, |
| 371 | }; |
| 372 | |
| 373 | // Initial load |
| 374 | if ( ! state.initialLoad && isLoading ) { |
| 375 | return ( |
| 376 | <Notice> |
| 377 | <Spinner /> |
| 378 | <h2>{ __( 'Loading log activity', 'give' ) }</h2> |
| 379 | </Notice> |
| 380 | ); |
| 381 | } |
| 382 | |
| 383 | // Is error? |
| 384 | if ( isError ) { |
| 385 | return ( |
| 386 | <Notice> |
| 387 | <h2>{ __( 'Something went wrong!', 'give' ) }</h2> |
| 388 | <div> |
| 389 | Try to <a onClick={ () => window.location.reload() } href="#">reload</a> the browser |
| 390 | </div> |
| 391 | </Notice> |
| 392 | ); |
| 393 | } |
| 394 | |
| 395 | return ( |
| 396 | <> |
| 397 | <div className={ styles.headerRow }> |
| 398 | |
| 399 | <Select |
| 400 | options={ getStatuses() } |
| 401 | onChange={ setCurrentStatus } |
| 402 | defaultValue={ state.currentStatus } |
| 403 | className={ styles.headerItem } |
| 404 | data-givewp-test="logs-status-dropdown" |
| 405 | /> |
| 406 | |
| 407 | <Select |
| 408 | options={ getCategories() } |
| 409 | onChange={ setCurrentCategory } |
| 410 | defaultValue={ state.currentCategory } |
| 411 | className={ styles.headerItem } |
| 412 | data-givewp-test="logs-category-dropdown" |
| 413 | /> |
| 414 | |
| 415 | <Select |
| 416 | options={ getSources() } |
| 417 | onChange={ setCurrentSource } |
| 418 | defaultValue={ state.currentSource } |
| 419 | className={ styles.headerItem } |
| 420 | data-givewp-test="logs-source-dropdown" |
| 421 | /> |
| 422 | |
| 423 | <PeriodSelector |
| 424 | period={ { |
| 425 | startDate: state.startDate, |
| 426 | endDate: state.endDate, |
| 427 | } } |
| 428 | setDates={ setDates } |
| 429 | /> |
| 430 | |
| 431 | <Button onClick={ resetQueryParameters }> |
| 432 | { __( 'Reset', 'give' ) } |
| 433 | </Button> |
| 434 | |
| 435 | <div className={ styles.pagination }> |
| 436 | <Pagination |
| 437 | currentPage={ state.currentPage } |
| 438 | setPage={ setCurrentPage } |
| 439 | totalPages={ state.pages } |
| 440 | disabled={ isLoading } |
| 441 | /> |
| 442 | </div> |
| 443 | </div> |
| 444 | |
| 445 | <Card> |
| 446 | <Table |
| 447 | columns={ columns } |
| 448 | data={ data } |
| 449 | columnFilters={ columnFilters } |
| 450 | isLoading={ isLoading } |
| 451 | isSorting={ state.isSorting } |
| 452 | stripped={ false } |
| 453 | data-givewp-test="logs-table" |
| 454 | /> |
| 455 | </Card> |
| 456 | |
| 457 | <div className={ styles.footerRow }> |
| 458 | { data && ( data.length > 0 ) && ( |
| 459 | <button className="button" onClick={ openLogFlushModal } data-givewp-test="flush-logs-btn"> |
| 460 | { __( 'Flush all logs', 'give' ) } |
| 461 | </button> |
| 462 | ) } |
| 463 | |
| 464 | <div className={ styles.pagination }> |
| 465 | <Pagination |
| 466 | currentPage={ state.currentPage } |
| 467 | setPage={ setCurrentPage } |
| 468 | totalPages={ state.pages } |
| 469 | disabled={ isLoading } |
| 470 | /> |
| 471 | </div> |
| 472 | </div> |
| 473 | |
| 474 | { logModal.visible && getLogModal() } |
| 475 | { logFlushModal.visible && getLogFlushConfirmationModal() } |
| 476 | </> |
| 477 | ); |
| 478 | }; |
| 479 | |
| 480 | export default Logs; |
| 481 |