update
This commit is contained in:
Raymond Garciano 2021-01-14 22:49:56 +08:00
parent c91470b4db
commit 4de9f79382
9 changed files with 509 additions and 85 deletions

View File

@ -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: (
<div>
<h3>
{error && error.data && error.data.message}
</h3>
</div>
),
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: (
<div>
<div>Something went wrong deleting records.</div>
- {error && error.data && error.data.message}
</div>
),
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 (
<Popconfirm
placement='bottomRight'
key={action.key}
title={'Are you sure you want to delete this record?'}
onConfirm={() => this.delete(record.id)}
okText='Yes'
cancelText='No'
icon={<Icon type='close-circle' />}
>
<Tooltip key={action.key} placement='top' title={action.title}>
<Icon
type={action.icon}
style={{
padding: '5px 14px 5px 0',
color: 'rgb(231, 70, 16)',
cursor: 'pointer',
}}
/>
</Tooltip>
</Popconfirm>
);
}
}
return (
<Tooltip key={action.key} placement='top' title={action.title}>
<Icon
type={action.icon}
style={{
padding: '5px 14px 5px 0',
color: 'rgb(231, 70, 16)',
cursor: 'pointer',
}}
onClick={actionBtn}
/>
</Tooltip>
);
}),
};
}
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 (
<div style={{ margin: '0 24px', padding: '24px 0' }}>
<Row type='flex' justify='space-between' align='bottom' style={{ paddingBottom: 25 }}>
<Col>
{this.props.url.csv ? (
<RangePicker onChange={this.handleDateRangePicker} />
) : (
<Input
onChange={this.handleSearchChange}
style={{ width: 300 }}
value={this.state.search_filter}
prefix={<Icon type='search' style={{ color: 'rgba(0,0,0,.25)' }} />}
type='text'
placeholder='Search'
/>
)}
</Col>
<Col className='table-operations'>
{/* <Button onClick = {this.clearFilters}><b>Clear filters</b></Button>*/}
<Button onClick={this.clearAll}>Clear filters</Button>
{this.props.url.csv && <DropdownExport defaultFilter={this.props.defaultFilter} url={this.props.url.csv} />}
</Col>
</Row>
<Table
size='middle'
rowSelection={apiDelete && rowSelection}
columns={columns}
dataSource={this.state.data ? this.state.data : null}
pagination={false}
rowKey={(record) => record[this.props.keyValue]}
onChange={this.handleTableChange}
loading={loading}
/>
<Row type='flex' justify='space-between' style={{ marginTop: 20 }}>
<Col>
{apiDelete && (
<div>
<Popconfirm
placement='top'
title={'Are you sure you want to delete this record?'}
onConfirm={this.handleBatchDelete}
okText='Yes'
cancelText='No'
icon={<Icon type='close-circle' />}
>
<Button type='danger' disabled={!hasSelected} icon='delete' loading={loading}>
Delete
</Button>
<span style={{ marginLeft: 8 }}>
{hasSelected ? `Selected ${selectedRowKeys.length} item(s)` : ''}
</span>
</Popconfirm>
</div>
)}
</Col>
<Col>
{this.state.total > 0 ? (
<Pagination
style={{ float: 'right' }}
showSizeChanger
defaultCurrent={parseInt(urlParamsObject.page, 10) || 1}
defaultPageSize={parseInt(urlParamsObject.page_size, 10) || 10}
pageSizeOptions={[ '10','20']}
total={this.state.total}
showTotal={(total, range) =>
`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}
</Col>
</Row>
</div>
);
}
}
export default withRouter(Index);

View File

@ -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 (
<Form noValidate>
<HeaderForm
@ -58,7 +65,7 @@ function AddNotificationForm(props) {
/>
<Field
name='isSchedule'
name='isScheduled'
layout={formItemLayout}
optionsList={[
{
@ -80,7 +87,7 @@ function AddNotificationForm(props) {
:
<div>
<Field
name="Schedule"
name="schedule"
type="date-time"
layout={formItemLayout}
label="Schedule"

View File

@ -11,7 +11,7 @@ import AddNotificationForm from './components/AddNotificationForm'
// HELPER FUNCTIONS
import { userDetailsSchema } from './validationSchema'
import { customAction } from "actions";
import { API_GET, API_POST } from "utils/NotificationApi";
import { API_UNI_OIL } from 'utils/NotificationApi';
class CreateNotification extends Component {
@ -21,38 +21,43 @@ class CreateNotification extends Component {
schedule: 'false',
}
componentDidMount() {
handleSubmit = async (values, actions) => {
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
}
});
}
// handleSubmit = async (values, actions) => {
// 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()
// }
// this.props.customAction({
// type: "USERMANAGEMENT_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 {
<AddNotificationForm
{...props}
history={this.props.history}
loading={userManagement.createRequestPending || loading}
loading={notificationCreate.createRequestPending || loading}
schedule={schedule}
handleScheduleStatus={this.handleScheduleStatus}
/>
@ -112,7 +119,7 @@ class CreateNotification extends Component {
CreateNotification = connect(
state => ({
//userInfo: state
userManagement: state.userManagement,
notificationCreate: state.notificationCreate,
}),
{ customAction }
)(CreateNotification);

View File

@ -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;
export default notificationCreateReducer;

View File

@ -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({

View File

@ -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!')
})

View File

@ -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 {
/>
<AdvanceTable
updating={this.state.updating}
keyValue='subject'
keyValue='id'
url={{
default: 'notifications',
defaultWithFilter: 'notifications',
default: 'notification',
defaultWithFilter:'notification',
}}
columns={[
{
title: 'ID',
dataIndex: 'id',
key: 'id',
sorter: true,
filters: [],
width: '10%',
},
{
title: 'Subject',
dataIndex: 'subject',

View File

@ -4,6 +4,7 @@ import logoutReducer from './logoutReducer';
import userManagementCreateReducer from 'containers/private/UserManagement/Create/reducer';
import systemPreferencesCreateReducer from 'containers/private/SystemPreferences/Create/reducer';
import branchesCreateReducer from 'containers/private/StationLocator/Branches/Create/reducer';
import notificationCreateReducer from 'containers/private/Notifications/Create/reducer';
import fuelsCreateReducer from 'containers/private/StationLocator/Fuels/Create/reducer'
import stationCreateReducer from 'containers/private/StationLocator/Location/Create/reducer';
import stationUpdateReducer from 'containers/private/StationLocator/Location/Edit/reducer';
@ -17,6 +18,7 @@ const reducers = combineReducers({
userManagement: userManagementCreateReducer,
systemPreferences: systemPreferencesCreateReducer,
branchesCreate: branchesCreateReducer,
notificationCreate: notificationCreateReducer,
fuelsCreate: fuelsCreateReducer,
stationCreate: stationCreateReducer,
stationUpdate: stationUpdateReducer

View File

@ -8,6 +8,7 @@ import errorHandler from "./errorHanlder";
import userManagementCreateSaga from "containers/private/UserManagement/Create/saga"
import systemPreferencesCreateSaga from "containers/private/SystemPreferences/Create/saga"
import BranchCreateSaga from 'containers/private/StationLocator/Branches/Create/saga'
import NotificationCreateSaga from 'containers/private/Notifications/Create/saga'
import FuelCreateSaga from 'containers/private/StationLocator/Fuels/Create/saga'
import stationCreateSaga from 'containers/private/StationLocator/Location/Create/saga'
import stationUpdateSaga from 'containers/private/StationLocator/Location/Edit/saga';
@ -21,6 +22,7 @@ export default function* rootSaga() {
fork(userManagementCreateSaga),
fork(systemPreferencesCreateSaga),
fork(BranchCreateSaga),
fork(NotificationCreateSaga),
fork(FuelCreateSaga),
fork(stationCreateSaga),
fork(fetchDataSaga),