Compare commits
No commits in common. "main" and "685417b2599e69ddfb30baf685625304930a2c9a" have entirely different histories.
main
...
685417b259
|
@ -0,0 +1,15 @@
|
||||||
|
REACT_APP_DOMAIN=192.168.0.68
|
||||||
|
REACT_APP_PATH=UniOilLoyaltyApp-BackEnd/public/index.php/api/cms
|
||||||
|
REACT_APP_DEV=http://$REACT_APP_DOMAIN/$REACT_APP_PATH
|
||||||
|
REACT_APP_QA=http://$REACT_APP_DOMAIN:4000/$REACT_APP_PATH
|
||||||
|
REACT_APP_IMG_URL=http://$REACT_APP_DOMAIN/UniOilLoyaltyApp-BackEnd/
|
||||||
|
REACT_APP_API=https://mobileapi.unioilapps.com/api/cms
|
||||||
|
REACT_APP_TOKEN=X_TOKEN
|
||||||
|
NODE_PATH=src/
|
||||||
|
GENERATE_SOURCEMAP=false
|
||||||
|
REACT_APP_PUBLIC=false
|
||||||
|
REACT_APP_NOTIF_API=http://unioilcms.taxikel.com:3000/api/
|
||||||
|
REACT_APP_STATION_API_TESTING=https://testing-api-station-locator.herokuapp.com/api/
|
||||||
|
REACT_APP_STATION_API_TESTINGX=https://station-locator-api.herokuapp.com/api/
|
||||||
|
REACT_APP_STATION_API=https://cors-anywhere.herokuapp.com/13.212.2.253/api/
|
||||||
|
REACT_APP_API_SYNC=https://mobileapi.unioilapps.com/api/mobile/
|
|
@ -0,0 +1,12 @@
|
||||||
|
[ignore]
|
||||||
|
./components
|
||||||
|
[include]
|
||||||
|
|
||||||
|
[libs]
|
||||||
|
.*/node_modules/*
|
||||||
|
[lints]
|
||||||
|
|
||||||
|
[options]
|
||||||
|
|
||||||
|
|
||||||
|
[strict]
|
|
@ -0,0 +1,50 @@
|
||||||
|
# These are some examples of commonly ignored file patterns.
|
||||||
|
# You should customize this list as applicable to your project.
|
||||||
|
# Learn more about .gitignore:
|
||||||
|
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
||||||
|
|
||||||
|
# Node artifact files
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Compiled Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Compiled Python bytecode
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Package files
|
||||||
|
*.jar
|
||||||
|
|
||||||
|
# Maven
|
||||||
|
target/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# JetBrains IDE
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Unit test reports
|
||||||
|
TEST*.xml
|
||||||
|
|
||||||
|
# Generated by MacOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Generated by Windows
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Applications
|
||||||
|
*.app
|
||||||
|
*.exe
|
||||||
|
*.war
|
||||||
|
|
||||||
|
# Large media files
|
||||||
|
*.mp4
|
||||||
|
*.tiff
|
||||||
|
*.avi
|
||||||
|
*.flv
|
||||||
|
*.mov
|
||||||
|
*.wmv
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
image: node:latest
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- apt-get update -qy
|
||||||
|
- apt-get install -y ruby-dev
|
||||||
|
- gem install dpl
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- testing
|
||||||
|
- staging
|
||||||
|
- production
|
||||||
|
|
||||||
|
testing:
|
||||||
|
type: deploy
|
||||||
|
stage: testing
|
||||||
|
image: ruby:latest
|
||||||
|
script:
|
||||||
|
- dpl --provider=heroku --app=$HEROKU_APP_TESTING --api-key=$HEROKU_API_KEY
|
||||||
|
only:
|
||||||
|
- testing
|
||||||
|
|
||||||
|
staging:
|
||||||
|
type: deploy
|
||||||
|
stage: staging
|
||||||
|
image: ruby:latest
|
||||||
|
script:
|
||||||
|
- dpl --provider=heroku --app=$HEROKU_APP_STAGING --api-key=$HEROKU_API_KEY
|
||||||
|
only:
|
||||||
|
- staging
|
||||||
|
|
||||||
|
# production:
|
||||||
|
# type: deploy
|
||||||
|
# stage: production
|
||||||
|
# image: ruby:latest
|
||||||
|
# script:
|
||||||
|
# - dpl --provider=heroku --app=$HEROKU_APP_PRODUCTION --api-key=$HEROKU_API_KEY
|
||||||
|
# only:
|
||||||
|
# - master
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Stage 1 - the build process
|
||||||
|
FROM node:8.11.3 as builder
|
||||||
|
RUN mkdir /usr/src/app
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||||
|
ARG REACT_APP_PATH
|
||||||
|
ARG REACT_APP_DOMAIN
|
||||||
|
ARG REACT_APP_IMG_PATH
|
||||||
|
ARG REACT_APP_PUBLIC
|
||||||
|
ARG PUBLIC_URL
|
||||||
|
ARG GENERATE_SOURCEMAP
|
||||||
|
ENV REACT_APP_API $REACT_APP_DOMAIN/$REACT_APP_PATH
|
||||||
|
ENV REACT_APP_IMG_URL $REACT_APP_DOMAIN/$REACT_APP_IMG_PATH
|
||||||
|
RUN echo "DEBUG": $PUBLIC_URL
|
||||||
|
|
||||||
|
COPY package.json yarn.lock /usr/src/app/
|
||||||
|
RUN yarn install
|
||||||
|
COPY . /usr/src/app
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
# Stage 2 - the production environment
|
||||||
|
FROM nginx:1.13.9-alpine
|
||||||
|
|
||||||
|
RUN rm -rf /etc/nginx/conf.d
|
||||||
|
COPY conf /etc/nginx
|
||||||
|
|
||||||
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
|
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
|
@ -0,0 +1,54 @@
|
||||||
|
dev-server:
|
||||||
|
docker-compose up -d --build dev-build
|
||||||
|
.PHONY: dev-server
|
||||||
|
|
||||||
|
dev-server-stop:
|
||||||
|
docker-compose stop -t 1 dev-build
|
||||||
|
.PHONY: dev-server-stop
|
||||||
|
|
||||||
|
test-server:
|
||||||
|
docker-compose up -d --build test-build
|
||||||
|
.PHONY: test-server
|
||||||
|
|
||||||
|
test-server-stop:
|
||||||
|
docker-compose stop -t 1 test-build
|
||||||
|
.PHONY: test-server-stop
|
||||||
|
|
||||||
|
prod-server:
|
||||||
|
docker-compose -f docker-compose-prod.yml up -d --build
|
||||||
|
.PHONY: prod-server
|
||||||
|
|
||||||
|
prod-server-stop:
|
||||||
|
docker-compose -f docker-compose-prod.yml down
|
||||||
|
.PHONY: prod-server-stop
|
||||||
|
|
||||||
|
dev-public-callback-server:
|
||||||
|
docker-compose up -d --build public-dev-build
|
||||||
|
.PHONY: dev-public-callback-server
|
||||||
|
|
||||||
|
dev-public-callback-server-stop:
|
||||||
|
docker-compose stop -t 1 public-dev-build
|
||||||
|
.PHONY: dev-public-callback-server-stop
|
||||||
|
|
||||||
|
test-public-callback-server:
|
||||||
|
docker-compose up -d --build public-test-build
|
||||||
|
.PHONY: test-public-callback-server
|
||||||
|
|
||||||
|
test-public-callback-server-stop:
|
||||||
|
docker-compose stop -t 1 public-test-build
|
||||||
|
.PHONY: test-public-callback-server-stop
|
||||||
|
|
||||||
|
prod-public-callback-server:
|
||||||
|
docker-compose -f docker-compose-public.yml up -d --build
|
||||||
|
.PHONY: prod-public-callback-server
|
||||||
|
|
||||||
|
prod-public-callback-server-stop:
|
||||||
|
docker-compose -f docker-compose-public.yml down
|
||||||
|
.PHONY: prod-public-callback-server-stop
|
||||||
|
|
||||||
|
stop:
|
||||||
|
docker-compose down
|
||||||
|
.PHONY: stop
|
||||||
|
|
||||||
|
clean:
|
||||||
|
docker system prune --volumes -f
|
74
README.md
74
README.md
|
@ -1,2 +1,74 @@
|
||||||
# loyalty-cms
|
|
||||||
|
## Server Dependences
|
||||||
|
|
||||||
|
* [Docker/Linux](https://docs.docker.com/install/linux/docker-ce/ubuntu/)
|
||||||
|
* [Docker Compose/Linux](https://docs.docker.com/compose/install/#install-compose)
|
||||||
|
|
||||||
|
### `sudo apt-get update`
|
||||||
|
### `sudo apt-get install build-essential`
|
||||||
|
|
||||||
|
## CMS - Local Development Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.<br>
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.<br>
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
## Development Server Scripts
|
||||||
|
|
||||||
|
Up the server for staging development run:
|
||||||
|
### `make dev-server`
|
||||||
|
|
||||||
|
Stop the server run:
|
||||||
|
### `make dev-server-stop`
|
||||||
|
|
||||||
|
## QA/Testing Server Scripts
|
||||||
|
|
||||||
|
Up the server for staging QA/Testing run:
|
||||||
|
### `make test-server`
|
||||||
|
|
||||||
|
Stop the server run:
|
||||||
|
### `make test-server-stop`
|
||||||
|
|
||||||
|
## Production Server Scripts
|
||||||
|
|
||||||
|
Up the server for production run:
|
||||||
|
### `make prod-server`
|
||||||
|
|
||||||
|
Stop the server run:
|
||||||
|
### `make prod-server-stop`
|
||||||
|
|
||||||
|
Clean image container run:
|
||||||
|
### `make clean`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Public Development Server Scripts
|
||||||
|
|
||||||
|
Up the server for staging development run:
|
||||||
|
### `make dev-public-callback-server`
|
||||||
|
|
||||||
|
Stop the server run:
|
||||||
|
### `make dev-public-callback-server-stop`
|
||||||
|
|
||||||
|
## Public QA/Testing Server Scripts
|
||||||
|
|
||||||
|
Up the server for staging QA/Testing run:
|
||||||
|
### `make test-public-callback-server`
|
||||||
|
|
||||||
|
Stop the server run:
|
||||||
|
### `make test-public-callback-server-stop`
|
||||||
|
|
||||||
|
## Public Production Server Scripts
|
||||||
|
|
||||||
|
Up the server for staging QA/Testing run:
|
||||||
|
### `make prod-public-callback-server`
|
||||||
|
|
||||||
|
Stop the server run:
|
||||||
|
### `make prod-public-callback-server-stop`
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
# read more here http://tautt.com/best-nginx-configuration-for-security/
|
||||||
|
|
||||||
|
# add_header Server "mystartup/1.0" always;
|
||||||
|
# config to don't allow the browser to render the page inside an frame or iframe
|
||||||
|
# and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking
|
||||||
|
# if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri
|
||||||
|
# https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
|
||||||
|
add_header X-Frame-Options SAMEORIGIN;
|
||||||
|
|
||||||
|
# when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header,
|
||||||
|
# to disable content-type sniffing on some browsers.
|
||||||
|
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
|
||||||
|
# currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx
|
||||||
|
# http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx
|
||||||
|
# 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
|
||||||
|
# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers.
|
||||||
|
# It's usually enabled by default anyway, so the role of this header is to re-enable the filter for
|
||||||
|
# this particular website if it was disabled by the user.
|
||||||
|
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
|
||||||
|
# with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy),
|
||||||
|
# you can tell the browser that it can only download content from the domains you explicitly allow
|
||||||
|
# http://www.html5rocks.com/en/tutorials/security/content-security-policy/
|
||||||
|
# https://www.owasp.org/index.php/Content_Security_Policy
|
||||||
|
# I need to change our application code so we can increase security by disabling 'unsafe-inline' 'unsafe-eval'
|
||||||
|
# directives for css and js(if you have inline css or js, you will need to keep it too).
|
||||||
|
# more: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful
|
||||||
|
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://assets.zendesk.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'";
|
||||||
|
# add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
|
||||||
|
default_type application/octet-stream;
|
||||||
|
# more_clear_headers Server;
|
||||||
|
# more_set_headers 'Server: Powered by Yondu';
|
||||||
|
# don't send the nginx version number in error pages and Server header
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_disable "MSIE [1-6]\.";
|
||||||
|
gzip_min_length 256;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rss+xml
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
# text/html is always compressed by gzip module
|
||||||
|
gzip_comp_level 9;
|
||||||
|
server_name *.example.org;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
# . files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
# cache.appcache, your document html and data
|
||||||
|
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
|
||||||
|
expires -1;
|
||||||
|
# access_log logs/static.log; # I don't usually include a static log
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Media: images, icons, video, audio, HTC
|
||||||
|
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
|
||||||
|
expires 1M;
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# location ~* (service-worker\.js)$ {
|
||||||
|
# add_header Cache-Control no-store, no-cache;
|
||||||
|
# expires -1;
|
||||||
|
# proxy_no_cache 1;
|
||||||
|
|
||||||
|
# }
|
||||||
|
|
||||||
|
# CSS and Javascript
|
||||||
|
location ~* /static/.*\.(?:css|js)$ {
|
||||||
|
expires 1y;
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# location /service-worker.js {
|
||||||
|
# expires -1;
|
||||||
|
# add_header Cache-Control no-store, no-cache;
|
||||||
|
# access_log off;
|
||||||
|
# }
|
||||||
|
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,491 @@
|
||||||
|
const { injectBabelPlugin } = require("react-app-rewired");
|
||||||
|
const rewireLess = require("react-app-rewire-less");
|
||||||
|
// const VariablesOutput = require('less-plugin-variables-output');
|
||||||
|
|
||||||
|
// const fs = require('fs');
|
||||||
|
// const path = require('path');
|
||||||
|
// const less = require('less');
|
||||||
|
|
||||||
|
module.exports = function override(config, env) {
|
||||||
|
|
||||||
|
config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config); // change importing css to less
|
||||||
|
config = rewireLess.withLoaderOptions({
|
||||||
|
javascriptEnabled: true,
|
||||||
|
modifyVars: {
|
||||||
|
// "@primary-color": "#1DA57A" ,
|
||||||
|
|
||||||
|
|
||||||
|
'@primary-color' : '#1890ff',
|
||||||
|
'@info-color' : '#1890ff',
|
||||||
|
'@success-color ' : '#52c41a',
|
||||||
|
'@processing-color' : '#1890ff',
|
||||||
|
'@error-color' : '#f5222d',
|
||||||
|
'@highlight-color' : '#f5222d',
|
||||||
|
'@warning-color' : '#faad14',
|
||||||
|
'@normal-color' : '#d9d9d9',
|
||||||
|
|
||||||
|
// Color used by default to control hover and active backgrounds and for
|
||||||
|
// alert info backgrounds.
|
||||||
|
// @primary-1: color(~`colorPalette("@{primary-color}", 1)`); // replace tint(@primary-color, 90%)
|
||||||
|
// @primary-2: color(~`colorPalette("@{primary-color}", 2)`); // replace tint(@primary-color, 80%)
|
||||||
|
// @primary-3: color(~`colorPalette("@{primary-color}", 3)`); // unused
|
||||||
|
// @primary-4: color(~`colorPalette("@{primary-color}", 4)`); // unused
|
||||||
|
// @primary-5: color(~`colorPalette("@{primary-color}", 5)`); // color used to control the text color in many active and hover states, replace tint(@primary-color, 20%)
|
||||||
|
// @primary-6: @primary-color; // color used to control the text color of active buttons, don't use, use @primary-color
|
||||||
|
// @primary-7: color(~`colorPalette("@{primary-color}", 7)`); // replace shade(@primary-color, 5%)
|
||||||
|
// @primary-8: color(~`colorPalette("@{primary-color}", 8)`); // unused
|
||||||
|
// @primary-9: color(~`colorPalette("@{primary-color}", 9)`); // unused
|
||||||
|
// @primary-10: color(~`colorPalette("@{primary-color}", 10)`); // unused
|
||||||
|
|
||||||
|
// Base Scaffolding Variables
|
||||||
|
// ---
|
||||||
|
|
||||||
|
// Background color for `<body>`
|
||||||
|
// @body-background : #fff;
|
||||||
|
// Base background color for most components
|
||||||
|
// @component-background : #fff;
|
||||||
|
// @font-family-no-number : "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
// @font-family : "Monospaced Number", @font-family-no-number;
|
||||||
|
// @code-family : Consolas, Menlo, Courier, monospace;
|
||||||
|
// @heading-color : fade(#000, 85%);
|
||||||
|
// @text-color : fade(#000, 65%);
|
||||||
|
// @text-color-secondary : fade(#000, 45%);
|
||||||
|
// @heading-color-dark : fade(#fff, 100%);
|
||||||
|
// @text-color-dark : fade(#fff, 85%);
|
||||||
|
// @text-color-secondary-dark: fade(#fff, 65%);
|
||||||
|
'@font-size-base' : '14px'
|
||||||
|
// @font-size-lg : @font-size-base + 2px;
|
||||||
|
// @font-size-sm : 12px;
|
||||||
|
// @line-height-base : 1.5;
|
||||||
|
// @border-radius-base : 4px;
|
||||||
|
// @border-radius-sm : 2px;
|
||||||
|
|
||||||
|
// vertical paddings
|
||||||
|
// @padding-lg : 24px; // containers
|
||||||
|
// @padding-md : 16px; // small containers and buttons
|
||||||
|
// @padding-sm : 12px; // Form controls and items
|
||||||
|
// @padding-xs : 8px; // small items
|
||||||
|
|
||||||
|
// vertical padding for all form controls
|
||||||
|
// @control-padding-horizontal: @padding-sm;
|
||||||
|
// @control-padding-horizontal-sm: @padding-xs;
|
||||||
|
|
||||||
|
// The background colors for active and hover states for things like
|
||||||
|
// list items or table cells.
|
||||||
|
// @item-active-bg : @primary-1;
|
||||||
|
// @item-hover-bg : @primary-1;
|
||||||
|
|
||||||
|
// ICONFONT
|
||||||
|
// @iconfont-css-prefix : anticon;
|
||||||
|
// @icon-url : "https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i";
|
||||||
|
|
||||||
|
// LINK
|
||||||
|
// @link-color : @primary-color;
|
||||||
|
// @link-hover-color : color(~`colorPalette("@{link-color}", 5)`);
|
||||||
|
// @link-active-color : color(~`colorPalette("@{link-color}", 7)`);
|
||||||
|
// @link-decoration : none;
|
||||||
|
// @link-hover-decoration : none;
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
// @ease-out : cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
|
// @ease-in : cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||||
|
// @ease-in-out : cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
|
// @ease-out-back : cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||||
|
// @ease-in-back : cubic-bezier(0.71, -0.46, 0.88, 0.6);
|
||||||
|
// @ease-in-out-back : cubic-bezier(0.71, -0.46, 0.29, 1.46);
|
||||||
|
// @ease-out-circ : cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||||
|
// @ease-in-circ : cubic-bezier(0.6, 0.04, 0.98, 0.34);
|
||||||
|
// @ease-in-out-circ : cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
// @ease-out-quint : cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
// @ease-in-quint : cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||||
|
// @ease-in-out-quint : cubic-bezier(0.86, 0, 0.07, 1);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
// @border-color-base : hsv(0, 0, 85%); // base border outline a component
|
||||||
|
// @border-color-split : hsv(0, 0, 91%); // split border inside a component
|
||||||
|
// @border-width-base : 1px; // width of the border for a component
|
||||||
|
// @border-style-base : solid; // style of a components border
|
||||||
|
|
||||||
|
// Outline
|
||||||
|
// @outline-blur-size : 0;
|
||||||
|
// @outline-width : 2px;
|
||||||
|
// @outline-color : @primary-color;
|
||||||
|
|
||||||
|
// @background-color-light : hsv(0, 0, 98%); // background of header and selected item
|
||||||
|
// @background-color-base : hsv(0, 0, 96%); // Default grey background color
|
||||||
|
|
||||||
|
// Disabled states
|
||||||
|
// @disabled-color : fade(#000, 25%);
|
||||||
|
// @disabled-bg : @background-color-base;
|
||||||
|
// @disabled-color-dark : fade(#fff, 35%);
|
||||||
|
|
||||||
|
// Shadow
|
||||||
|
// @shadow-color : rgba(0, 0, 0, .15);
|
||||||
|
// @box-shadow-base : @shadow-1-down;
|
||||||
|
// @shadow-1-up : 0 2px 8px @shadow-color;
|
||||||
|
// @shadow-1-down : 0 2px 8px @shadow-color;
|
||||||
|
// @shadow-1-left : -2px 0 8px @shadow-color;
|
||||||
|
// @shadow-1-right : 2px 0 8px @shadow-color;
|
||||||
|
// @shadow-2 : 0 4px 12px @shadow-color;
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
// @btn-font-weight : 400;
|
||||||
|
// @btn-border-radius-base : @border-radius-base;
|
||||||
|
// @btn-border-radius-sm : @border-radius-base;
|
||||||
|
|
||||||
|
// @btn-primary-color : #fff;
|
||||||
|
// @btn-primary-bg : @primary-color;
|
||||||
|
|
||||||
|
// @btn-default-color : @text-color;
|
||||||
|
// @btn-default-bg : #fff;
|
||||||
|
// @btn-default-border : @border-color-base;
|
||||||
|
|
||||||
|
// @btn-danger-color : @error-color;
|
||||||
|
// @btn-danger-bg : @background-color-base;
|
||||||
|
// @btn-danger-border : @border-color-base;
|
||||||
|
|
||||||
|
// @btn-disable-color : @disabled-color;
|
||||||
|
// @btn-disable-bg : @disabled-bg;
|
||||||
|
// @btn-disable-border : @border-color-base;
|
||||||
|
|
||||||
|
// @btn-padding-base : 0 @padding-md - 1px;
|
||||||
|
// @btn-font-size-lg : @font-size-lg;
|
||||||
|
// @btn-font-size-sm : @font-size-base;
|
||||||
|
// @btn-padding-lg : @btn-padding-base;
|
||||||
|
// @btn-padding-sm : 0 @padding-xs - 1px;
|
||||||
|
|
||||||
|
// @btn-height-base : 32px;
|
||||||
|
// @btn-height-lg : 40px;
|
||||||
|
// @btn-height-sm : 24px;
|
||||||
|
|
||||||
|
// @btn-circle-size : @btn-height-base;
|
||||||
|
// @btn-circle-size-lg : @btn-height-lg;
|
||||||
|
// @btn-circle-size-sm : @btn-height-sm;
|
||||||
|
|
||||||
|
// @btn-group-border : @primary-5;
|
||||||
|
|
||||||
|
// Checkbox
|
||||||
|
// @checkbox-size : 16px;
|
||||||
|
// @checkbox-color : @primary-color;
|
||||||
|
|
||||||
|
// Radio
|
||||||
|
// @radio-size : 16px;
|
||||||
|
// @radio-dot-color : @primary-color;
|
||||||
|
|
||||||
|
// Radio buttons
|
||||||
|
// @radio-button-bg : @btn-default-bg;
|
||||||
|
// @radio-button-color : @btn-default-color;
|
||||||
|
// @radio-button-hover-color : @primary-5;
|
||||||
|
// @radio-button-active-color : @primary-7;
|
||||||
|
|
||||||
|
// Media queries breakpoints
|
||||||
|
// Extra small screen / phone
|
||||||
|
// @screen-xs : 480px;
|
||||||
|
// @screen-xs-min : @screen-xs;
|
||||||
|
|
||||||
|
// Small screen / tablet
|
||||||
|
// @screen-sm : 576px;
|
||||||
|
// @screen-sm-min : @screen-sm;
|
||||||
|
|
||||||
|
// Medium screen / desktop
|
||||||
|
// @screen-md : 768px;
|
||||||
|
// @screen-md-min : @screen-md;
|
||||||
|
|
||||||
|
// Large screen / wide desktop
|
||||||
|
// @screen-lg : 992px;
|
||||||
|
// @screen-lg-min : @screen-lg;
|
||||||
|
|
||||||
|
// Extra large screen / full hd
|
||||||
|
// @screen-xl : 1200px;
|
||||||
|
// @screen-xl-min : @screen-xl;
|
||||||
|
|
||||||
|
// Extra extra large screen / large descktop
|
||||||
|
// @screen-xxl : 1600px;
|
||||||
|
// @screen-xxl-min : @screen-xxl;
|
||||||
|
|
||||||
|
// provide a maximum
|
||||||
|
// @screen-xs-max : (@screen-sm-min - 1px);
|
||||||
|
// @screen-sm-max : (@screen-md-min - 1px);
|
||||||
|
// @screen-md-max : (@screen-lg-min - 1px);
|
||||||
|
// @screen-lg-max : (@screen-xl-min - 1px);
|
||||||
|
// @screen-xl-max : (@screen-xxl-min - 1px);
|
||||||
|
|
||||||
|
// Grid system
|
||||||
|
// @grid-columns : 24;
|
||||||
|
// @grid-gutter-width : 0;
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
// @layout-body-background : #f0f2f5;
|
||||||
|
// @layout-header-background : #001529;
|
||||||
|
// @layout-footer-background : @layout-body-background;
|
||||||
|
// @layout-header-height : 64px;
|
||||||
|
// @layout-header-padding : 0 50px;
|
||||||
|
// @layout-footer-padding : 24px 50px;
|
||||||
|
// @layout-sider-background : @layout-header-background;
|
||||||
|
// @layout-trigger-height : 48px;
|
||||||
|
// @layout-trigger-background : #002140;
|
||||||
|
// @layout-trigger-color : #fff;
|
||||||
|
// @layout-zero-trigger-width : 36px;
|
||||||
|
// @layout-zero-trigger-height : 42px;
|
||||||
|
// Layout light theme
|
||||||
|
// @layout-sider-background-light : #fff;
|
||||||
|
// @layout-trigger-background-light: #fff;
|
||||||
|
// @layout-trigger-color-light : @text-color;
|
||||||
|
|
||||||
|
// z-index list
|
||||||
|
// @zindex-affix : 10;
|
||||||
|
// @zindex-back-top : 10;
|
||||||
|
// @zindex-modal-mask : 1000;
|
||||||
|
// @zindex-modal : 1000;
|
||||||
|
// @zindex-notification : 1010;
|
||||||
|
// @zindex-message : 1010;
|
||||||
|
// @zindex-popover : 1030;
|
||||||
|
// @zindex-picker : 1050;
|
||||||
|
// @zindex-dropdown : 1050;
|
||||||
|
// @zindex-tooltip : 1060;
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
// @animation-duration-slow: .3s; // Modal
|
||||||
|
// @animation-duration-base: .2s;
|
||||||
|
// @animation-duration-fast: .1s; // Tooltip
|
||||||
|
|
||||||
|
// Form
|
||||||
|
// ---
|
||||||
|
// @label-required-color : @highlight-color;
|
||||||
|
// @label-color : @heading-color;
|
||||||
|
// @form-item-margin-bottom : 24px;
|
||||||
|
// @form-item-trailing-colon : true;
|
||||||
|
// @form-vertical-label-padding : 0 0 8px;
|
||||||
|
// @form-vertical-label-margin : 0;
|
||||||
|
|
||||||
|
// Input
|
||||||
|
// ---
|
||||||
|
// @input-height-base : 32px;
|
||||||
|
// @input-height-lg : 40px;
|
||||||
|
// @input-height-sm : 24px;
|
||||||
|
// @input-padding-horizontal : @control-padding-horizontal - 1px;
|
||||||
|
// @input-padding-horizontal-base: @input-padding-horizontal;
|
||||||
|
// @input-padding-horizontal-sm : @control-padding-horizontal-sm - 1px;
|
||||||
|
// @input-padding-horizontal-lg : @input-padding-horizontal;
|
||||||
|
// @input-padding-vertical-base : 4px;
|
||||||
|
// @input-padding-vertical-sm : 1px;
|
||||||
|
// @input-padding-vertical-lg : 6px;
|
||||||
|
// @input-placeholder-color : hsv(0, 0, 75%);
|
||||||
|
// @input-color : @text-color;
|
||||||
|
// @input-border-color : @border-color-base;
|
||||||
|
// @input-bg : #fff;
|
||||||
|
// @input-addon-bg : @background-color-light;
|
||||||
|
// @input-hover-border-color : @primary-color;
|
||||||
|
// @input-disabled-bg : @disabled-bg;
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
// ---
|
||||||
|
//* Tooltip max width
|
||||||
|
// @tooltip-max-width: 250px;
|
||||||
|
//** Tooltip text color
|
||||||
|
// @tooltip-color: #fff;
|
||||||
|
//** Tooltip background color
|
||||||
|
// @tooltip-bg: rgba(0, 0, 0, .75);
|
||||||
|
//** Tooltip arrow width
|
||||||
|
// @tooltip-arrow-width: 5px;
|
||||||
|
//** Tooltip distance with trigger
|
||||||
|
// @tooltip-distance: @tooltip-arrow-width - 1px + 4px;
|
||||||
|
//** Tooltip arrow color
|
||||||
|
// @tooltip-arrow-color: @tooltip-bg;
|
||||||
|
|
||||||
|
// Popover
|
||||||
|
// ---
|
||||||
|
//** Popover body background color
|
||||||
|
// @popover-bg: #fff;
|
||||||
|
//** Popover text color
|
||||||
|
// @popover-color: @text-color;
|
||||||
|
//** Popover maximum width
|
||||||
|
// @popover-min-width: 177px;
|
||||||
|
//** Popover arrow width
|
||||||
|
// @popover-arrow-width: 6px;
|
||||||
|
//** Popover arrow color
|
||||||
|
// @popover-arrow-color: @popover-bg;
|
||||||
|
//** Popover outer arrow width
|
||||||
|
//** Popover outer arrow color
|
||||||
|
// @popover-arrow-outer-color: @popover-bg;
|
||||||
|
//** Popover distance with trigger
|
||||||
|
// @popover-distance: @popover-arrow-width + 4px;
|
||||||
|
|
||||||
|
// Modal
|
||||||
|
// --
|
||||||
|
// @modal-mask-bg: rgba(0, 0, 0, 0.65);
|
||||||
|
|
||||||
|
// Progress
|
||||||
|
// --
|
||||||
|
// @progress-default-color: @processing-color;
|
||||||
|
// @progress-remaining-color: @background-color-base;
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
// ---
|
||||||
|
// @menu-inline-toplevel-item-height: 40px;
|
||||||
|
// @menu-item-height: 40px;
|
||||||
|
// @menu-collapsed-width: 80px;
|
||||||
|
// @menu-bg: @component-background;
|
||||||
|
// @menu-item-color: @text-color;
|
||||||
|
// @menu-highlight-color: @primary-color;
|
||||||
|
// @menu-item-active-bg: @item-active-bg;
|
||||||
|
// @menu-item-group-title-color: @text-color-secondary;
|
||||||
|
// dark theme
|
||||||
|
// @menu-dark-color: @text-color-secondary-dark;
|
||||||
|
// @menu-dark-bg: @layout-header-background;
|
||||||
|
// @menu-dark-arrow-color: #fff;
|
||||||
|
// @menu-dark-submenu-bg: #000c17;
|
||||||
|
// @menu-dark-highlight-color: #fff;
|
||||||
|
// @menu-dark-item-active-bg: @primary-color;
|
||||||
|
|
||||||
|
// Spin
|
||||||
|
// ---
|
||||||
|
// @spin-dot-size-sm: 14px;
|
||||||
|
// @spin-dot-size: 20px;
|
||||||
|
// @spin-dot-size-lg: 32px;
|
||||||
|
|
||||||
|
// Table
|
||||||
|
// --
|
||||||
|
// @table-header-bg: @background-color-light;
|
||||||
|
// @table-header-sort-bg: @background-color-base;
|
||||||
|
// @table-row-hover-bg: @primary-1;
|
||||||
|
// @table-selected-row-bg: #fafafa;
|
||||||
|
// @table-expanded-row-bg: #fbfbfb;
|
||||||
|
// @table-padding-vertical: 16px;
|
||||||
|
// @table-padding-horizontal: 16px;
|
||||||
|
|
||||||
|
// Tag
|
||||||
|
// --
|
||||||
|
// @tag-default-bg: @background-color-light;
|
||||||
|
// @tag-default-color: @text-color;
|
||||||
|
// @tag-font-size: @font-size-sm;
|
||||||
|
|
||||||
|
// TimePicker
|
||||||
|
// ---
|
||||||
|
// @time-picker-panel-column-width: 56px;
|
||||||
|
// @time-picker-panel-width: @time-picker-panel-column-width * 3;
|
||||||
|
// @time-picker-selected-bg: @background-color-base;
|
||||||
|
|
||||||
|
// Carousel
|
||||||
|
// ---
|
||||||
|
// @carousel-dot-width: 16px;
|
||||||
|
// @carousel-dot-height: 3px;
|
||||||
|
// @carousel-dot-active-width: 24px;
|
||||||
|
|
||||||
|
// Badge
|
||||||
|
// ---
|
||||||
|
// @badge-height: 20px;
|
||||||
|
// @badge-dot-size: 6px;
|
||||||
|
// @badge-font-size: @font-size-sm;
|
||||||
|
// @badge-font-weight: normal;
|
||||||
|
// @badge-status-size: 6px;
|
||||||
|
|
||||||
|
// Rate
|
||||||
|
// ---
|
||||||
|
// @rate-star-color: @yellow-6;
|
||||||
|
// @rate-star-bg: @border-color-split;
|
||||||
|
|
||||||
|
// Card
|
||||||
|
// ---
|
||||||
|
// @card-head-color: @heading-color;
|
||||||
|
// @card-head-background: @component-background;
|
||||||
|
// @card-head-padding: 16px;
|
||||||
|
// @card-inner-head-padding: 12px;
|
||||||
|
// @card-padding-base: 24px;
|
||||||
|
// @card-padding-wider: 32px;
|
||||||
|
// @card-actions-background: @background-color-light;
|
||||||
|
// @card-shadow: 0 2px 8px rgba(0, 0, 0, .09);
|
||||||
|
|
||||||
|
// Tabs
|
||||||
|
// ---
|
||||||
|
// '@tabs-card-head-background': '@background-color-light';
|
||||||
|
// '@tabs-card-height': '40px';
|
||||||
|
// '@tabs-card-active-color': '@primary-color';
|
||||||
|
// '@tabs-title-font-size': '@font-size-base';
|
||||||
|
// '@tabs-title-font-size-lg': '@font-size-lg';
|
||||||
|
// '@tabs-title-font-size-sm': '@font-size-base';
|
||||||
|
// '@tabs-ink-bar-color': '@primary-color';
|
||||||
|
// '@tabs-bar-margin': '0 0 16px 0';
|
||||||
|
// '@tabs-horizontal-margin': '0 32px 0 0';
|
||||||
|
// '@tabs-horizontal-padding': '12px 16px';
|
||||||
|
// '@tabs-vertical-padding': '8px 24px';
|
||||||
|
// '@tabs-vertical-margin': '0 0 16px 0';
|
||||||
|
// '@tabs-scrolling-size': '32px';
|
||||||
|
// '@tabs-highlight-color': '@primary-color';
|
||||||
|
// '@tabs-hover-color': '@primary-5';
|
||||||
|
// '@tabs-active-color': '@primary-7';
|
||||||
|
|
||||||
|
// BackTop
|
||||||
|
// ---
|
||||||
|
// '@back-top-color': '#fff';
|
||||||
|
// '@back-top-bg': '@text-color-secondary';
|
||||||
|
// '@back-top-hover-bg': '@text-color';
|
||||||
|
|
||||||
|
// Avatar
|
||||||
|
// ---
|
||||||
|
// '@avatar-size-base': '32px';
|
||||||
|
// '@avatar-size-lg': '40px';
|
||||||
|
// '@avatar-size-sm': '24px';
|
||||||
|
// '@avatar-font-size-base': '18px';
|
||||||
|
// '@avatar-font-size-lg': '24px';
|
||||||
|
// '@avatar-font-size-sm': '14px';
|
||||||
|
// '@avatar-bg': '#ccc';
|
||||||
|
// '@avatar-color': '#fff';
|
||||||
|
// '@avatar-border-radius': '@border-radius-base;'
|
||||||
|
|
||||||
|
// Switch
|
||||||
|
// ---
|
||||||
|
// '@switch-height': '22px';
|
||||||
|
// '@switch-sm-height': '16px';
|
||||||
|
// '@switch-sm-checked-margin-left': '-(@switch-sm-height - 3px)';
|
||||||
|
// '@switch-disabled-opacity': '0.4';
|
||||||
|
// '@switch-color': '@primary-color';
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
// ---
|
||||||
|
// '@pagination-item-size': '32px';
|
||||||
|
// '@pagination-item-size-sm': '24px';
|
||||||
|
// '@pagination-font-family': 'Arial';
|
||||||
|
// '@pagination-font-weight-active': '500';
|
||||||
|
|
||||||
|
// Breadcrumb
|
||||||
|
// ---
|
||||||
|
// '@breadcrumb-base-color': '@text-color-secondary';
|
||||||
|
// '@breadcrumb-last-item-color': '@text-color';
|
||||||
|
// '@breadcrumb-font-size': '@font-size-base';
|
||||||
|
// '@breadcrumb-icon-font-size': '@font-size-sm';
|
||||||
|
// '@breadcrumb-link-color': '@text-color-secondary';
|
||||||
|
// '@breadcrumb-link-color-hover': '@primary-5';
|
||||||
|
// '@breadcrumb-separator-color': '@text-color-secondary';
|
||||||
|
// '@breadcrumb-separator-margin': '0 @padding-xs';
|
||||||
|
|
||||||
|
// Slider
|
||||||
|
// ---
|
||||||
|
// '@slider-margin': '14px 6px 10px';
|
||||||
|
// '@slider-rail-background-color': '@background-color-base';
|
||||||
|
// '@slider-rail-background-color-hover': '#e1e1e1';
|
||||||
|
// '@slider-track-background-color': '@primary-3';
|
||||||
|
// '@slider-track-background-color-hover': '@primary-4';
|
||||||
|
// '@slider-handle-color': '@primary-3';
|
||||||
|
// '@slider-handle-color-hover': '@primary-4';
|
||||||
|
// '@slider-handle-color-focus': 'tint(@primary-color, 20%)';
|
||||||
|
// '@slider-handle-color-focus-shadow': 'tint(@primary-color, 50%)';
|
||||||
|
// '@slider-handle-color-tooltip-open': '@primary-color';
|
||||||
|
// '@slider-dot-border-color': '@border-color-split';
|
||||||
|
// '@slider-dot-border-color-active': 'tint(@primary-color, 50%)';
|
||||||
|
// '@slider-disabled-color': '@disabled-color';
|
||||||
|
// '@slider-disabled-background-color': '@component-background';
|
||||||
|
|
||||||
|
// Collapse
|
||||||
|
// ---
|
||||||
|
// '@collapse-header-padding': '12px 0 12px 40px';
|
||||||
|
// '@collapse-header-bg': '@background-color-light';
|
||||||
|
// '@collapse-content-padding': '@padding-md';
|
||||||
|
// '@collapse-content-bg': '@component-background';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
})(config, env);
|
||||||
|
return config;
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
version: '3.5'
|
||||||
|
services:
|
||||||
|
prod-build:
|
||||||
|
container_name: prod-build
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
# - REACT_APP_DOMAIN=http://mobilecorelb-1807532632.ap-southeast-1.elb.amazonaws.com/
|
||||||
|
# - REACT_APP_DOMAIN=https://mobilelink.unioil.com
|
||||||
|
- REACT_APP_DOMAIN=https://mobileapi.unioilapps.com
|
||||||
|
- REACT_APP_PATH=api/cms
|
||||||
|
- REACT_APP_IMG_PATH=unioilQA/
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8089:80
|
||||||
|
volumes:
|
||||||
|
- '.:/usr/src/app'
|
||||||
|
- '/usr/src/app/node_modules'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
version: '3.5'
|
||||||
|
services:
|
||||||
|
public-build:
|
||||||
|
container_name: public-build
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- REACT_APP_PUBLIC=true
|
||||||
|
# - PUBLIC_URL=/paypal
|
||||||
|
- REACT_APP_DOMAIN=https://mobileapid.unioil.com
|
||||||
|
- REACT_APP_PATH=api/cms
|
||||||
|
- REACT_APP_IMG_PATH=unioilQA
|
||||||
|
- GENERATE_SOURCEMAP=false
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8095:80
|
||||||
|
volumes:
|
||||||
|
- '.:/usr/src/app'
|
||||||
|
- '/usr/src/app/node_modules'
|
|
@ -0,0 +1,80 @@
|
||||||
|
version: '3.5'
|
||||||
|
services:
|
||||||
|
test-build:
|
||||||
|
container_name: test-build
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- REACT_APP_DOMAIN=http://192.168.0.68
|
||||||
|
- REACT_APP_PATH=unioilQA/public/index.php/api/cms
|
||||||
|
- REACT_APP_IMG_PATH=unioilQA/
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8089:80
|
||||||
|
volumes:
|
||||||
|
- '.:/usr/src/app'
|
||||||
|
- '/usr/src/app/node_modules'
|
||||||
|
dev-build:
|
||||||
|
container_name: dev-build
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- REACT_APP_DOMAIN=http://192.168.0.68
|
||||||
|
- REACT_APP_PATH=UniOilLoyaltyApp-BackEnd/public/index.php/api/cms
|
||||||
|
- REACT_APP_IMG_PATH=UniOilLoyaltyApp-BackEnd
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8081:80
|
||||||
|
volumes:
|
||||||
|
- '.:/usr/src/app'
|
||||||
|
- '/usr/src/app/node_modules'
|
||||||
|
public-dev-build:
|
||||||
|
container_name: public-dev-build
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- REACT_APP_PUBLIC=true
|
||||||
|
- REACT_APP_DOMAIN=http://192.168.0.68
|
||||||
|
- REACT_APP_PATH=UniOilLoyaltyApp-BackEnd/public/index.php/api/cms
|
||||||
|
- REACT_APP_IMG_PATH=UniOilLoyaltyApp-BackEnd/
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8093:80
|
||||||
|
volumes:
|
||||||
|
- '.:/usr/src/app'
|
||||||
|
- '/usr/src/app/node_modules'
|
||||||
|
public-test-build:
|
||||||
|
container_name: public-test-build
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- REACT_APP_PUBLIC=true
|
||||||
|
- REACT_APP_DOMAIN=http://192.168.0.68
|
||||||
|
- REACT_APP_PATH=unioilQA/public/index.php/api/cms
|
||||||
|
- REACT_APP_IMG_PATH=unioilQA
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8090:80
|
||||||
|
volumes:
|
||||||
|
- '.:/usr/src/app'
|
||||||
|
- '/usr/src/app/node_modules'
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2015",
|
||||||
|
"baseUrl": "src",
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"build",
|
||||||
|
"public",
|
||||||
|
"__test__"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
{
|
||||||
|
"posts": [
|
||||||
|
{
|
||||||
|
"id": 1441,
|
||||||
|
"title": "11-json-server",
|
||||||
|
"author": "11-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "14-Title",
|
||||||
|
"author": "14-Title Data",
|
||||||
|
"id": 4414
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes wafe",
|
||||||
|
"author": "weataewtaew",
|
||||||
|
"id": 1446
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "1-json-server-test",
|
||||||
|
"author": "werewrwe",
|
||||||
|
"id": 1447
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 447,
|
||||||
|
"title": "7-json-server",
|
||||||
|
"author": "7-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 449,
|
||||||
|
"title": "9-json-server",
|
||||||
|
"author": "9-tserver-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14440,
|
||||||
|
"title": "10-json-server",
|
||||||
|
"author": "10-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1441,
|
||||||
|
"title": "11-json-server",
|
||||||
|
"author": "11-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12443,
|
||||||
|
"title": "12-json-server",
|
||||||
|
"author": "12-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes",
|
||||||
|
"author": "ewrwer",
|
||||||
|
"id": 1334444
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "14-Title",
|
||||||
|
"author": "14-Title Data",
|
||||||
|
"id": 143444
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "fytyiftyfy",
|
||||||
|
"author": "ftftcftc",
|
||||||
|
"id": 15344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes wafe",
|
||||||
|
"author": "weataewtaew",
|
||||||
|
"id": 163444
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "1-json-server-test",
|
||||||
|
"author": "werewrwe",
|
||||||
|
"id": 17344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 744,
|
||||||
|
"title": "7-json-server",
|
||||||
|
"author": "7-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 944,
|
||||||
|
"title": "9-json-server",
|
||||||
|
"author": "9-tserver-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 104,
|
||||||
|
"title": "10-json-server",
|
||||||
|
"author": "10-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 141,
|
||||||
|
"title": "11-json-server",
|
||||||
|
"author": "11-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1244,
|
||||||
|
"title": "12-json-server",
|
||||||
|
"author": "12-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes",
|
||||||
|
"author": "ewrwer",
|
||||||
|
"id": 1344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "14-Title",
|
||||||
|
"author": "14-Title Data",
|
||||||
|
"id": 1444
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "fytyiftyfy",
|
||||||
|
"author": "ftftcftc",
|
||||||
|
"id": 1544
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes wafe",
|
||||||
|
"author": "weataewtaew",
|
||||||
|
"id": 164
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "1-json-server-test",
|
||||||
|
"author": "werewrwe",
|
||||||
|
"id": 1744
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 744,
|
||||||
|
"title": "7-json-server",
|
||||||
|
"author": "7-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 944,
|
||||||
|
"title": "9-json-server",
|
||||||
|
"author": "9-tserver-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1044,
|
||||||
|
"title": "10-json-server",
|
||||||
|
"author": "10-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1144,
|
||||||
|
"title": "11-json-server",
|
||||||
|
"author": "11-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12344,
|
||||||
|
"title": "12-json-server",
|
||||||
|
"author": "12-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes",
|
||||||
|
"author": "ewrwer",
|
||||||
|
"id": 13344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "14-Title",
|
||||||
|
"author": "14-Title Data",
|
||||||
|
"id": 14344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "fytyiftyfy",
|
||||||
|
"author": "ftftcftc",
|
||||||
|
"id": 15344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes wafe",
|
||||||
|
"author": "weataewtaew",
|
||||||
|
"id": 16344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "1-json-server-test",
|
||||||
|
"author": "werewrwe",
|
||||||
|
"id": 17344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 744,
|
||||||
|
"title": "7-json-server",
|
||||||
|
"author": "7-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 944,
|
||||||
|
"title": "9-json-server",
|
||||||
|
"author": "9-tserver-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1044,
|
||||||
|
"title": "10-json-server",
|
||||||
|
"author": "10-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 114444,
|
||||||
|
"title": "11-json-server",
|
||||||
|
"author": "11-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1244,
|
||||||
|
"title": "12-json-server",
|
||||||
|
"author": "12-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes",
|
||||||
|
"author": "ewrwer",
|
||||||
|
"id": 1344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "14-Title",
|
||||||
|
"author": "14-Title Data",
|
||||||
|
"id": 1444
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "fytyiftyfy",
|
||||||
|
"author": "ftftcftc",
|
||||||
|
"id": 1544
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes wafe",
|
||||||
|
"author": "weataewtaew",
|
||||||
|
"id": 1644
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "1-json-server-test",
|
||||||
|
"author": "werewrwe",
|
||||||
|
"id": 174
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 744,
|
||||||
|
"title": "7-json-server",
|
||||||
|
"author": "7-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 944,
|
||||||
|
"title": "9-json-server",
|
||||||
|
"author": "9-tserver-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 104,
|
||||||
|
"title": "10-json-server",
|
||||||
|
"author": "10-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 114,
|
||||||
|
"title": "11-json-server",
|
||||||
|
"author": "11-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1234,
|
||||||
|
"title": "12-json-server",
|
||||||
|
"author": "12-server-deauthor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes",
|
||||||
|
"author": "ewrwer",
|
||||||
|
"id": 1334
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "14-Title",
|
||||||
|
"author": "14-Title Data",
|
||||||
|
"id": 1434
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "fytyiftyfy",
|
||||||
|
"author": "ftftcftc",
|
||||||
|
"id": 15344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "tes wafe",
|
||||||
|
"author": "weataewtaew",
|
||||||
|
"id": 1634
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "1-json-server-test",
|
||||||
|
"author": "werewrwe",
|
||||||
|
"id": 1734
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comments": [],
|
||||||
|
"profile": {
|
||||||
|
"name": "typicode"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"name": "my-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "4.0.0",
|
||||||
|
"@react-google-maps/api": "^1.9.7",
|
||||||
|
"antd": "^3.9.1",
|
||||||
|
"antd-input-password": "^0.3.0",
|
||||||
|
"axios": "^0.18.0",
|
||||||
|
"crypto-js": "^3.1.9-1",
|
||||||
|
"dsbridge": "^3.1.3",
|
||||||
|
"filesize": "^3.6.1",
|
||||||
|
"formik": "^1.2.0",
|
||||||
|
"history": "^4.7.2",
|
||||||
|
"js-file-download": "^0.4.4",
|
||||||
|
"libsodium-wrappers": "^0.7.3",
|
||||||
|
"moment": "^2.27.0",
|
||||||
|
"query-string": "^5.1.1",
|
||||||
|
"react": "^16.7.0",
|
||||||
|
"react-copy-to-clipboard": "^5.0.1",
|
||||||
|
"react-csv": "^2.0.3",
|
||||||
|
"react-csv-reader": "^3.1.0",
|
||||||
|
"react-device-detect": "^1.6.1",
|
||||||
|
"react-dom": "^16.5.0",
|
||||||
|
"react-geocode": "^0.2.1",
|
||||||
|
"react-google-autocomplete": "^1.2.6",
|
||||||
|
"react-google-map": "^3.1.1",
|
||||||
|
"react-google-maps": "^9.4.5",
|
||||||
|
"react-helmet": "^5.2.0",
|
||||||
|
"react-idle-timer": "^4.0.9",
|
||||||
|
"react-input-mask": "^2.0.4",
|
||||||
|
"react-loadable": "^5.5.0",
|
||||||
|
"react-papaparse": "^3.7.1",
|
||||||
|
"react-redux": "^5.0.7",
|
||||||
|
"react-router-dom": "^4.2.2",
|
||||||
|
"react-router-redux": "^4.0.8",
|
||||||
|
"react-scripts": "1.1.5",
|
||||||
|
"redux": "^4.0.0",
|
||||||
|
"redux-logger": "^3.0.6",
|
||||||
|
"redux-saga": "^0.16.0",
|
||||||
|
"styled-components": "^3.4.5",
|
||||||
|
"universal-cookie": "3.0.4",
|
||||||
|
"yup": "^0.26.3"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-app-rewired start",
|
||||||
|
"build": "react-app-rewired build",
|
||||||
|
"test": "react-app-rewired test --env=jsdom",
|
||||||
|
"eject": "react-app-rewired eject",
|
||||||
|
"dev": "docker-compose up -d --build",
|
||||||
|
"dev:stop": "docker-compose down",
|
||||||
|
"prod": "docker-compose -f docker-compose-prod.yml up -d --build",
|
||||||
|
"prod:stop": "docker-compose -f docker-compose-prod.yml down",
|
||||||
|
"flow": "flow"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-plugin-import": "^1.7.0",
|
||||||
|
"debug": "^3.1.0",
|
||||||
|
"flow-bin": "0.80.0",
|
||||||
|
"less-plugin-variables-output": "^1.2.0",
|
||||||
|
"react-app-rewire-less": "^2.1.3",
|
||||||
|
"react-app-rewired": "^1.6.2"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,69 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon_unioil.ico">
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
// For Adroid
|
||||||
|
//window.AndroidTopUpSuccess = AndroidTopUpFailed;
|
||||||
|
window.AndroidTopUpFailed = AndroidTopUpFailed;
|
||||||
|
|
||||||
|
// For iOS
|
||||||
|
window.iOStopUpSuccess = iOStopUpSuccess;
|
||||||
|
window.iOStopUpFailed = iOStopUpFailed;
|
||||||
|
|
||||||
|
function AndroidTopUpFailed() {
|
||||||
|
// For Adroid
|
||||||
|
AndroidInterface.returnTopUpPage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function iOStopUpSuccess() {
|
||||||
|
//alert('top up success ' );
|
||||||
|
// For iOS
|
||||||
|
window.webkit.messageHandlers.backToHomePage.postMessage({'ButtonId':'clickMeButton'})
|
||||||
|
}
|
||||||
|
|
||||||
|
function iOStopUpFailed() {
|
||||||
|
//alert('back to top up' );
|
||||||
|
// For iOS
|
||||||
|
window.webkit.messageHandlers.backToTopUp.postMessage({'ButtonId':'clickMeButton'})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon_unioil.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "./index.html",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import { BrowserRouter as Router, Route, Redirect, Switch, withRouter } from 'react-router-dom';
|
||||||
|
import Loadable from 'react-loadable';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import LoginLayoutRoute from './components/Login/Routes';
|
||||||
|
import DashboardRoute from './components/Dashboard/Routes';
|
||||||
|
import Loading from './components/Loading';
|
||||||
|
|
||||||
|
import { getCookie } from './utils/cookie';
|
||||||
|
import { customAction } from './actions';
|
||||||
|
import { API_UNI_OIL, API_POST } from 'utils/Api';
|
||||||
|
|
||||||
|
const AsyncLogin = Loadable({
|
||||||
|
loader: () => import('./containers/public/Login'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncRegistration = Loadable({
|
||||||
|
loader: () => import('./containers/public/Registration'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncChangePassword = Loadable({
|
||||||
|
loader: () => import('./containers/public/ChangePassword'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncPublicTopSuccessPage = Loadable({
|
||||||
|
loader: () => import('./containers/public/PublicTopSuccessPage'),
|
||||||
|
loading: () => {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncPublicTopErrorPage = Loadable({
|
||||||
|
loader: () => import('./containers/public/PublicTopErrorPage'),
|
||||||
|
loading: () => {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncMyProfile = Loadable({
|
||||||
|
loader: () => import('./containers/private/MyProfile'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncNotification = Loadable({
|
||||||
|
loader: () => import('./containers/private/Notifications'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncUserManagement = Loadable({
|
||||||
|
loader: () => import('./containers/private/UserManagement'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncMemberManagement = Loadable({
|
||||||
|
loader: () => import('./containers/private/MemberManagement'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncPhotoSlider = Loadable({
|
||||||
|
loader: () => import('./containers/private/HomePage/PhotoSlider'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncPromotions = Loadable({
|
||||||
|
loader: () => import('./containers/private/Promotions'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncTopUp = Loadable({
|
||||||
|
loader: () => import('./containers/private/TopUp'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncCardTypes = Loadable({
|
||||||
|
loader: () => import('./containers/private/AboutUs'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncReports = Loadable({
|
||||||
|
loader: () => import('./containers/private/Reports'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncSystemPreferences = Loadable({
|
||||||
|
loader: () => import('./containers/private/SystemPreferences'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AsyncPage404 = Loadable({
|
||||||
|
loader: () => import('./components/PageError/404'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
const AsyncLocator = Loadable({
|
||||||
|
loader: () => import('./containers/private/StationLocator/Location'),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
const AsyncBranches = Loadable({
|
||||||
|
loader: () => import('./containers/private/StationLocator/Branches'),
|
||||||
|
loading: Loading,
|
||||||
|
})
|
||||||
|
|
||||||
|
const AsyncFuels = Loadable({
|
||||||
|
loader: () => import('./containers/private/StationLocator/Fuels'),
|
||||||
|
loading: Loading,
|
||||||
|
})
|
||||||
|
|
||||||
|
const CaptureRouteNotFound = withRouter(({ children, location }) => {
|
||||||
|
return location && location.state && location.state.pageNotFound ? <AsyncPage404 /> : children;
|
||||||
|
});
|
||||||
|
|
||||||
|
const publicRoutes = [
|
||||||
|
'/',
|
||||||
|
'/login',
|
||||||
|
'/registration',
|
||||||
|
'/forgot-password',
|
||||||
|
'/change-password',
|
||||||
|
'/topup-success-page',
|
||||||
|
'/topup-error-page',
|
||||||
|
];
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
state = {
|
||||||
|
accessAuth: false,
|
||||||
|
mounting: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount = async () => {
|
||||||
|
if (getCookie('TOKEN')) {
|
||||||
|
let { history, customAction } = this.props;
|
||||||
|
let { replace, location } = history;
|
||||||
|
|
||||||
|
API_UNI_OIL.defaults.headers.common['Authorization'] = `Bearer ${getCookie('TOKEN').token}`;
|
||||||
|
//customAction({type: 'LOGIN_SUCCESS' });
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await API_POST(`adminProfile`);
|
||||||
|
response.data.data['userInfo'] = { ...response.data.data };
|
||||||
|
|
||||||
|
customAction({
|
||||||
|
type: 'LOGIN_SUCCESS',
|
||||||
|
payload: { ...response.data.data },
|
||||||
|
});
|
||||||
|
} catch ({ response: error }) {
|
||||||
|
//notification.error({ message: "Error", description: "Something went wrong loading user data." , duration: 20, });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicRoutes.includes(location.pathname)) replace('/user-management');
|
||||||
|
}
|
||||||
|
this.setState({ mounting: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
console.log('====================================');
|
||||||
|
console.log(process.env.REACT_APP_API, process.env.REACT_APP_IMG_URL, process.env.REACT_APP_PUBLIC, 'API LIST!!!');
|
||||||
|
console.log('====================================');
|
||||||
|
if (this.state.mounting) return null;
|
||||||
|
|
||||||
|
if (process.env.REACT_APP_PUBLIC === 'false') {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<Switch>
|
||||||
|
<Redirect exact from='/' to='/login' />
|
||||||
|
<LoginLayoutRoute exact path='/login' component={AsyncLogin} />
|
||||||
|
<LoginLayoutRoute exact path='/registration' component={AsyncRegistration} />
|
||||||
|
<LoginLayoutRoute exact path='/change-password' component={AsyncChangePassword} />
|
||||||
|
{/* PRIVATE ROUTES */}
|
||||||
|
<DashboardRoute path='/user-management' component={AsyncUserManagement} />
|
||||||
|
<DashboardRoute path='/notifications' component={AsyncNotification} />
|
||||||
|
<DashboardRoute path='/member-management' component={AsyncMemberManagement} />
|
||||||
|
<DashboardRoute path='/home-page' component={AsyncPhotoSlider} />
|
||||||
|
<DashboardRoute path='/promotions' component={AsyncPromotions} />
|
||||||
|
<DashboardRoute path='/top-up' component={AsyncTopUp} />
|
||||||
|
<DashboardRoute path='/about-us' component={AsyncCardTypes} />
|
||||||
|
<DashboardRoute path='/reports' component={AsyncReports} />
|
||||||
|
<DashboardRoute path='/stations' component={AsyncLocator}/>
|
||||||
|
<DashboardRoute path='/branches' component={AsyncBranches}/>
|
||||||
|
<DashboardRoute path='/fuels' component={AsyncFuels} />
|
||||||
|
<DashboardRoute path='/system-parameters' component={AsyncSystemPreferences} />
|
||||||
|
<DashboardRoute path='/my-profile' component={AsyncMyProfile} />
|
||||||
|
<Route exact path='/topup-success-page' component={AsyncPublicTopSuccessPage} />
|
||||||
|
<Route exact path='/topup-error-page' component={AsyncPublicTopErrorPage} />
|
||||||
|
<Route exact path='/404' component={AsyncPage404} />
|
||||||
|
<DashboardRoute path='*' component={AsyncPage404} />
|
||||||
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<Switch>
|
||||||
|
<Redirect exact from='/' to='/topup-success-page' />
|
||||||
|
<Redirect exact from='/login' to='/topup-success-page' />
|
||||||
|
<Route exact path='/topup-success-page' component={AsyncPublicTopSuccessPage} />
|
||||||
|
<Route exact path='/topup-error-page' component={AsyncPublicTopErrorPage} />
|
||||||
|
<Route exact path='/404' component={AsyncPage404} />
|
||||||
|
<DashboardRoute path='*' component={AsyncPage404} />
|
||||||
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App = connect(
|
||||||
|
(state) => ({
|
||||||
|
isAuthenticated: state.auth.isAuthenticated,
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(App);
|
||||||
|
|
||||||
|
export default withRouter(App);
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
ReactDOM.render(<App />, div);
|
||||||
|
ReactDOM.unmountComponentAtNode(div);
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
export function customAction({type, payload}) {
|
||||||
|
return {
|
||||||
|
type: type,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 224 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57.834 57.955"><defs><style>.a{fill:#f4825c;stroke:#707070;}.b{fill:#e74610;}.c{stroke:none;}.d{fill:none;}</style></defs><g transform="translate(-0.498 0.199)"><g transform="translate(0 0)"><g class="a" transform="translate(1.009 0.934)"><circle class="c" cx="27.989" cy="27.989" r="27.989"/><circle class="d" cx="27.989" cy="27.989" r="27.489"/></g><g transform="translate(-26.502 -27.6)"><g transform="translate(27 27.4)"><path class="b" d="M55.917,85.355A28.978,28.978,0,1,1,84.834,56.317,29.012,29.012,0,0,1,55.917,85.355Zm0-54.31A25.272,25.272,0,1,0,81.189,56.317,25.253,25.253,0,0,0,55.917,31.045Z" transform="translate(-27 -27.4)"/><g transform="translate(18.529 18.65)"><path class="b" d="M59.638,63.344a1.827,1.827,0,0,1-1.337-.486l-8.5-8.5a1.762,1.762,0,0,1,0-2.551l8.5-8.5a1.8,1.8,0,0,1,2.551,2.551l-7.168,7.168,7.168,7.169a1.762,1.762,0,0,1,0,2.551A1.234,1.234,0,0,1,59.638,63.344Z" transform="translate(-40.745 -42.75)"/><path class="b" d="M44.133,63.323a1.827,1.827,0,0,1-1.336-.486,1.762,1.762,0,0,1,0-2.551l7.169-7.169L42.8,45.948A1.8,1.8,0,0,1,45.348,43.4l8.5,8.5a1.762,1.762,0,0,1,0,2.551l-8.5,8.5A1.814,1.814,0,0,1,44.133,63.323Z" transform="translate(-42.25 -42.729)"/></g></g></g></g></g></svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58.184 58.184"><defs><style>.a{fill:#56b68b;stroke:#707070;}.b{fill:#018952;fill-rule:evenodd;}.c{stroke:none;}.d{fill:none;}</style></defs><g transform="translate(-138.502 -248.613)"><g transform="translate(138.502 248.613)"><g transform="translate(0 0)"><g class="a" transform="translate(1.009 0.934)"><circle class="c" cx="27.989" cy="27.989" r="27.989"/><circle class="d" cx="27.989" cy="27.989" r="27.489"/></g><path class="b" d="M47.664,22.747l2.3,2.3L31.8,43.209l-2.3,2.3-2.3-2.3-8.978-8.978,2.3-2.3L29.5,40.913ZM34.092,5A29.092,29.092,0,1,1,5,34.092,29.079,29.079,0,0,1,34.092,5Zm0,3.967A25.16,25.16,0,1,1,8.967,34.092,25.173,25.173,0,0,1,34.092,8.967Z" transform="translate(-5 -5)"/></g></g></g></svg>
|
After Width: | Height: | Size: 763 B |
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -0,0 +1,683 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
Input,
|
||||||
|
Icon,
|
||||||
|
Divider,
|
||||||
|
Tooltip,
|
||||||
|
Popconfirm,
|
||||||
|
message,
|
||||||
|
Button,
|
||||||
|
Pagination,
|
||||||
|
Row,
|
||||||
|
Col
|
||||||
|
} from "antd";
|
||||||
|
//import { callApi } from "utils/Api";
|
||||||
|
import { Link, withRouter } from "react-router-dom";
|
||||||
|
import querystring from "querystring";
|
||||||
|
import { encrypt, decrypt } from 'utils/encrypto'
|
||||||
|
// const querystring = require("querystring");
|
||||||
|
const { Column, ColumnGroup } = Table;
|
||||||
|
const Search = Input.Search;
|
||||||
|
|
||||||
|
class CustomTable extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
data: [],
|
||||||
|
loading: false,
|
||||||
|
filteredInfo: null,
|
||||||
|
sortedInfo: null,
|
||||||
|
pageName: '_page',
|
||||||
|
currentPage: 1,
|
||||||
|
sizeName: '_limit',
|
||||||
|
pageSize: 10,
|
||||||
|
totalData: 100,
|
||||||
|
searchValue: null,
|
||||||
|
headerColumns: props.columns.map(column => {
|
||||||
|
if (column.sorter) {
|
||||||
|
column.sorter = (a, b) => a[column.dataIndex] - b[column.dataIndex];
|
||||||
|
column.sortOrder = null;
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.handleInitialLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||||
|
const { currentPage, pageSize, pageName } = this.state;
|
||||||
|
|
||||||
|
if (prevProps.location.search !== this.props.location.search) {
|
||||||
|
|
||||||
|
if (this.props.location.search === "") {
|
||||||
|
this.handleclearAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevState.sortedInfo !== this.state.sortedInfo) {
|
||||||
|
//Sort Columns Function
|
||||||
|
this.setState({
|
||||||
|
headerColumns: this.state.headerColumns.map(column => {
|
||||||
|
if (
|
||||||
|
this.state.sortedInfo &&
|
||||||
|
this.state.sortedInfo.columnKey === column.dataIndex
|
||||||
|
) {
|
||||||
|
column.sortOrder = this.state.sortedInfo.order;
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
handleInitialLoad = () => {
|
||||||
|
const { search } = this.props.location;
|
||||||
|
const { currentPage, pageSize, pageName, sizeName } = this.state;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
let parsed = querystring.parse(search.substring(1));
|
||||||
|
console.log('====================================');
|
||||||
|
console.log(parsed,pageName, sizeName,parsed[pageName], parsed[sizeName], "&&&&&&SEARCH!!");
|
||||||
|
console.log('====================================');
|
||||||
|
|
||||||
|
if (parsed) {
|
||||||
|
if (parsed[pageName] && parsed[sizeName] && parsed.q && parsed._sort && parsed._order ) {
|
||||||
|
this.handleUpdateData({
|
||||||
|
page: parseInt(parsed[pageName]),
|
||||||
|
size: parseInt(parsed[sizeName]),
|
||||||
|
search: parsed.q
|
||||||
|
});
|
||||||
|
} else if (parsed[pageName] && parsed[sizeName] && parsed.q) {
|
||||||
|
alert("Search")
|
||||||
|
this.handleUpdateData({
|
||||||
|
page: parseInt(parsed[pageName]),
|
||||||
|
size: parseInt(parsed[sizeName]),
|
||||||
|
search: parsed.q
|
||||||
|
});
|
||||||
|
} else if (parsed[pageName] && parsed[sizeName]) {
|
||||||
|
this.handleUpdateData({
|
||||||
|
page: parseInt(parsed[pageName]),
|
||||||
|
size: parseInt(parsed[sizeName]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// this.fetch({
|
||||||
|
// ...parsed
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.handleUpdateData({
|
||||||
|
page: currentPage,
|
||||||
|
size: pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
const { currentPage, pageSize, searchValue } = this.state;
|
||||||
|
console.log("====================================");
|
||||||
|
console.log(filters, sorter, "tesadasdas");
|
||||||
|
console.log("====================================");
|
||||||
|
// this.setState({
|
||||||
|
// filteredInfo: filters,
|
||||||
|
// sortedInfo: sorter
|
||||||
|
// });
|
||||||
|
if (Object.keys(sorter).length !== 0) {
|
||||||
|
// this.fetch({
|
||||||
|
// _page: currentPage,
|
||||||
|
// _limit: pageSize,
|
||||||
|
// _sort: sorter.field,
|
||||||
|
// _order: sorter.order === "ascend" ? "asc" : "desc",
|
||||||
|
|
||||||
|
// ...filters
|
||||||
|
// });
|
||||||
|
|
||||||
|
if (searchValue) {
|
||||||
|
this.handleUpdateData({
|
||||||
|
sort: sorter,
|
||||||
|
filter: filters,
|
||||||
|
search: searchValue
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.handleUpdateData({ sort: sorter, filter: filters });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (searchValue) {
|
||||||
|
this.handleUpdateData({ filter: filters, search: searchValue });
|
||||||
|
} else {
|
||||||
|
this.handleUpdateData({ filter: filters });
|
||||||
|
}
|
||||||
|
// this.fetch({
|
||||||
|
// _page: currentPage,
|
||||||
|
// _limit: pageSize,
|
||||||
|
// ...filters
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch = async (params = {}) => {
|
||||||
|
let { url, history } = this.props;
|
||||||
|
const stringified = querystring.stringify(params);
|
||||||
|
|
||||||
|
console.log("GGGGG3333", url.default, stringified, params, window.location);
|
||||||
|
this.setState({ loading: true });
|
||||||
|
// history.push(`${url.default}?${stringified}`);
|
||||||
|
history.push({
|
||||||
|
pathname: url.default,
|
||||||
|
search: stringified
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
// let response = await callApi({
|
||||||
|
// url: url.default,
|
||||||
|
// params: {
|
||||||
|
// _page: params._page,
|
||||||
|
// _limit: params._limit,
|
||||||
|
// ...params
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (response.status === 200) {
|
||||||
|
// this.setState({
|
||||||
|
// loading: false,
|
||||||
|
// data: response.data,
|
||||||
|
// totalData: response.data.total ? response.data.total : 100
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleUpdateData = ({ search, sort, page, size, filter }) => {
|
||||||
|
const {
|
||||||
|
currentPage,
|
||||||
|
pageSize,
|
||||||
|
filteredInfo,
|
||||||
|
sortedInfo,
|
||||||
|
searchValue
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
console.log("====================================");
|
||||||
|
console.log(
|
||||||
|
search, sort, page, size, filter,
|
||||||
|
"WWOOOPS!!"
|
||||||
|
);
|
||||||
|
console.log("====================================");
|
||||||
|
|
||||||
|
if (search && sort && filter) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: filter,
|
||||||
|
sortedInfo: sort,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search,
|
||||||
|
_sort: sort.field,
|
||||||
|
_order: sort.order === "ascend" ? "asc" : "desc",
|
||||||
|
...filter
|
||||||
|
});
|
||||||
|
|
||||||
|
//filteredInfo value
|
||||||
|
} else if (filter) {
|
||||||
|
if (sort) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: filter,
|
||||||
|
sortedInfo: sort,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
_sort: sort.field,
|
||||||
|
_order: sort.order === "ascend" ? "asc" : "desc",
|
||||||
|
...filter
|
||||||
|
});
|
||||||
|
} else if (search) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: filter,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search,
|
||||||
|
...filter
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: filter,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
...filter
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//sortedInfo value
|
||||||
|
} else if (sort) {
|
||||||
|
if (filter) {
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: filter,
|
||||||
|
sortedInfo: sort,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
_sort: sort.field,
|
||||||
|
_order: sort.order === "ascend" ? "asc" : "desc",
|
||||||
|
...filter
|
||||||
|
});
|
||||||
|
} else if (search) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
sortedInfo: sort,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search,
|
||||||
|
_sort: sort.field,
|
||||||
|
_order: sort.order === "ascend" ? "asc" : "desc"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
sortedInfo: sort,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
_sort: sort.field,
|
||||||
|
_order: sort.order === "ascend" ? "asc" : "desc"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//search Value
|
||||||
|
} else if (search) {
|
||||||
|
if (filter) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: filter,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search,
|
||||||
|
...filter
|
||||||
|
});
|
||||||
|
} else if (sort) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
sortedInfo: sort,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search,
|
||||||
|
_sort: sort.field,
|
||||||
|
_order: sort.order === "ascend" ? "asc" : "desc"
|
||||||
|
});
|
||||||
|
} else if (page && size) {
|
||||||
|
alert(page, size, "OOOPS@")
|
||||||
|
this.setState({
|
||||||
|
currentPage: page,
|
||||||
|
pageSize: size,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
this.fetch({
|
||||||
|
_page: page,
|
||||||
|
_limit: size,
|
||||||
|
q: search
|
||||||
|
});
|
||||||
|
} else if (page) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
currentPage: page,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: page,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search
|
||||||
|
});
|
||||||
|
} else if (size) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
pageSize: size,
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: size,
|
||||||
|
q: search
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
searchValue: search,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize,
|
||||||
|
q: search
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (page && size) {
|
||||||
|
this.setState({
|
||||||
|
currentPage: page,
|
||||||
|
pageSize: size,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: page,
|
||||||
|
_limit: size
|
||||||
|
});
|
||||||
|
} else if (page) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
currentPage: page,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: page,
|
||||||
|
_limit: pageSize
|
||||||
|
});
|
||||||
|
} else if (size) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
pageSize: size,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: size
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.fetch({
|
||||||
|
_page: currentPage,
|
||||||
|
_limit: pageSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSearch = e => {
|
||||||
|
const { currentPage, pageSize, filteredInfo, sortedInfo } = this.state;
|
||||||
|
console.log("====================================");
|
||||||
|
console.log(e);
|
||||||
|
console.log("====================================");
|
||||||
|
// this.setState({
|
||||||
|
// searchValue: e
|
||||||
|
// });
|
||||||
|
if (e) {
|
||||||
|
this.handleUpdateData({ search: e, page: 1 });
|
||||||
|
} else {
|
||||||
|
this.handleUpdateData({ search: null, page: 1 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSearchChange = e => {
|
||||||
|
const { currentPage, pageSize, filteredInfo, sortedInfo } = this.state;
|
||||||
|
|
||||||
|
// this.setState({
|
||||||
|
// searchValue: e
|
||||||
|
// });
|
||||||
|
if (e) {
|
||||||
|
this.setState({
|
||||||
|
searchValue: e.target.value
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
searchValue: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleDeleteConfirmYes = e => {
|
||||||
|
console.log(e);
|
||||||
|
message.success("Click on Yes");
|
||||||
|
};
|
||||||
|
|
||||||
|
handleDeleteConfirmNo = e => {
|
||||||
|
console.log(e);
|
||||||
|
message.error("Click on No");
|
||||||
|
};
|
||||||
|
handleclearAll = () => {
|
||||||
|
console.log("====================================");
|
||||||
|
console.log("reset");
|
||||||
|
console.log("====================================");
|
||||||
|
this.setState({
|
||||||
|
filteredInfo: null,
|
||||||
|
sortedInfo: null,
|
||||||
|
searchValue: null,
|
||||||
|
// currentPage: 1,
|
||||||
|
// pageSize: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
this.handleUpdateData({
|
||||||
|
page: 1,
|
||||||
|
size: 10,
|
||||||
|
});
|
||||||
|
// this.fetch({
|
||||||
|
// _page: 1,
|
||||||
|
// _limit: 10
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
handlePagination = page => {
|
||||||
|
const { pageSize, searchValue } = this.state;
|
||||||
|
|
||||||
|
// this.fetch({
|
||||||
|
// _page: page,
|
||||||
|
// _limit: pageSize
|
||||||
|
// });
|
||||||
|
|
||||||
|
if (searchValue) {
|
||||||
|
this.handleUpdateData({ page, search: searchValue });
|
||||||
|
} else {
|
||||||
|
this.handleUpdateData({ page });
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.setState({
|
||||||
|
// currentPage: page
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSizeChange = (current, pageSize) => {
|
||||||
|
console.log("TEST!", current, pageSize, searchValue);
|
||||||
|
const { searchValue } = this.state;
|
||||||
|
// this.fetch({
|
||||||
|
// _page: current,
|
||||||
|
// _limit: pageSize
|
||||||
|
// });
|
||||||
|
|
||||||
|
if (searchValue) {
|
||||||
|
this.handleUpdateData({
|
||||||
|
page: current,
|
||||||
|
size: pageSize,
|
||||||
|
search: searchValue
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.handleUpdateData({ page: current, size: pageSize });
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.setState({
|
||||||
|
// currentPage: current,
|
||||||
|
// pageSize
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
handleRenderActionButton = ({action, item}) => {
|
||||||
|
let {keyValue} = this.props
|
||||||
|
|
||||||
|
let idValue = item[keyValue].toString()
|
||||||
|
console.log('====================================');
|
||||||
|
|
||||||
|
console.log('====================================');
|
||||||
|
switch (action.type) {
|
||||||
|
case 'edit':
|
||||||
|
return <Tooltip key={action.key} placement="top" title={action.name}>
|
||||||
|
<Link to={`${action.path}/1`} style={{padding: '5px 8px'}}>
|
||||||
|
<Icon type='edit' />
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'view':
|
||||||
|
return <Tooltip key={action.key} placement="top" title={action.name}>
|
||||||
|
<Link to={`${action.path}/2`} style={{padding: '5px 8px'}}>
|
||||||
|
<Icon type='right-circle-o' />
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete':
|
||||||
|
return <Tooltip key={action.key} placement="top" title={action.name}>
|
||||||
|
<Popconfirm
|
||||||
|
title="Are you sure delete this item?"
|
||||||
|
onConfirm={() => this.handleDeleteConfirmYes(item)}
|
||||||
|
onCancel={() => this.handleDeleteConfirmNo(item)}
|
||||||
|
okText="Yes"
|
||||||
|
cancelText="No" >
|
||||||
|
<a href="javascript:;" style={{padding: '5px 8px'}}>
|
||||||
|
<Icon type='delete' />
|
||||||
|
</a>
|
||||||
|
</Popconfirm>
|
||||||
|
</Tooltip>
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
let { columns, keyValue, actions } = this.props;
|
||||||
|
let {
|
||||||
|
sortedInfo,
|
||||||
|
filteredInfo,
|
||||||
|
headerColumns,
|
||||||
|
currentPage,
|
||||||
|
pageSize,
|
||||||
|
totalData,
|
||||||
|
data,
|
||||||
|
searchValue
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
// let headerColumns = columns.map(column => {
|
||||||
|
// if (column.sorter) {
|
||||||
|
// column.sorter = (a, b) => a[column.dataIndex] - b[column.dataIndex];
|
||||||
|
// column.sortOrder =
|
||||||
|
// sortedInfo &&
|
||||||
|
// sortedInfo.columnKey === column.dataIndex &&
|
||||||
|
// sortedInfo.order;
|
||||||
|
// }
|
||||||
|
// return column;
|
||||||
|
// });
|
||||||
|
|
||||||
|
console.log("====================================");
|
||||||
|
console.log(currentPage, pageSize, "TESTASDASD");
|
||||||
|
console.log("====================================");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="table-operations">
|
||||||
|
<Button onClick={this.handleclearAll}>
|
||||||
|
Clear filters
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Search
|
||||||
|
value={searchValue}
|
||||||
|
placeholder="input search text"
|
||||||
|
onSearch={this.handleSearch}
|
||||||
|
onChange={this.handleSearchChange}
|
||||||
|
enterButton
|
||||||
|
/>
|
||||||
|
<Table
|
||||||
|
rowKey={keyValue}
|
||||||
|
dataSource={data}
|
||||||
|
loading={this.state.loading}
|
||||||
|
onChange={this.handleTableChange}
|
||||||
|
pagination={false}
|
||||||
|
>
|
||||||
|
{columns &&
|
||||||
|
columns.map(column => (
|
||||||
|
<Column
|
||||||
|
key={column.dataIndex ? column.dataIndex : column.key}
|
||||||
|
{...column}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{
|
||||||
|
actions && actions.length > 0 && <Column
|
||||||
|
title="Action"
|
||||||
|
align="center"
|
||||||
|
key="action"
|
||||||
|
width={130}
|
||||||
|
render={(text, record) => (
|
||||||
|
|
||||||
|
actions.map(item => {
|
||||||
|
return item.access === true && this.handleRenderActionButton({action: item, item: record})
|
||||||
|
})
|
||||||
|
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Table>
|
||||||
|
<Row style={{paddingTop: 20}}>
|
||||||
|
<Col span={8}>col-12</Col>
|
||||||
|
<Col span={16} style={{textAlign: 'right'}}>
|
||||||
|
<Pagination
|
||||||
|
// size="small"
|
||||||
|
current={currentPage}
|
||||||
|
pageSize={pageSize}
|
||||||
|
showSizeChanger
|
||||||
|
onChange={this.handlePagination}
|
||||||
|
onShowSizeChange={this.handleSizeChange}
|
||||||
|
// defaultCurrent={currentPage}
|
||||||
|
total={totalData}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(CustomTable);
|
|
@ -0,0 +1,90 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import { Icon, Avatar, Dropdown, Menu, notification } from "antd";
|
||||||
|
import { Link, withRouter } from "react-router-dom";
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { customAction } from 'actions'
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { API_UNI_OIL , API_POST } from 'utils/Api'
|
||||||
|
|
||||||
|
const HeaderButton = styled.a`
|
||||||
|
/* This renders the buttons above... Edit me! */
|
||||||
|
padding: 0 10px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-transition: all .3s,padding 0s;
|
||||||
|
transition: all .3s,padding 0s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(243, 243, 243);
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
class HeaderDropdown extends Component {
|
||||||
|
state = {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
handleLogout = () => {
|
||||||
|
this.props.customAction({type: 'LOGOUT'});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
//const { userInfo } = this.state
|
||||||
|
const { history, userInfo } = this.props;
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu style={{width: 150 , margin: '0 0 0 auto'}} >
|
||||||
|
<Menu.Item key="0">
|
||||||
|
<a
|
||||||
|
onClick={()=> history.push("/my-profile")}
|
||||||
|
role="button"
|
||||||
|
rel="noopener noreferrer" >
|
||||||
|
<Icon type="user" /> My Profile
|
||||||
|
</a>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item key="1">
|
||||||
|
<a
|
||||||
|
role="button"
|
||||||
|
onClick={this.handleLogout}>
|
||||||
|
<Icon type="logout" /> Logout
|
||||||
|
</a>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Helmet title = "Dashboard" />
|
||||||
|
<Dropdown overlay={menu} placement="bottomRight">
|
||||||
|
<HeaderButton role="button" style={{
|
||||||
|
marginRight: 16, color: '#8E8E93', maxWidth: '256px',
|
||||||
|
whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' , position: 'relative'
|
||||||
|
}} >
|
||||||
|
<Avatar size="small"
|
||||||
|
style={{ background: '#B8BBC9', marginRight: 5 }} icon="user"
|
||||||
|
/> { userInfo && (`${userInfo.firstname} ${userInfo.lastname}`) } <Icon type="down" />
|
||||||
|
</HeaderButton>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderDropdown = connect(
|
||||||
|
state => ({
|
||||||
|
//fetchData: state.fetchData
|
||||||
|
// put initial values from account reducer
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(HeaderDropdown)
|
||||||
|
|
||||||
|
export default withRouter(HeaderDropdown);
|
|
@ -0,0 +1,75 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Breadcrumb, Icon } from 'antd';
|
||||||
|
|
||||||
|
// import styled from 'styled-components';
|
||||||
|
import { Link, withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
|
||||||
|
function MainBreadcrumbs(props) {
|
||||||
|
const {
|
||||||
|
pageRoutes,
|
||||||
|
// match,
|
||||||
|
location,
|
||||||
|
root
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const pathSnippets = location.pathname.split('/').filter(i => i);
|
||||||
|
const extraBreadcrumbItems = pathSnippets.map((route, index) => {
|
||||||
|
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
|
||||||
|
const routeCompare = pageRoutes.find((myRoute) => myRoute.path === url)
|
||||||
|
const paramsId = pathSnippets[pathSnippets.length - 1]
|
||||||
|
|
||||||
|
if (routeCompare) {
|
||||||
|
if (routeCompare.params) {
|
||||||
|
return (
|
||||||
|
<Breadcrumb.Item key={index}>
|
||||||
|
<Link to={`${url}/${paramsId}`}>
|
||||||
|
{routeCompare.name}
|
||||||
|
</Link>
|
||||||
|
</Breadcrumb.Item>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Breadcrumb.Item key={index}>
|
||||||
|
<Link to={url}>
|
||||||
|
{routeCompare.name}
|
||||||
|
</Link>
|
||||||
|
</Breadcrumb.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return <Breadcrumb.Item key={index}></Breadcrumb.Item>;
|
||||||
|
})
|
||||||
|
if (root) {
|
||||||
|
return (
|
||||||
|
<Breadcrumb
|
||||||
|
separator={<Icon type="right" style={{fontSize: '10px', opacity: 0.6}} />}
|
||||||
|
routeComparestyle={{ padding: '11px 24px 9px', fontSize: '12px' }}
|
||||||
|
>
|
||||||
|
<Breadcrumb.Item>
|
||||||
|
<Link to='/my-profile'>
|
||||||
|
<Icon type="home" /> {` Home`}
|
||||||
|
</Link>
|
||||||
|
</Breadcrumb.Item>
|
||||||
|
</Breadcrumb>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Breadcrumb
|
||||||
|
separator={<Icon type="right" style={{fontSize: '10px', opacity: 0.6}} />}
|
||||||
|
style={{ padding: '11px 24px 9px', fontSize: '12px' }}
|
||||||
|
>
|
||||||
|
<Breadcrumb.Item>
|
||||||
|
<Link to='/my-profile'>
|
||||||
|
<Icon type="home" /> {` Home`}
|
||||||
|
</Link>
|
||||||
|
</Breadcrumb.Item>
|
||||||
|
{extraBreadcrumbItems}
|
||||||
|
</Breadcrumb>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(MainBreadcrumbs);
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
|
||||||
|
import MainBreadcrumbs from './MainBreadcrumbs'
|
||||||
|
const { Content } = Layout;
|
||||||
|
|
||||||
|
function MainContent(props) {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
pageRoutes,
|
||||||
|
root
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
props.location && props.location.key && (
|
||||||
|
<div key={1} style={{
|
||||||
|
background: '#fff' ,
|
||||||
|
marginBottom: '75px',
|
||||||
|
position: 'fixed',
|
||||||
|
marginTop: '-110px',
|
||||||
|
width: '100%'}}
|
||||||
|
>
|
||||||
|
<MainBreadcrumbs root={root} pageRoutes={pageRoutes} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
,
|
||||||
|
<Content key={2} style={{ margin: '0 16px', padding: '0', background: '#fff', }}>
|
||||||
|
{children}
|
||||||
|
</Content>,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(MainContent);
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Layout } from "antd";
|
||||||
|
|
||||||
|
// import styled from 'styled-components';
|
||||||
|
// import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const { Footer } = Layout;
|
||||||
|
|
||||||
|
function MainFooter(props) {
|
||||||
|
// const {} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Footer style={{ textAlign: "center", background: '#fcfcfc' }} >
|
||||||
|
{/* YONDU-SDG ©2018 Created by Front End Team! */}
|
||||||
|
</Footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainFooter;
|
|
@ -0,0 +1,83 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Layout, Icon } from "antd";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import HeaderDropdown from './HeaderDropdown';
|
||||||
|
|
||||||
|
const { Header } = Layout;
|
||||||
|
|
||||||
|
|
||||||
|
const HeaderLink = styled(Link)`
|
||||||
|
/* This renders the buttons above... Edit me! */
|
||||||
|
padding: 0 10px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-transition: all .3s,padding 0s;
|
||||||
|
transition: all .3s,padding 0s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const RightHeader = styled.div`
|
||||||
|
/* This renders the buttons above... Edit me! */
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const IconTrigger = styled(Icon)`
|
||||||
|
/* This renders the buttons above... Edit me! */
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 69px;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-transition: all .3s,padding 0s;
|
||||||
|
transition: all .3s,padding 0s;
|
||||||
|
padding: 0 24px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
function MainHeader(props) {
|
||||||
|
const {
|
||||||
|
collapsed,
|
||||||
|
toggle,
|
||||||
|
userInfo,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Header style={{ background: '#fff', padding: 0, height: '66px', lineHeight: '69px', borderBottom: '1px solid rgb(230, 236, 245)', }}>
|
||||||
|
<IconTrigger
|
||||||
|
className="trigger"
|
||||||
|
type={collapsed ? 'menu-unfold' : 'menu-fold'}
|
||||||
|
onClick={toggle}
|
||||||
|
/>
|
||||||
|
<RightHeader>
|
||||||
|
<HeaderDropdown userInfo={userInfo}/>
|
||||||
|
</RightHeader>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MainHeader = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
// pull initial values from account reducer
|
||||||
|
}),
|
||||||
|
// { customAction }
|
||||||
|
)(MainHeader);
|
||||||
|
|
||||||
|
export default MainHeader;
|
|
@ -0,0 +1,262 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Layout, Icon, Menu } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { withRouter, Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const { SubMenu } = Menu;
|
||||||
|
const { Sider } = Layout;
|
||||||
|
|
||||||
|
const LogoPlaceholder = styled.div`
|
||||||
|
height: 32px;
|
||||||
|
margin: 16px;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function MainSidebar(props) {
|
||||||
|
const { collapsed, match, location, userInfo, systemPreferences } = props;
|
||||||
|
|
||||||
|
const navigation = [
|
||||||
|
{
|
||||||
|
key: 0,
|
||||||
|
label: 'User Management',
|
||||||
|
path: '/user-management',
|
||||||
|
icon: 'team',
|
||||||
|
access: userInfo && userInfo.role == 1 ? true : false,
|
||||||
|
//access: userInfo && (userInfo.role == 1 || userInfo.role == 3) ? true : false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 9,
|
||||||
|
label: 'Notifications',
|
||||||
|
path: '/notifications',
|
||||||
|
icon: 'notification',
|
||||||
|
access: true,
|
||||||
|
//access: userInfo && (userInfo.role == 1 || userInfo.role == 3) ? true : false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 4,
|
||||||
|
label: 'Member Management',
|
||||||
|
path: '/member-management',
|
||||||
|
icon: 'credit-card',
|
||||||
|
access: userInfo && userInfo.role == 1 ? true : false,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
key: 0.0,
|
||||||
|
label: 'Card Member',
|
||||||
|
path: '/member-management/card-member',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.1,
|
||||||
|
label: 'Locked Accounts',
|
||||||
|
path: '/member-management/lock-account',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 8,
|
||||||
|
label: 'Home Page ( Mobile ) ',
|
||||||
|
path: '/home-page',
|
||||||
|
icon: 'home',
|
||||||
|
access: true,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
key: 0.0,
|
||||||
|
label: 'Photo Slider',
|
||||||
|
path: '/home-page/photo-slider',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 3,
|
||||||
|
label: 'Promotions',
|
||||||
|
path: '/promotions',
|
||||||
|
icon: 'tags',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 2,
|
||||||
|
label: 'Top-Up',
|
||||||
|
path: '/top-up',
|
||||||
|
icon: 'plus-circle',
|
||||||
|
access: userInfo && userInfo.role == 1 ? true : false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 6,
|
||||||
|
label: 'About Us',
|
||||||
|
path: '/about-us',
|
||||||
|
icon: 'info-circle',
|
||||||
|
access: userInfo && userInfo.role == 1 ? true : false,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
key: 0.6,
|
||||||
|
label: 'Card Types',
|
||||||
|
path: '/about-us/card-types',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.5,
|
||||||
|
label: 'Terms & Privacy',
|
||||||
|
path: '/about-us/term-privacy',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 7,
|
||||||
|
label: 'Reports',
|
||||||
|
path: '/reports',
|
||||||
|
icon: 'file-text',
|
||||||
|
access: true,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
key: 0.7,
|
||||||
|
label: 'Registration Report',
|
||||||
|
path: '/reports/registration-report',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.8,
|
||||||
|
label: 'Top-Up Usage Report',
|
||||||
|
path: '/reports/top-up',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.9,
|
||||||
|
label: 'Mobile Usage Report',
|
||||||
|
path: '/reports/mobile-report',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.1,
|
||||||
|
label: 'Station Rating Report',
|
||||||
|
path: '/reports/station-rating',
|
||||||
|
access: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 8,
|
||||||
|
label: 'System Parameters',
|
||||||
|
path: '/system-parameters',
|
||||||
|
icon: 'setting',
|
||||||
|
access: userInfo && userInfo.role == 1 ? true : false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 12,
|
||||||
|
label: 'Station Locator',
|
||||||
|
path:'',
|
||||||
|
icon:'environment',
|
||||||
|
access: true,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
key: 0.11,
|
||||||
|
label: 'Branches',
|
||||||
|
path:'/branches',
|
||||||
|
access: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.12,
|
||||||
|
label: 'Stations',
|
||||||
|
path:'/stations',
|
||||||
|
access: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 0.13,
|
||||||
|
label: 'Fuels',
|
||||||
|
path:'/fuels',
|
||||||
|
access: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let newURL = location.pathname.split('/');
|
||||||
|
let appendedUrl = newURL[2];
|
||||||
|
if (appendedUrl == 'create' || appendedUrl == 'view' || appendedUrl == 'edit') appendedUrl = null;
|
||||||
|
let isSeondDaryPathExist = appendedUrl ? `/${appendedUrl}` : '';
|
||||||
|
let secondaryURL = `${match.path}${isSeondDaryPathExist}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sider
|
||||||
|
trigger={null}
|
||||||
|
collapsible
|
||||||
|
width={295}
|
||||||
|
collapsed={collapsed}
|
||||||
|
style={{ background: '#fff', border: 'solid 1px #e6ecf5', zIndex: '999' }}
|
||||||
|
>
|
||||||
|
{!collapsed ? (
|
||||||
|
<div style={{ height: '65px', padding: '12px 0', textAlign: 'center', borderBottom: '1px solid #e6ecf5' }}>
|
||||||
|
{/* <img src={ require("assets/img/logo_unioil.png") } style={{ height: 40 }} /> */}
|
||||||
|
{userInfo && (
|
||||||
|
<img src={`${systemPreferences ? systemPreferences : userInfo.logo}`} style={{ height: '100%' }} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<LogoPlaceholder
|
||||||
|
className='logo'
|
||||||
|
style={{ backgroundImage: `url(${systemPreferences ? systemPreferences : userInfo.logo})` }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Menu
|
||||||
|
style={{ borderRight: !collapsed ? 0 : null, height: '90vh', overflow: 'auto', paddingTop: '17px' }}
|
||||||
|
//inlineIndent={10}
|
||||||
|
defaultOpenKeys={[ match.path ]}
|
||||||
|
selectedKeys={[ secondaryURL ]}
|
||||||
|
mode='inline'
|
||||||
|
>
|
||||||
|
{navigation.map((item) => {
|
||||||
|
if (item.access) {
|
||||||
|
if (item.child) {
|
||||||
|
return (
|
||||||
|
<SubMenu
|
||||||
|
key={item.path}
|
||||||
|
title={
|
||||||
|
<span>
|
||||||
|
<Icon type={item.icon} />
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{item.child.map((subItem) => {
|
||||||
|
if (subItem.access) {
|
||||||
|
return (
|
||||||
|
<Menu.Item key={subItem.path}>
|
||||||
|
<Link to={subItem.path} style={{ paddingLeft: '15px' }}>
|
||||||
|
{subItem.icon && <Icon type={subItem.icon} />}
|
||||||
|
{subItem.label}
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</SubMenu>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Menu.Item key={item.path}>
|
||||||
|
<Link to={item.path}>
|
||||||
|
{item.icon && <Icon type={item.icon} />}
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
</Sider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(MainSidebar);
|
|
@ -0,0 +1,107 @@
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import { notification } from "antd";
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import IdleTimer from 'react-idle-timer'
|
||||||
|
|
||||||
|
import MainFooter from './components/MainFooter'
|
||||||
|
import MainHeader from './components/MainHeader'
|
||||||
|
import MainSidebar from './components/MainSidebar'
|
||||||
|
|
||||||
|
import { API_UNI_OIL , API_POST } from 'utils/Api'
|
||||||
|
import { customAction } from 'actions'
|
||||||
|
|
||||||
|
class DashboardLayout extends React.Component {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.idleTimer = null
|
||||||
|
this.onActive = this.handleActive.bind(this)
|
||||||
|
this.onIdle = this.handleIdle.bind(this)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
collapsed: false,
|
||||||
|
userInfo: null,
|
||||||
|
updatedLogo: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nexProps, prevProps) {
|
||||||
|
if(nexProps && nexProps.systemPreferences) {
|
||||||
|
if(nexProps.systemPreferences.data && nexProps.systemPreferences.data) {
|
||||||
|
this.setState({
|
||||||
|
updatedLogo: nexProps.systemPreferences.data.logo
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(nexProps) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleActive(e) {
|
||||||
|
// console.log('user is active', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleIdle(e) {
|
||||||
|
// console.log('user is idle', e)
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>You are logout automatically for being idle more than 10 minutes.</div>,
|
||||||
|
duration: 0,
|
||||||
|
key: 'idle-notification-1'
|
||||||
|
});
|
||||||
|
this.props.customAction({type: 'LOGOUT'});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.setState({
|
||||||
|
collapsed: !this.state.collapsed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
|
||||||
|
//const { userInfo } = this.state
|
||||||
|
const { children, userInfo } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ height: '100%' }}>
|
||||||
|
<MainSidebar collapsed={this.state.collapsed} userInfo={userInfo.data.userInfo} systemPreferences={this.state.updatedLogo}/>
|
||||||
|
<Layout style={{background: '#fcfcfc', paddingBottom: '10px'}}>
|
||||||
|
<MainHeader
|
||||||
|
collapsed={this.state.collapsed}
|
||||||
|
toggle={this.toggle}
|
||||||
|
userInfo={userInfo.data.userInfo}
|
||||||
|
/>
|
||||||
|
<div style={{ overflow: 'auto', marginTop: '94px', paddingTop: '16px', position: 'relative' }}>
|
||||||
|
<IdleTimer
|
||||||
|
ref={ref => { this.idleTimer = ref }}
|
||||||
|
element={document}
|
||||||
|
onActive={this.onActive}
|
||||||
|
onIdle={this.onIdle}
|
||||||
|
timeout={600000}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</IdleTimer>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DashboardLayout = connect(
|
||||||
|
state => ({
|
||||||
|
// pull initial values from account reducer
|
||||||
|
userInfo: state.login,
|
||||||
|
systemPreferences: state.systemPreferences
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(DashboardLayout);
|
||||||
|
|
||||||
|
export default DashboardLayout
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import DashboardLayout from '../Layout'
|
||||||
|
import { Route, Redirect } from 'react-router-dom'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
|
||||||
|
function DashboardRoute({ component: Component, isAuthenticated, ...rest }) {
|
||||||
|
return (
|
||||||
|
<Route {...rest} render={props => isAuthenticated ? (
|
||||||
|
<DashboardLayout>
|
||||||
|
<Component {...props} />
|
||||||
|
</DashboardLayout>
|
||||||
|
) : <Redirect
|
||||||
|
to={{
|
||||||
|
pathname: "/login",
|
||||||
|
state: { from: props.location, message: "You must log in to Enter this page" }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashboardRoute = connect(
|
||||||
|
state => ({
|
||||||
|
isAuthenticated: state.auth.isAuthenticated
|
||||||
|
}),
|
||||||
|
)(DashboardRoute);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { List, Avatar } from 'antd';
|
||||||
|
|
||||||
|
import { fetchData } from 'utils/Api';
|
||||||
|
|
||||||
|
class ListDataDisplay extends Component {
|
||||||
|
state = {
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
// const { url } = this.props;
|
||||||
|
// const response = await fetchData(url);
|
||||||
|
// this.setState({
|
||||||
|
// data: response.data
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { layout, avatar, viewPath, header, footer } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<List
|
||||||
|
header={header}
|
||||||
|
footer={footer}
|
||||||
|
itemLayout={layout}
|
||||||
|
dataSource={this.state && this.state.data.data}
|
||||||
|
renderItem={item => (
|
||||||
|
<List.Item>
|
||||||
|
<List.Item.Meta
|
||||||
|
avatar={avatar && <Avatar
|
||||||
|
src={item.avatar}
|
||||||
|
/>}
|
||||||
|
title={<Link to={viewPath.replace(':id', item.id)}>{item.first_name}</Link>}
|
||||||
|
description={`${item.first_name.toLowerCase()}_${item.last_name.toLowerCase()}@gmail.com`}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default ListDataDisplay;
|
|
@ -0,0 +1,5 @@
|
||||||
|
import List from './List';
|
||||||
|
|
||||||
|
export {
|
||||||
|
List,
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, { PureComponent } from "react";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import { Menu, Dropdown, Button, notification } from "antd";
|
||||||
|
import DownloadFile from "js-file-download";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
|
||||||
|
class DropdownExport extends PureComponent {
|
||||||
|
state = {
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
|
||||||
|
handleExportCSV = async() => {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
|
||||||
|
let { location } = this.props;
|
||||||
|
let { search } = location;
|
||||||
|
let params = queryString.parse(search);
|
||||||
|
|
||||||
|
if(this.props.defaultFilter){
|
||||||
|
params = {
|
||||||
|
...params,
|
||||||
|
...this.props.defaultFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
let response = await API_UNI_OIL.get(this.props.url.path, {
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200 || response.status === 201) {
|
||||||
|
//let dateNow = moment(new Date()).format('DD-MMM-YYYY')
|
||||||
|
let dateNow = moment(new Date()).format('MMDDYYYY')
|
||||||
|
DownloadFile(response.data, `${this.props.url.fileName}_${dateNow}.csv`);
|
||||||
|
|
||||||
|
this.setState({ loading: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const { loading } = this.state;
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
onClick={this.handleExportCSV}
|
||||||
|
style={{background: 'rgb(231, 70, 16)', borderColor: 'rgb(231, 70, 16)', color: '#fff'}}
|
||||||
|
>
|
||||||
|
Export CSV
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(DropdownExport);
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as DropdownExport } from "./DropdownExport";
|
|
@ -0,0 +1,69 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
//import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Form, Row, Col, Button, Icon, Input,Select } from 'antd';
|
||||||
|
|
||||||
|
import { required } from 'constants/validation';
|
||||||
|
import {AInput,ASelect,ARangePicker} from './AntdForms'
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
class AdvancedSearchForm extends Component {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const { handleSubmit,onSubmit, reset } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
className="login-form"
|
||||||
|
>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={8}>
|
||||||
|
{/* <Field
|
||||||
|
name="filter_field"
|
||||||
|
label="Filter by Code"
|
||||||
|
component={ASelect}
|
||||||
|
validate={required}
|
||||||
|
defaultValue="1"
|
||||||
|
>
|
||||||
|
<Option value="ff0000">Red</Option>
|
||||||
|
<Option value="00ff00">Green</Option>
|
||||||
|
<Option value="0000ff">Blue</Option>
|
||||||
|
</Field> */}
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
{/* <Field
|
||||||
|
name="filter_value"
|
||||||
|
label="Filter by Name"
|
||||||
|
component={ARangePicker}
|
||||||
|
width={4}
|
||||||
|
placeholder={["From", "To"]}
|
||||||
|
hasFeedback
|
||||||
|
onFocus={e => e.preventDefault()}
|
||||||
|
onBlur={e => e.preventDefault()}
|
||||||
|
// validate={required}
|
||||||
|
/> */}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Col span={24} style={{ textAlign: 'right' }}>
|
||||||
|
<Button type="primary" htmlType="submit">Search</Button>
|
||||||
|
<Button style={{ marginLeft: 8 }} onClick={reset}>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// AdvancedSearchForm = reduxForm({
|
||||||
|
// form: "CreateEmployeeFieldsForm",
|
||||||
|
// })(AdvancedSearchForm);
|
||||||
|
|
||||||
|
export default AdvancedSearchForm;
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Form, Input, Radio, Select, Checkbox, Button, DatePicker } from "antd";
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const RadioGroup = Radio.Group;
|
||||||
|
const { Option } = Select;
|
||||||
|
const { TextArea } = Input;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
// labelCol: {
|
||||||
|
// xs: { span: 24 },
|
||||||
|
// sm: { span: 7 }
|
||||||
|
// },
|
||||||
|
// wrapperCol: {
|
||||||
|
// xs: { span: 24 },
|
||||||
|
// sm: { span: 14 }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const makeField = Component => ({ input, meta, children, hasFeedback, label, ...rest }) => {
|
||||||
|
const hasError = meta.touched && meta.invalid;
|
||||||
|
const inputs = {...input};
|
||||||
|
const rests = {...rest};
|
||||||
|
console.log(inputs,'inputss', rests , 'ressttss')
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
label={label}
|
||||||
|
validateStatus={hasError ? "error" : "success"}
|
||||||
|
hasFeedback={hasFeedback && hasError}
|
||||||
|
help={hasError && meta.error}
|
||||||
|
>
|
||||||
|
<Component {...input} {...rest} children={children} />
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AInput = makeField(Input);
|
||||||
|
export const ARadioGroup = makeField(RadioGroup);
|
||||||
|
export const ASelect = makeField(Select);
|
||||||
|
export const ACheckbox = makeField(Checkbox);
|
||||||
|
export const ATextarea = makeField(TextArea);
|
||||||
|
export const ARangePicker = makeField(RangePicker);
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Cascader } from 'antd';
|
||||||
|
import { fetchData } from 'utils/Api';
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
const options = [{
|
||||||
|
value: 'zhejiang',
|
||||||
|
label: 'Zhejiang',
|
||||||
|
children: [{
|
||||||
|
value: 'hangzhou',
|
||||||
|
label: 'Hangzhou',
|
||||||
|
children: [{
|
||||||
|
value: 'xihu',
|
||||||
|
label: 'West Lake',
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
value: 'jiangsu',
|
||||||
|
label: 'Jiangsu',
|
||||||
|
children: [{
|
||||||
|
value: 'nanjing',
|
||||||
|
label: 'Nanjing',
|
||||||
|
children: [{
|
||||||
|
value: 'zhonghuamen',
|
||||||
|
label: 'Zhong Hua Men',
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}];
|
||||||
|
|
||||||
|
class CascaderForm extends Component {
|
||||||
|
async componentDidMount() {
|
||||||
|
const { url } = this.props;
|
||||||
|
const response = await fetchData(url);
|
||||||
|
this.setState({
|
||||||
|
options: response.data.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
field: { name, /* ...field */ },
|
||||||
|
form: { errors, setFieldValue, /* ...form */ },
|
||||||
|
layout,
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={label}
|
||||||
|
validateStatus={errors[name] && 'error'}
|
||||||
|
help={errors[name]}
|
||||||
|
>
|
||||||
|
<Cascader
|
||||||
|
{...props}
|
||||||
|
onChange={this.handleChange = (value) => { setFieldValue(name, value) }}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CascaderForm;
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Checkbox, Form } from 'antd';
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
const CheckboxForm = ({
|
||||||
|
field: { name, ...field },
|
||||||
|
form: { touched, errors, handleChange, setFieldValue, ...form },
|
||||||
|
label,
|
||||||
|
inline,
|
||||||
|
layout,
|
||||||
|
required,
|
||||||
|
...props,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
if (inline) {
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
{...props}
|
||||||
|
{...field}
|
||||||
|
name={name}
|
||||||
|
type="checkbox"
|
||||||
|
checked={field.value}
|
||||||
|
onChange={this.handleChange = (value) => { setFieldValue(name, !field.value) }}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Checkbox>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
style={{marginBottom: '10px',marginLeft: '20.9%'}}
|
||||||
|
validateStatus={touched[name] && errors[name] && 'error'}
|
||||||
|
help={touched[name] && errors[name]}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
{...props}
|
||||||
|
{...field}
|
||||||
|
|
||||||
|
name={name}
|
||||||
|
type="checkbox"
|
||||||
|
checked={field.value}
|
||||||
|
onChange={this.handleChange = (value) => { setFieldValue(name, !field.value) }}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Checkbox>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CheckboxForm;
|
|
@ -0,0 +1,160 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Form, DatePicker } from 'antd';
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const DatePickerForm = ({
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, handleSubmit, setFieldValue, handlePanelChange, ...form },
|
||||||
|
type,
|
||||||
|
layout,
|
||||||
|
label,
|
||||||
|
format,
|
||||||
|
minDateToday,
|
||||||
|
required,
|
||||||
|
disabledDateStart,
|
||||||
|
dateStartEnd,
|
||||||
|
disabledDateStartEndPhotoSlider,
|
||||||
|
disabledDateStartEndPhotoSliderEndDate,
|
||||||
|
isEdit,
|
||||||
|
isAutoFill,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const onDateChange = (value, isDateRange = false) => {
|
||||||
|
|
||||||
|
value && setFieldValue(field.name, isDateRange
|
||||||
|
? [value[0].format(format), value[1].format(format)]
|
||||||
|
: value.format(format)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if(value == null) {
|
||||||
|
setFieldValue(field.name, isDateRange
|
||||||
|
? [value[0].format(format), value[1].format(format)]
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable date less than `Today`
|
||||||
|
// use minDateToday props
|
||||||
|
// const disabledDate = (current) => {
|
||||||
|
// if (minDateToday) {
|
||||||
|
// var oneDay = (1 * 24 * 60 * 60 * 1000);
|
||||||
|
// return current && (current.valueOf() < (Date.now() - oneDay));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const disabledDate = (current) => {
|
||||||
|
// Can not select days before today and today
|
||||||
|
|
||||||
|
// for promotions
|
||||||
|
if(disabledDateStart && !disabledDateStartEndPhotoSlider) {
|
||||||
|
if(form.values.date_start) {
|
||||||
|
return current && current < moment(form.values.date_start);
|
||||||
|
} else {
|
||||||
|
//return current && moment(current).add(2,'days') < moment().endOf('day').add(2,'days');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for photo slider Date Start
|
||||||
|
if(disabledDateStartEndPhotoSlider && !disabledDateStartEndPhotoSliderEndDate) {
|
||||||
|
if(dateStartEnd) {
|
||||||
|
if(current && current.format() < moment(dateStartEnd.date_start).format()) {
|
||||||
|
if(isEdit) {
|
||||||
|
//return current && current.format() < moment(dateStartEnd.date_start).subtract(1,'days').format()
|
||||||
|
}
|
||||||
|
return current && current.format() < moment(dateStartEnd.date_start).format()
|
||||||
|
} else {
|
||||||
|
if(isEdit) {
|
||||||
|
return current && current.format() > moment(dateStartEnd.date_end).format();
|
||||||
|
}
|
||||||
|
//return current && current.format() > moment(dateStartEnd.date_end).add(1,'days').format();
|
||||||
|
return current && current.format() > moment(dateStartEnd.date_end).format();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for photo slider date End
|
||||||
|
if(disabledDateStartEndPhotoSliderEndDate) {
|
||||||
|
if(dateStartEnd) {
|
||||||
|
if(current && current.format() < form.values.date_start) {
|
||||||
|
// disabled previous date
|
||||||
|
return current && current < moment(form.values.date_start);
|
||||||
|
} else {
|
||||||
|
// diabled past date
|
||||||
|
if(isEdit) {
|
||||||
|
return current && current.format() > moment(dateStartEnd.date_end).format();
|
||||||
|
}
|
||||||
|
//return current && current.format() > moment(dateStartEnd.date_end).add(1,'days').format();
|
||||||
|
return current && current.format() > moment(dateStartEnd.date_end).format();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(disabledDateStart) {
|
||||||
|
if(dateStartEnd) {
|
||||||
|
// return
|
||||||
|
} else {
|
||||||
|
if(form.values.date_start) {
|
||||||
|
return current && current < moment(form.values.date_start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let _props = {...props}; let _field = {...field};
|
||||||
|
|
||||||
|
|
||||||
|
if(_field.value !== "") {
|
||||||
|
_props.value = _field.value && moment(_field.value,format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
{ type === 'date' &&
|
||||||
|
<DatePicker
|
||||||
|
{..._props}
|
||||||
|
onChange={(value) => onDateChange(value)}
|
||||||
|
format={format}
|
||||||
|
disabledDate={disabledDateStartEndPhotoSlider || disabledDateStart ? disabledDate : ()=> { return false } }
|
||||||
|
style={{width: '250px'}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ type === 'date-time' &&
|
||||||
|
<DatePicker
|
||||||
|
showTime={true}
|
||||||
|
{..._props}
|
||||||
|
onChange={(value) => onDateChange(value)}
|
||||||
|
format={format}
|
||||||
|
style={{width: '250px'}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ type === 'range' &&
|
||||||
|
<RangePicker
|
||||||
|
{...props}
|
||||||
|
onChange={(value) => onDateChange(value, true)}
|
||||||
|
format={format}
|
||||||
|
disabledDate={disabledDate}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DatePickerForm;
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Button,Popconfirm, Icon, Dropdown, Menu } from 'antd';
|
||||||
|
|
||||||
|
|
||||||
|
class HeaderForm extends Component {
|
||||||
|
|
||||||
|
|
||||||
|
confirm(action) {
|
||||||
|
action();
|
||||||
|
// message.success('Click on Yes');
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(e) {
|
||||||
|
// console.log(e);
|
||||||
|
// message.error('Click on No');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { action, cancel, deleteAction , title , actionBtnName, cancelBtnName,
|
||||||
|
deleteBtnName, loading, withConfirm, styleBtn, isDropDown , actionPrivacy,
|
||||||
|
actionTerms, disabled, withCancelConfirm, isInsideForm } = this.props;
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu className="terms-management"
|
||||||
|
//onClick={handleMenuClick}
|
||||||
|
>
|
||||||
|
<Menu.Item key="1" onClick={actionPrivacy}>{`Terms & Condition`}</Menu.Item>
|
||||||
|
<Menu.Item key="2" onClick={actionTerms}>Privacy Policy</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderBottom: '1px solid #E6ECF5',
|
||||||
|
background: '#fff',
|
||||||
|
position: 'fixed', width: '100%',
|
||||||
|
padding: '0px 24px 5px', zIndex: 99,
|
||||||
|
marginTop: isInsideForm == true ? '-154px' : '-73px' , marginLeft: '-17px' }}
|
||||||
|
>
|
||||||
|
<h1 style={{fontSize: '24px'}}>{title}</h1>
|
||||||
|
<div style={{display: 'flex', position: 'fixed', right: '24px'}}>
|
||||||
|
{
|
||||||
|
action &&
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
withConfirm
|
||||||
|
?
|
||||||
|
(
|
||||||
|
<Popconfirm
|
||||||
|
placement="bottom"
|
||||||
|
onConfirm={()=>this.confirm(action)}
|
||||||
|
onCancel={this.cancel} okText="Yes" cancelText="No"
|
||||||
|
title={withConfirm && withConfirm.message}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
|
loading={loading}
|
||||||
|
style={{ margin: '0 4px', width: '135px',
|
||||||
|
display: 'block', background: '#E74610', borderColor:'#E74610',
|
||||||
|
color: '#fff' ,
|
||||||
|
opacity: disabled ? 0.5 : 'initial'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{actionBtnName}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
) :
|
||||||
|
!isDropDown && (
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
onClick={action}
|
||||||
|
disabled={disabled}
|
||||||
|
style={{
|
||||||
|
margin: '0 4px', width: '135px', display: 'block',
|
||||||
|
background: styleBtn ? styleBtn.background : '#E74610',
|
||||||
|
borderColor: styleBtn ? styleBtn.borderColor : '#E74610',
|
||||||
|
color: styleBtn ? styleBtn.color : '#fff',
|
||||||
|
opacity: disabled ? 0.5 : 'initial'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{actionBtnName}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cancel &&
|
||||||
|
<Popconfirm
|
||||||
|
placement="bottomRight"
|
||||||
|
onConfirm={cancel}
|
||||||
|
onCancel={null} okText="Yes" cancelText="No"
|
||||||
|
title={withCancelConfirm && withCancelConfirm.message}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
onClick={withCancelConfirm ? null : cancel}
|
||||||
|
style={{ margin: '0 4px', width: '135px', display: 'block', background: 'white', borderColor:'#b8bbc9', color: '#65697f' }}
|
||||||
|
>
|
||||||
|
{cancelBtnName}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
deleteAction &&
|
||||||
|
(
|
||||||
|
<Popconfirm
|
||||||
|
placement="bottom" title={'Are you sure you want to delete this record?'}
|
||||||
|
onConfirm={deleteAction} okText="Yes" cancelText="No"
|
||||||
|
icon={ <Icon type="close-circle" /> }
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
|
type="danger"
|
||||||
|
loading={loading}
|
||||||
|
style={{ margin: '0 4px', width: '135px', display: 'block', background: 'white', borderColor:'#b8bbc9', color: '#65697f' }}
|
||||||
|
>
|
||||||
|
{deleteBtnName}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isDropDown && (
|
||||||
|
<div style={{position: 'relative'}} className="terms-management-parent">
|
||||||
|
<Dropdown overlay={menu} className="terms-management">
|
||||||
|
<Button disabled={disabled}>
|
||||||
|
Add <Icon type="down" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HeaderForm;
|
|
@ -0,0 +1,37 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Form, Icon, Input, Row, Col, Button } from 'antd';
|
||||||
|
import ReactInputMask from 'react-input-mask';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
const InputNumberForm = ({
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
loading,
|
||||||
|
mask,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<ReactInputMask {...props}
|
||||||
|
className="ant-input"
|
||||||
|
{...field} mask={mask} maskChar=" "
|
||||||
|
/>
|
||||||
|
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputNumberForm;
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Icon, InputNumber } from 'antd';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
class InputNumberAntD extends Component {
|
||||||
|
|
||||||
|
handleChange = (value) => {
|
||||||
|
const { setFieldValue } = this.props.form;
|
||||||
|
const { name } = this.props.field;
|
||||||
|
|
||||||
|
//let valueNum = parseFloat(value).toFixed(2)
|
||||||
|
// Add custom action `onChange`
|
||||||
|
return setFieldValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const children = [];
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
step,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const _props = {...props}
|
||||||
|
|
||||||
|
_props.value = field.value
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<InputNumber
|
||||||
|
{..._props}
|
||||||
|
//defaultValue={0}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
min={min && min}
|
||||||
|
max={max && max}
|
||||||
|
step={step && step}
|
||||||
|
//formatter={value => value && `${value}`}
|
||||||
|
//parser={value => value && value.replace('', '')}
|
||||||
|
// formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
|
||||||
|
// parser={value => value.replace(/\$\s?|(,*)/g, '')}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputNumberAntD;
|
|
@ -0,0 +1,31 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Form, Icon, Input, Row, Col, Button } from 'antd';
|
||||||
|
import InputPassword from 'antd-input-password';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
const InputPasswords = ({
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<Input.Password {...props} {...field} />
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputPasswords;
|
|
@ -0,0 +1,67 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Form, Icon, Input, Tooltip } from 'antd';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<span>
|
||||||
|
<div>This content will be used in the</div>
|
||||||
|
<div>"Enter ID Number" page as part of</div>
|
||||||
|
<div>the Apply for a Card process of</div>
|
||||||
|
<div>the Unioil Mobile App.</div>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const InputTextArea = ({
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, setFieldValue, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
onCountText,
|
||||||
|
charsperpage,
|
||||||
|
pagecount,
|
||||||
|
hasIcon,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
<Icon type="question-circle" />
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={
|
||||||
|
<span>
|
||||||
|
{`${props.label} `}
|
||||||
|
{
|
||||||
|
hasIcon &&
|
||||||
|
<Tooltip placement="top" title={content}>
|
||||||
|
<Icon type="question-circle" />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextArea
|
||||||
|
{...props}
|
||||||
|
{...field}
|
||||||
|
prefix={icon && <Icon type={icon} style={{ color: 'rgba(0,0,0,.25)' }} />}
|
||||||
|
/>
|
||||||
|
{onCountText && <div style={{position: 'relative'}}>
|
||||||
|
<div style={{position: 'absolute',right: '0%', top: '-18px'}}>
|
||||||
|
<span style={{color: field.value.length > charsperpage ? 'red' : 'rgba(0, 0, 0, 0.65)'}}>{field.value.length}</span>/{charsperpage && charsperpage}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputTextArea;
|
|
@ -0,0 +1,93 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Form, Icon, Input, Row, Col, Button, message } from 'antd';
|
||||||
|
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
const InputForm = ({
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
loading,
|
||||||
|
isCopyUsername,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
!withActionBtn
|
||||||
|
?
|
||||||
|
<div style={{display: 'flex'}}>
|
||||||
|
<Input
|
||||||
|
{...props}
|
||||||
|
{...field}
|
||||||
|
prefix={icon && <Icon type={icon} style={{ color: 'rgba(0,0,0,.25)' }} />}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
isCopyUsername && (
|
||||||
|
<CopyToClipboard text={field.value}>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
disabled={
|
||||||
|
field.value.length > 0 ? false : true
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
padding: '0 30px',
|
||||||
|
opacity: field.value.length > 0 ? 'initial' : 0.8,
|
||||||
|
margin: '0 0 0 10px',background: '#E74610', borderColor:'#E74610', color: '#fff'
|
||||||
|
}}
|
||||||
|
onClick={()=> message.success('Username successfully copied.')}>
|
||||||
|
Copy
|
||||||
|
</Button>
|
||||||
|
</CopyToClipboard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<Row gutter={8}>
|
||||||
|
<Col span={13}>
|
||||||
|
<Input
|
||||||
|
{...props}
|
||||||
|
{...field}
|
||||||
|
className="generated-input"
|
||||||
|
prefix={icon && <Icon type={icon} style={{ color: 'rgba(0,0,0,.25)' }} />}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={11}>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
disabled={withActionBtn.disabled}
|
||||||
|
style={{opacity: withActionBtn.disabled ? 0.8 : 'initial' ,background: '#E74610', borderColor:'#E74610', color: '#fff'}} onClick={withActionBtn.action}>{withActionBtn.name}
|
||||||
|
</Button>
|
||||||
|
<CopyToClipboard text={withActionBtn.password} onCopy={withActionBtn.copyAction ? withActionBtn.copyAction : ()=> {return null}}>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
disabled={withActionBtn.copyAction ? false : true}
|
||||||
|
style={{
|
||||||
|
padding: '0 30px',
|
||||||
|
opacity: withActionBtn.copyAction ? 'initial' : 0.8 , background: '#E74610', borderColor:'#E74610', color: '#fff', marginLeft: '5px'}}
|
||||||
|
// onClick={withActionBtn.copyAction ? withActionBtn.copyAction : ()=> {return null} }
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</Button>
|
||||||
|
</CopyToClipboard>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
}
|
||||||
|
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputForm;
|
|
@ -0,0 +1,86 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Select } from 'antd';
|
||||||
|
|
||||||
|
import { fetchData } from 'utils/Api';
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const Option = Select.Option;
|
||||||
|
|
||||||
|
class MultiSelectForm extends Component {
|
||||||
|
async componentDidMount() {
|
||||||
|
// const { url } = this.props;
|
||||||
|
// const response = await fetchData(url);
|
||||||
|
// this.setState({
|
||||||
|
// options: response.data.data
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange = (value) => {
|
||||||
|
const { setFieldValue } = this.props.form;
|
||||||
|
const { name } = this.props.field;
|
||||||
|
|
||||||
|
if(this.props.handleGetDate) {
|
||||||
|
this.props.handleGetDate(value);
|
||||||
|
this.props.handleAutoFillDeatils(value,setFieldValue,this.props);
|
||||||
|
}
|
||||||
|
// Add custom action `onChange`
|
||||||
|
return setFieldValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const children = [];
|
||||||
|
if (this.state && this.state.options) {
|
||||||
|
this.state.options.map((item, key) => {
|
||||||
|
children.push(<Option value={item.id.toString()}>{item.first_name}</Option>)
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
layout,
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
optionFilterProp,
|
||||||
|
mode,
|
||||||
|
optionsList,
|
||||||
|
branchesOptionsTwo,
|
||||||
|
placeholder,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
// console.log(field)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
{...props}
|
||||||
|
// filterOption={
|
||||||
|
// optionFilterProp ?
|
||||||
|
// (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 : ''
|
||||||
|
// }
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={field.value}
|
||||||
|
mode={mode}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
children={children}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
optionsList && (optionsList.map((item,i) => {
|
||||||
|
return <Option value={item.id} key={`${i}-${field.name}`}>{item.name}</Option>
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
</Select>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MultiSelectForm;
|
|
@ -0,0 +1,69 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import { Form , Select } from 'antd';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
import { Input } from 'components/Forms';
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 4 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 20 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
class MultiSelectOptions extends Component {
|
||||||
|
state= {
|
||||||
|
value: []
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(event){
|
||||||
|
console.log(event)
|
||||||
|
this.setState({value: event})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const Option = Select.Option;
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
optionsList,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder="Select fuels."
|
||||||
|
defaultValue={[]}
|
||||||
|
onChange={(e)=>this.props.onChange (e)}
|
||||||
|
optionLabelProp="label"
|
||||||
|
mode="multiple"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
optionsList && (optionsList.map((item,i) => {
|
||||||
|
return <Option value={item.id} key={`${i}-${field.name}`} label={item.name}>{item.name}</Option>
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormItem>
|
||||||
|
)};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultiSelectOptions;
|
|
@ -0,0 +1,84 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Radio } from 'antd';
|
||||||
|
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const RadioGroup = Radio.Group;
|
||||||
|
|
||||||
|
let styles = {
|
||||||
|
':select': {
|
||||||
|
backgroundColor: 'yellow',
|
||||||
|
color: 'red'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class InputForm extends Component {
|
||||||
|
|
||||||
|
|
||||||
|
handleChange = (e) => {
|
||||||
|
const { setFieldValue } = this.props.form;
|
||||||
|
const { name } = this.props.field;
|
||||||
|
//Add custom action `onChange`
|
||||||
|
if(this.props.handleResetValue) {
|
||||||
|
this.props.handleResetValue(this.props.form);
|
||||||
|
}
|
||||||
|
if(this.props.handleScheduleStatus) {
|
||||||
|
this.props.handleScheduleStatus(e.target.value)
|
||||||
|
}
|
||||||
|
return setFieldValue(name, e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
optionsList,
|
||||||
|
isRadioButton,
|
||||||
|
...props,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let _props = {...props};
|
||||||
|
let _field = {...field};
|
||||||
|
|
||||||
|
if(!_field.value) {
|
||||||
|
_field.value = _props.defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<Radio.Group {..._field} {..._props} onChange={this.handleChange}>
|
||||||
|
{
|
||||||
|
optionsList ? (
|
||||||
|
optionsList.map((item,i) => {
|
||||||
|
if(isRadioButton) {
|
||||||
|
return <Radio.Button value={item.value} disabled={item.isDisabled} key={i} style={styles}>
|
||||||
|
{item.label}
|
||||||
|
</Radio.Button>
|
||||||
|
}
|
||||||
|
return <Radio value={item.value} key={i} style={styles}>
|
||||||
|
{item.label}
|
||||||
|
</Radio>
|
||||||
|
})
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</Radio.Group>
|
||||||
|
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputForm;
|
|
@ -0,0 +1,91 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Select } from 'antd';
|
||||||
|
|
||||||
|
import { fetchData } from 'utils/Api';
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const Option = Select.Option;
|
||||||
|
|
||||||
|
class SelectForm extends Component {
|
||||||
|
async componentDidMount() {
|
||||||
|
// const { url } = this.props;
|
||||||
|
// const response = await fetchData(url);
|
||||||
|
// this.setState({
|
||||||
|
// options: response.data.data
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange = (value) => {
|
||||||
|
const { setFieldValue } = this.props.form;
|
||||||
|
const { name } = this.props.field;
|
||||||
|
|
||||||
|
if(this.props.handleGetDate) {
|
||||||
|
this.props.handleGetDate(value);
|
||||||
|
this.props.handleAutoFillDeatils(value,setFieldValue,this.props);
|
||||||
|
}
|
||||||
|
// Add custom action `onChange`
|
||||||
|
return setFieldValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const children = [];
|
||||||
|
|
||||||
|
if (this.state && this.state.options) {
|
||||||
|
this.state.options.map((item, key) => {
|
||||||
|
children.push(<Option value={item.id.toString()}>{item.first_name}</Option>)
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
layout,
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
optionFilterProp,
|
||||||
|
mode,
|
||||||
|
optionsList,
|
||||||
|
branchesOptionsTwo,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
{...props}
|
||||||
|
// filterOption={
|
||||||
|
// optionFilterProp ?
|
||||||
|
// (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 : ''
|
||||||
|
// }
|
||||||
|
mode={mode}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
children={children}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
branchesOptionsTwo
|
||||||
|
?
|
||||||
|
branchesOptionsTwo.map(item => (
|
||||||
|
<Select.Option key={item} value={item}>
|
||||||
|
{item}
|
||||||
|
</Select.Option>
|
||||||
|
))
|
||||||
|
:
|
||||||
|
optionsList && (optionsList.map((item,i) => {
|
||||||
|
return <Option value={item.value} key={`${i}-${field.name}`}>{item.label}</Option>
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
</Select>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectForm;
|
|
@ -0,0 +1,185 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Icon, Input, Upload, message } from 'antd';
|
||||||
|
import filesize from 'filesize';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
|
||||||
|
class BackgroundUploadImage extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fileUpload: null,
|
||||||
|
loading: false,
|
||||||
|
hasError: false,
|
||||||
|
imageUrl: props.imageUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
normFile = (info) => {
|
||||||
|
const { handleFileUpload } = this.props;
|
||||||
|
const isJPG = info.file.type === 'image/jpeg' || info.file.type === 'image/png' || info.file.type === 'image/gif' ;
|
||||||
|
|
||||||
|
if(isJPG && info.file.originFileObj) {
|
||||||
|
|
||||||
|
this.getBase64(info.file.originFileObj, imageUrl => this.setState({
|
||||||
|
imageUrl,
|
||||||
|
loading: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let imageUrl = this.state.imageUrl;
|
||||||
|
this.props.form.setFieldValue(this.props.field.name, imageUrl);
|
||||||
|
|
||||||
|
// this.props.form.setFieldValue("logo", 'imageValue');
|
||||||
|
|
||||||
|
handleFileUpload(info,this.props.form.setFieldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (Array.isArray(e)) {
|
||||||
|
// return this.setState({fileUpload: e});
|
||||||
|
// }
|
||||||
|
// return e && this.setState({fileUpload: e.fileList});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getBase64 =(img, callback)=> {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(img);
|
||||||
|
reader.addEventListener('load', () => {
|
||||||
|
callback(reader.result)
|
||||||
|
this.props.form.setFieldValue(this.props.field.name, reader.result);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeUpload =(file)=> {
|
||||||
|
const {notAcceptedImg} = this.props;
|
||||||
|
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' ;
|
||||||
|
if (!isJPG) {
|
||||||
|
message.error('You can only upload JPG, PNG or GIF file!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(notAcceptedImg && notAcceptedImg.length > 0) {
|
||||||
|
notAcceptedImg.map(item => {
|
||||||
|
if(file.type == item) return message.error('You can only upload JPG or PNG file!');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileSize; let isLt2M;
|
||||||
|
|
||||||
|
if(this.props.limit100kb) {
|
||||||
|
fileSize = filesize(file.size, {output: "array"} ) // 100kb
|
||||||
|
isLt2M = fileSize[0] < 104 && fileSize[1] == "KB"
|
||||||
|
} else {
|
||||||
|
isLt2M = file.size / 1024 / 1024 < 2; // 2MB
|
||||||
|
}
|
||||||
|
//const isLt2M = fileSize[0] < 104;
|
||||||
|
//const isLt2M = file.size / 1024 / 1024 < 2;
|
||||||
|
|
||||||
|
if (!isLt2M) {
|
||||||
|
if(this.props.limit100kb) {
|
||||||
|
message.error('Image must smaller than 100KB!');
|
||||||
|
} else {
|
||||||
|
message.error('Image must smaller than 2MB!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isJPG && isLt2M;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
fileList,
|
||||||
|
messageUpload,
|
||||||
|
multipleFileUpload,
|
||||||
|
imgWidth,
|
||||||
|
imgStyle,
|
||||||
|
isRatioMessage,
|
||||||
|
notAcceptedImg,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let _props = {...props};
|
||||||
|
let _field = {...field};
|
||||||
|
|
||||||
|
const { onChange, onBlur, ...restField } = field;
|
||||||
|
const { fileUpload } =this.state;
|
||||||
|
|
||||||
|
// let props_list_image = {
|
||||||
|
// action: '',
|
||||||
|
// listType: 'picture',
|
||||||
|
// defaultFileList: [...fileList],
|
||||||
|
// className: 'upload-list-inline',
|
||||||
|
// };
|
||||||
|
|
||||||
|
const uploadButton = (
|
||||||
|
<div>
|
||||||
|
<Icon type={this.state.loading ? 'loading' : 'plus'} />
|
||||||
|
<div className="ant-upload-text">Upload</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
multipleFileUpload ?
|
||||||
|
(
|
||||||
|
<Upload.Dragger
|
||||||
|
{..._props} {..._field}
|
||||||
|
//{...props_list_image}
|
||||||
|
onChange={this.normFile}
|
||||||
|
>
|
||||||
|
<p className="ant-upload-drag-icon">
|
||||||
|
<Icon type="inbox" />
|
||||||
|
</p>
|
||||||
|
<p className="ant-upload-text">Click or drag file to this area to upload</p>
|
||||||
|
<p className="ant-upload-hint">Support for a single or bulk upload.</p>
|
||||||
|
</Upload.Dragger>
|
||||||
|
) :
|
||||||
|
(
|
||||||
|
<Upload
|
||||||
|
style={{padding: '13px 22px'}}
|
||||||
|
name="avatar"
|
||||||
|
listType="picture-card"
|
||||||
|
className="avatar-uploader"
|
||||||
|
accept=".jpg , .png , .gif"
|
||||||
|
showUploadList={false}
|
||||||
|
notAcceptedImg={notAcceptedImg}
|
||||||
|
beforeUpload={this.beforeUpload}
|
||||||
|
onChange={this.normFile}
|
||||||
|
className="upload-image"
|
||||||
|
>
|
||||||
|
{this.state.imageUrl ? <img src={this.state.imageUrl} alt="avatar" width={imgStyle ? imgStyle.width : "100%" } height={imgStyle ? imgStyle.height : "135"} /> : uploadButton}
|
||||||
|
<div style={{width: imgWidth ? imgWidth : 'initial', margin: '0 auto'}}>
|
||||||
|
<p className="ant-upload-text">Click or drag file to this area to upload.</p>
|
||||||
|
<p className="ant-upload-hint">Support for a single upload only.</p>
|
||||||
|
{ isRatioMessage && <p className="ant-upload-hint">{isRatioMessage.isRatioMessage && isRatioMessage.isRatioMessage}</p> }
|
||||||
|
</div>
|
||||||
|
</Upload>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BackgroundUploadImage;
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Form, DatePicker, TimePicker } from 'antd';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const TimePickerForm = ({
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, handleSubmit, setFieldValue, handlePanelChange, ...form },
|
||||||
|
type,
|
||||||
|
layout,
|
||||||
|
label,
|
||||||
|
format,
|
||||||
|
minDateToday,
|
||||||
|
required,
|
||||||
|
isAutoFill,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const onDateChange = (value, isDateRange = false) => {
|
||||||
|
value && setFieldValue(field.name, isDateRange
|
||||||
|
? [value[0].format(format), value[1].format(format)]
|
||||||
|
: value.format(format)
|
||||||
|
)
|
||||||
|
|
||||||
|
if(value == null) {
|
||||||
|
setFieldValue(field.name, isDateRange
|
||||||
|
? [value[0].format(format), value[1].format(format)]
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable date less than `Today`
|
||||||
|
// use minDateToday props
|
||||||
|
const disabledDate = (current) => {
|
||||||
|
if (minDateToday) {
|
||||||
|
var oneDay = (1 * 24 * 60 * 60 * 1000);
|
||||||
|
return current && (current.valueOf() < (Date.now() - oneDay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _props = {...props}; let _field = {...field};
|
||||||
|
|
||||||
|
|
||||||
|
if(_field.value !== "") {
|
||||||
|
_props.value = _field.value && moment(_field.value,format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
<TimePicker
|
||||||
|
{..._props}
|
||||||
|
onChange={(value) => onDateChange(value)}
|
||||||
|
format={format}
|
||||||
|
//disabledDate={disabledDate}
|
||||||
|
style={{width: '250px'}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TimePickerForm;
|
|
@ -0,0 +1,201 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Form, Icon, Input, Upload, message } from 'antd';
|
||||||
|
import filesize from 'filesize';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
|
||||||
|
class UploadImage extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fileUpload: null,
|
||||||
|
loading: false,
|
||||||
|
hasError: false,
|
||||||
|
imageUrl: props.imageUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
normFile = (info) => {
|
||||||
|
const { handleFileUpload } = this.props;
|
||||||
|
const isJPG = info.file.type === 'image/jpeg' || info.file.type === 'image/png' || info.file.type === 'image/gif' ;
|
||||||
|
|
||||||
|
if(isJPG && info.file.originFileObj) {
|
||||||
|
|
||||||
|
this.getBase64(info.file.originFileObj, imageUrl => this.setState({
|
||||||
|
imageUrl,
|
||||||
|
loading: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if(this.props.isDefault) {
|
||||||
|
// this.props.form.setFieldValue("image", 'imageValue');
|
||||||
|
// } else {
|
||||||
|
// let imageUrl = this.state.imageUrl ? this.state.imageUrl : this.props.imageUrl;
|
||||||
|
// this.props.form.setFieldValue("image", imageUrl);
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.props.form.setFieldValue("logo", 'imageValue');
|
||||||
|
|
||||||
|
handleFileUpload(info,this.props.form.setFieldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (Array.isArray(e)) {
|
||||||
|
// return this.setState({fileUpload: e});
|
||||||
|
// }
|
||||||
|
// return e && this.setState({fileUpload: e.fileList});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getBase64 =(img, callback)=> {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(img);
|
||||||
|
//reader.addEventListener('load', () => callback(reader.result));
|
||||||
|
reader.addEventListener('load', () => {
|
||||||
|
callback(reader.result)
|
||||||
|
this.props.form.setFieldValue(this.props.field.name, reader.result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeUpload =(file)=> {
|
||||||
|
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' ;
|
||||||
|
if (!isJPG) {
|
||||||
|
message.error('You can only upload JPG, PNG or GIF file!');
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileSize; let isLt2M;
|
||||||
|
|
||||||
|
if(this.props.limit100kb) {
|
||||||
|
fileSize = filesize(file.size, {output: "array"} ) // 100kb
|
||||||
|
isLt2M = fileSize[0] < 104 && fileSize[1] == "KB"
|
||||||
|
} else {
|
||||||
|
isLt2M = file.size / 1024 / 1024 < 2; // 2MB
|
||||||
|
}
|
||||||
|
//const isLt2M = fileSize[0] < 104;
|
||||||
|
//const isLt2M = file.size / 1024 / 1024 < 2;
|
||||||
|
|
||||||
|
if (!isLt2M) {
|
||||||
|
if(this.props.limit100kb) {
|
||||||
|
message.error('Image must smaller than 100KB!');
|
||||||
|
} else {
|
||||||
|
message.error('Image must smaller than 2MB!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isJPG && isLt2M;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
required,
|
||||||
|
icon,
|
||||||
|
layout,
|
||||||
|
withActionBtn,
|
||||||
|
action,
|
||||||
|
fileList,
|
||||||
|
messageUpload,
|
||||||
|
multipleFileUpload,
|
||||||
|
imgWidth,
|
||||||
|
imgStyle,
|
||||||
|
isRatioMessage,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let _props = {...props};
|
||||||
|
let _field = {...field};
|
||||||
|
|
||||||
|
const { onChange, onBlur, ...restField } = field;
|
||||||
|
const { fileUpload } =this.state;
|
||||||
|
|
||||||
|
// let props_list_image = {
|
||||||
|
// action: '',
|
||||||
|
// listType: 'picture',
|
||||||
|
// defaultFileList: [...fileList],
|
||||||
|
// className: 'upload-list-inline',
|
||||||
|
// };
|
||||||
|
|
||||||
|
const uploadButton = (
|
||||||
|
<div>
|
||||||
|
<Icon
|
||||||
|
style={{fontSize: '30px'}}
|
||||||
|
type={this.state.loading ? 'loading' : 'plus'}
|
||||||
|
/>
|
||||||
|
{/* <div className="ant-upload-text">Upload</div> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
let imageUrl;
|
||||||
|
|
||||||
|
if(this.props.isDefault) {
|
||||||
|
imageUrl = this.state.imageUrl
|
||||||
|
} else {
|
||||||
|
imageUrl = this.props.imageUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
{...layout}
|
||||||
|
required={required}
|
||||||
|
label={props.label}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
validateStatus={touched[field.name] && errors[field.name] && 'error'}
|
||||||
|
help={touched[field.name] && errors[field.name]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
multipleFileUpload ?
|
||||||
|
(
|
||||||
|
<Upload.Dragger
|
||||||
|
{..._props} {..._field}
|
||||||
|
//{...props_list_image}
|
||||||
|
onChange={this.normFile}
|
||||||
|
>
|
||||||
|
<p className="ant-upload-drag-icon">
|
||||||
|
<Icon type="inbox" />
|
||||||
|
</p>
|
||||||
|
<p className="ant-upload-text">Click or drag file to this area to upload</p>
|
||||||
|
<p className="ant-upload-hint">Support for a single or bulk upload.</p>
|
||||||
|
</Upload.Dragger>
|
||||||
|
) :
|
||||||
|
(
|
||||||
|
<Upload
|
||||||
|
style={{padding: '13px 22px'}}
|
||||||
|
name="avatar"
|
||||||
|
listType="picture-card"
|
||||||
|
className="avatar-uploader"
|
||||||
|
accept=".jpg , .png , .gif"
|
||||||
|
showUploadList={false}
|
||||||
|
beforeUpload={this.beforeUpload}
|
||||||
|
onChange={this.normFile}
|
||||||
|
className="upload-image"
|
||||||
|
>
|
||||||
|
{imageUrl ? <img src={imageUrl} alt="avatar" width={imgStyle ? imgStyle.width : "100%" } height={imgStyle ? imgStyle.height : "135"} /> : uploadButton}
|
||||||
|
<div style={{width: imgWidth ? imgWidth : 'initial', margin: '0 auto'}}>
|
||||||
|
<p
|
||||||
|
style={{fontWeight: 'bold',marginTop:'3px'}}
|
||||||
|
className="ant-upload-text"
|
||||||
|
>Click or drag file to this area to upload</p>
|
||||||
|
<p
|
||||||
|
style={{fontSize: '13px'}}
|
||||||
|
className="ant-upload-hint"
|
||||||
|
>Support for a single upload only</p>
|
||||||
|
{ isRatioMessage &&
|
||||||
|
<div className="ant-upload-hint">
|
||||||
|
{isRatioMessage.message ? isRatioMessage.message : "Aspect Ratio 4:3 (ex. 1024 x 768)"}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Upload>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UploadImage;
|
|
@ -0,0 +1,33 @@
|
||||||
|
import Checkbox from './Checkbox';
|
||||||
|
import Inputs from './Inputs';
|
||||||
|
import Select from './Select';
|
||||||
|
import Cascader from './Cascader';
|
||||||
|
import DatePicker from './DatePicker';
|
||||||
|
import Radio from './Radio';
|
||||||
|
import InputPassword from './InputPassword';
|
||||||
|
import InputTextArea from './InputTextArea';
|
||||||
|
import UploadImage from './UploadImage';
|
||||||
|
import MultiSelectOptions from './MultiSelectOptions';
|
||||||
|
import InputMaskNumber from './InputMaskNumber';
|
||||||
|
import TimePickerForm from './TimePicker'
|
||||||
|
import InputNumberAntD from './InputNumberAntD'
|
||||||
|
import SingleUploadImage from './SingleUploadImage'
|
||||||
|
import MultiSelect from './MultiSelect'
|
||||||
|
|
||||||
|
export {
|
||||||
|
Checkbox,
|
||||||
|
Inputs,
|
||||||
|
Select,
|
||||||
|
Cascader,
|
||||||
|
DatePicker,
|
||||||
|
Radio,
|
||||||
|
InputPassword,
|
||||||
|
InputTextArea,
|
||||||
|
UploadImage,
|
||||||
|
MultiSelectOptions,
|
||||||
|
MultiSelect,
|
||||||
|
InputMaskNumber,
|
||||||
|
TimePickerForm,
|
||||||
|
InputNumberAntD,
|
||||||
|
SingleUploadImage
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
import { Icon } from 'antd';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Loading = () => {
|
||||||
|
return (
|
||||||
|
<div style={{padding: 20, display: 'flex' , justifyContent: 'center' , marginLeft: '-144px'}}>
|
||||||
|
<div>
|
||||||
|
<Icon type="sync" spin /> Loading Data Please wait...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Loading;
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
const { Header, Footer, Content, Sider } = Layout;
|
||||||
|
|
||||||
|
|
||||||
|
function LoginLayout({children, ...rest}) {
|
||||||
|
return (
|
||||||
|
<Layout style={{height: "100%"}}>
|
||||||
|
<Sider width='50%' style={{ background: `url(${require("assets/img/bg_cms.png")}) center`, backgroundSize: 'cover' } }></Sider>
|
||||||
|
<Layout>
|
||||||
|
<Content style={{padding: 16}} >{children}</Content>
|
||||||
|
<Footer style={{textAlign: 'center', fontSize: '12px'}}>
|
||||||
|
<div style={{margin: '25px auto', padding: '17px 0', width: '325px', borderTop: '1px solid #e0e0e0', textAlign: 'left', color: '#8E8E93' }}>
|
||||||
|
By logging in you agree to Unioil's Terms of Service, <br/>Privacy Policy and Content Policies.
|
||||||
|
</div>
|
||||||
|
</Footer>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginLayout
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import LoginLayout from '../Layout'
|
||||||
|
import { Route } from 'react-router-dom'
|
||||||
|
const LoginLayoutRoute = ({component: Component, ...rest}) => {
|
||||||
|
return (
|
||||||
|
<Route {...rest} render={matchProps => (
|
||||||
|
<LoginLayout>
|
||||||
|
<Component {...matchProps} />
|
||||||
|
</LoginLayout>
|
||||||
|
)} />
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default LoginLayoutRoute
|
|
@ -0,0 +1,84 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
|
||||||
|
import { Modal, Button } from "antd";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
class ModalCancel extends React.Component {
|
||||||
|
state = { visible: false };
|
||||||
|
|
||||||
|
showModal = () => {
|
||||||
|
this.setState({
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleOk = e => {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCancel = e => {
|
||||||
|
console.log(e);
|
||||||
|
this.setState({
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const {
|
||||||
|
path,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
id,
|
||||||
|
dirty,
|
||||||
|
name,
|
||||||
|
loading
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key="button"
|
||||||
|
disabled={loading}
|
||||||
|
onClick={this.showModal}>
|
||||||
|
{name}
|
||||||
|
</Button>,
|
||||||
|
<Modal
|
||||||
|
getContainer={() => document.getElementById(id)}
|
||||||
|
key="modal"
|
||||||
|
width={300}
|
||||||
|
title={title ? title : "Your Title"}
|
||||||
|
visible={this.state.visible}
|
||||||
|
onOk={this.handleOk}
|
||||||
|
onCancel={this.handleCancel}
|
||||||
|
footer={[
|
||||||
|
<Button key={1} type="primary" onClick={this.handleCancel}>No</Button>,
|
||||||
|
<Link key={2} to={path} style={{marginLeft: 10}}>
|
||||||
|
<Button >
|
||||||
|
Yes
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{message ? message : "Your Message"}
|
||||||
|
</Modal>
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return <Link to={path} disabled={loading}>
|
||||||
|
<Button key="back" disabled={loading}>
|
||||||
|
{name}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalCancel;
|
|
@ -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);
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { withRouter} from 'react-router-dom'
|
||||||
|
|
||||||
|
function Page404(){
|
||||||
|
const error = "Page not found";
|
||||||
|
const errorMessage = "Sorry, but the page you are looking for doesn't exist";
|
||||||
|
|
||||||
|
return <Fragment>
|
||||||
|
<div align="center" style={{ marginTop: "10%" }}>
|
||||||
|
<h1 style={{ fontSize: 150, fontWeight: 'bold', margin: 0 }}>404</h1>
|
||||||
|
<p style={{ fontSize: 30, fontWeight: 'bold', margin: 0 }}>{error}</p>
|
||||||
|
<p>{errorMessage}</p>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(Page404);
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
function PageError(){
|
||||||
|
const error = "Forbidden";
|
||||||
|
const errorMessage = "Access to this resources on the server is denied!"
|
||||||
|
|
||||||
|
return <Fragment>
|
||||||
|
<div align="center" style={{ marginTop: "10%" }}>
|
||||||
|
<h1 style={{ fontSize: 150, fontWeight: 'bold', margin: 0 }}>403</h1>
|
||||||
|
<p style={{ fontSize: 30, fontWeight: 'bold', margin: 0 }}>{error}</p>
|
||||||
|
<p>{errorMessage}</p>
|
||||||
|
<Link to={'/locations'}>Go Back</Link>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PageError;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { withRouter} from 'react-router-dom'
|
||||||
|
|
||||||
|
function Page404(){
|
||||||
|
const error = "Page not found";
|
||||||
|
const errorMessage = "Sorry, but the page you are looking for doesn't exist";
|
||||||
|
|
||||||
|
return <Fragment>
|
||||||
|
<div align="center">
|
||||||
|
<h1 style={{ fontSize: 150, fontWeight: 'bold', margin: 0 }}>404</h1>
|
||||||
|
<p style={{ fontSize: 30, fontWeight: 'bold', margin: 0 }}>{error}</p>
|
||||||
|
<p>{errorMessage}</p>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(Page404);
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as PAGE403 } from "./403";
|
||||||
|
export { default as PAGE404 } from "./404";
|
|
@ -0,0 +1,445 @@
|
||||||
|
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_BRANCH, API_GET_FUELS } from 'utils/StationApi';
|
||||||
|
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)
|
||||||
|
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 == 'branches'){
|
||||||
|
console.log(defaulUrl, params)
|
||||||
|
response = await API_GET_BRANCH('branches', params);
|
||||||
|
console.log(response.data, params, 'response ng branches');
|
||||||
|
console.log(getCookie('TOKEN').token);
|
||||||
|
data = response.data.data.length > 0 ? response.data.data : null;
|
||||||
|
total = response.data.total
|
||||||
|
}
|
||||||
|
if(defaulUrl == 'fuels'){
|
||||||
|
console.log(defaulUrl, params)
|
||||||
|
response = await API_GET_FUELS('fuels', params)
|
||||||
|
console.log(response, 'fuels');
|
||||||
|
console.log(getCookie('TOKEN').token);
|
||||||
|
data = response.data.data.length > 0 ? response.data.data : null;
|
||||||
|
total = response.data.total
|
||||||
|
console.log(data, 'fuel data')
|
||||||
|
}
|
||||||
|
if(defaulUrl == 'location'){
|
||||||
|
console.log(defaulUrl, params)
|
||||||
|
response = await API_GET_BRANCH('stations', params);
|
||||||
|
console.log(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']}
|
||||||
|
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);
|
|
@ -0,0 +1,324 @@
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Table, Button, Popconfirm, Popover, message,
|
||||||
|
Pagination, Input ,Row, Col , Select, Form, Icon,
|
||||||
|
DatePicker, Collapse, notification } from 'antd';
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import queryString from 'query-string';
|
||||||
|
|
||||||
|
//import { fetchAllItem , jsonServerApi } from 'utils/Api';
|
||||||
|
import { fnQueryParams } from "utils/helper";
|
||||||
|
const Search = Input.Search;
|
||||||
|
const RangePicker = DatePicker.RangePicker;
|
||||||
|
const Panel = Collapse.Panel;
|
||||||
|
|
||||||
|
class TableLayout extends Component {
|
||||||
|
state = {
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
columns: null,
|
||||||
|
selectedRowKeys: []
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.renderColumns();
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaginationChange = (page, page_size) => {
|
||||||
|
|
||||||
|
let _page = page, _limit = page_size;
|
||||||
|
this.fnFilterChange({_page, _limit});
|
||||||
|
|
||||||
|
this.fetch({_page, _limit});
|
||||||
|
}
|
||||||
|
|
||||||
|
fnFilterChange = (props) => {
|
||||||
|
let { location, history } = this.props;
|
||||||
|
let { pathname, search } = location;
|
||||||
|
let values = queryString.parse(search);
|
||||||
|
let params = { ...values, ...props };
|
||||||
|
history.push({ pathname, search:fnQueryParams(params) });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch = async (params = {}) => {
|
||||||
|
let values = queryString.parse(this.props.location.search); //get parameters
|
||||||
|
if(params) {
|
||||||
|
if(params._page) values = params;
|
||||||
|
}
|
||||||
|
const { api } = this.props;
|
||||||
|
this.setState({ loading: true });
|
||||||
|
let url = api.default;
|
||||||
|
// let asysnData = await fetchAllItem(url, {
|
||||||
|
// ...values
|
||||||
|
// });
|
||||||
|
// if(asysnData) {
|
||||||
|
// this.setState({
|
||||||
|
// loading: false,
|
||||||
|
// data: asysnData.data,
|
||||||
|
// total: asysnData.headers['x-total-count'],
|
||||||
|
// search: values.search
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
remove = async (params={}) => {
|
||||||
|
let { path, uuid } = params
|
||||||
|
|
||||||
|
try {
|
||||||
|
// const response = await jsonServerApi.delete(`${path}/${uuid}`);
|
||||||
|
|
||||||
|
// if(response) {
|
||||||
|
|
||||||
|
// this.fetch()
|
||||||
|
|
||||||
|
// notification.success({
|
||||||
|
// message: 'Success',
|
||||||
|
// description: `Delete Successfull.`,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: `Error response message here.`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actionHandler = props => {
|
||||||
|
let { path, uuid, action, data } = props;
|
||||||
|
|
||||||
|
let { history, location } = this.props;
|
||||||
|
let { search, pathname } = location;
|
||||||
|
if(action === 'update') {
|
||||||
|
history.push({
|
||||||
|
pathname: `${path}/${uuid}`,
|
||||||
|
state: {
|
||||||
|
initialValues: data
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(action === 'delete') {
|
||||||
|
//this.remove({path, uuid});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(action === 'view') {
|
||||||
|
history.push({
|
||||||
|
pathname: `${path}/${uuid}`,
|
||||||
|
state: {
|
||||||
|
prevPath:`${pathname}${search}`,
|
||||||
|
initialValues: data
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm(items,id) {
|
||||||
|
this.remove({path: items.path, uuid: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(e) {
|
||||||
|
message.error('You Click Cancel.');
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange = (dates, dateStrings) => {
|
||||||
|
console.log('From: ', dates[0], ', to: ', dates[1]);
|
||||||
|
console.log('From: ', dateStrings[0], ', to: ', dateStrings[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderColumns = () => {
|
||||||
|
|
||||||
|
const { headers,actions,keyValue } = this.props;
|
||||||
|
|
||||||
|
let columns = headers;
|
||||||
|
|
||||||
|
headers.map((item, index) => {
|
||||||
|
if(item.renderActions) {
|
||||||
|
item['render'] = (text, record) => {
|
||||||
|
return item.renderActions.map((itemAction,key) => {
|
||||||
|
if(itemAction.action_name) {
|
||||||
|
return (
|
||||||
|
<span key={key} style={{marginRight: '10px'}}>
|
||||||
|
{
|
||||||
|
(itemAction.action || itemAction.action_name == "delete")
|
||||||
|
?
|
||||||
|
<Popconfirm
|
||||||
|
title="Are you sure you want to delete this record?"
|
||||||
|
content={itemAction.message}
|
||||||
|
onConfirm={
|
||||||
|
()=> this.confirm(itemAction, record[keyValue])
|
||||||
|
}
|
||||||
|
onCancel={()=> this.cancel()} okText="Yes" cancelText="Cancel"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={itemAction.icon}
|
||||||
|
type="danger"
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
:
|
||||||
|
<Popover
|
||||||
|
title={itemAction.action_name}
|
||||||
|
content={itemAction.action_message}
|
||||||
|
trigger="hover"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={
|
||||||
|
()=> this.actionHandler({
|
||||||
|
path: itemAction.path,
|
||||||
|
uuid: record[keyValue],
|
||||||
|
action: itemAction.action_name,
|
||||||
|
data: record
|
||||||
|
})
|
||||||
|
}
|
||||||
|
icon={itemAction.icon}
|
||||||
|
type="primary"
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.setState({ columns: columns })
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteHandler = () => {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
// ajax request after empty completing
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
|
||||||
|
this.setState((prevState, props) => ({
|
||||||
|
selectedRowKeys: [],
|
||||||
|
loading: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.fetch();
|
||||||
|
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectChange = (selectedRowKeys) => {
|
||||||
|
this.setState({ selectedRowKeys });
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilterSearch = (val) => {
|
||||||
|
console.log('Received values of form: ', val);
|
||||||
|
this.fnFilterChange(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { keyValue,scrollbale, advanceFilter:AdvancedSearchForm } = this.props;
|
||||||
|
const { loading,selectedRowKeys } = this.state;
|
||||||
|
|
||||||
|
const rowSelection = { selectedRowKeys, onChange: this.onSelectChange };
|
||||||
|
const hasSelected = selectedRowKeys.length > 0;
|
||||||
|
// VALUES FROM PARAMS URL
|
||||||
|
let values = queryString.parse(this.props.location.search);
|
||||||
|
return (
|
||||||
|
|
||||||
|
<Fragment>
|
||||||
|
|
||||||
|
<Collapse style={{marginBottom: 16}}>
|
||||||
|
<Panel header="Advance Filter" key="1">
|
||||||
|
<Collapse defaultActiveKey="1" style={{padding: 16}}>
|
||||||
|
<AdvancedSearchForm onSubmit={this.onFilterSearch}/>
|
||||||
|
</Collapse>
|
||||||
|
</Panel>
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
<Row gutter={16}>
|
||||||
|
|
||||||
|
<Col span={8} >
|
||||||
|
<h5>Date Range :</h5>
|
||||||
|
<RangePicker
|
||||||
|
ranges={{ Today: [moment(), moment()], 'This Month': [moment(), moment().endOf('month')] }}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8} >
|
||||||
|
<h5>Date</h5>
|
||||||
|
<DatePicker onChange={this.onChange} />
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Col span={8} offset={16} style={{marginBottom: 16}}>
|
||||||
|
<span>Search :</span>
|
||||||
|
<Search
|
||||||
|
defaultValue={values.search}
|
||||||
|
onSearch={(val)=> this.fnFilterChange({search: val})}
|
||||||
|
placeholder="Input search text"
|
||||||
|
enterButton="Search"
|
||||||
|
label={'Search'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: 16, marginTop: 16 }}>
|
||||||
|
<Button
|
||||||
|
type="danger"
|
||||||
|
onClick={this.onDeleteHandler}
|
||||||
|
disabled={!hasSelected}
|
||||||
|
icon="delete"
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
Delete All
|
||||||
|
</Button>
|
||||||
|
<span style={{ marginLeft: 8 }}>
|
||||||
|
{hasSelected ? `Selected ${selectedRowKeys.length} items` : ''}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
columns={this.state.columns}
|
||||||
|
dataSource={this.state.data}
|
||||||
|
rowKey={record => record[keyValue]}
|
||||||
|
pagination={false}
|
||||||
|
loading={this.state.loading}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
scroll={scrollbale ? scrollbale : {x: 0, y: 0}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
this.state.total > 0
|
||||||
|
?
|
||||||
|
<Pagination
|
||||||
|
style={{float: 'right' , marginTop: 16}}
|
||||||
|
showSizeChanger
|
||||||
|
showQuickJumper
|
||||||
|
defaultCurrent={parseInt(values._page) || 1}
|
||||||
|
defaultPageSize={parseInt(values._limit) || 10}
|
||||||
|
pageSizeOptions={['5','10','15','20']}
|
||||||
|
total={parseInt(this.state.total)}
|
||||||
|
showTotal={(total, range) => `${range[0]}-${range[1]} of ${total} items`}
|
||||||
|
onChange={this.onPaginationChange}
|
||||||
|
onShowSizeChange={this.onPaginationChange}
|
||||||
|
/>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default withRouter(TableLayout);
|
||||||
|
|
|
@ -0,0 +1,427 @@
|
||||||
|
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, API_CREATE_NOTIF } from 'utils/NotificationApi';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
class AdvanceTable extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
data: null,
|
||||||
|
total: null,
|
||||||
|
loading: false,
|
||||||
|
selectedRowKeys: [],
|
||||||
|
columns: [],
|
||||||
|
search_filter: '',
|
||||||
|
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) => {
|
||||||
|
this.handleFilterChange({ page, page_size });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleFilterChange = (props, isClearFilter) => {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
console.log(props)
|
||||||
|
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) });
|
||||||
|
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 == 'notifications'){
|
||||||
|
response = await API_GET_NOTIF(defaulUrl, params);
|
||||||
|
console.log(response, 'response');
|
||||||
|
console.log(getCookie('TOKEN').token);
|
||||||
|
data = response.data.length > 0 ? response.data : null;
|
||||||
|
total = response.data.length > 0 ? response.data.total : 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = await API_GET(defaulUrl, params);
|
||||||
|
console.log(response, 'response');
|
||||||
|
console.log(getCookie('TOKEN').token);
|
||||||
|
data = response.data.data.length > 0 ? response.data.data : null;
|
||||||
|
total = response.data.data.length > 0 ? response.data.data.total : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.table(data, 'data');
|
||||||
|
// console.table(total, 'total');
|
||||||
|
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) => {
|
||||||
|
try {
|
||||||
|
await API_UNI_OIL.delete(`${this.props.url.default}/${uuid}`);
|
||||||
|
this.handleFilterChange({});
|
||||||
|
message.success('Record was successfully deleted.');
|
||||||
|
} catch ({ response: error }) {
|
||||||
|
this.handleFilterChange({});
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: (
|
||||||
|
<div>
|
||||||
|
<div>Something went wrong deleting record.</div>
|
||||||
|
- {error && error.data && error.data.message}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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.');
|
||||||
|
} 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 == 'edit')
|
||||||
|
actionBtn = () => history.push({ pathname: `${location.pathname}/edit/${record[keyValue]}` });
|
||||||
|
if (action.key == 'view')
|
||||||
|
actionBtn = () => history.push({ pathname: `${location.pathname}/view/${record[keyValue]}` });
|
||||||
|
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[keyValue])}
|
||||||
|
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={ this.update }
|
||||||
|
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', '50', '100' ]}
|
||||||
|
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(AdvanceTable);
|
|
@ -0,0 +1,4 @@
|
||||||
|
.table-operations > button {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
const RESPONSE = {
|
||||||
|
HTTP_CONTINUE: 100,
|
||||||
|
HTTP_SWITCHING_PROTOCOLS: 101,
|
||||||
|
HTTP_PROCESSING: 102,
|
||||||
|
HTTP_EARLY_HINTS: 103,
|
||||||
|
HTTP_OK: 200,
|
||||||
|
HTTP_CREATED: 201,
|
||||||
|
HTTP_ACCEPTED: 202,
|
||||||
|
HTTP_NON_AUTHORITATIVE_INFORMATION: 203,
|
||||||
|
HTTP_NO_CONTENT: 204,
|
||||||
|
HTTP_RESET_CONTENT: 205,
|
||||||
|
HTTP_PARTIAL_CONTENT: 206,
|
||||||
|
HTTP_MULTI_STATUS: 207,
|
||||||
|
HTTP_ALREADY_REPORTED: 208,
|
||||||
|
HTTP_IM_USED: 226,
|
||||||
|
HTTP_MULTIPLE_CHOICES: 300,
|
||||||
|
HTTP_MOVED_PERMANENTLY: 301,
|
||||||
|
HTTP_FOUND: 302,
|
||||||
|
HTTP_SEE_OTHER: 303,
|
||||||
|
HTTP_NOT_MODIFIED: 304,
|
||||||
|
HTTP_USE_PROXY: 305,
|
||||||
|
HTTP_RESERVED: 306,
|
||||||
|
HTTP_TEMPORARY_REDIRECT: 307,
|
||||||
|
HTTP_PERMANENTLY_REDIRECT: 308,
|
||||||
|
HTTP_BAD_REQUEST: 400,
|
||||||
|
HTTP_UNAUTHORIZED: 401,
|
||||||
|
HTTP_PAYMENT_REQUIRED: 402,
|
||||||
|
HTTP_FORBIDDEN: 403,
|
||||||
|
HTTP_NOT_FOUND: 404,
|
||||||
|
HTTP_METHOD_NOT_ALLOWED: 405,
|
||||||
|
HTTP_NOT_ACCEPTABLE: 406,
|
||||||
|
HTTP_PROXY_AUTHENTICATION_REQUIRED: 407,
|
||||||
|
HTTP_REQUEST_TIMEOUT: 408,
|
||||||
|
HTTP_CONFLICT: 409,
|
||||||
|
HTTP_GONE: 410,
|
||||||
|
HTTP_LENGTH_REQUIRED: 411,
|
||||||
|
HTTP_PRECONDITION_FAILED: 412,
|
||||||
|
HTTP_REQUEST_ENTITY_TOO_LARGE: 413,
|
||||||
|
HTTP_REQUEST_URI_TOO_LONG: 414,
|
||||||
|
HTTP_UNSUPPORTED_MEDIA_TYPE: 415,
|
||||||
|
HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: 416,
|
||||||
|
HTTP_EXPECTATION_FAILED: 417,
|
||||||
|
HTTP_I_AM_A_TEAPOT: 418,
|
||||||
|
HTTP_MISDIRECTED_REQUEST: 421,
|
||||||
|
HTTP_UNPROCESSABLE_ENTITY: 422,
|
||||||
|
HTTP_LOCKED: 423,
|
||||||
|
HTTP_FAILED_DEPENDENCY: 424,
|
||||||
|
HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL: 425,
|
||||||
|
HTTP_UPGRADE_REQUIRED: 426,
|
||||||
|
HTTP_PRECONDITION_REQUIRED: 428,
|
||||||
|
HTTP_TOO_MANY_REQUESTS: 429,
|
||||||
|
HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
|
||||||
|
HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 451,
|
||||||
|
HTTP_INTERNAL_SERVER_ERROR: 500,
|
||||||
|
HTTP_NOT_IMPLEMENTED: 501,
|
||||||
|
HTTP_BAD_GATEWAY: 502,
|
||||||
|
HTTP_SERVICE_UNAVAILABLE: 503,
|
||||||
|
HTTP_GATEWAY_TIMEOUT: 504,
|
||||||
|
HTTP_VERSION_NOT_SUPPORTED: 505,
|
||||||
|
HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL: 506,
|
||||||
|
HTTP_INSUFFICIENT_STORAGE: 507,
|
||||||
|
HTTP_LOOP_DETECTED: 508,
|
||||||
|
HTTP_NOT_EXTENDED: 510,
|
||||||
|
HTTP_NETWORK_AUTHENTICATION_REQUIRED: 511,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RESPONSE;
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const LOGOUT = 'LOGOUT'
|
||||||
|
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'
|
||||||
|
export const LOGOUT_RESET = 'LOGOUT_RESET'
|
||||||
|
|
||||||
|
export const FETCH_DATA = 'FETCH_DATA'
|
||||||
|
export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS'
|
||||||
|
export const FETCH_DATA_ERROR = 'FETCH_DATA_ERROR'
|
||||||
|
export const FETCH_DATA_RESET = 'FETCH_DATA_RESET'
|
|
@ -0,0 +1,5 @@
|
||||||
|
const global = {
|
||||||
|
loadingTime: 3000
|
||||||
|
}
|
||||||
|
|
||||||
|
export default global;
|
|
@ -0,0 +1,71 @@
|
||||||
|
export const required = value => (value ? undefined : 'This is a required field.');
|
||||||
|
export const dropdownRequired = value =>
|
||||||
|
value === null || value === undefined
|
||||||
|
? 'This is a required field.'
|
||||||
|
: undefined;
|
||||||
|
export const requiredAtleastOne = value => (value ? undefined : 'At least one must be selected.');
|
||||||
|
export const arrayChecker = value => (value && value.length > 0 ? undefined : 'This is a required field.');
|
||||||
|
export const email = value =>
|
||||||
|
value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
|
||||||
|
? 'Please provide a valid email address.'
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
export const alphaNumeric = value =>
|
||||||
|
value && /[^a-zA-Z0-9 ]/i.test(value)
|
||||||
|
? 'Only alphanumeric characters'
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const length = size => value =>
|
||||||
|
value && value.length !== size
|
||||||
|
? `Must be ${size} characters.`
|
||||||
|
: undefined;
|
||||||
|
export const length4 = length(4);
|
||||||
|
export const length10 = length(10);
|
||||||
|
export const length11 = length(11);
|
||||||
|
export const length12 = length(12);
|
||||||
|
export const length13 = length(13);
|
||||||
|
|
||||||
|
const minLength = min => value =>
|
||||||
|
value && value.length < min
|
||||||
|
? `Must be ${min} characters or more`
|
||||||
|
: undefined;
|
||||||
|
export const minLength2 = minLength(2)
|
||||||
|
|
||||||
|
const maxLength = max => value =>
|
||||||
|
value && value.length > max
|
||||||
|
? `Must be ${max} characters or less.`
|
||||||
|
: undefined;
|
||||||
|
export const maxLength5 = maxLength(5);
|
||||||
|
export const maxLength10 = maxLength(10);
|
||||||
|
export const maxLength15 = maxLength(15);
|
||||||
|
|
||||||
|
export const numbers = value =>
|
||||||
|
value && !/^[0-9]*$/i.test(value)
|
||||||
|
? 'Must be whole numbers only.'
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
export const numbersWithDecimals = value =>
|
||||||
|
value && !/^\d+(\.\d{1,2})?$/i.test(value)
|
||||||
|
? 'Must be a valid number with two (2) decimals only.'
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const minValue = min => value =>
|
||||||
|
value && parseFloat(value.toString().replace(/,/g, '')) <= min
|
||||||
|
? `Must be greater than ${min}.`
|
||||||
|
: undefined;
|
||||||
|
export const minValue0 = minValue(0);
|
||||||
|
|
||||||
|
const minAllowableValue = min => value =>
|
||||||
|
value && parseFloat(value.toString().replace(/,/g, '')) < min
|
||||||
|
? `Must be greater than ${min}.`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
export const minAllowableValue0 = minAllowableValue(0)
|
||||||
|
|
||||||
|
const maxValue = max => value =>
|
||||||
|
value && parseFloat(value.toString().replace(/,/g, '')) >= max
|
||||||
|
? `Must be less than ${max}.`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
export const maxValue1M = maxValue(1000000)
|
||||||
|
export const maxValue100 = maxValue(100)
|
|
@ -0,0 +1,205 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react';
|
||||||
|
import { Row, Button, Col } from 'antd';
|
||||||
|
import { Form, Field } from 'formik';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import { Inputs, Checkbox, Radio, UploadImage, InputTextArea, SingleUploadImage } from 'components/Forms';
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 6 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 10 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function AddCardForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
handleSubmit,
|
||||||
|
loading,
|
||||||
|
handleFileUpload,
|
||||||
|
handleFileUploadBackground,
|
||||||
|
history
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate>
|
||||||
|
<HeaderForm
|
||||||
|
isInsideForm
|
||||||
|
loading={loading}
|
||||||
|
disabled={props.isValid == false ? true : false}
|
||||||
|
title="Card Types"
|
||||||
|
action={handleSubmit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> {history.push("/about-us/card-types")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="code"
|
||||||
|
type="text"
|
||||||
|
//icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Card Code"
|
||||||
|
placeholder="Card Code"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="type"
|
||||||
|
type="text"
|
||||||
|
//icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Card Type Description"
|
||||||
|
placeholder="Card Type Description"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
onCountText
|
||||||
|
charsperpage={140}
|
||||||
|
maxLength="140"
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
name="description"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Card Type Short Description"
|
||||||
|
placeholder="Card Type Short Description"
|
||||||
|
rows={4}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
limit100kb
|
||||||
|
name="image"
|
||||||
|
type="file"
|
||||||
|
accept=".jpg , .png, .gif"
|
||||||
|
notAcceptedImg={["image/gif"]}
|
||||||
|
multiple={false}
|
||||||
|
imageUrl={props.values.image && `${props.values.image}`}
|
||||||
|
className="upload-list-inline"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Upload Card Type Image"
|
||||||
|
placeholder="Upload Card Type Image"
|
||||||
|
component={SingleUploadImage}
|
||||||
|
imgWidth="294px"
|
||||||
|
handleFileUpload={handleFileUpload}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="virtual_card_font_color"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
optionsList={[
|
||||||
|
{
|
||||||
|
label: "White",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Black",
|
||||||
|
value: 2,
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
label="Virtual Card Font Color"
|
||||||
|
component={Radio}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
limit100kb
|
||||||
|
name="bg_image"
|
||||||
|
type="file"
|
||||||
|
multiple={false}
|
||||||
|
notAcceptedImg={["image/gif"]}
|
||||||
|
isRatioMessage={{isRatioMessage: 'Image Size (ex. 375 x 260).'}}
|
||||||
|
imageUrl={props.values.image && `${props.values.bg_image}`}
|
||||||
|
className="upload-list-inline"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Upload Card Type Cover Image"
|
||||||
|
placeholder="Upload Card Type Cover Image"
|
||||||
|
component={SingleUploadImage}
|
||||||
|
imgWidth="294px"
|
||||||
|
handleFileUpload={handleFileUploadBackground}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="id_number"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
optionsList={[
|
||||||
|
{
|
||||||
|
label: "Yes",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "No",
|
||||||
|
value: 2,
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
label="ID Number Required?"
|
||||||
|
component={Radio}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
onCountText
|
||||||
|
charsperpage={40}
|
||||||
|
maxLength="40"
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
disabled={props.values.id_number && props.values.id_number == 2 ? true: false}
|
||||||
|
name="id_number_description"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="ID Number Description"
|
||||||
|
placeholder="ID Number Description"
|
||||||
|
rows={2}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h4 style={{marginLeft: '190px'}}>DATA PRIVACY</h4>
|
||||||
|
<Field
|
||||||
|
name="terms_and_conditions"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label={`Terms & Conditions`}
|
||||||
|
placeholder={`Terms & Conditions`}
|
||||||
|
rows={10}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="faqs"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="FAQs"
|
||||||
|
placeholder="FAQs"
|
||||||
|
rows={10}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AddCardForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(AddCardForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default AddCardForm;
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { connect } from "react-redux"
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import { message,notification } from 'antd';
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import AddCardForm from './components/AddCardForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { userDetailsSchema } from './validationSchema'
|
||||||
|
import { customAction } from "actions";
|
||||||
|
import { API_GET, API_POST, API_UNI_OIL } from "utils/Api";
|
||||||
|
import { apiFormValidation } from "utils/helper";
|
||||||
|
|
||||||
|
|
||||||
|
class CardTypeCreate extends Component {
|
||||||
|
state = {
|
||||||
|
generated_password: null,
|
||||||
|
userInfo: null,
|
||||||
|
loading: false,
|
||||||
|
fileUpload: null,
|
||||||
|
fileUploadBackground: null
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async (values, actions) => {
|
||||||
|
|
||||||
|
const { fileUpload, fileUploadBackground } = this.state;
|
||||||
|
const { history } = this.props;
|
||||||
|
const { setErrors } = actions;
|
||||||
|
|
||||||
|
this.setState({loading: true})
|
||||||
|
try {
|
||||||
|
const headers = {
|
||||||
|
'ContentType': 'multipart/form-data',
|
||||||
|
};
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if(fileUpload) {
|
||||||
|
fileUpload.forEach((t, i) => {
|
||||||
|
formData.append( `image`, t.originFileObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(fileUploadBackground) {
|
||||||
|
fileUploadBackground.forEach((t, i) => {
|
||||||
|
formData.append( `bg_image`, t.originFileObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(values.id_number == 1) {
|
||||||
|
values.id_number = 1;
|
||||||
|
if(!values.id_number_description) {
|
||||||
|
setErrors({id_number_description: "ID Number Description is required!"})
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
Something went wrong creating new record.
|
||||||
|
<div>- ID Number Description is required!</div>
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
return this.setState({loading: false})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
values.id_number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(values.virtual_card_font_color == 2) {
|
||||||
|
values.virtual_card_font_color = 1
|
||||||
|
} else {
|
||||||
|
values.virtual_card_font_color = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
values.code && (formData.append('code', values.code));
|
||||||
|
values.type && (formData.append('type', values.type));
|
||||||
|
values.description && (formData.append('description', values.description));
|
||||||
|
values.terms_and_conditions && (formData.append('terms_and_conditions', values.terms_and_conditions));
|
||||||
|
values.faqs && (formData.append('faqs', values.faqs));
|
||||||
|
formData.append('id_number', values.id_number);
|
||||||
|
formData.append('id_number_description', values.id_number_description);
|
||||||
|
formData.append('virtual_card_font_color', values.virtual_card_font_color);
|
||||||
|
let response = await API_UNI_OIL.post('cardType', formData , headers)
|
||||||
|
|
||||||
|
if(response) {
|
||||||
|
message.success('New record added.');
|
||||||
|
this.setState({loading: false})
|
||||||
|
history.push({ pathname: "/about-us/card-types" })
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
if (error.status === 422) {
|
||||||
|
apiFormValidation({ data: error.data.data, setErrors });
|
||||||
|
}
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
Something went wrong creating new record.
|
||||||
|
{ error && error.data && error.data.data && error.data.data.image
|
||||||
|
&& (<div>- {error.data.data.image[0]} </div>) }
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
this.setState({loading: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
handleAddCardTypes =()=> {
|
||||||
|
this.form.submitForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileUpload =(e)=> {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return this.setState({fileUpload: e});
|
||||||
|
}
|
||||||
|
return e && this.setState({fileUpload: e.fileList});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileUploadBackground =(e)=> {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return this.setState({fileUploadBackground: e});
|
||||||
|
}
|
||||||
|
return e && this.setState({fileUploadBackground: e.fileList});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { userManagement } = this.props
|
||||||
|
const { loading, isGenerated } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '10px'}}>
|
||||||
|
{/* <HeaderForm
|
||||||
|
loading={loading}
|
||||||
|
title="Card Types"
|
||||||
|
action={this.handleAddCardTypes}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> {this.props.history.push("/about-us/card-types")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/> */}
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '25px 35px'}}>Card Type Details</h2>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
code: '',
|
||||||
|
type: '',
|
||||||
|
description: '',
|
||||||
|
image: '',
|
||||||
|
terms_and_conditions: '',
|
||||||
|
faqs: '',
|
||||||
|
virtual_card_font_color: 1,
|
||||||
|
bg_image: '',
|
||||||
|
id_number: 2,
|
||||||
|
id_number_description: '',
|
||||||
|
}}
|
||||||
|
ref={node => (this.form = node)}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={userDetailsSchema}
|
||||||
|
onSubmit={this.handleSubmit }
|
||||||
|
render = {(props)=>
|
||||||
|
<AddCardForm
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
history={this.props.history}
|
||||||
|
handleFileUpload={this.handleFileUpload}
|
||||||
|
handleFileUploadBackground={this.handleFileUploadBackground}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CardTypeCreate = connect(
|
||||||
|
state => ({
|
||||||
|
//userInfo: state
|
||||||
|
userManagement: state.userManagement,
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(CardTypeCreate);
|
||||||
|
|
||||||
|
export default CardTypeCreate;
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const userDetailsSchema = Yup.object().shape({
|
||||||
|
code: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
//.max(12,"Card Code must be 20 characters only. "),
|
||||||
|
.required('Card Code is required!'),
|
||||||
|
type: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
.required('Card Type Description is required!'),
|
||||||
|
description: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(140, "Maximum character is 140.")
|
||||||
|
.required('Card Type Short Description is required!'),
|
||||||
|
image: Yup.string()
|
||||||
|
.required('Upload Card Type Image is required!'),
|
||||||
|
virtual_card_font_color: Yup.string()
|
||||||
|
.required('Virtual Card Font Color is required!'),
|
||||||
|
bg_image: Yup.string()
|
||||||
|
.required('Upload Card Type Cover Image is required!'),
|
||||||
|
terms_and_conditions: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000.")
|
||||||
|
.required('Terms and Condition is required!'),
|
||||||
|
faqs: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000.")
|
||||||
|
.required('FAQs is required!'),
|
||||||
|
id_number: Yup.string()
|
||||||
|
.required('ID Number is required!'),
|
||||||
|
id_number_description: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(40, "Maximum character is 40."),
|
||||||
|
//.required('ID Number Description is required!'),
|
||||||
|
|
||||||
|
})
|
|
@ -0,0 +1,204 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react';
|
||||||
|
import { Row, Button, Col } from 'antd';
|
||||||
|
import { Form, Field } from 'formik';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import { Inputs, Radio, InputTextArea, UploadImage,SingleUploadImage } from 'components/Forms';
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 6 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 10 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function EditCardForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
loading,
|
||||||
|
handleSubmit,
|
||||||
|
handleFileUpload,
|
||||||
|
history,
|
||||||
|
handleFileUploadBackground
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate>
|
||||||
|
<HeaderForm
|
||||||
|
isInsideForm
|
||||||
|
loading={loading}
|
||||||
|
disabled={props.isValid == false ? true : false}
|
||||||
|
title="Update Card Type"
|
||||||
|
action={handleSubmit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withConfirm={{message: "Save changes to this record?"}}
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> { history.push("/about-us/card-types")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="code"
|
||||||
|
type="text"
|
||||||
|
//icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Card Code"
|
||||||
|
placeholder="Card Code"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="type"
|
||||||
|
type="text"
|
||||||
|
//icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Card Type Description"
|
||||||
|
placeholder="Card Type Description"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
onCountText
|
||||||
|
charsperpage={140}
|
||||||
|
maxLength="140"
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
name="description"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Card Type Short Description"
|
||||||
|
placeholder="Card Type Short Description"
|
||||||
|
rows={4}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
limit100kb
|
||||||
|
name="image"
|
||||||
|
type="file"
|
||||||
|
accept=".jpg , .png, .gif"
|
||||||
|
notAcceptedImg={["image/gif"]}
|
||||||
|
multiple={false}
|
||||||
|
imageUrl={props.values.image && `${props.values.image}`}
|
||||||
|
className="upload-list-inline"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Upload Card Type Image"
|
||||||
|
placeholder="Upload Card Type Image"
|
||||||
|
component={SingleUploadImage}
|
||||||
|
imgWidth="294px"
|
||||||
|
handleFileUpload={handleFileUpload}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="virtual_card_font_color"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
optionsList={[
|
||||||
|
{
|
||||||
|
label: "White",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Black",
|
||||||
|
value: 2,
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
label="Virtual Card Font Color"
|
||||||
|
component={Radio}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
limit100kb
|
||||||
|
name="bg_image"
|
||||||
|
type="file"
|
||||||
|
multiple={false}
|
||||||
|
notAcceptedImg={["image/gif"]}
|
||||||
|
isRatioMessage={{isRatioMessage: 'Image Size (ex. 375 x 260).'}}
|
||||||
|
imageUrl={props.values.image && `${props.values.bg_image}`}
|
||||||
|
className="upload-list-inline"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Upload Card Type Cover Image"
|
||||||
|
placeholder="Upload Card Type Cover Image"
|
||||||
|
component={SingleUploadImage}
|
||||||
|
imgWidth="294px"
|
||||||
|
handleFileUpload={handleFileUploadBackground}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="id_number"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
optionsList={[
|
||||||
|
{
|
||||||
|
label: "Yes",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "No",
|
||||||
|
value: 2,
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
label="ID Number Required?"
|
||||||
|
component={Radio}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
onCountText
|
||||||
|
charsperpage={40}
|
||||||
|
maxLength="40"
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
disabled={props.values.id_number && props.values.id_number == 2 ? true: false}
|
||||||
|
name="id_number_description"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="ID Number Description"
|
||||||
|
placeholder="ID Number Description"
|
||||||
|
rows={2}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h4 style={{marginLeft: '190px'}}>DATA PRIVACY</h4>
|
||||||
|
<Field
|
||||||
|
name="terms_and_conditions"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label={`Terms & Conditions`}
|
||||||
|
placeholder={`Terms & Conditions`}
|
||||||
|
rows={10}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="faqs"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="FAQs"
|
||||||
|
placeholder="FAQs"
|
||||||
|
rows={10}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EditCardForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(EditCardForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default EditCardForm;
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import { withRouter } from "react-router-dom"
|
||||||
|
import { notification, message } from "antd"
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import EditCardForm from './components/EditCardForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { userDetailsSchema } from './validationSchema'
|
||||||
|
import { API_GET, API_PUT, API_POST } from "utils/Api";
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
import { apiFormValidation } from "utils/helper";
|
||||||
|
|
||||||
|
class CardTypeEdit extends Component {
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
userInfo: null,
|
||||||
|
mounted: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
|
||||||
|
const { match } = this.props;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await API_UNI_OIL.get(`cardType/${match.params.id}`);
|
||||||
|
this.setState({
|
||||||
|
userInfo: {...response.data.data},
|
||||||
|
mounted: true
|
||||||
|
})
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong loading data.</div>
|
||||||
|
- {error && error.data && error.data.message}
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
if(error.status == 404) {
|
||||||
|
if(this.props.location.pathname)
|
||||||
|
this.props.history.push(`${this.props.location.pathname}/404`); return
|
||||||
|
}
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async (values, actions) => {
|
||||||
|
|
||||||
|
const { fileUpload, userInfo, fileUploadBackground } = this.state;
|
||||||
|
const { history } = this.props;
|
||||||
|
const { setErrors } = actions;
|
||||||
|
|
||||||
|
this.setState({loading: true})
|
||||||
|
try {
|
||||||
|
const headers = {
|
||||||
|
'ContentType': 'multipart/form-data',
|
||||||
|
};
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if(fileUpload) {
|
||||||
|
fileUpload.forEach((t, i) => {
|
||||||
|
formData.append( `image`, t.originFileObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(fileUploadBackground) {
|
||||||
|
fileUploadBackground.forEach((t, i) => {
|
||||||
|
formData.append( `bg_image`, t.originFileObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(values.id_number == 1) {
|
||||||
|
values.id_number = 1;
|
||||||
|
if(!values.id_number_description) {
|
||||||
|
setErrors({id_number_description: "ID Number Description is required!"})
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
Something went wrong.
|
||||||
|
<div>- ID Number Description is required!</div>
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
return this.setState({loading: false})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
values.id_number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(values.virtual_card_font_color == 2) {
|
||||||
|
values.virtual_card_font_color = 1
|
||||||
|
} else {
|
||||||
|
values.virtual_card_font_color = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
values.code && (formData.append('code', values.code));
|
||||||
|
values.type && (formData.append('type', values.type));
|
||||||
|
values.description && (formData.append('description', values.description));
|
||||||
|
values.terms_and_conditions && (formData.append('terms_and_conditions', values.terms_and_conditions));
|
||||||
|
values.faqs && (formData.append('faqs', values.faqs));
|
||||||
|
formData.append('id_number', values.id_number);
|
||||||
|
formData.append('id_number_description', values.id_number_description);
|
||||||
|
formData.append('virtual_card_font_color', values.virtual_card_font_color);
|
||||||
|
|
||||||
|
let response = await API_UNI_OIL.post(`cardTypeUpdate/${userInfo.cardtype_uuid}`, formData , headers)
|
||||||
|
|
||||||
|
if(response) {
|
||||||
|
message.success('Record was successfully update.');
|
||||||
|
this.setState({loading: false})
|
||||||
|
history.push({ pathname: "/about-us/card-types" })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
if (error.status === 422) {
|
||||||
|
apiFormValidation({ data: error.data.data, setErrors });
|
||||||
|
}
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
Something went wrong creating new record.
|
||||||
|
{ error && error.data && error.data.data && error.data.data.image
|
||||||
|
&& (<div>- {error.data.data.image[0]} </div>) }
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
this.setState({loading: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEditCardTypes =()=> {
|
||||||
|
this.form.submitForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileUpload =(e)=> {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return this.setState({fileUpload: e});
|
||||||
|
}
|
||||||
|
return e && this.setState({fileUpload: e.fileList});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileUploadBackground =(e)=> {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return this.setState({fileUploadBackground: e});
|
||||||
|
}
|
||||||
|
return e && this.setState({fileUploadBackground: e.fileList});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if(!this.state.mounted) return null;
|
||||||
|
|
||||||
|
const { loading, userInfo } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '10px'}}>
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '25px 35px'}}>Card Types Details</h2>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
code: userInfo.code || '',
|
||||||
|
type: userInfo.name || '',
|
||||||
|
description: userInfo.description || '',
|
||||||
|
image: userInfo.image || '',
|
||||||
|
terms_and_conditions: userInfo.terms_and_conditions || '',
|
||||||
|
faqs: userInfo.faqs || '',
|
||||||
|
id_number: userInfo.id_number && userInfo.id_number == 1 ? userInfo.id_number : 2 || '',
|
||||||
|
id_number_description: userInfo.id_number_description || '',
|
||||||
|
virtual_card_font_color: userInfo.virtual_card_font_color && userInfo.virtual_card_font_color == 1 ? 2 : 1 || 1,
|
||||||
|
bg_image: userInfo.bg_image || '',
|
||||||
|
id_number_description: userInfo.id_number_description || '',
|
||||||
|
}}
|
||||||
|
ref={node => (this.form = node)}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={userDetailsSchema}
|
||||||
|
onSubmit={this.handleSubmit }
|
||||||
|
render = {(props)=>
|
||||||
|
<EditCardForm
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
history={this.props.history}
|
||||||
|
handleFileUpload={this.handleFileUpload}
|
||||||
|
handleFileUploadBackground={this.handleFileUploadBackground}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(CardTypeEdit);
|
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const userDetailsSchema = Yup.object().shape({
|
||||||
|
code: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
//.max(12,"Card Code must be 12 characters only. "),
|
||||||
|
.required('Card Code is required!'),
|
||||||
|
type: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
.required('Card Type Description is required!'),
|
||||||
|
description: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(140, "Maximum character is 140.")
|
||||||
|
.required('Card Type Short Description is required!'),
|
||||||
|
image: Yup.string()
|
||||||
|
.required('Upload Card Type Image is required!'),
|
||||||
|
virtual_card_font_color: Yup.string()
|
||||||
|
.required('Virtual Card Font Color is required!'),
|
||||||
|
bg_image: Yup.string()
|
||||||
|
.required('Upload Card Type Cover Image is required!'),
|
||||||
|
terms_and_conditions: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000.")
|
||||||
|
.required('Terms and Condition is required!'),
|
||||||
|
faqs: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000.")
|
||||||
|
.required('FAQs is required!'),
|
||||||
|
id_number: Yup.string()
|
||||||
|
.required('ID Number is required!'),
|
||||||
|
id_number_description: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(40, "Maximum character is 40."),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Menu, Dropdown, notification, Icon, message } from "antd"
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import AdvanceTable from "components/Tables/AdvanceTable";
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm";
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
import { customAction } from 'actions';
|
||||||
|
|
||||||
|
class CardTypeList extends Component {
|
||||||
|
state= {
|
||||||
|
updating: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
delete =(admin_uuid)=> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDropDown = async(e) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { match, history } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{border:'1px solid #E6ECF5'}}>
|
||||||
|
<HeaderForm
|
||||||
|
title="Card Types"
|
||||||
|
action={()=> history.push({ pathname: `${match.url}/create` })}
|
||||||
|
actionBtnName="Add Card"
|
||||||
|
/>
|
||||||
|
<AdvanceTable
|
||||||
|
updating = { this.state.updating }
|
||||||
|
keyValue="cardtype_uuid"
|
||||||
|
url={{
|
||||||
|
//default: 'admin?page=1&page_size=10&_sort_by=create_dt&_sort_order=desc'
|
||||||
|
apiDelete: 'cardTypeBatchDelete',
|
||||||
|
default: 'cardType',
|
||||||
|
filter: '?page=1&page_size=10&_sort_by=create_dt&_sort_order=desc'
|
||||||
|
}}
|
||||||
|
filterValues ={["role", "status"]}
|
||||||
|
columns={
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: 'Card Type Code',
|
||||||
|
dataIndex: 'code',
|
||||||
|
key: 'code',
|
||||||
|
sorter: true,
|
||||||
|
filters: [],
|
||||||
|
width: "17%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Card Type Description',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
sorter: true,
|
||||||
|
filters:[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Action',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
width: 150,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
title: "Edit",
|
||||||
|
icon: 'edit',
|
||||||
|
url: 'about-us/card-types/edit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
title: "Delete",
|
||||||
|
icon: 'delete',
|
||||||
|
url: '',
|
||||||
|
action: this.delete
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'view',
|
||||||
|
title: "View",
|
||||||
|
icon: 'right-circle-o',
|
||||||
|
url: 'about-us/card-types/view'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CardTypeList = connect(
|
||||||
|
state => ({
|
||||||
|
//user: state.viewUser.data,
|
||||||
|
//status: state.viewUser.code,
|
||||||
|
//responseMsg: state.viewUser.messages
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(CardTypeList);
|
||||||
|
|
||||||
|
export default CardTypeList;
|
|
@ -0,0 +1,103 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Icon, Avatar, Row , Col } from 'antd'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
function ViewCardForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
userInfo
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{padding: '15px 30px 0px', borderTop: 0}}>
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '0 0 20px'}}>Details</h2>
|
||||||
|
{/*Account Details */}
|
||||||
|
<h2 style={{fontWeight: 'bold', fontSize: '16px'}}>CARD DETAILS</h2>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.code}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>Card Code:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.name}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>Card Type Description:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.description}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>Card Type Short Description:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>
|
||||||
|
<img
|
||||||
|
src={userInfo && userInfo.image && `${userInfo.image}`}
|
||||||
|
alt="avatar"
|
||||||
|
width="300"
|
||||||
|
style={{maxHeight: '250px'}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>Card Type Image:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.virtual_card_font_color && userInfo.virtual_card_font_color ? "Black": "White"}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>Virtual Card Font Color:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>
|
||||||
|
<img
|
||||||
|
src={userInfo && userInfo.image && `${userInfo.bg_image}`}
|
||||||
|
alt="avatar"
|
||||||
|
width="300"
|
||||||
|
style={{maxHeight: '250px'}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>Card Type Cover Image:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.id_number && userInfo.id_number ? "Yes": "No"}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>ID Number Required:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.id_number_description}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>ID Number Description:</span></Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
{/*Account Details */}
|
||||||
|
<div style={{margin: '15px 0 10px'}}>
|
||||||
|
<h2 style={{fontWeight: 'bold', fontSize: '16px'}}>DATA PRIVACY DETAILS</h2>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.terms_and_conditions}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>{`Terms and Conditions:`}</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={styles.marginTop}>
|
||||||
|
<Col span={18} push={5}>{userInfo && userInfo.faqs}</Col>
|
||||||
|
<Col span={5} pull={18}><span style={{fontWeight: '600'}}>FAQs:</span></Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ViewCardForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(ViewCardForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default ViewCardForm;
|
||||||
|
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
marginTop: {
|
||||||
|
marginTop: '5px'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { withRouter } from "react-router-dom"
|
||||||
|
import { notification, message } from "antd"
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from 'components/Forms/HeaderForm'
|
||||||
|
import ViewCardForm from './components/ViewCardForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
|
||||||
|
|
||||||
|
class CardTypeView extends Component {
|
||||||
|
state = {
|
||||||
|
mounted: false,
|
||||||
|
userInfo: null
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
|
||||||
|
const { match } = this.props;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await API_UNI_OIL.get(`cardType/${match.params.id}`)
|
||||||
|
this.setState({
|
||||||
|
userInfo: {...response.data.data},
|
||||||
|
mounted: true
|
||||||
|
})
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong loading data.</div>
|
||||||
|
- { error && error.data && error.data.message }
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
if(error.status == 404) {
|
||||||
|
if(this.props.location.pathname)
|
||||||
|
this.props.history.push(`${this.props.location.pathname}/404`); return
|
||||||
|
}
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delete = async (uuid) => {
|
||||||
|
|
||||||
|
const { userInfo } = this.state;
|
||||||
|
const { match } = this.props;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await API_UNI_OIL.delete(`cardType/${userInfo.cardtype_uuid}`);
|
||||||
|
message.success('Record was successfully deleted.');
|
||||||
|
this.props.history.push("/about-us/card-types");
|
||||||
|
} catch ({response:error}) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong deleting record.</div>
|
||||||
|
- { error && error.data && error.data.message }
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if(!this.state.mounted) return null;
|
||||||
|
|
||||||
|
const { userInfo } = this.state
|
||||||
|
const { history, match } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '10px'}}>
|
||||||
|
<HeaderForm
|
||||||
|
title="Card Type Details"
|
||||||
|
action={()=> {this.props.history.push(`/about-us/card-types/edit/${match.params.id}`)}}
|
||||||
|
actionBtnName="Update"
|
||||||
|
styleBtn={{background: 'white', borderColor: 'rgb(184, 187, 201)',color: 'rgb(101, 105, 127)'}}
|
||||||
|
deleteAction={this.delete}
|
||||||
|
deleteBtnName="Delete"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<ViewCardForm userInfo={userInfo} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default withRouter(CardTypeView);
|
|
@ -0,0 +1,83 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react';
|
||||||
|
import { Row, Button, Col } from 'antd';
|
||||||
|
import { Form, Field } from 'formik';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import { Inputs, InputTextArea } from 'components/Forms';
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 4 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 16 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function AddUserManagementForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
handleSubmit,
|
||||||
|
generatePassword,
|
||||||
|
loading,
|
||||||
|
isGenerated,
|
||||||
|
match,
|
||||||
|
history
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate>
|
||||||
|
<HeaderForm
|
||||||
|
isInsideForm
|
||||||
|
loading={loading}
|
||||||
|
disabled={props.isValid == false ? true : false}
|
||||||
|
title={match.params.id == "1" ? "Terms" : "Privacy Policy"}
|
||||||
|
action={handleSubmit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> { history.push("/about-us/term-privacy")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="title"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Title"
|
||||||
|
placeholder="Title"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="details"
|
||||||
|
type="text"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Details"
|
||||||
|
placeholder="Details"
|
||||||
|
rows={10}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AddUserManagementForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(AddUserManagementForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default AddUserManagementForm;
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { connect } from "react-redux"
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import { message,notification } from 'antd';
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import AddUserManagementForm from './components/AddUserManagementForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { userDetailsSchema } from './validationSchema'
|
||||||
|
import { customAction } from "actions";
|
||||||
|
import { API_GET, API_POST, API_UNI_OIL } from "utils/Api";
|
||||||
|
import { apiFormValidation } from "utils/helper";
|
||||||
|
|
||||||
|
|
||||||
|
class TermAndPrivacyCreate extends Component {
|
||||||
|
state = {
|
||||||
|
generated_password: null,
|
||||||
|
userInfo: null,
|
||||||
|
loading: false,
|
||||||
|
isGenerated: false
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async (values, actions) => {
|
||||||
|
const { setSubmitting, setErrors } = actions;
|
||||||
|
let { history, match } = this.props;
|
||||||
|
let params = { ...values, type: match.params.id }
|
||||||
|
this.setState({ loading: true });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await API_UNI_OIL.post('TermsAndPrivacy', params);
|
||||||
|
if(response) {
|
||||||
|
message.success('New record added.');
|
||||||
|
this.setState({ loading: false });
|
||||||
|
history.push({ pathname: '/about-us/term-privacy' });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ({response:error}) {
|
||||||
|
if (error.status === 422) {
|
||||||
|
apiFormValidation({ data: error.data.data, setErrors });
|
||||||
|
}
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong creating new record.</div>
|
||||||
|
- { error && error.data && error.data.message }
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
this.setState({ loading: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCreateTermPrivacy =()=> {
|
||||||
|
this.form.submitForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { userManagement, match } = this.props
|
||||||
|
const { loading, isGenerated } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '30px'}}>
|
||||||
|
{/* <HeaderForm
|
||||||
|
loading={loading}
|
||||||
|
title={match.params.id == "1" ? "Terms" : "Privacy Policy"}
|
||||||
|
action={this.handleCreateTermPrivacy}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> { this.props.history.push("/about-us/term-privacy")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/> */}
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '25px 35px'}}>{match.params.id == "1" ? "Terms" : "Privacy Policy"} Details</h2>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
title: '',
|
||||||
|
details: '',
|
||||||
|
}}
|
||||||
|
ref={node => (this.form = node)}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={userDetailsSchema}
|
||||||
|
onSubmit={this.handleSubmit }
|
||||||
|
render = {(props)=>
|
||||||
|
<AddUserManagementForm
|
||||||
|
{...props}
|
||||||
|
loading={userManagement.createRequestPending || loading}
|
||||||
|
match={match}
|
||||||
|
history={this.props.history}
|
||||||
|
isGenerated={isGenerated}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TermAndPrivacyCreate = connect(
|
||||||
|
state => ({
|
||||||
|
//userInfo: state
|
||||||
|
userManagement: state.userManagement,
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(TermAndPrivacyCreate);
|
||||||
|
|
||||||
|
export default TermAndPrivacyCreate;
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const userDetailsSchema = Yup.object().shape({
|
||||||
|
title: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
.required('Title is required!'),
|
||||||
|
details: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000.")
|
||||||
|
.required('Details is required!'),
|
||||||
|
// type: Yup.string()
|
||||||
|
// .required('Type is required!')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react';
|
||||||
|
import { Row, Button, Col } from 'antd';
|
||||||
|
import { Form, Field } from 'formik';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import { Inputs, InputTextArea } from 'components/Forms';
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 4 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 16 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function EditUserManagementForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
generatePassword,
|
||||||
|
loading,
|
||||||
|
handleSubmit,
|
||||||
|
isGenerated,
|
||||||
|
history
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate>
|
||||||
|
<HeaderForm
|
||||||
|
isInsideForm
|
||||||
|
loading={loading}
|
||||||
|
disabled={props.isValid == false ? true : false}
|
||||||
|
title="Update Terms"
|
||||||
|
action={handleSubmit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withConfirm={{message: "Save changes to this record?"}}
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> { history.push("/about-us/term-privacy")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="title"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Title"
|
||||||
|
placeholder="Title"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="details"
|
||||||
|
type="text"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Details"
|
||||||
|
placeholder="Details"
|
||||||
|
rows={10}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EditUserManagementForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(EditUserManagementForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default EditUserManagementForm;
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import { withRouter } from "react-router-dom"
|
||||||
|
import { notification, message } from "antd"
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import EditUserManagementForm from './components/EditUserManagementForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { userDetailsSchema } from './validationSchema'
|
||||||
|
import { API_GET, API_PUT, API_POST } from "utils/Api";
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
import { apiFormValidation } from "utils/helper";
|
||||||
|
|
||||||
|
class TermAndPrivacyEdit extends Component {
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
userInfo: null,
|
||||||
|
mounted: false,
|
||||||
|
timerCount: 20,
|
||||||
|
isGenerated: false
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
|
||||||
|
const { match } = this.props;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await API_UNI_OIL.get(`TermsAndPrivacy/${match.params.id}`);
|
||||||
|
this.setState({
|
||||||
|
userInfo: {...response.data.data},
|
||||||
|
mounted: true
|
||||||
|
})
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong loading data.</div>
|
||||||
|
- {error && error.data && error.data.message}
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
if(error.status == 404) {
|
||||||
|
if(this.props.location.pathname)
|
||||||
|
this.props.history.push(`${this.props.location.pathname}/404`); return
|
||||||
|
}
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEditUserManagement =()=> {
|
||||||
|
this.form.submitForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async (values, actions) => {
|
||||||
|
|
||||||
|
const { setErrors, setSubmitting } = actions;
|
||||||
|
const { userInfo } = this.state;
|
||||||
|
let { history } = this.props;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
...values,
|
||||||
|
type : userInfo.type
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({loading: true})
|
||||||
|
try {
|
||||||
|
const response = await API_PUT(`TermsAndPrivacy/${userInfo.tp_uuid}`, params);
|
||||||
|
if(response.status === 422){
|
||||||
|
if (response.status === 422) {
|
||||||
|
apiFormValidation({ data: response.data.data, setErrors });
|
||||||
|
}
|
||||||
|
notification.error({ message: "Success", description: "Something went wrong updating record" });
|
||||||
|
setSubmitting(false)
|
||||||
|
this.setState({loading: false})
|
||||||
|
}else {
|
||||||
|
message.success('Record was successfully update.');
|
||||||
|
this.setState({loading: false})
|
||||||
|
this.props.history.push("/about-us/term-privacy");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setSubmitting(false)
|
||||||
|
this.setState({loading: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if(!this.state.mounted) return null;
|
||||||
|
|
||||||
|
const { loading, userInfo } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '30px'}}>
|
||||||
|
{/* <HeaderForm
|
||||||
|
loading={loading}
|
||||||
|
title="Update Terms"
|
||||||
|
action={this.handleEditUserManagement}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withConfirm={{message: "Save changes to this record?"}}
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> { this.props.history.push("/about-us/term-privacy")}}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/> */}
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '25px 35px'}}>Details</h2>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
title: userInfo.title || '',
|
||||||
|
details: userInfo.details || '',
|
||||||
|
}}
|
||||||
|
ref={node => (this.form = node)}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={userDetailsSchema}
|
||||||
|
onSubmit={this.handleSubmit }
|
||||||
|
render = {(props)=>
|
||||||
|
<EditUserManagementForm
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
history={this.props.history}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(TermAndPrivacyEdit);
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const userDetailsSchema = Yup.object().shape({
|
||||||
|
title: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
.required('Title is required!'),
|
||||||
|
details: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000.")
|
||||||
|
.required('Details is required!'),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Menu, Dropdown, notification, Icon, message } from "antd"
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import AdvanceTable from "components/Tables/AdvanceTable";
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm";
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
import { customAction } from 'actions';
|
||||||
|
|
||||||
|
class TermAndPrivacyList extends Component {
|
||||||
|
state= {
|
||||||
|
updating: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
delete =(admin_uuid)=> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDropDown = async(e) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { match, history } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{border:'1px solid #E6ECF5'}}>
|
||||||
|
<HeaderForm
|
||||||
|
isDropDown
|
||||||
|
title={`Terms & Privacy`}
|
||||||
|
actionPrivacy={()=> history.push({ pathname: `${match.url}/create/1` }) }
|
||||||
|
actionTerms={()=> history.push({ pathname: `${match.url}/create/2` })}
|
||||||
|
actionBtnName="Add User"
|
||||||
|
/>
|
||||||
|
<AdvanceTable
|
||||||
|
updating = { this.state.updating }
|
||||||
|
keyValue="tp_uuid"
|
||||||
|
url={{
|
||||||
|
//default: 'admin?page=1&page_size=10&_sort_by=create_dt&_sort_order=desc'
|
||||||
|
apiDelete: 'TermsAndPrivacyBatchDelete',
|
||||||
|
default: 'TermsAndPrivacy',
|
||||||
|
filter: '?page=1&page_size=10&_sort_by=create_dt&_sort_order=desc'
|
||||||
|
}}
|
||||||
|
filterValues ={["role", "status"]}
|
||||||
|
columns={
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: 'Title',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
sorter: true,
|
||||||
|
filters: [],
|
||||||
|
width: "20%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Details',
|
||||||
|
dataIndex: 'details',
|
||||||
|
key: 'details',
|
||||||
|
sorter: true,
|
||||||
|
filters:[],
|
||||||
|
render: (text, record) => (
|
||||||
|
<div style={{ width: '490px', whiteSpace: 'noWrap',
|
||||||
|
overflow: 'hidden', textOverflow: 'ellipsis'
|
||||||
|
}}>
|
||||||
|
{record && record.details}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
width: "8%",
|
||||||
|
sorter: true,
|
||||||
|
filters:[],
|
||||||
|
render: (text, record) => (
|
||||||
|
<span >
|
||||||
|
{record && record.type == 1 ? "Terms": "Privacy"}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Action',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
width: 150,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
title: "Edit",
|
||||||
|
icon: 'edit',
|
||||||
|
url: '/user-management/edit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
title: "Delete",
|
||||||
|
icon: 'delete',
|
||||||
|
url: '',
|
||||||
|
action: this.delete
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'view',
|
||||||
|
title: "View",
|
||||||
|
icon: 'right-circle-o',
|
||||||
|
url: '/user-management/view'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TermAndPrivacyList = connect(
|
||||||
|
state => ({
|
||||||
|
//user: state.viewUser.data,
|
||||||
|
//status: state.viewUser.code,
|
||||||
|
//responseMsg: state.viewUser.messages
|
||||||
|
}),
|
||||||
|
{ customAction }
|
||||||
|
)(TermAndPrivacyList);
|
||||||
|
|
||||||
|
export default TermAndPrivacyList;
|
|
@ -0,0 +1,55 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Icon, Avatar, Row , Col } from 'antd'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
function ViewUserManagementForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
userInfo
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{padding: '15px 30px 30px', borderTop: 0}}>
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '0 0 20px'}}>Details</h2>
|
||||||
|
{/*Account Details */}
|
||||||
|
<Row style={{marginBottom: '5px'}}>
|
||||||
|
<Col span={18} push={3}>{userInfo && userInfo.title}</Col>
|
||||||
|
<Col span={3} pull={18}><span style={{fontWeight: '600'}}>Title:</span></Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col span={18} push={3}>{userInfo && <pre style={{
|
||||||
|
overflow: 'auto' ,
|
||||||
|
overflowWrap: 'break-word',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordWrap: 'break-word',}}>{userInfo.details}</pre>}
|
||||||
|
</Col>
|
||||||
|
<Col span={3} pull={18}><span style={{fontWeight: '600'}}>Details:</span></Col>
|
||||||
|
</Row>
|
||||||
|
{/* <Row>
|
||||||
|
<Col span={18} push={3}>{userInfo && userInfo.type == "1" ? "Terms" : "Privacy"}</Col>
|
||||||
|
<Col span={3} pull={18}><span style={{fontWeight: '600'}}>Type:</span></Col>
|
||||||
|
</Row> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ViewUserManagementForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(ViewUserManagementForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default ViewUserManagementForm;
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { withRouter } from "react-router-dom"
|
||||||
|
import { notification, message } from "antd"
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from 'components/Forms/HeaderForm'
|
||||||
|
import ViewUserManagementForm from './components/ViewUserManagementForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
|
||||||
|
|
||||||
|
class TermAndPrivacyView extends Component {
|
||||||
|
state = {
|
||||||
|
mounted: false,
|
||||||
|
userInfo: null
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
|
||||||
|
const { match } = this.props;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await API_UNI_OIL.get(`TermsAndPrivacy/${match.params.id}`)
|
||||||
|
this.setState({
|
||||||
|
userInfo: {...response.data.data},
|
||||||
|
mounted: true
|
||||||
|
})
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong loading data.</div>
|
||||||
|
- { error && error.data && error.data.message }
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
if(error.status == 404) {
|
||||||
|
if(this.props.location.pathname)
|
||||||
|
this.props.history.push(`${this.props.location.pathname}/404`); return
|
||||||
|
}
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delete = async (uuid) => {
|
||||||
|
|
||||||
|
const { userInfo } = this.state
|
||||||
|
const { match } = this.props;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await API_UNI_OIL.delete(`TermsAndPrivacy/${userInfo.tp_uuid}`);
|
||||||
|
message.success('Record was successfully deleted.');
|
||||||
|
this.props.history.push("/about-us/term-privacy");
|
||||||
|
} catch ({response:error}) {
|
||||||
|
// this.props.history.push("/404");
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong deleting record.</div>
|
||||||
|
{/* - { error && error.data && error.data.message } */}
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const { userInfo } = this.state
|
||||||
|
const { history, match } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '10px'}}>
|
||||||
|
<HeaderForm
|
||||||
|
title={"Terms & Privacy Details"}
|
||||||
|
action={()=> {this.props.history.push(`/about-us/term-privacy/edit/${match.params.id}`)}}
|
||||||
|
actionBtnName="Update"
|
||||||
|
styleBtn={{background: 'white', borderColor: 'rgb(184, 187, 201)',color: 'rgb(101, 105, 127)'}}
|
||||||
|
deleteAction={this.delete}
|
||||||
|
deleteBtnName="Delete"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<ViewUserManagementForm userInfo={userInfo} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default withRouter(TermAndPrivacyView);
|
|
@ -0,0 +1,137 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import { Switch, Route, Redirect } from 'react-router-dom';
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import CardTypeList from './CardTypes/List';
|
||||||
|
import CardTypeCreate from './CardTypes/Create';
|
||||||
|
import CardTypeEdit from './CardTypes/Edit';
|
||||||
|
import CardTypeView from './CardTypes/View';
|
||||||
|
|
||||||
|
import TermAndPrivacyList from './TermAndPrivacy/List';
|
||||||
|
import TermAndPrivacyCreate from './TermAndPrivacy/Create';
|
||||||
|
import TermAndPrivacyEdit from './TermAndPrivacy/Edit';
|
||||||
|
import TermAndPrivacyView from './TermAndPrivacy/View';
|
||||||
|
|
||||||
|
// import TermAndPrivacyList from './TermAndPrivacy/List';
|
||||||
|
|
||||||
|
import { PAGE404 } from "components/PageError/index"
|
||||||
|
import MainContent from 'components/Dashboard/Layout/components/MainContent';
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AboutUs extends Component {
|
||||||
|
state = {
|
||||||
|
pageRoutes: [
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/card-types`,
|
||||||
|
name: "Card Types",
|
||||||
|
component: CardTypeList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/card-types/create`,
|
||||||
|
name: "Create Card Types",
|
||||||
|
component: CardTypeCreate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/card-types/edit`,
|
||||||
|
params: ':id',
|
||||||
|
name: "Update Card Types",
|
||||||
|
component: CardTypeEdit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/card-types/view`,
|
||||||
|
params: ':id',
|
||||||
|
name: "View Card Types",
|
||||||
|
component: CardTypeView,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/term-privacy`,
|
||||||
|
name: "Terms & Privacy",
|
||||||
|
component: TermAndPrivacyList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/term-privacy/create`,
|
||||||
|
params: ':id',
|
||||||
|
name: "Terms",
|
||||||
|
component: TermAndPrivacyCreate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/term-privacy/edit`,
|
||||||
|
params: ':id',
|
||||||
|
name: "Terms & Privacy",
|
||||||
|
component:TermAndPrivacyEdit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${this.props.match.url}/term-privacy/view`,
|
||||||
|
params: ':id',
|
||||||
|
name: "Terms & Privacy Details",
|
||||||
|
component: TermAndPrivacyView,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// {
|
||||||
|
// path: `${this.props.match.url}/lock-account`,
|
||||||
|
// name: "Locked Accounts",
|
||||||
|
// component: CardMemberList,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: `${this.props.match.url}/lock-account/view`,
|
||||||
|
// params: ':id',
|
||||||
|
// name: "View Locked Account",
|
||||||
|
// component: CardMemberView,
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const { userInfo } = this.props
|
||||||
|
const { pageRoutes } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div style={{position: 'relative'}}>
|
||||||
|
<MainContent pageRoutes={userInfo.data.userInfo.role == 1 ? pageRoutes : []}>
|
||||||
|
{
|
||||||
|
userInfo.data.userInfo.role == 1
|
||||||
|
?
|
||||||
|
<Switch>
|
||||||
|
<Redirect exact from="/about-us" to="/about-us/card-types"/>
|
||||||
|
<Route exact path = "/about-us/card-types" component = { CardTypeList } />
|
||||||
|
<Route exact path = "/about-us/card-types/create" component = { CardTypeCreate } />
|
||||||
|
<Route exact path = "/about-us/card-types/view/:id" component = { CardTypeView } />
|
||||||
|
<Route exact path = "/about-us/card-types/edit/:id" component = { CardTypeEdit } />
|
||||||
|
|
||||||
|
<Route exact path = "/about-us/term-privacy" component = { TermAndPrivacyList } />
|
||||||
|
<Route exact path = "/about-us/term-privacy/create/:id" component = { TermAndPrivacyCreate } />
|
||||||
|
<Route exact path = "/about-us/term-privacy/view/:id" component = { TermAndPrivacyView } />
|
||||||
|
<Route exact path = "/about-us/term-privacy/edit/:id" component = { TermAndPrivacyEdit } />
|
||||||
|
<PAGE404 />
|
||||||
|
</Switch>
|
||||||
|
:
|
||||||
|
<Switch>
|
||||||
|
<PAGE404 />
|
||||||
|
</Switch>
|
||||||
|
}
|
||||||
|
</MainContent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AboutUs = connect(
|
||||||
|
state => ({
|
||||||
|
userInfo: state.login
|
||||||
|
}),
|
||||||
|
)(AboutUs);
|
||||||
|
|
||||||
|
|
||||||
|
export default AboutUs;
|
|
@ -0,0 +1,254 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react';
|
||||||
|
import { Row, Button, Col } from 'antd';
|
||||||
|
import { Form, Field } from 'formik';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import { Inputs, Select, DatePicker , InputTextArea, UploadImage, TimePickerForm } from 'components/Forms';
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 5 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 10 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function AddPhotoSliderForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
handleSubmit,
|
||||||
|
loading,
|
||||||
|
promotionsOptions,
|
||||||
|
handleFileUpload,
|
||||||
|
handleGetDate,
|
||||||
|
photoSliderLimit,
|
||||||
|
dateStartEnd,
|
||||||
|
history,
|
||||||
|
handleAutoFillDeatils
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate>
|
||||||
|
<HeaderForm
|
||||||
|
isInsideForm
|
||||||
|
loading={loading}
|
||||||
|
disabled={props.isValid == false ? true : false}
|
||||||
|
title="Photo Slider"
|
||||||
|
action={handleSubmit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={() => { history.push("/home-page/photo-slider") }}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
allowClear
|
||||||
|
name="promotion_uuid"
|
||||||
|
type="select"
|
||||||
|
icon=""
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Promotion Name"
|
||||||
|
placeholder="Promotion Name"
|
||||||
|
mode="single"
|
||||||
|
optionsList={promotionsOptions}
|
||||||
|
handleGetDate={handleGetDate}
|
||||||
|
component={Select}
|
||||||
|
handleAutoFillDeatils={handleAutoFillDeatils}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="title"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Title"
|
||||||
|
placeholder="Title"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="description"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Description"
|
||||||
|
placeholder="Description"
|
||||||
|
rows={6}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
isRatioMessage={{
|
||||||
|
message: <div style={{fontSize: '13px'}} >
|
||||||
|
<div>Image Size: 1020 x 621</div>
|
||||||
|
<div>Maximum File Size: 100KB</div>
|
||||||
|
</div>,
|
||||||
|
// dimensions: [1021,621]
|
||||||
|
}}
|
||||||
|
limit100kb
|
||||||
|
name="image"
|
||||||
|
type="file"
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
accept=".jpg , .png, .gif"
|
||||||
|
multiple={false}
|
||||||
|
imageUrl={props.values.image && `${props.values.image}`}
|
||||||
|
className="upload-list-inline"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Upload Image"
|
||||||
|
placeholder="Upload Image"
|
||||||
|
component={UploadImage}
|
||||||
|
imgWidth="294px"
|
||||||
|
imgStyle={{width:"100%", height:"170"}}
|
||||||
|
handleFileUpload={handleFileUpload}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="date_start"
|
||||||
|
type="date"
|
||||||
|
icon=""
|
||||||
|
disabledDateStartEndPhotoSlider
|
||||||
|
dateStartEnd={props.values.promotion_uuid ? dateStartEnd : null}
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Start Appearance Date"
|
||||||
|
placeholder="Start Appearance Date"
|
||||||
|
component={DatePicker}
|
||||||
|
isAutoFill
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="date_end"
|
||||||
|
type="date"
|
||||||
|
icon=""
|
||||||
|
disabledDateStart
|
||||||
|
disabledDateStartEndPhotoSlider
|
||||||
|
disabledDateStartEndPhotoSliderEndDate
|
||||||
|
dateStartEnd={props.values.promotion_uuid ? dateStartEnd : null}
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="End Appearance Date"
|
||||||
|
placeholder="End Appearance Date"
|
||||||
|
component={DatePicker}
|
||||||
|
isAutoFill
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="start_time"
|
||||||
|
type="date"
|
||||||
|
disabledHours={()=> {
|
||||||
|
if(props.values.promotion_uuid) {
|
||||||
|
let time = dateStartEnd && dateStartEnd.date_start && moment(dateStartEnd.date_start, 'YYYY-MM-DDTHH:mm:ss').format('HH:mm:ss').replace(/[^0-9]/g,'').substring(0, 2)
|
||||||
|
let disabledTime = [];
|
||||||
|
let timeLimit = time;
|
||||||
|
// if(time) {
|
||||||
|
// while(timeLimit > 0) {
|
||||||
|
// timeLimit--;
|
||||||
|
// disabledTime.push(timeLimit)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return disabledTime
|
||||||
|
|
||||||
|
let date_start = moment(props.values.date_start).format('YYYY-MM-DD');
|
||||||
|
let promotion_date_start = moment(dateStartEnd.date_start).format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
if(date_start != promotion_date_start) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
if(time) {
|
||||||
|
while(timeLimit > 0) {
|
||||||
|
timeLimit--;
|
||||||
|
disabledTime.push(timeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disabledTime
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
defaultOpenValue={moment('00:00', 'HH:mm')}
|
||||||
|
format={'HH:mm'}
|
||||||
|
icon=""
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Start Time"
|
||||||
|
placeholder="Start Time"
|
||||||
|
component={TimePickerForm}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="end_time"
|
||||||
|
type="date"
|
||||||
|
disabledHours={() => {
|
||||||
|
if(props.values.promotion_uuid) {
|
||||||
|
let time = dateStartEnd && dateStartEnd.date_end && moment(dateStartEnd.date_end, 'YYYY-MM-DDTHH:mm:ss').format('HH:mm:ss').replace(/[^0-9]/g,'').substring(0, 2)
|
||||||
|
let disabledEndTime = [];
|
||||||
|
let timeLimit = time;
|
||||||
|
// if(time) {
|
||||||
|
// while(timeLimit < 23) {
|
||||||
|
// timeLimit++;
|
||||||
|
// disabledEndTime.push(timeLimit)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return disabledEndTime
|
||||||
|
|
||||||
|
let date_end = moment(props.values.date_end).format('YYYY-MM-DD');
|
||||||
|
let promotion_date_end = moment(dateStartEnd.date_end).format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
if(date_end != promotion_date_end) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
if(time) {
|
||||||
|
while(timeLimit < 23) {
|
||||||
|
timeLimit++;
|
||||||
|
disabledEndTime.push(timeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disabledEndTime
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
defaultOpenValue={moment('00:00', 'HH:mm')}
|
||||||
|
format={'HH:mm'}
|
||||||
|
icon=""
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="End Time"
|
||||||
|
placeholder="End Time"
|
||||||
|
component={TimePickerForm}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AddPhotoSliderForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(AddPhotoSliderForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default AddPhotoSliderForm;
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import { message, notification } from 'antd'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import AddPhotoSliderForm from './components/AddPhotoSliderForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { userDetailsSchema } from './validationSchema'
|
||||||
|
import { API_GET, API_POST, API_UNI_OIL } from "utils/Api"
|
||||||
|
import { apiFormValidation } from "utils/helper"
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePhotoSlider extends Component {
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
promotionsOptions: null,
|
||||||
|
mounted: false,
|
||||||
|
photoSliderLimit: false,
|
||||||
|
dateStartEnd: null
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
let photoSlider = await API_UNI_OIL('photoSliderCount');
|
||||||
|
if(photoSlider)
|
||||||
|
this.setState({photoSliderLimit: false})
|
||||||
|
} catch ({response:error}) {
|
||||||
|
this.setState({photoSliderLimit: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
let promotionsList = await API_GET('getPromotions');
|
||||||
|
|
||||||
|
if (promotionsList) {
|
||||||
|
|
||||||
|
let promotionsOptions = []
|
||||||
|
|
||||||
|
await promotionsList.data.data.map(item => {
|
||||||
|
promotionsOptions.push({
|
||||||
|
label: item.title,
|
||||||
|
value: item.promotion_uuid,
|
||||||
|
date: { dateStart: item.date_start , dateEnd: item.date_end }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
promotionsOptions: promotionsOptions,
|
||||||
|
mounted: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ({ response: error }) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong loading data.</div>
|
||||||
|
- {error && error.data && error.data.message}
|
||||||
|
</div>,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async (values, actions) => {
|
||||||
|
|
||||||
|
const { fileUpload } = this.state;
|
||||||
|
const { history } = this.props;
|
||||||
|
const { setErrors } = actions;
|
||||||
|
|
||||||
|
this.setState({ loading: true })
|
||||||
|
try {
|
||||||
|
const headers = {
|
||||||
|
'ContentType': 'multipart/form-data',
|
||||||
|
};
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if (fileUpload) {
|
||||||
|
fileUpload.forEach((t, i) => {
|
||||||
|
formData.append(`image`, t.originFileObj);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if(values.image) {
|
||||||
|
let imageUrlPath = values.image
|
||||||
|
formData.append(`image`, imageUrlPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let date_start = moment(values.date_start).format('YYYY-MM-DD');
|
||||||
|
let start_time = moment(values.start_time).format('HH:mm:ss');
|
||||||
|
|
||||||
|
|
||||||
|
if(start_time == 'Invalid date') {
|
||||||
|
start_time = values.start_time
|
||||||
|
} else {
|
||||||
|
start_time = moment(values.start_time).format('HH:mm:ss');
|
||||||
|
}
|
||||||
|
|
||||||
|
let date_end = moment(values.date_end).format('YYYY-MM-DD');
|
||||||
|
let end_time = moment(values.end_time).format('HH:mm:ss');
|
||||||
|
|
||||||
|
if(end_time == 'Invalid date') {
|
||||||
|
end_time = values.end_time
|
||||||
|
} else {
|
||||||
|
end_time = moment(values.end_time).format('HH:mm:ss');
|
||||||
|
}
|
||||||
|
|
||||||
|
let startDateTime = moment(date_start + ' ' + start_time, 'YYYY-MM-DDTHH:mm:ss');
|
||||||
|
let endDateTime = moment(date_end + ' ' + end_time, 'YYYY-MM-DDTHH:mm:ss');
|
||||||
|
|
||||||
|
values.promotion_uuid && (formData.append('promotion_uuid', values.promotion_uuid));
|
||||||
|
values.title && (formData.append('title', values.title));
|
||||||
|
values.description && (formData.append('description', values.description));
|
||||||
|
values.date_start && (formData.append('date_start', startDateTime.format('YYYY-MM-DDTHH:mm:ss')));
|
||||||
|
values.date_end && (formData.append('date_end', endDateTime.format('YYYY-MM-DDTHH:mm:ss')));
|
||||||
|
|
||||||
|
let response = await API_UNI_OIL.post('photoSlider', formData, headers)
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
message.success('New record added.');
|
||||||
|
this.setState({ loading: false })
|
||||||
|
history.push({ pathname: "/home-page/photo-slider" })
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ({ response: error }) {
|
||||||
|
if (error.status === 422) {
|
||||||
|
apiFormValidation({ data: error.data.data, setErrors })
|
||||||
|
}
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
{error && error.data && error.data.data && error.data.data.image
|
||||||
|
&& (<div>{error.data.data.image[0] ? `- ${error.data.data.image[0]}` : "Something went wrong creating new record."} </div>) }
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
this.setState({ loading: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAddPhotoSlider = () => {
|
||||||
|
this.form.submitForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileUpload = (e) => {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return this.setState({ fileUpload: e });
|
||||||
|
}
|
||||||
|
return e && this.setState({ fileUpload: e.fileList });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGetDate = async (id) => {
|
||||||
|
const {promotionsOptions} = this.state;
|
||||||
|
|
||||||
|
if(promotionsOptions) {
|
||||||
|
await promotionsOptions.map(item=> {
|
||||||
|
if(item.value == id) {
|
||||||
|
this.setState({
|
||||||
|
dateStartEnd: {
|
||||||
|
date_start: item.date.dateStart,
|
||||||
|
date_end: item.date.dateEnd
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAutoFillDeatils = async (id,setFieldValue) => {
|
||||||
|
|
||||||
|
if(id) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
let promotionsList = await API_GET(`promotion/${id}`);
|
||||||
|
let autofillData = {
|
||||||
|
...promotionsList.data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldValue('title', autofillData.title);
|
||||||
|
setFieldValue('description', autofillData.description);
|
||||||
|
setFieldValue('image', `${autofillData.image}`);
|
||||||
|
setFieldValue('date_start', moment(autofillData.date_start, 'YYYY-MM-DDTHH:mm'));
|
||||||
|
setFieldValue('date_end', moment(autofillData.date_end, 'YYYY-MM-DDTHH:mm'));
|
||||||
|
setFieldValue('start_time', moment(autofillData.date_start, 'YYYY-MM-DDTHH:mm').format('HH:mm') );
|
||||||
|
setFieldValue('end_time', moment(autofillData.date_end, 'YYYY-MM-DDTHH:mm').format('HH:mm') );
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isAutofill: true
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
Something went wrong autofill records.
|
||||||
|
- {error && error.data && error.data.message}
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if (!this.state.mounted) return null;
|
||||||
|
|
||||||
|
const { loading, promotionsOptions, photoSliderLimit, dateStartEnd } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border: '1px solid #E6ECF5', paddingBottom: '10px' }}>
|
||||||
|
{/* <HeaderForm
|
||||||
|
loading={loading}
|
||||||
|
title="Photo Slider"
|
||||||
|
action={this.handleAddPhotoSlider}
|
||||||
|
disabled={photoSliderLimit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={() => { this.props.history.push("/home-page/photo-slider") }}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/> */}
|
||||||
|
<div>
|
||||||
|
<h2 style={{ margin: '25px 35px' }}>Photo Slider Content Details</h2>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
promotion_uuid: '',
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
image: '',
|
||||||
|
date_start: '',
|
||||||
|
date_end: '',
|
||||||
|
start_time: '',
|
||||||
|
end_time: '',
|
||||||
|
}}
|
||||||
|
ref={node => (this.form = node)}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={userDetailsSchema}
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
render={(props) =>
|
||||||
|
<AddPhotoSliderForm
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
history={this.props.history}
|
||||||
|
photoSliderLimit={photoSliderLimit}
|
||||||
|
promotionsOptions={promotionsOptions}
|
||||||
|
handleGetDate={this.handleGetDate}
|
||||||
|
dateStartEnd={dateStartEnd}
|
||||||
|
handleFileUpload={this.handleFileUpload}
|
||||||
|
handleAutoFillDeatils={this.handleAutoFillDeatils}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default CreatePhotoSlider
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const userDetailsSchema = Yup.object().shape({
|
||||||
|
promotion_uuid: Yup.string(),
|
||||||
|
//.required('Branches is required!'),
|
||||||
|
title: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
.required('Title is required!'),
|
||||||
|
description: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000."),
|
||||||
|
//.required('Description is required!'),
|
||||||
|
image: Yup.string()
|
||||||
|
.required('Upload Image is required!'),
|
||||||
|
date_start: Yup.string()
|
||||||
|
.required('Start Appearance Date is required!'),
|
||||||
|
date_end: Yup.string()
|
||||||
|
.required('End Appearance Date is required!'),
|
||||||
|
start_time: Yup.string()
|
||||||
|
.required('Start Time is required!'),
|
||||||
|
end_time: Yup.string()
|
||||||
|
.required('End Time is required!'),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React from 'react'
|
||||||
|
import { Row, Button, Col } from 'antd'
|
||||||
|
import { Form, Field } from 'formik'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import { Inputs, Select, DatePicker , InputTextArea, UploadImage ,TimePickerForm } from 'components/Forms'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 5 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 10 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function EditUserManagementForm(props) {
|
||||||
|
const {
|
||||||
|
isSubmitting,
|
||||||
|
loading,
|
||||||
|
handleSubmit,
|
||||||
|
promotionsDefaultValue,
|
||||||
|
promotionsOptions,
|
||||||
|
handleFileUpload,
|
||||||
|
dateStartEnd,
|
||||||
|
handleGetDate,
|
||||||
|
history,
|
||||||
|
handleAutoFillDeatils,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate>
|
||||||
|
<HeaderForm
|
||||||
|
isInsideForm
|
||||||
|
loading={loading}
|
||||||
|
disabled={props.isValid == false ? true : false}
|
||||||
|
title="Update Photo Slider"
|
||||||
|
action={handleSubmit}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withConfirm={{message: "Save changes to this record?"}}
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> history.push("/home-page/photo-slider")}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
allowClear
|
||||||
|
name="promotion_uuid"
|
||||||
|
type="select"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Promotion Name"
|
||||||
|
placeholder="Promotion Name"
|
||||||
|
mode="single"
|
||||||
|
defaultValue={promotionsDefaultValue && (promotionsDefaultValue)}
|
||||||
|
optionsList={promotionsOptions}
|
||||||
|
handleGetDate={handleGetDate}
|
||||||
|
component={Select}
|
||||||
|
handleAutoFillDeatils={handleAutoFillDeatils}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="title"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Title"
|
||||||
|
placeholder="Title"
|
||||||
|
component={Inputs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="description"
|
||||||
|
type="text"
|
||||||
|
icon=""
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Description"
|
||||||
|
placeholder="Description"
|
||||||
|
rows={6}
|
||||||
|
component={InputTextArea}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
isRatioMessage={{
|
||||||
|
message: <div style={{fontSize: '13px'}} >
|
||||||
|
<div>Image Size: 1020 x 621</div>
|
||||||
|
<div>Maximum File Size: 100KB</div>
|
||||||
|
</div>,
|
||||||
|
// dimensions: [1021,621]
|
||||||
|
}}
|
||||||
|
limit100kb
|
||||||
|
name="image"
|
||||||
|
type="file"
|
||||||
|
accept=".jpg , .png, .gif"
|
||||||
|
multiple={false}
|
||||||
|
imageUrl={props.values.image && `${props.values.image}`}
|
||||||
|
className="upload-list-inline"
|
||||||
|
icon="user"
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Upload Image"
|
||||||
|
placeholder="Upload Image"
|
||||||
|
component={UploadImage}
|
||||||
|
imgWidth="294px"
|
||||||
|
imgStyle={{width:"100%", height:"170"}}
|
||||||
|
handleFileUpload={handleFileUpload}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="date_start"
|
||||||
|
type="date"
|
||||||
|
icon=""
|
||||||
|
isEdit
|
||||||
|
disabledDateStartEndPhotoSlider
|
||||||
|
dateStartEnd={props.values.promotion_uuid ? dateStartEnd : null }
|
||||||
|
defaultValue={ moment(props.values.date_start, 'YYYY-MM-DD') }
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Start Appearance Date"
|
||||||
|
placeholder="Start Appearance Date"
|
||||||
|
component={DatePicker}
|
||||||
|
isAutoFill
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="date_end"
|
||||||
|
type="date"
|
||||||
|
icon=""
|
||||||
|
isEdit
|
||||||
|
disabledDateStart
|
||||||
|
disabledDateStartEndPhotoSlider
|
||||||
|
disabledDateStartEndPhotoSliderEndDate
|
||||||
|
dateStartEnd={props.values.promotion_uuid ? dateStartEnd : null }
|
||||||
|
defaultValue={ moment(props.values.date_end, 'YYYY-MM-DD') }
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="End Appearance Date"
|
||||||
|
placeholder="End Appearance Date"
|
||||||
|
component={DatePicker}
|
||||||
|
isAutoFill
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="start_time"
|
||||||
|
type="date"
|
||||||
|
icon=""
|
||||||
|
disabledHours={()=> {
|
||||||
|
if(props.values.promotion_uuid) {
|
||||||
|
let time = dateStartEnd && dateStartEnd.date_start && moment(dateStartEnd.date_start, 'YYYY-MM-DDTHH:mm').format('HH:mm').replace(/[^0-9]/g,'').substring(0, 2)
|
||||||
|
let disabledTime = [];
|
||||||
|
let timeLimit = time;
|
||||||
|
|
||||||
|
let date_start = moment(props.values.date_start).format('YYYY-MM-DD');
|
||||||
|
let promotion_date_start = moment(dateStartEnd.date_start).format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
if(date_start != promotion_date_start) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
if(time) {
|
||||||
|
while(timeLimit > 0) {
|
||||||
|
timeLimit--;
|
||||||
|
disabledTime.push(timeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disabledTime
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
defaultValue={moment(props.values.start_time, 'HH:mm')}
|
||||||
|
format={'HH:mm'}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="Start Time"
|
||||||
|
placeholder="Start Time"
|
||||||
|
component={TimePickerForm}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="end_time"
|
||||||
|
type="date"
|
||||||
|
icon=""
|
||||||
|
disabledHours={() => {
|
||||||
|
if(props.values.promotion_uuid) {
|
||||||
|
let time = dateStartEnd && dateStartEnd.date_end && moment(dateStartEnd.date_end, 'YYYY-MM-DDTHH:mm').format('HH:mm').replace(/[^0-9]/g,'').substring(0, 2)
|
||||||
|
let disabledEndTime = [];
|
||||||
|
let timeLimit = time;
|
||||||
|
|
||||||
|
let date_end = moment(props.values.date_end).format('YYYY-MM-DD');
|
||||||
|
let promotion_date_end = moment(dateStartEnd.date_end).format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
if(date_end != promotion_date_end) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
if(time) {
|
||||||
|
while(timeLimit < 23) {
|
||||||
|
timeLimit++;
|
||||||
|
disabledEndTime.push(timeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disabledEndTime
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
defaultValue={moment(props.values.end_time, 'HH:mm')}
|
||||||
|
format={'HH:mm'}
|
||||||
|
layout={formItemLayout}
|
||||||
|
label="End Time"
|
||||||
|
placeholder="End Time"
|
||||||
|
component={TimePickerForm}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EditUserManagementForm = connect(
|
||||||
|
state => ({
|
||||||
|
|
||||||
|
}),
|
||||||
|
)(EditUserManagementForm);
|
||||||
|
|
||||||
|
|
||||||
|
export default EditUserManagementForm;
|
||||||
|
|
|
@ -0,0 +1,331 @@
|
||||||
|
// LIBRARIES
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Formik } from 'formik'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { notification, message } from "antd"
|
||||||
|
|
||||||
|
// COMPONENTS
|
||||||
|
import HeaderForm from "components/Forms/HeaderForm"
|
||||||
|
import EditPhotoSliderForm from './components/EditPhotoSliderForm'
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
import { userDetailsSchema } from './validationSchema'
|
||||||
|
import { API_GET, API_PUT, API_POST } from "utils/Api";
|
||||||
|
import { API_UNI_OIL } from "utils/Api";
|
||||||
|
import { apiFormValidation } from "utils/helper";
|
||||||
|
|
||||||
|
class EditPhotoSlider extends Component {
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
userInfo: null,
|
||||||
|
mounted: false,
|
||||||
|
promotionsDefaultValue: null,
|
||||||
|
promotionsDefaultKeyValue: null,
|
||||||
|
promotionsOptions: null,
|
||||||
|
dateStartEnd: null
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
|
||||||
|
const { match } = this.props;
|
||||||
|
let promotionsDefaultValue = []
|
||||||
|
let promotionsDefaultKeyValue = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await API_UNI_OIL.get(`photoSlider/${match.params.id}`);
|
||||||
|
// default options branch
|
||||||
|
|
||||||
|
let promotions = []
|
||||||
|
|
||||||
|
if(response.data.data.promotion) {
|
||||||
|
|
||||||
|
promotions.push({...response.data.data.promotion})
|
||||||
|
|
||||||
|
await promotions.map(item => {
|
||||||
|
promotionsDefaultValue.push(
|
||||||
|
item.title
|
||||||
|
)
|
||||||
|
promotionsDefaultKeyValue.push(
|
||||||
|
item.promotion_uuid
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// default options promotype
|
||||||
|
|
||||||
|
let dateStartEnd = promotions && promotions[0] && {
|
||||||
|
date_start: promotions[0].date_start,
|
||||||
|
date_end: promotions[0].date_end
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
userInfo: {...response.data.data},
|
||||||
|
mounted: true,
|
||||||
|
promotionsDefaultValue,
|
||||||
|
promotionsDefaultKeyValue,
|
||||||
|
dateStartEnd: dateStartEnd
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: "Error",
|
||||||
|
description: <div>
|
||||||
|
<div>Something went wrong loading data.</div>
|
||||||
|
- { error && error.data && error.data.message }
|
||||||
|
</div> ,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
if(error.status == 404) {
|
||||||
|
if(this.props.location.pathname) {
|
||||||
|
this.props.history.push(`${this.props.location.pathname}/404`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// options
|
||||||
|
try {
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
selected_promotion: promotionsDefaultKeyValue.length > 0 && promotionsDefaultKeyValue[0]
|
||||||
|
}
|
||||||
|
let promotionsOptions = []; let promoTypeOptions = []
|
||||||
|
let promotionsList = await API_GET('getPromotions', params);
|
||||||
|
|
||||||
|
if(promotionsList) {
|
||||||
|
await promotionsList.data.data.map(item => {
|
||||||
|
promotionsOptions.push({
|
||||||
|
label: item.title,
|
||||||
|
value: item.promotion_uuid,
|
||||||
|
date: { dateStart: item.date_start , dateEnd: item.date_end }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
promotionsOptions: promotionsOptions,
|
||||||
|
mounted: true
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
this.setState({ mounted: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async (values, actions) => {
|
||||||
|
|
||||||
|
const { fileUpload, branchesOptions, userInfo } = this.state;
|
||||||
|
const { history } = this.props;
|
||||||
|
const { setErrors } = actions;
|
||||||
|
|
||||||
|
console.log(values, 'valuesvaluesvalues')
|
||||||
|
|
||||||
|
this.setState({loading: true})
|
||||||
|
try {
|
||||||
|
const headers = {
|
||||||
|
'ContentType': 'multipart/form-data',
|
||||||
|
};
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if(fileUpload) {
|
||||||
|
fileUpload.forEach((t, i) => {
|
||||||
|
formData.append( `image`, t.originFileObj);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if(values.image) {
|
||||||
|
let imageUrlPath = values.image
|
||||||
|
formData.append(`image`, imageUrlPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let date_start = moment(values.date_start).format('YYYY-MM-DD');
|
||||||
|
let start_time = moment(values.start_time).format('HH:mm:ss');
|
||||||
|
|
||||||
|
if(start_time == 'Invalid date') {
|
||||||
|
start_time = values.start_time
|
||||||
|
} else {
|
||||||
|
start_time = moment(values.start_time).format('HH:mm:ss');
|
||||||
|
}
|
||||||
|
|
||||||
|
let date_end = moment(values.date_end).format('YYYY-MM-DD');
|
||||||
|
let end_time = moment(values.end_time).format('HH:mm:ss');
|
||||||
|
|
||||||
|
if(end_time == 'Invalid date') {
|
||||||
|
end_time = values.end_time
|
||||||
|
} else {
|
||||||
|
end_time = moment(values.end_time).format('HH:mm:ss');
|
||||||
|
}
|
||||||
|
|
||||||
|
let startDateTime = moment(date_start + ' ' + start_time, 'YYYY-MM-DDTHH:mm:ss');
|
||||||
|
let endDateTime = moment(date_end + ' ' + end_time, 'YYYY-MM-DDTHH:mm:ss');
|
||||||
|
|
||||||
|
// let branchesList = []
|
||||||
|
|
||||||
|
// values.promotion.map(item => {
|
||||||
|
// branchesOptions.map(userInfo => {
|
||||||
|
// if(userInfo.label == item || userInfo.value == item) {
|
||||||
|
// branchesList.push(userInfo.value);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
|
values.promotion_uuid && (formData.append('promotion_uuid', values.promotion_uuid ));
|
||||||
|
values.title && (formData.append('title', values.title));
|
||||||
|
values.description && (formData.append('description', values.description));
|
||||||
|
values.date_start && (formData.append('date_start', startDateTime.format('YYYY-MM-DDTHH:mm:ss') ) );
|
||||||
|
values.date_end && (formData.append('date_end', endDateTime.format('YYYY-MM-DDTHH:mm:ss') ) );
|
||||||
|
|
||||||
|
// log formdata
|
||||||
|
// for (var pair of formData.entries()) {
|
||||||
|
// console.log(pair[0]+ ', ' + pair[1]);
|
||||||
|
// }
|
||||||
|
let response = await API_UNI_OIL.post(`updatePhotoSlider/${userInfo.photoslider_uuid}`, formData , headers)
|
||||||
|
|
||||||
|
if(response) {
|
||||||
|
message.success('Record was successfully update.');
|
||||||
|
this.setState({loading: false})
|
||||||
|
history.push({ pathname: "/home-page/photo-slider" })
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
if (error.status === 422) {
|
||||||
|
apiFormValidation({ data: error.data.data, setErrors })
|
||||||
|
}
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
{ error && error.data && error.data.data && error.data.data.image
|
||||||
|
&& (<div>{error.data.data.image[0] ? `- ${error.data.data.image[0]}` : "Something went wrong updating record."} </div>) }
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
this.setState({loading: false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEditPhotoSlider =()=> {
|
||||||
|
this.form.submitForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileUpload =(e)=> {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return this.setState({fileUpload: e});
|
||||||
|
}
|
||||||
|
return e && this.setState({fileUpload: e.fileList});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGetDate = async (id) => {
|
||||||
|
const {promotionsOptions} = this.state;
|
||||||
|
|
||||||
|
if(promotionsOptions) {
|
||||||
|
await promotionsOptions.map(item=> {
|
||||||
|
if(item.value == id) {
|
||||||
|
this.setState({
|
||||||
|
dateStartEnd: {
|
||||||
|
date_start: item.date.dateStart,
|
||||||
|
date_end: item.date.dateEnd
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAutoFillDeatils = async (id,setFieldValue) => {
|
||||||
|
|
||||||
|
if(id) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
let promotionsList = await API_GET(`promotion/${id}`);
|
||||||
|
let autofillData = {
|
||||||
|
...promotionsList.data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldValue('title', autofillData.title);
|
||||||
|
setFieldValue('description', autofillData.description);
|
||||||
|
setFieldValue('image', `${autofillData.image}`);
|
||||||
|
setFieldValue('date_start', moment(autofillData.date_start, 'YYYY-MM-DDTHH:mm'));
|
||||||
|
setFieldValue('date_end', moment(autofillData.date_end, 'YYYY-MM-DDTHH:mm'));
|
||||||
|
setFieldValue('start_time', moment(autofillData.date_start, 'YYYY-MM-DDTHH:mm').format('HH:mm') );
|
||||||
|
setFieldValue('end_time', moment(autofillData.date_end, 'YYYY-MM-DDTHH:mm').format('HH:mm') );
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isAutofill: true
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch ({response: error}) {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: <div>
|
||||||
|
Something went wrong autofill records.
|
||||||
|
- {error && error.data && error.data.message}
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if(!this.state.mounted) return null;
|
||||||
|
|
||||||
|
const { loading, userInfo, promotionsOptions, promotionsDefaultValue , promotionsDefaultKeyValue, dateStartEnd } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border:'1px solid #E6ECF5' , paddingBottom: '10px'}}>
|
||||||
|
{/* <HeaderForm
|
||||||
|
loading={loading}
|
||||||
|
title="Update Photo Slider"
|
||||||
|
action={this.handleEditPhotoSlider}
|
||||||
|
actionBtnName="Submit"
|
||||||
|
withConfirm={{message: "Save changes to this record?"}}
|
||||||
|
withCancelConfirm={{ message: 'Are you sure you want to discard changes?'}}
|
||||||
|
cancel={()=> this.props.history.push("/home-page/photo-slider")}
|
||||||
|
cancelBtnName="Cancel"
|
||||||
|
/> */}
|
||||||
|
<div>
|
||||||
|
<h2 style={{margin: '25px 35px'}}>Photo Slider Content Details</h2>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
promotion_uuid: promotionsDefaultKeyValue[0] || '',
|
||||||
|
title: userInfo.title || '',
|
||||||
|
description: userInfo.description || '',
|
||||||
|
image: userInfo.image || '',
|
||||||
|
date_start: moment(userInfo.date_start, 'YYYY-MM-DDTHH:mm').format('YYYY-MM-DD') || '',
|
||||||
|
date_end: moment( userInfo.date_end, 'YYYY-MM-DDTHH:mm').format('YYYY-MM-DD') || '',
|
||||||
|
start_time: moment(userInfo.date_start, 'YYYY-MM-DDTHH:mm').format('HH:mm') || '',
|
||||||
|
end_time: moment(userInfo.date_end, 'YYYY-MM-DDTHH:mm' ).format('HH:mm') || '',
|
||||||
|
}}
|
||||||
|
ref={node => (this.form = node)}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={userDetailsSchema}
|
||||||
|
onSubmit={this.handleSubmit }
|
||||||
|
render = {(props)=>
|
||||||
|
<EditPhotoSliderForm
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
history={this.props.history}
|
||||||
|
dateStartEnd={dateStartEnd}
|
||||||
|
promotionsOptions={promotionsOptions}
|
||||||
|
promotionsDefaultValue={promotionsDefaultValue}
|
||||||
|
handleFileUpload={this.handleFileUpload}
|
||||||
|
handleGetDate={this.handleGetDate}
|
||||||
|
handleAutoFillDeatils={this.handleAutoFillDeatils}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default EditPhotoSlider
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const userDetailsSchema = Yup.object().shape({
|
||||||
|
promotion_uuid: Yup.string(),
|
||||||
|
//.required('Promotion Name is required!'),
|
||||||
|
title: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(128, "Maximum character is 128.")
|
||||||
|
.required('Title is required!'),
|
||||||
|
description: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.max(32000, "Maximum character is 32,000."),
|
||||||
|
// .required('Description is required!'),
|
||||||
|
image: Yup.string()
|
||||||
|
.required('Upload Image is required!'),
|
||||||
|
date_start: Yup.string()
|
||||||
|
.required('Start Appearance Date is required!'),
|
||||||
|
date_end: Yup.string()
|
||||||
|
.required('End Appearance Date is required!'),
|
||||||
|
start_time: Yup.string()
|
||||||
|
.required('Start Time is required!'),
|
||||||
|
end_time: Yup.string()
|
||||||
|
.required('End Time is required!'),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue