diff --git a/src/components/NotificationTables/index.js b/src/components/NotificationTables/index.js new file mode 100644 index 0000000..e8c31a0 --- /dev/null +++ b/src/components/NotificationTables/index.js @@ -0,0 +1,429 @@ +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import queryString from 'query-string'; +import { getCookie } from '../../utils/cookie'; + +import _ from 'lodash'; +import { + Table, + Button, + Row, + Col, + Input, + Icon, + Pagination, + Tooltip, + notification, + Popconfirm, + message, + DatePicker, +} from 'antd'; + +import { DropdownExport } from 'components/Dropdown/index'; +import { fnQueryParams } from 'utils/helper'; +import { API_UNI_OIL, API_GET, API_DELETE } from 'utils/Api'; +import { API_GET_NOTIF } from 'utils/NotificationApi'; +import '../Tables/index.css'; + +const { RangePicker } = DatePicker; + +class Index extends Component { + constructor(props) { + super(props); + this.state = { + data: null, + total: null, + loading: false, + selectedRowKeys: [], + columns: [], + search_filter: '', + visible: false, + mounted: false, + test: true, + updating: false, + }; + + this.delayFetchRequest = _.debounce(this.fetch, 500); + this.handleSearchChangeDebounce = _.debounce(this.handleSearchStateChange, 1000); + } + + componentDidMount() { + this.setState({ mounted: true }); + this.handleFilterChange({}); + } + + componentDidUpdate(prevProps, prevState, snapshot) { + if (prevState.updating !== prevProps.updating) { + this.setState({ updating: prevProps.updating }); + } + } + + shouldComponentUpdate(nextProps, nextState) { + if (nextProps.updating !== nextState.updating) { + this.handleFilterChange({}); + return true; + } + return true; + } + + componentWillMount() { + this.delayFetchRequest.cancel(); + this.handleSearchChangeDebounce.cancel(); + } + + handleTableChange = (pagination, filters, sorter) => { + let _sort_order; + if (sorter.order) _sort_order = sorter.order === 'ascend' ? 'asc' : 'desc'; + + if (sorter.column) { + if (sorter.column.sortByValue) sorter.field = sorter.column.sortByValue; + } + + this.handleFilterChange({ + ...filters, + _sort_by: sorter.field, + _sort_order, + }); + }; + + handleSearchChange = (e) => { + this.setState({ search_filter: e.target.value }); + this.handleSearchChangeDebounce(e.target.value); + }; + + handleSearchStateChange = (search_filter) => { + this.setState({ search_filter }); + this.handleFilterChange({ search: this.state.search_filter }); + }; + + onPaginationChange = (page, page_size) => { + console.log("page") + console.log(page) + this.handleFilterChange({ page }); + }; + + handleFilterChange = (props, isClearFilter) => { + console.log(props) + this.setState({ loading: true }); + + let { history, location } = this.props; + let { search, pathname } = location; + let urlParamsObject = isClearFilter ? props : queryString.parse(search); + urlParamsObject = props ? { ...urlParamsObject,page: 1, ...props } : {}; + urlParamsObject = fnQueryParams(urlParamsObject); + urlParamsObject = queryString.parse(urlParamsObject); + history.push({ pathname, search: fnQueryParams(urlParamsObject) }); + console.log({ pathname, search: fnQueryParams(urlParamsObject) }) + this.delayFetchRequest(urlParamsObject); + }; + + clearFilters = () => { + let { history, location } = this.props; + let { search, pathname } = location; + let urlParamsObject = queryString.parse(search); + delete urlParamsObject['search']; + Object.keys(urlParamsObject).map((key, index) => { + if (this.props.filterValues.includes(key)) delete urlParamsObject[key]; + }); + + history.push({ pathname, search: fnQueryParams(urlParamsObject) }); + this.handleFilterChange(urlParamsObject, true); + }; + + clearAll = () => { + this.setState({ search_filter: '' }); + this.handleFilterChange(); + }; + + fetch = async (params = {}) => { + let defaulUrl; + + if (this.props.defaultFilter) { + params = { + ...params, + ...this.props.defaultFilter, + }; + } + + if (this.props.url.defaultWithFilter) { + defaulUrl = this.props.url.defaultWithFilter; + } else { + defaulUrl = this.props.url.default; + } + + try { + let response, data, total; + if(defaulUrl == 'notification'){ + console.log(defaulUrl, params) + response = await API_GET_NOTIF('notification', params); + console.log(response.data, params, 'response'); + console.log(getCookie('TOKEN').token); + data = response.data.data.length > 0 ? response.data.data : null; + total = response.data.total + } + console.table(data, 'data'); + this.setState({ data, total, loading: false }); + if (data == null && this.props.isEmptyMessagePopUp) { + message.info('No records found.'); + } + if (this.props.dataResponse) { + this.props.dataResponse(data.length); + } + } catch (error) { + this.setState({ loading: false, total: 0 }); + console.log('An error encountered: ' + error); + } + }; + + update = async (params = {}) => { + notification.success({ + message: 'Success', + description: `Delete Successful.`, + }); + }; + + remove = async (params = {}) => { + notification.error({ + message: 'Error', + description: `Error message.`, + }); + }; + + delete = async (uuid) => { + console.log(uuid) + let search = this.props.location; + console.log(search.pathname) + let api = process.env.REACT_APP_STATION_API + let path = search.pathname.substring(1) + try { + await API_UNI_OIL.delete(`${api}${path}/${uuid}`); + this.handleFilterChange({}); + message.success('Record was successfully deleted.'); + } catch ({ response: error }) { + this.handleFilterChange({}); + notification.error({ + message: 'Something went wrong deleting record!', + description: ( +
+

+ {error && error.data && error.data.message} +

+
+ ), + duration: 4, + }); + } + }; + + handleBatchDelete = async () => { + const data = { [this.props.keyValue]: this.state.selectedRowKeys }; + this.setState({ selectedRowKeys: [] }); + + try { + // await API_UNI_OIL.delete(this.props.url.apiDelete, { data }); + // this.handleFilterChange({}); + // message.success('Record was successfully deleted.'); + console.log(this.props.url.apiDelete) + } catch ({ response: error }) { + this.handleFilterChange({}); + notification.error({ + message: 'Error', + description: ( +
+
Something went wrong deleting records.
+ - {error && error.data && error.data.message} +
+ ), + duration: 3, + }); + } + }; + + onSelectChange = (selectedRowKeys) => { + this.setState({ selectedRowKeys }); + }; + + handleDateRangePicker = async (date, dateString) => { + this.handleFilterChange({ + date_start: dateString[0], + date_end: dateString[1], + }); + }; + + render() { + if (!this.state.mounted) return null; + const { loading, selectedRowKeys } = this.state; + const rowSelection = { + selectedRowKeys, + onChange: this.onSelectChange, + getCheckboxProps: (record) => ({ + disabled: record.editable == false, // Column configuration not to be checked + //name: record.name, + }), + }; + const hasSelected = selectedRowKeys.length > 0; + + let { history, keyValue, location, url: { apiDelete } } = this.props; + + let { search } = this.props.location; + let urlParamsObject = queryString.parse(search); + let { _sort_order } = urlParamsObject; + if (_sort_order) _sort_order = _sort_order === 'asc' ? 'ascend' : 'descend'; + + const columns = this.props.columns.map((data) => { + if (data.dataIndex === 'action') { + return { + ...data, + render: (text, record) => + data.buttons.map((action) => { + let actionBtn; + if(action.key == 'location'){ + actionBtn = () => this.props.locationData(record) + } + if(action.key == 'edit'){ + actionBtn = () => history.push({ pathname: `${location.pathname}/view/${record.id}` }); + } + if (action.key == 'delete') { + actionBtn = action.action; + if (record.editable == false) { + return; + } else { + return ( + this.delete(record.id)} + okText='Yes' + cancelText='No' + icon={} + > + + + + + ); + } + } + + return ( + + + + ); + }), + }; + } + let filteredValue = null; + if (Array.isArray(urlParamsObject[data.dataIndex])) { + filteredValue = urlParamsObject[data.dataIndex]; + } else if (urlParamsObject[data.dataIndex]) { + filteredValue = [ urlParamsObject[data.dataIndex] ]; + } + + return { + ...data, + filteredValue, + sortOrder: data.sorter ? urlParamsObject._sort_by === data.dataIndex && _sort_order : null, + }; + }); + + return ( +
+ + + {this.props.url.csv ? ( + + ) : ( + } + type='text' + placeholder='Search' + /> + )} + + + {/* */} + + {this.props.url.csv && } + + + + record[this.props.keyValue]} + onChange={this.handleTableChange} + loading={loading} + /> + + + + {apiDelete && ( +
+ } + > + + + {hasSelected ? `Selected ${selectedRowKeys.length} item(s)` : ''} + + +
+ )} + + + + {this.state.total > 0 ? ( + + `Showing ${this.state.total > 0 ? range[0] : 0}-${this.state.total > 0 ? range[1] : 0} of ${this.state + .total > 0 + ? total + : 0}`} + onChange={this.onPaginationChange} + onShowSizeChange={this.onPaginationChange} + /> + ) : null} + + + + ); + } +} + +export default withRouter(Index); diff --git a/src/containers/private/Notifications/Create/components/AddNotificationForm.js b/src/containers/private/Notifications/Create/components/AddNotificationForm.js index 9f0d385..e202b20 100644 --- a/src/containers/private/Notifications/Create/components/AddNotificationForm.js +++ b/src/containers/private/Notifications/Create/components/AddNotificationForm.js @@ -22,7 +22,14 @@ const formItemLayout = { }; function AddNotificationForm(props) { - const { isSubmitting, handleSubmit, loading, history, schedule, handleScheduleStatus } = props; + const { + isSubmitting, + handleSubmit, + loading, + history, + schedule, + handleScheduleStatus + } = props; return (
{ - - // const { setErrors, setSubmitting } = actions; - // let { history } = this.props; - // let _self = this; - // this.setState({loading: true}) - // values.role = parseInt(values.role); - // if(values.status) { - // values.status = values.status - // } else { - // values.status = "active" - // } - // if(values.username) { - // values.username = values.username.toLowerCase() - // } + handleSubmit = async (values, actions) => { - // this.props.customAction({ - // type: "USERMANAGEMENT_CREATE_REQUEST", - // payload: { - // values, - // setSubmitting, - // setErrors, - // history, - // _self - // } - // }); - // } + const { setErrors, setSubmitting } = actions; + let { history } = this.props; + let _self = this; + let params = { ...values } + + console.log(params); + + // const response = await API_UNI_OIL.post('notification',params); + // if(response) { + // console.log(response); + // } + // this.setState({loading: true}) + // values.role = parseInt(values.role); + // if(values.status) { + // values.status = values.status + // } else { + // values.status = "active" + // } + // if(values.username) { + // values.username = values.username.toLowerCase() + // } + + this.props.customAction({ + type: "NOTIFICATION_CREATE_REQUEST", + payload: { + values, + setSubmitting, + setErrors, + history, + _self + } + }); + } handleAddUser =()=> { this.form.submitForm() @@ -62,7 +67,9 @@ class CreateNotification extends Component { } render() { - const { userManagement } = this.props + + const { match, history } = this.props; + const { notificationCreate } = this.props const { loading, schedule } = this.state; return ( @@ -83,7 +90,7 @@ class CreateNotification extends Component { initialValues={{ subject: '', content: '', - isSchedule: 'false', + isScheduled: 'false', schedule: '', expiration: '' @@ -96,7 +103,7 @@ class CreateNotification extends Component { @@ -112,7 +119,7 @@ class CreateNotification extends Component { CreateNotification = connect( state => ({ //userInfo: state - userManagement: state.userManagement, + notificationCreate: state.notificationCreate, }), { customAction } )(CreateNotification); diff --git a/src/containers/private/Notifications/Create/reducer.js b/src/containers/private/Notifications/Create/reducer.js index f15f688..92751f9 100644 --- a/src/containers/private/Notifications/Create/reducer.js +++ b/src/containers/private/Notifications/Create/reducer.js @@ -7,7 +7,7 @@ const initialState = { createRequestPending: false } -const NotificationCreateReducer = (state = initialState, { type, payload }) => { +const notificationCreateReducer = (state = initialState, { type, payload }) => { switch(type) { case NOTIFICATION_CREATE_REQUEST: return { @@ -30,4 +30,4 @@ const NotificationCreateReducer = (state = initialState, { type, payload }) => { } }; - export default NotificationCreateReducer; \ No newline at end of file + export default notificationCreateReducer; \ No newline at end of file diff --git a/src/containers/private/Notifications/Create/saga.js b/src/containers/private/Notifications/Create/saga.js index d191338..2f0d45b 100644 --- a/src/containers/private/Notifications/Create/saga.js +++ b/src/containers/private/Notifications/Create/saga.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import { call, takeLatest, put } from "redux-saga/effects"; -import { API_UNI_OIL } from "utils/Api"; +import { API_UNI_OIL } from "utils/NotificationApi"; import { setCookie } from "utils/cookie"; import { notification, message } from "antd"; @@ -18,11 +18,10 @@ function* NotificationSagaFlow({ payload }) { _self } = payload; try { - const { data } = yield call(() => API_UNI_OIL.post('admin', { ...values })); //username - - yield put({ type: NOTIFICATION_CREATE_SUCCESS, payload: data.data }); - message.success('User account created successfully. Please send the login credentials to the user.'); _self.setState({loading: false}) - history.push({ pathname: '/user-management' }); + const { data } = yield call(() => API_UNI_OIL.post('notification', { ...values })); + yield put({ type: NOTIFICATION_CREATE_SUCCESS, payload: data.newNotification }); + message.success('Notification created successfully.'); _self.setState({loading: false}) + history.push({ pathname: '/notifications' }); } catch ({response: error}) { notification.error({ diff --git a/src/containers/private/Notifications/Create/validationSchema.js b/src/containers/private/Notifications/Create/validationSchema.js index 8b1a88e..413df0e 100644 --- a/src/containers/private/Notifications/Create/validationSchema.js +++ b/src/containers/private/Notifications/Create/validationSchema.js @@ -2,40 +2,10 @@ import * as Yup from 'yup' export const userDetailsSchema = Yup.object().shape({ - username: Yup.string() - .trim() - .max(128, "Maximum character is 128.") - .required('Username is required!') - .matches( - /^[a-zA-Z0-9_@.ñÑ ]+$/, - { - message: 'Invalid Username.', - excludeEmptyString: true, - }, - ), - firstname: Yup.string() - .trim() - .max(128, "Maximum character is 128.") - .matches(/^[A-Za-z ñÑ-]+$/, { excludeEmptyString: false, message: "Invalid First Name" }) - .required('First Name is required!'), - lastname: Yup.string() - .trim() - .max(128, "Maximum character is 128.") - .matches(/^[A-Za-z ñÑ-]+$/, { excludeEmptyString: false, message: "Invalid Last Name" }) - .trim() - .required('Last Name is required!'), - email: Yup.string() - .trim() - .max(128, "Maximum character is 128.") - .required('Email is required!') - .matches(/^[A-Za-z0-9@_.ñÑ ]+$/, { excludeEmptyString: false, message: "Invalid Email Address" }) - .email("Invalid Email Address"), - role: Yup.string() - .required('Role is required!'), - status: Yup.string() - .required('Status is required!'), - password: Yup.string() - .required('Default Password is required!') + content: Yup.string() + .required('Content is required!'), + subject: Yup.string() + .required('Subject is required!') }) diff --git a/src/containers/private/Notifications/List/index.js b/src/containers/private/Notifications/List/index.js index f0e521e..4088eb7 100644 --- a/src/containers/private/Notifications/List/index.js +++ b/src/containers/private/Notifications/List/index.js @@ -6,11 +6,11 @@ import { Link } from 'react-router-dom'; import { getCookie } from '../../../../utils/cookie'; // COMPONENTS -import AdvanceTable from 'components/Tables/AdvanceTable'; +import AdvanceTable from "components/NotificationTables"; import HeaderForm from 'components/Forms/HeaderForm'; // HELPER FUNCTIONS -import { API_UNI_OIL } from 'utils/Api'; +import { API_UNI_OIL } from 'utils/NotificationApi'; import { customAction } from 'actions'; class NotificationList extends Component { @@ -163,12 +163,20 @@ class NotificationList extends Component { />