root
1 year ago
13 changed files with 3790 additions and 24 deletions
@ -1,20 +1,22 @@ |
|||||||
FROM node:12 |
# Stage 1 |
||||||
|
FROM node:14 as build-stage |
||||||
|
|
||||||
WORKDIR /react-docker |
WORKDIR /react-docker |
||||||
COPY ./package*.json ./ |
COPY package.json . |
||||||
ENV PATH /app/node_modules/.bin:$PATH |
|
||||||
RUN npm install |
RUN npm install |
||||||
RUN npm i react@16.14.0 |
COPY . . |
||||||
RUN npm i react-scripts |
|
||||||
RUN npm audit fix |
#ARG REACT_APP_API_BASE_URL |
||||||
|
#ENV REACT_APP_API_BASE_URL=$REACT_APP_API_BASE_URL |
||||||
|
|
||||||
EXPOSE 8445 |
RUN npm run build |
||||||
|
|
||||||
# Comment as needed (Production / Dev) |
# Stage 2 |
||||||
# [PROD] Use for Production |
#FROM nginx:1.17.0-alpine |
||||||
# COPY . . # uncomment prod |
FROM nginx:alpine |
||||||
|
|
||||||
# [DEV] Live Reload |
COPY --from=build-stage /react-docker/build /usr/share/nginx/html |
||||||
RUN mkdir -p /react-docker/src # comment for prod |
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf |
||||||
RUN mkdir -p /react-docker/public # comment for prod |
EXPOSE 80 |
||||||
|
|
||||||
CMD PORT=8445 npm start #--host 0.0.0.0 --port 3000 --disableHostCheck true |
CMD nginx -g 'daemon off;' |
||||||
|
@ -0,0 +1,17 @@ |
|||||||
|
server { |
||||||
|
|
||||||
|
listen 80; |
||||||
|
|
||||||
|
location / { |
||||||
|
root /usr/share/nginx/html; |
||||||
|
index index.html index.htm; |
||||||
|
try_files $uri $uri/ /index.html; |
||||||
|
} |
||||||
|
|
||||||
|
error_page 500 502 503 504 /50x.html; |
||||||
|
|
||||||
|
location = /50x.html { |
||||||
|
root /usr/share/nginx/html; |
||||||
|
} |
||||||
|
|
||||||
|
} |
After Width: | Height: | Size: 54 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,345 @@ |
|||||||
|
import React, { Component } from 'react'; |
||||||
|
import { Link } from 'react-router-dom'; |
||||||
|
import "slick-carousel/slick/slick.css"; |
||||||
|
import "slick-carousel/slick/slick-theme.css"; |
||||||
|
import Slider from "react-slick"; |
||||||
|
import { Spin } from 'antd'; |
||||||
|
import { Checkbox } from 'antd'; |
||||||
|
import { LoadingOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; |
||||||
|
import { |
||||||
|
Button, |
||||||
|
Card, |
||||||
|
CardBody, |
||||||
|
CardGroup, |
||||||
|
Col, |
||||||
|
Container, |
||||||
|
Form, |
||||||
|
Input, |
||||||
|
InputGroup, |
||||||
|
InputGroupAddon, |
||||||
|
InputGroupText, |
||||||
|
Row, |
||||||
|
UncontrolledAlert, |
||||||
|
Alert, |
||||||
|
Carousel, |
||||||
|
CarouselIndicators, |
||||||
|
CarouselCaption, |
||||||
|
CarouselItem, |
||||||
|
CarouselControl |
||||||
|
} from 'reactstrap'; |
||||||
|
import { USER_LOGIN, USER_LOGIN_V2, CALERTUSER_SEARCH, MENU_MANAGEMENT, APP_MODE } from '../../../const/ApiConst.js'; |
||||||
|
import { appConfig, reloadConstants } from '../../../const/MapConst.js'; |
||||||
|
import { APP_NAME } from '../../../const/AppConst.js' |
||||||
|
import moment from "moment" |
||||||
|
import axios from 'axios'; |
||||||
|
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||||
|
import logo_login_adw from '../../../assets/img/logo_adyawinsa.jpg' |
||||||
|
import logo_login_kit from '../../../assets/img/logo_kit.png' |
||||||
|
import logo_login_nawakara from '../../../assets/img/logo_nawakara.png' |
||||||
|
import logo_login_si from '../../../assets/img/logo-surveyor-indonesia.png' |
||||||
|
import logo_ospro from '../../../assets/img/OSPRO.png' |
||||||
|
|
||||||
|
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />; |
||||||
|
|
||||||
|
const settings = { |
||||||
|
dots: true, |
||||||
|
infinite: true, |
||||||
|
speed: 500, |
||||||
|
arrows: false, |
||||||
|
autoplay: true, |
||||||
|
slidesToShow: 1, |
||||||
|
slidesToScroll: 1 |
||||||
|
}; |
||||||
|
|
||||||
|
class Login extends Component { |
||||||
|
constructor(props) { |
||||||
|
super(props); |
||||||
|
this.state = { |
||||||
|
name: '', |
||||||
|
password: '', |
||||||
|
remember: '', |
||||||
|
alertVisible: false, |
||||||
|
alertMessage: '', |
||||||
|
alertColor: 'success', |
||||||
|
validate: { |
||||||
|
emailState: '', |
||||||
|
}, |
||||||
|
loader: false, |
||||||
|
type: 'password' |
||||||
|
} |
||||||
|
this.handleChange = this.handleChange.bind(this); |
||||||
|
this.showHide = this.showHide.bind(this); |
||||||
|
} |
||||||
|
showHide(e) { |
||||||
|
e.preventDefault(); |
||||||
|
e.stopPropagation(); |
||||||
|
this.setState({ |
||||||
|
type: this.state.type === 'input' ? 'password' : 'input' |
||||||
|
}) |
||||||
|
} |
||||||
|
validateEmail(e) { |
||||||
|
const emailRex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; |
||||||
|
const { validate } = this.state |
||||||
|
if (emailRex.test(e.target.value)) { |
||||||
|
validate.emailState = 'has-success' |
||||||
|
} else { |
||||||
|
validate.emailState = 'has-danger' |
||||||
|
} |
||||||
|
this.setState({ validate }) |
||||||
|
} |
||||||
|
|
||||||
|
handleChange = async (event) => { |
||||||
|
const { target } = event; |
||||||
|
const value = target.type === 'checkbox' ? target.checked : target.value; |
||||||
|
const { name } = target; |
||||||
|
await this.setState({ |
||||||
|
[name]: value, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
getDataMenu = async (token, role_id) => { |
||||||
|
const config = { |
||||||
|
headers: |
||||||
|
{ |
||||||
|
Authorization: `Bearer ${token}`, |
||||||
|
"Content-type": `application/json` |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
let url = MENU_MANAGEMENT(role_id) |
||||||
|
const result = await axios |
||||||
|
.get(url, config) |
||||||
|
.then(res => res) |
||||||
|
.catch((error) => error.response); |
||||||
|
|
||||||
|
if (result && result.data && result.data.code == 200) { |
||||||
|
let resData = result.data.data |
||||||
|
window.localStorage.setItem('menu_login', JSON.stringify(resData)); |
||||||
|
this.setState({ loader: false }) |
||||||
|
// custom redirect home after login |
||||||
|
if (role_id == 28) { |
||||||
|
this.props.history.push("/dashboard-customer/58/63"); |
||||||
|
} |
||||||
|
else { |
||||||
|
this.props.history.push("/dashboard"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
NotificationManager.error('Login Failed', 'Failed'); |
||||||
|
this.setState({ loader: false }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
submitForm = (event) => { |
||||||
|
event.preventDefault(); |
||||||
|
this.submitFormLogin(); |
||||||
|
} |
||||||
|
|
||||||
|
submitFormLogin = async () => { |
||||||
|
this.setState({ loader: true }) |
||||||
|
const { name, password, remember } = this.state |
||||||
|
|
||||||
|
if (name === '') { |
||||||
|
NotificationManager.error('Please fill username', 'Login Failed!'); |
||||||
|
this.setState({ loader: false }) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (password === '') { |
||||||
|
NotificationManager.error('Please fill password', 'Login Failed!'); |
||||||
|
this.setState({ loader: false }) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
let formData = { |
||||||
|
username: name, |
||||||
|
password, |
||||||
|
remember, |
||||||
|
} |
||||||
|
|
||||||
|
const doLogin = await axios.post(USER_LOGIN_V2, formData) |
||||||
|
.then(response => response) |
||||||
|
.catch(error => { |
||||||
|
this.setState({ loader: false }); |
||||||
|
}); |
||||||
|
|
||||||
|
if (doLogin && doLogin.data && doLogin.data.code === 200) { |
||||||
|
const { access_token, data_user } = doLogin.data.data |
||||||
|
this.getDataMenu(access_token, data_user.role_id) |
||||||
|
window.localStorage.setItem('isLogin', true); |
||||||
|
window.localStorage.setItem('token', access_token); |
||||||
|
window.localStorage.setItem('user_id', data_user.id); |
||||||
|
window.localStorage.setItem('user_name', data_user.name); |
||||||
|
window.localStorage.setItem('role_id', data_user.role_id); |
||||||
|
} else { |
||||||
|
console.log("kode : ", doLogin.data.code); |
||||||
|
// NotificationManager.error('Cek username atau password anda!', 'Gagal Login!'); |
||||||
|
NotificationManager.error(doLogin.data.message, 'Login Failed!'); |
||||||
|
this.setState({ loader: false }); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
getConfigAlert = async (token, user_id) => { |
||||||
|
const config = { |
||||||
|
headers: |
||||||
|
{ |
||||||
|
Authorization: `Bearer ${token}`, |
||||||
|
"Content-type": `application/json` |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const payload = { |
||||||
|
"paging": { "start": 0, "length": -1 }, |
||||||
|
"columns": [ |
||||||
|
{ "name": "user_id", "logic_operator": "=", "value": `${user_id}`, "operator": "AND" } |
||||||
|
], |
||||||
|
"joins": [], |
||||||
|
"orders": { "columns": ["id"], "ascending": false } |
||||||
|
} |
||||||
|
|
||||||
|
const result = await axios |
||||||
|
.post(CALERTUSER_SEARCH, payload, config) |
||||||
|
.then(res => res) |
||||||
|
.catch((error) => error.response); |
||||||
|
|
||||||
|
if (result && result.data && result.data.code == 200) { |
||||||
|
let resData = result.data.data |
||||||
|
let configAlert = [] |
||||||
|
resData.map((val, index) => { |
||||||
|
configAlert.push(val.config_alert_id); |
||||||
|
}); |
||||||
|
window.localStorage.setItem('userConfigAlert', configAlert.join()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onShowAlert = (alertColor, alertMessage) => { |
||||||
|
this.setState({ alertVisible: true, alertColor: alertColor, alertMessage: alertMessage }, () => { |
||||||
|
window.setTimeout(() => { |
||||||
|
this.setState({ alertVisible: false }) |
||||||
|
}, 3000) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
getLoginLogo = () => { |
||||||
|
return <div style={{ textAlign: 'center' }}><img style={{ width: '100%' }} src={logo_ospro} /></div> |
||||||
|
} |
||||||
|
|
||||||
|
getLoginSlider = () => { |
||||||
|
return <Slider {...settings} > |
||||||
|
<div> |
||||||
|
<img src={require('../../../assets/img/login/slide1.jpg')} className="img-avatar" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<img src={require('../../../assets/img/login/slide2.jpg')} className="img-avatar" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<img src={require('../../../assets/img/login/slide3.jpg')} className="img-avatar" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<img src={require('../../../assets/img/login/slide4.jpg')} className="img-avatar" /> |
||||||
|
</div> |
||||||
|
</Slider> |
||||||
|
} |
||||||
|
|
||||||
|
onChecked = (e) => { |
||||||
|
this.setState({ remember: e.target.checked }) |
||||||
|
}; |
||||||
|
|
||||||
|
render() { |
||||||
|
const { name, password, remember } = this.state; |
||||||
|
const u_group = window.localStorage.getItem('u_group') |
||||||
|
if (u_group !== null) { |
||||||
|
this.props.history.push("/dashboard"); |
||||||
|
} |
||||||
|
return ( |
||||||
|
<div className="app flex-row align-items-center"> |
||||||
|
<Container> |
||||||
|
<NotificationContainer /> |
||||||
|
|
||||||
|
<Alert color={this.state.alertColor} isOpen={this.state.alertVisible} > |
||||||
|
{this.state.alertMessage} |
||||||
|
</Alert> |
||||||
|
<Row className="justify-content-center"> |
||||||
|
<Col md="7"> |
||||||
|
{this.getLoginSlider()} |
||||||
|
</Col> |
||||||
|
<Col md="5"> |
||||||
|
<div style={{ |
||||||
|
boxShadow: '1px 1px 11px 1px rgba(221,211,211,0.75)' |
||||||
|
}}> |
||||||
|
<CardGroup> |
||||||
|
<Card className="p-4"> |
||||||
|
<CardBody> |
||||||
|
<Form onSubmit={this.submitForm}> |
||||||
|
{this.getLoginLogo()} <p className="text-muted">Sign In to your account</p> <InputGroup className="mb-3"> |
||||||
|
<InputGroupAddon addonType="prepend"> |
||||||
|
<InputGroupText> |
||||||
|
<i className="icon-user"></i> |
||||||
|
</InputGroupText> |
||||||
|
</InputGroupAddon> |
||||||
|
<Input |
||||||
|
type="text" |
||||||
|
name="name" |
||||||
|
id="exampleName" |
||||||
|
placeholder="Username" |
||||||
|
value={name} |
||||||
|
onChange={(e) => { |
||||||
|
this.setState({ name: e.target.value }) |
||||||
|
}} |
||||||
|
/> |
||||||
|
</InputGroup> |
||||||
|
<InputGroup className="mb-4"> |
||||||
|
<InputGroupAddon addonType="prepend"> |
||||||
|
<InputGroupText> |
||||||
|
<i className="icon-lock"></i> |
||||||
|
</InputGroupText> |
||||||
|
</InputGroupAddon> |
||||||
|
<Input |
||||||
|
type={this.state.type} |
||||||
|
name="password" |
||||||
|
id="examplePassword" |
||||||
|
placeholder="********" |
||||||
|
value={password} |
||||||
|
onChange={(e) => this.setState({ password: e.target.value })} |
||||||
|
/> |
||||||
|
<InputGroupAddon addonType="prepend"> |
||||||
|
<InputGroupText |
||||||
|
onClick={this.showHide} |
||||||
|
> |
||||||
|
{this.state.type === 'input' ? <EyeOutlined /> : <EyeInvisibleOutlined />} |
||||||
|
|
||||||
|
</InputGroupText> |
||||||
|
</InputGroupAddon> |
||||||
|
<span className="password__show" ></span> |
||||||
|
</InputGroup> |
||||||
|
<Row> |
||||||
|
<Col xs="8"> |
||||||
|
<Checkbox checked={this.state.remember} onChange={this.onChecked}>Remember me (7 days)</Checkbox> |
||||||
|
</Col> |
||||||
|
<Col xs="4"> |
||||||
|
{this.state.loader ? ( |
||||||
|
<Spin indicator={antIcon} /> |
||||||
|
) : ( |
||||||
|
<Button onClick={() => this.submitFormLogin()} color="primary" className="px-4" >Login</Button> |
||||||
|
)} |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col className="text-right mt-5"> |
||||||
|
<Button color="link" className="px-0">Forgot password?</Button> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</Form> |
||||||
|
</CardBody> |
||||||
|
</Card> |
||||||
|
</CardGroup> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</Container> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default Login; |
Loading…
Reference in new issue