Compare commits

6 Commits

Author SHA1 Message Date
Thomas Berrod 11c191b05f Fix : paginaiton + range to 20 2019-03-06 14:16:11 +01:00
Thomas Berrod 1edb5739a3 purge 2019-02-20 09:45:31 +01:00
Thomas Berrod ad314b49e6 fix : tokenIsValid bug 2019-02-20 09:44:08 +01:00
Thomas Berrod db5481118d purge 2019-02-20 09:39:01 +01:00
Thomas Berrod 73ade5e752 ménage 2019-02-15 16:34:46 +01:00
Thomas Berrod 26d58036a8 Feat: Poc Api SAV AG2L 2019-02-14 18:16:13 +01:00
22 changed files with 829 additions and 1300 deletions
+74 -36
View File
@@ -1,15 +1,17 @@
import React, { Component } from "react";
import { connect } from 'react-redux';
import classnames from "classnames";
import _ from "lodash";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Griddle from "griddle-react";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import EditIcon from "@material-ui/icons/Edit";
import MenuIcon from "@material-ui/icons/Menu";
import Map from '@material-ui/icons/Map'
import red from "@material-ui/core/colors/red";
import Collapse from "@material-ui/core/Collapse";
import Link from 'next/link'
// import { FormattedDate, FormattedTime } from "react-intl";
import { withStyles, grey400 } from "@material-ui/core/styles";
import {
@@ -21,6 +23,9 @@ import {
MenuItem
} from "@material-ui/core";
import GriddleCustomTableComponent from "../custom/GriddleCustomTableComponent";
import GoogleMapReact from 'google-map-react';
import compose from 'recompose/compose';
import withHandlers from 'recompose/withHandlers';
const styles = theme => ({
card: {
@@ -92,13 +97,14 @@ class CustomRowComponent extends React.Component {
// ClassObjectId nvarchar (Nothing)
//griddle sample https://codesandbox.io/s/r50q23027o
render() {
const { rowData } = this.props;
const { rowData, griddleKey } = this.props;
// console.log(griddleKey)
let interventionDate = new Date(rowData.Dt_Itv);
return (
<Card>
<CardHeader title={rowData.Lbl_Itv} subheader={(interventionDate).toLocaleDateString("fr-FR") + ' ' + interventionDate.toLocaleTimeString("fr-FR", {hour: '2-digit', minute:'2-digit'})}/>
<CardHeader title={rowData.Id_Cli} subheader={(interventionDate).toLocaleDateString("fr-FR") + ' ' + interventionDate.toLocaleTimeString("fr-FR", { hour: '2-digit', minute: '2-digit' })} />
<CardContent>
</CardContent>
<CardActions>
@@ -112,23 +118,33 @@ class CustomRowComponent extends React.Component {
>
<ExpandMoreIcon />
</IconButton>
<EditIcon onClick={() => alert('future edit')} />
{/* <Link href={{
pathname: '/edit',
query: {
rowData:JSON.stringify(rowData)
}
}} >
<a><EditIcon /></a>
</Link> */}
</CardActions>
<Collapse in={this.state.expanded} timeout="auto" unmountOnExit>
<CardContent>
<h3>Details</h3>
<p>
{rowData.Id_Adr_Key}
</p>
<p>
{rowData.Id_Cct_Key}
</p>
<p>
{rowData.Lbm_Itv_Dmd}
</p>
<p>
{rowData.Id_Opr_Itv}
</p>
<h3>{rowData.Lbc_Cct_1 + ' ' + rowData.Lbc_Cct_2}</h3>
<h3>{rowData.Tel_Cct_1}</h3>
<h4>{rowData.Lbl_Itv}</h4>
<p>{rowData.Lbl_Adr_2}</p>
<p>{rowData.Lbl_Adr_3}</p>
<p>{rowData.Lbl_Adr_4}</p>
<p>{rowData.Lbc_Adr_Vil}</p>
{
!!rowData.Lbm_Adr && (
<Link href={`https://www.google.com/maps/place/${rowData.Lbm_Adr}`}>
<a> <Map /></a>
</Link>
)
}
</CardContent>
</Collapse>
@@ -145,15 +161,14 @@ CustomRowComponent.propTypes = {
// eslint-disable-next-line
class Intervention extends Component {
state = {
data: []
data: [],
currentPage: 0,
pageSize: 0,
recordCount: 0
};
componentWillMount() {
console.log("componentWillMount Intervention props : ", this.props.data);
this.setState({
data: this.props.data
});
}
componentDidMount() { }
@@ -161,11 +176,16 @@ class Intervention extends Component {
componentWillReceiveProps(nextProps) {
console.log(
"componentWillReceiveProps Intervention nextProps",
nextProps.data
nextProps.data,
nextProps.currentPage, nextProps.pageSize, nextProps.recordCount
);
this.setState({
data: nextProps.data
data: nextProps.data,
currentPage: nextProps.currentPage,
pageSize: nextProps.pageSize,
recordCount: nextProps.recordCount
});
}
// rowDataSelector = (state, props) => {
@@ -198,31 +218,49 @@ class Intervention extends Component {
);
_onNext = () => {
// const { currentPage, pageSize, filterText } = this.state;
// this.props.fetchUserGamesRequest(currentPage + 1, pageSize, filterText);
const { currentPage, pageSize, filterText } = this.state;
this.props._onGetPage(currentPage + 1, pageSize, filterText)
};
_onPrevious = () => {
// const { currentPage, pageSize, filterText } = this.state;
// this.props.fetchUserGamesRequest(currentPage - 1, pageSize, filterText);
const { currentPage, pageSize, filterText } = this.state;
this.props._onGetPage(currentPage - 1, pageSize, filterText)
};
_onGetPage = pageNumber => {
const { pageSize, filterText } = this.state;
const { pageSize, filterText } = this.state;
this.props._onGetPage(pageNumber, pageSize, filterText)
this.props._onGetPage(pageNumber, pageSize, filterText)
.then( (data) => this.setState(data))
// this.props.fetchUserGamesRequest(pageNumber, pageSize, filterText);
};
_onFilter = filterText => {
// this.setState({ currentPage: 1 });
// const { currentPage, pageSize } = this.state;
// this.setState({ filterText });
const { pageSize } = this.state;
this.setState({ filterText });
// this.props.fetchUserGamesRequest(currentPage, pageSize, filterText);
this.props._onGetPage(1, pageSize, filterText)
};
render() {
// const rowDataSelector = (state, { griddleKey }) => {
// return state
// .get('data')
// .find(rowMap => rowMap.get('griddleKey') === griddleKey)
// .toJSON();
// };
// const enhancedWithRowData = connect((state, props) => {
// return {
// // rowData will be available into MyCustomComponent
// rowData: rowDataSelector(state, props)
// };
// });
const { data, currentPage, pageSize, recordCount } = this.state;
return (
<div>
@@ -235,7 +273,7 @@ class Intervention extends Component {
)}
{
<Griddle
data={this.props.data}
data={data}
pageProperties={{
currentPage,
pageSize,
+295
View File
@@ -0,0 +1,295 @@
import React, { Component } from "react";
import { connect } from 'react-redux';
import classnames from "classnames";
import _ from "lodash";
import PropTypes from "prop-types";
import Griddle from "griddle-react";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import EditIcon from "@material-ui/icons/Edit";
import MenuIcon from "@material-ui/icons/Menu";
import red from "@material-ui/core/colors/red";
import Collapse from "@material-ui/core/Collapse";
// import { FormattedDate, FormattedTime } from "react-intl";
import { withStyles, grey400 } from "@material-ui/core/styles";
import {
Card,
CardHeader,
CardContent,
CardActions,
IconButton,
MenuItem
} from "@material-ui/core";
import GriddleCustomTableComponent from "../custom/GriddleCustomTableComponent";
import GoogleMapReact from 'google-map-react';
import compose from 'recompose/compose';
import withHandlers from 'recompose/withHandlers';
const styles = theme => ({
card: {
maxWidth: 400
},
media: {
height: 0,
paddingTop: "56.25%" // 16:9
},
actions: {
display: "flex"
},
expand: {
transform: "rotate(0deg)",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest
}),
marginLeft: "auto"
},
expandOpen: {
transform: "rotate(180deg)"
},
avatar: {
backgroundColor: red[500]
}
});
const iconButtonElement = (
<IconButton touch tooltip="more" tooltipPosition="bottom-left">
<MoreVertIcon color={grey400} />
</IconButton>
);
const rightIconMenu = (
<MenuIcon iconButtonElement={iconButtonElement}>
<MenuItem>Call</MenuItem>
</MenuIcon>
);
class CustomRowComponent extends React.Component {
state = {
expanded: false
};
handleExpandClick = () => {
this.setState({ expanded: !this.state.expanded });
};
// SchemaName SchemaType Caption
// Id_Indice int (Nothing)
// Id_Itv nvarchar Code
// Id_Res_Aff uniqueidentifier Affaire
// Id_Res_Elt uniqueidentifier Elément
// Dt_Crt datetime Date création
// Dt_Itv datetime Date
// Id_Opr_Sas nvarchar Demandeur
// Id_Opr_Itv nvarchar Intervenant
// Id_Itv_Typ nvarchar Type
// Id_Cli nvarchar Code client
// Id_Adr_Key nvarchar Adresse
// Id_Cct_Key nvarchar Contact
// Id_Res_Elt_Fab uniqueidentifier Elément
// Lbl_Itv nvarchar Résumé
// Lbm_Itv_Dmd nvarchar Demande
// Lbm_Itv_Sol nvarchar Description intervention
// Ldv_Gar int Garantie
// Ldv_Sts int Statut
// Dt_Clt datetime Date clôture
// ClassObjectId nvarchar (Nothing)
//griddle sample https://codesandbox.io/s/r50q23027o
render() {
const { rowData, griddleKey } = this.props;
console.log(griddleKey)
let interventionDate = new Date(rowData.Dt_Itv);
return (
<Card>
<CardHeader title={rowData.Lbl_Itv} subheader={(interventionDate).toLocaleDateString("fr-FR") + ' ' + interventionDate.toLocaleTimeString("fr-FR", { hour: '2-digit', minute: '2-digit' })} />
<CardContent>
</CardContent>
<CardActions>
<IconButton
className={classnames(styles.expand, {
[styles.expandOpen]: this.state.expanded
})}
onClick={this.handleExpandClick}
aria-expanded={this.state.expanded}
aria-label="Show more"
>
<ExpandMoreIcon />
</IconButton>
<EditIcon onClick={() => alert('future edit : ' + griddleKey)} />
</CardActions>
<Collapse in={this.state.expanded} timeout="auto" unmountOnExit>
<CardContent>
<h3>Details</h3>
<p>
{rowData.Id_Adr_Key}
</p>
<p>
{rowData.Id_Cct_Key}
</p>
<p>
{rowData.Lbm_Itv_Dmd}
</p>
<p>
{rowData.Id_Opr_Itv}
</p>
</CardContent>
</Collapse>
</Card>
);
}
}
CustomRowComponent.propTypes = {
rowData: PropTypes.func.isRequired,
editQuest: PropTypes.func.isRequired,
};
// eslint-disable-next-line
class MapInt extends Component {
state = {
data: [],currentPage:1,
pageSize:2, recordCount:5
};
componentWillMount() {
this.setState({
data: this.props.data,
});
this.setState({
recordCount:5
});
}
componentDidMount() { }
componentWillReceiveProps(nextProps) {
console.log(
"componentWillReceiveProps Intervention nextProps",
nextProps.data
);
this.setState({
data: nextProps.data
});
}
// rowDataSelector = (state, props) => {
// let o = state
// .get("data")
// .find(rowMap => rowMap.get("griddleKey") === props.griddleKey);
// o = o.concat({ gameId: this.props.gameId });
// return o.toJSON();
// };
// enhancedWithRowData = connect((state, props) => ({
// editQuest: this.props.edit,
// // rowData will be available into MyCustomComponent
// rowData: this.rowDataSelector(state, props),
// value: this.props.gameId
// }));
NewLayout = ({ Table, Pagination, Filter }) => (
<div>
<Filter />
<Pagination />
<Table />
</div>
);
CustomTableBody = ({ rowIds, Row, style, className }) => (
<div style={style} className={className}>
{rowIds && rowIds.map(r => <Row key={r} griddleKey={r} />)}
</div>
);
_onNext = () => {
// const { currentPage, pageSize, filterText } = this.state;
// this.props.fetchUserGamesRequest(currentPage + 1, pageSize, filterText);
};
_onPrevious = () => {
// const { currentPage, pageSize, filterText } = this.state;
// this.props.fetchUserGamesRequest(currentPage - 1, pageSize, filterText);
};
_onGetPage = pageNumber => {
const { pageSize, filterText } = this.state;
this.props._onGetPage(pageNumber, pageSize, filterText)
.then((data) => {console.log('_onGetPage'+
data["server.Qtbl_Itv_Sav"]);this.setState({data:data["server.Qtbl_Itv_Sav"]})})
// this.props.fetchUserGamesRequest(pageNumber, pageSize, filterText);
};
_onFilter = filterText => {
// this.setState({ currentPage: 1 });
// const { currentPage, pageSize } = this.state;
// this.setState({ filterText });
// this.props.fetchUserGamesRequest(currentPage, pageSize, filterText);
const { pageSize } = this.state;
this.props._onGetPage(2, pageSize, filterText)
.then((data) => this.setState({data}))
};
render() {
// const rowDataSelector = (state, { griddleKey }) => {
// return state
// .get('data')
// .find(rowMap => rowMap.get('griddleKey') === griddleKey)
// .toJSON();
// };
// const enhancedWithRowData = connect((state, props) => {
// return {
// // rowData will be available into MyCustomComponent
// rowData: rowDataSelector(state, props)
// };
// });
const { data, currentPage, pageSize, recordCount } = this.state;
return (
<div>
{!this.props.data && (
<div className="text-center">
<div className="alert alert-info">
Aucune intervention disponnible.
</div>
</div>
)}
{
<Griddle
data={this.state.data}
pageProperties={{
currentPage,
pageSize,
recordCount
}}
components={{
Layout: this.NewLayout,
Row: CustomRowComponent,
TableContainer: GriddleCustomTableComponent,
TableBody: this.CustomTableBody
}}
events={{
onNext: this._onNext,
onPrevious: this._onPrevious,
onGetPage: this._onGetPage,
onFilter: this._onFilter
}}
/>
}
</div>
);
}
}
export default MapInt;
+22 -11
View File
@@ -21,12 +21,15 @@ import ListItem from '@material-ui/core/ListItem';
import { ListItemText } from '@material-ui/core';
import { Spring, animated } from 'react-spring'
import Button from '@material-ui/core/Button';
import GetApp from '@material-ui/icons/GetApp';
import PWAInstallSnack from './PWAInstallSnack'
const styles = theme => ({
root: {
width: '100%',
'& a':{
textDecoration:'none'
'& a': {
textDecoration: 'none'
}
},
@@ -119,12 +122,12 @@ class Header extends React.Component {
</ListItem></a>
</Link>
<Divider />
<Link href="/contact" prefetch >
<Link href="/listeSAV" prefetch >
<a><ListItem>
<ListItemIcon>
<SendIcon />
</ListItemIcon>
<ListItemText primary='contact'>
<ListItemText primary='Interventions'>
</ListItemText>
</ListItem></a>
</Link>
@@ -140,7 +143,7 @@ class Header extends React.Component {
</IconButton>
<Link href="/" prefetch>
<a><div onClick={this.toggle}>
<a><div onClick={this.toggle}>
<Spring native from={{ x: 0 }} to={{ x: this.state.toggle ? 1 : 0 }} config={{ duration: 1000 }}>
{({ x }) => (
<animated.div
@@ -159,12 +162,20 @@ class Header extends React.Component {
</Spring>
</div></a>
</Link>
<Link href="/contact" prefetch >
<a><Typography className={classes.title} variant="h5" color="inherit" noWrap>
Contact
</Typography></a>
</Link>
<PWAInstallSnack >
{({ initInstall }) =>
<Button
aria-label='install' variant="contained" color="primary" onClick={initInstall}>
<GetApp ></GetApp>
<Typography variant="h6" color="inherit">
Installer
</Typography>
</Button>
}
</PWAInstallSnack>
</Toolbar>
</AppBar>
</div >
+3 -1
View File
@@ -74,10 +74,12 @@ class PWAInstallSnack extends Component {
this.setState({ openSnack: false, buttonDisplay: false })
if(this.iOS())
this.setState({ openSnack: false, buttonDisplay: true })
this.setState({ buttonDisplay: true })
}
}else{
this.setState({ openSnack: false, buttonDisplay: false })
}
}
+52
View File
@@ -0,0 +1,52 @@
import axios from "axios";
import getConfig from 'next/config'
const { publicRuntimeConfig } = getConfig()
export default {
intervention: {
fetch: (page, pagesize, filter) => {
console.log('intervention.fetch')
console.log(page, pagesize, filter)
let url = publicRuntimeConfig.apiUrl.Qtbl_Itv_Sav
url += '?range=' + (page - 1) * pagesize + '-' + ((page * pagesize) - 1)
if (!!filter)
url += `&filter={"Lbl_Itv":{"$regex":"${filter}"}}`
console.log(url)
return axios.get(url)
}
},
adresse:
{
fetch:(ids)=>{
console.log('adresse.fetch')
console.log(ids)
let url = publicRuntimeConfig.apiUrl.Atbl_Adr
if(!!ids)
{
url += `?filter={"Id_Adr_Key":{"$in":[${ids.map(i=>'"'+i+'"')}]}}`
}
console.log(url)
return axios.get(url)
}
},
client:{
fetch:(ids)=>{
console.log('client.fetch')
console.log(ids)
let url = publicRuntimeConfig.apiUrl.Atbl_Cli_Ent
if(!!ids)
{
url += `?filter={"Id_Cct_Key":{"$in":[${ids.map(i=>'"'+i+'"')}]}}`
}
console.log(url)
return axios.get(url)
}
}
}
-85
View File
@@ -1,85 +0,0 @@
/**
* next-auth.config.js Example
*
* Environment variables for this example:
*
* PORT=3000
* SERVER_URL=http://localhost:3000
* MONGO_URI=mongodb://localhost:27017/my-database
*
* If you wish, you can put these in a `.env` to seperate your environment
* specific configuration from your code.
**/
// Load environment variables from a .env file if one exists
require('dotenv').load()
const nextAuthProviders = require('./next-auth.providers')
const nextAuthFunctions = require('./next-auth.functions')
// If we want to pass a custom session store then we also need to pass an
// instance of Express Session along with it.
const expressSession = require('express-session')
const MongoStore = require('connect-mongo')(expressSession)
// If no store set, NextAuth defaults to using Express Sessions in-memory
// session store (the fallback is intended as fallback for testing only).
let sessionStore
if (process.env.MONGO_URI) {
sessionStore = new MongoStore({
url: process.env.MONGO_URI,
autoRemove: 'interval',
autoRemoveInterval: 10, // Removes expired sessions every 10 minutes
collection: 'sessions',
stringify: false
})
}
module.exports = () => {
// We connect to the User DB before we define our functions.
// next-auth.functions.js returns an async method that does that and returns
// an object with the functions needed for authentication.
return nextAuthFunctions()
.then(functions => {
return new Promise((resolve, reject) => {
// This is the config block we return, ready to be passed to NextAuth
resolve({
// Define a port (if none passed, will not start Express)
// Note: This project omits a port for NextAuth as it uses Express to
// add additional routes for the examples, so it takes control of
// starting Express, rather than leaving it to NextAuth.
// port: process.env.PORT || 3000,
// Secret used to encrypt session data on the server.
sessionSecret: 'pwacrypretenonsecrettedsecsessi',
// Maximum Session Age in ms (optional, default is 7 days).
// The expiry time for a session is reset every time a user revisits
// the site or revalidates their session token. This is the maximum
// idle time value.
sessionMaxAge: 60000 * 60 * 24 * 7,
// Session Revalidation in X ms (optional, default is 60 seconds).
// Specifies how often a Single Page App should revalidate a session.
// Does not impact the session life on the server, but causes clients
// to refetch session info (even if it is in a local cache) after N
// seconds has elapsed since it was last checked so they always display
// state correctly.
// If set to 0 will revalidate a session before rendering every page.
sessionRevalidateAge: 60000,
// Canonical URL of the server (optiona, but recommended).
// e.g. 'http://localhost:3000' or 'https://www.example.com'
// Used in callbak URLs and email sign in links. It will be auto
// generated if not specified, which may cause problems if your site
// uses multiple aliases (e.g. 'example.com and 'www.examples.com').
serverUrl: process.env.SERVER_URL || null,
// Add an Express Session store.
expressSession: expressSession,
sessionStore: sessionStore,
// Define oAuth Providers
providers: nextAuthProviders(),
// Define functions for manging users and sending email.
functions: functions,
csrf:{whitelist:['/account/user']}
})
})
})
}
-271
View File
@@ -1,271 +0,0 @@
/**
* next-auth.functions.js Example
*
* This file defines functions NextAuth to look up, add and update users.
*
* It returns a Promise with the functions matching these signatures:
*
* {
* find: ({
* id,
* email,
* emailToken,
* provider,
* poviderToken
* } = {}) => {},
* update: (user) => {},
* insert: (user) => {},
* remove: (id) => {},
* serialize: (user) => {},
* deserialize: (id) => {}
* }
*
* Each function returns Promise.resolve() - or Promise.reject() on error.
*
* This specific example supports both MongoDB and NeDB, but can be refactored
* to work with any database.
*
* Environment variables for this example:
*
* MONGO_URI=mongodb://localhost:27017/my-database
* EMAIL_FROM=username@gmail.com
* EMAIL_SERVER=smtp.gmail.com
* EMAIL_PORT=465
* EMAIL_USERNAME=username@gmail.com
* EMAIL_PASSWORD=p4ssw0rd
*
* If you wish, you can put these in a `.env` to seperate your environment
* specific configuration from your code.
**/
// Load environment variables from a .env file if one exists
require('dotenv').load()
// This config file uses MongoDB for User accounts, as well as session storage.
// This config includes options for NeDB, which it defaults to if no DB URI
// is specified. NeDB is an in-memory only database intended here for testing.
const MongoClient = require('mongodb').MongoClient
const NeDB = require('nedb')
const MongoObjectId = (process.env.MONGO_URI) ? require('mongodb').ObjectId : (id) => { return id }
// Use Node Mailer for email sign in
const nodemailer = require('nodemailer')
const nodemailerSmtpTransport = require('nodemailer-smtp-transport')
const nodemailerDirectTransport = require('nodemailer-direct-transport')
// Send email direct from localhost if no mail server configured
let nodemailerTransport = nodemailerDirectTransport()
if (process.env.EMAIL_SERVER && process.env.EMAIL_USERNAME && process.env.EMAIL_PASSWORD) {
nodemailerTransport = nodemailerSmtpTransport({
host: process.env.EMAIL_SERVER,
port: process.env.EMAIL_PORT || 25,
secure: false,
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD
}
})
}
module.exports = () => {
return new Promise((resolve, reject) => {
if (process.env.MONGO_URI) {
// Connect to MongoDB Database and return user connection
MongoClient.connect(process.env.MONGO_URI, (err, mongoClient) => {
if (err) return reject(err)
const dbName = process.env.MONGO_URI.split('/').pop().split('?').shift()
const db = mongoClient.db(dbName)
return resolve(db.collection('users'))
})
} else {
// If no MongoDB URI string specified, use NeDB, an in-memory work-a-like.
// NeDB is not persistant and is intended for testing only.
let collection = new NeDB({ autoload: true })
collection.loadDatabase(err => {
if (err) return reject(err)
resolve(collection)
})
}
})
.then(usersCollection => {
return Promise.resolve({
// If a user is not found find() should return null (with no error).
find: ({id, email, emailToken, provider} = {}) => {
let query = {}
// Find needs to support looking up a user by ID, Email, Email Token,
// and Provider Name + Users ID for that Provider
if (id) {
query = { _id: MongoObjectId(id) }
} else if (email) {
query = { email: email }
} else if (emailToken) {
query = { emailToken: emailToken }
} else if (provider) {
query = { [`${provider.name}.id`]: provider.id }
}
return new Promise((resolve, reject) => {
usersCollection.findOne(query, (err, user) => {
if (err) return reject(err)
return resolve(user)
})
})
},
// The user parameter contains a basic user object to be added to the DB.
// The oAuthProfile parameter is passed when signing in via oAuth.
//
// The optional oAuthProfile parameter contains all properties associated
// with the users account on the oAuth service they are signing in with.
//
// You can use this to capture profile.avatar, profile.location, etc.
insert: (user, oAuthProfile) => {
return new Promise((resolve, reject) => {
usersCollection.insert(user, (err, response) => {
if (err) return reject(err)
// Mongo Client automatically adds an id to an inserted object, but
// if using a work-a-like we may need to add it from the response.
if (!user._id && response._id) user._id = response._id
return resolve(user)
})
})
},
// The user parameter contains a basic user object to be added to the DB.
// The oAuthProfile parameter is passed when signing in via oAuth.
//
// The optional oAuthProfile parameter contains all properties associated
// with the users account on the oAuth service they are signing in with.
//
// You can use this to capture profile.avatar, profile.location, etc.
update: (user, profile) => {
return new Promise((resolve, reject) => {
usersCollection.update({_id: MongoObjectId(user._id)}, user, {}, (err) => {
if (err) return reject(err)
return resolve(user)
})
})
},
// The remove parameter is passed the ID of a user account to delete.
//
// This method is not used in the current version of next-auth but will
// be in a future release, to provide an endpoint for account deletion.
remove: (id) => {
return new Promise((resolve, reject) => {
usersCollection.remove({_id: MongoObjectId(id)}, (err) => {
if (err) return reject(err)
return resolve(true)
})
})
},
// Seralize turns the value of the ID key from a User object
serialize: (user) => {
// Supports serialization from Mongo Object *and* deserialize() object
if (user.id) {
// Handle responses from deserialize()
return Promise.resolve(user.id)
} else if (user._id) {
// Handle responses from find(), insert(), update()
return Promise.resolve(user._id)
} else {
return Promise.reject(new Error("Unable to serialise user"))
}
},
// Deseralize turns a User ID into a normalized User object that is
// exported to clients. It should not return private/sensitive fields,
// only fields you want to expose via the user interface.
deserialize: (id) => {
return new Promise((resolve, reject) => {
usersCollection.findOne({ _id: MongoObjectId(id) }, (err, user) => {
if (err) return reject(err)
// If user not found (e.g. account deleted) return null object
if (!user) return resolve(null)
return resolve({
id: user._id,
name: user.name,
email: user.email,
emailVerified: user.emailVerified,
admin: user.admin || false
})
})
})
},
// Define method for sending links for signing in over email.
sendSignInEmail: ({
email = null,
url = null
} = {}) => {
nodemailer
.createTransport(nodemailerTransport)
.sendMail({
to: email,
from: process.env.EMAIL_FROM,
subject: 'Sign in link',
text: `Use the link below to sign in:\n\n${url}\n\n`,
html: `<p>Use the link below to sign in:</p><p>${url}</p>`
}, (err) => {
if (err) {
console.error('Error sending email to ' + email, err)
}
})
if (process.env.NODE_ENV === 'development') {
console.log('Generated sign in link ' + url + ' for ' + email)
}
},
// Credentials Sign In
//
// If you use this you will need to define your own way to validate
// credentials. Unlike with oAuth or Email Sign In, accounts are not
// created automatically so you will need to provide a way to create them.
//
// This feature is intended for strategies like Two Factor Authentication.
//
// To disable this option, do not set signin (or set it to null).
/*
*/
signIn: ({form, req}) => {
return new Promise((resolve, reject) => {
// Should validate credentials (e.g. hash password, compare 2FA token
// etc) and return a valid user object from a database.
return usersCollection.findOne({
email: form.email
}, (err, user) => {
if (err) return reject(err)
if (!user) return resolve(null)
// Check credentials - e.g. compare bcrypt password hashes
if (form.password === "test1234") {
// If valid, return user object - e.g. { id, name, email }
return resolve(user)
} else {
// If invalid, return null
return resolve(null)
}
})
})
},
// Session Object (optional)
//
// The session object that gets returned to the client. You don't need to
// specify this function here unless you want to override or extend the
// default (e.g. with any other properties you have added to req.session)
//
// Note: The object returned will be stored in localStorage and visible
// client side so do not return data you would not want the user to see.
/*
session: (session, req) => {
if (req.session && req.session.someCustomProperty)
session.someCustomProperty = req.session.someCustomProperty
session.someOtherCustomProperty = "Example custom property"
return session
}
*/
})
})
}
-131
View File
@@ -1,131 +0,0 @@
/**
* next-auth.providers.js Example
*
* This file returns a simple array of oAuth Provider objects for NextAuth.
*
* This example returns an array based on what environment variables are set,
* with explicit support for Facebook, Google and Twitter, but it can be used
* to add strategies for other oAuth providers.
*
* Environment variables for this example:
*
* FACEBOOK_ID=
* FACEBOOK_SECRET=
* GOOGLE_ID=
* GOOGLE_SECRET=
* TWITTER_KEY=
* TWITTER_SECRET=
*
* If you wish, you can put these in a `.env` to seperate your environment
* specific configuration from your code.
**/
// Load environment variables from a .env file if one exists
require('dotenv').load()
module.exports = () => {
let providers = []
if (process.env.FACEBOOK_ID && process.env.FACEBOOK_SECRET) {
providers.push({
providerName: 'Facebook',
providerOptions: {
scope: ['email', 'public_profile']
},
Strategy: require('passport-facebook').Strategy,
strategyOptions: {
clientID: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
profileFields: ['id', 'displayName', 'email', 'link']
},
getProfile(profile) {
// Normalize profile into one with {id, name, email} keys
return {
id: profile.id,
name: profile.displayName,
email: profile._json.email
}
}
})
}
if (process.env.LINKEDIN_ID && process.env.LINKEDIN_SECRET) {
providers.push({
providerName: 'Linkedin',
providerOptions: {
scope: ['r_emailaddress', 'r_basicprofile']
},
Strategy: require('passport-linkedin-oauth2').Strategy,
strategyOptions: {
clientID: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
profileFields: ['r_emailaddress', 'r_basicprofile']
},
getProfile(profile) {
console.log(profile);
// Normalize profile into one with {id, name, email} keys
return {
id: profile.id,
// name: profile.displayName,
// email: profile._json.email
}
}
})
}
if (process.env.GOOGLE_ID && process.env.GOOGLE_SECRET) {
providers.push({
providerName: 'Google',
providerOptions: {
scope: ['profile', 'email']
},
Strategy: require('passport-google-oauth').OAuth2Strategy,
strategyOptions: {
clientID: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
},
getProfile(profile) {
// Normalize profile into one with {id, name, email} keys
return {
id: profile.id,
name: profile.displayName,
email: profile.emails[0].value
}
}
})
}
/**
* Note: Twitter doesn't expose emails by default.
* If we don't get one NextAuth will create a placeholder in the form
* `{provider}-{account-id}@localhost.localdomain`
*
* To have your Twitter oAuth return emails go to apps.twitter.com and add
* links to your Terms and Conditions and Privacy Policy under the "Settings"
* tab, then check the "Request email addresses" from users box under the
* "Permissions" tab.
**/
if (process.env.TWITTER_KEY && process.env.TWITTER_SECRET) {
providers.push({
providerName: 'Twitter',
providerOptions: {
scope: []
},
Strategy: require('passport-twitter').Strategy,
strategyOptions: {
consumerKey: process.env.TWITTER_KEY,
consumerSecret: process.env.TWITTER_SECRET,
userProfileURL: 'https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true'
},
getProfile(profile) {
// Normalize profile into one with {id, name, email} keys
return {
id: profile.id,
name: profile.displayName,
email: (profile.emails && profile.emails[0].value) ? profile.emails[0].value : ''
}
}
})
}
return providers
}
+12 -7
View File
@@ -19,10 +19,7 @@ module.exports = withPlugins(
workboxOpts: {
globPatterns: ['static/**/*'],
globDirectory: '.',
runtimeCaching: [
{ urlPattern: /.*auth.*/, handler: 'networkOnly' },
{ urlPattern: /.*account.*/, handler: 'networkOnly' },
{ urlPattern: /^https?.*/, handler: 'networkFirst' },
runtimeCaching: [
{ urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
handler: "cacheFirst",
options: {
@@ -30,10 +27,16 @@ module.exports = withPlugins(
expiration: {
maxEntries: 100
}
}}
}
},
{ urlPattern: /.*auth.*/, handler: 'networkOnly' },
{ urlPattern: /.*account.*/, handler: 'networkOnly' },
{ urlPattern: /.*/, handler: 'networkFirst' },
],
importScripts:
['/static/js/firebase-messaging-sw.js',
[
//'/static/js/firebase-messaging-sw.js',
'/static/js/backgroundSync-sw.js'
]
},
@@ -57,7 +60,9 @@ module.exports = withPlugins(
},
apiUrl:{
'token':'https://poc-api.ag2l.fr:50000/token/',
'Qtbl_Itv_Sav': 'https://poc-api.ag2l.fr:50000/rest/server.Qtbl_Itv_Sav'
'Qtbl_Itv_Sav': 'https://poc-api.ag2l.fr:50000/rest/server.Qtbl_Itv_Sav',
'Atbl_Adr':'https://poc-api.ag2l.fr:50000/rest/server.Atbl_Adr',
'Atbl_Cli_Ent':'https://poc-api.ag2l.fr:50000/rest/server.Atbl_Cct_Ent'
},
ReCAPTCHA:{
"siteKey":""
+2
View File
@@ -22,6 +22,7 @@
"firebase": "^5.7.0",
"flubber": "^0.4.2",
"fs": "^0.0.1-security",
"google-map-react": "^1.1.2",
"googleapis": "^35.0.0",
"griddle": "^0.1.2",
"griddle-react": "^1.13.1",
@@ -60,6 +61,7 @@
"react-redux": "^6.0.0",
"react-spring": "^6.1.7",
"reactstrap": "^7.0.2",
"recompose": "^0.30.0",
"start-server-and-test": "^1.7.5",
"universal-cookie": "^3.0.7"
},
+22 -17
View File
@@ -4,23 +4,26 @@ import { MuiThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import JssProvider from 'react-jss/lib/JssProvider';
import getPageContext from '../src/getPageContext';
import axios from "axios";
import Router from 'next/router'
import getConfig from 'next/config'
import Head from 'next/head'
import { initGA, logPageView } from '../utils/analytics'
import setAuthorizationHeader from "../utils/setAuthorizationHeader";
const { publicRuntimeConfig } = getConfig()
function tokenIsValid(tokenExpireString) {
if (!!tokenExpireString)
return true
let tokenExpireDate = new Date(tokenExpireString)
if (!!!tokenExpireString)
return false
return (tokenExpireDate.getTime() > Date().now.getTime())
var d = new Date(0); // The 0 there is the key, which sets the date to the epoch
d.setUTCSeconds(tokenExpireString);
return (d.getTime() < Date.now())
}
class MyApp extends App {
@@ -54,31 +57,33 @@ class MyApp extends App {
logPageView()
Router.router.events.on('routeChangeComplete', logPageView)
}
if (!!localStorage.AG2LToken
|| (!!localStorage.AG2LTokenExpireDateTime && tokenIsValid(localStorage.AG2LTokenExpireDateTime))) {
if (!!!localStorage.AG2LToken
|| !tokenIsValid(localStorage.AG2LTokenExpireDateTime)) {
//get a new token
const res = await fetch(publicRuntimeConfig.apiUrl.token,
{
method: "POST",
headers: {
"Accept": "application/json"
"Accept": "application/json"
},
body: 'username=AG2L&grant_type=password&password=AG2L'
})
if(res.ok == true)
{
if (res.ok == true) {
const data = await res.json()
// setAuthorizationHeader(data.access_token)
localStorage.AG2LToken = data.access_token
localStorage.AG2LTokenExpireDateTime = new Date(Date.now() + data.expires_in).getTime()
axios.defaults.headers.common.authorization = `Bearer ${localStorage.AG2LToken}`;
localStorage.AG2LTokenExpireDateTime = new Date(Date.now() + data.expires_in).getTime()
}
else
{
//TODO: + trycatch
else {
}
} else {
// setAuthorizationHeader(localStorage.AG2LToken)
axios.defaults.headers.common.authorization = `Bearer ${localStorage.AG2LToken}`;
}
}
-295
View File
@@ -1,295 +0,0 @@
import Layout from '../components/MyLayout.js'
import axios from 'axios'
import { withStyles } from '@material-ui/core/styles'
import Slide from '@material-ui/core/Slide';
import Paper from '@material-ui/core/Paper';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import CircularProgress from '@material-ui/core/CircularProgress';
import Snackbar from '@material-ui/core/Snackbar';
import Typography from '@material-ui/core/Typography';
import ReCAPTCHA from "react-google-recaptcha";
import green from '@material-ui/core/colors/green';
import orange from '@material-ui/core/colors/orange';
import SnackbarContent from '@material-ui/core/SnackbarContent';
import { Transition, config } from 'react-spring'
import getConfig from 'next/config'
import Head from 'next/head'
const {publicRuntimeConfig} = getConfig()
//variable for client side only import
let back = ''
const styles = theme => ({
root: {
margin: theme.spacing.unit,
maxWidth: '400px', [theme.breakpoints.down('md')]: {
maxWidth: `${100 - (theme.spacing.unit)}vw`
},
},
wrapper: {
maxWidth: '400px',
[theme.breakpoints.down('md')]: {
maxWidth: `${100 - (theme.spacing.unit)}vw`
},
},
paper: {
'& form':{
[theme.breakpoints.down('md')]: {
width: `${100 - (theme.spacing.unit)}vw`
},
padding: theme.spacing.unit * 2,
display:'flex',
flexWrap: 'wrap',
}
},
website: {
width: 400,
[theme.breakpoints.down('md')]: {
width: `${100 - (theme.spacing.unit)}vw`
},
},
textField: {
width: '100%'
},
button: {
margin: theme.spacing.unit,
alignSelf:'flex-end'
},
link: {
cursor: 'pointer'
},
success: {
backgroundColor: green[600],
},
error: {
backgroundColor: theme.palette.error.dark,
},
warning: {
backgroundColor: orange[600],
},
spock: {
display: 'inline'
},
errorCaptcha: {
border: '2px',
borderColor: 'red',
borderStyle: 'solid'
}
});
let spring = { ...config.default, precision: 0.1 }
const recaptchaRef = React.createRef();
class Contact extends React.Component {
state = {
nom:"",
prenom:"",
mail:"",
tel:"",
message: "",
checked: false,
openSnack: false,
loading: false,
error: false,
warning: false,
success: false,
snackMessage: ""
};
handleMessage = event => {
this.setState({ success: false, message: event.target.value })
}
handleField = e => {
this.setState({ success: false, [e.target.name]: e.target.value })
}
sendMessage = (recaptchaValue) => {
axios.post("/feedback", {
nom:this.state.nom,
prenom:this.state.prenom,
tel:this.state.tel,
mail:this.state.mail,
message: this.state.message,
recaptchaValue: recaptchaValue
}).then(response => {
this.setState({ success: true, error: false, warning: false, message: '', snackMessage: "Votre message à bien été envoyé.", loading: false, openSnack: true })
}).catch(error => {
if (error.status != 500) {
if (process.browser) {
// client-side-only code
back.saveEventDataLocally([{ message: this.state.message, recaptchaValue }]);
}
this.setState({ success: false, error: false, warning: true,
snackMessage: "📴 Votre message n'a pas pu aboutir, mais a été sauvegardé. Nous l'enverrons lorsque nous aurons une meilleure connectivité.",
loading: false, openSnack: true })
}
else
this.setState({ success: false, error: true, warning: false,
snackMessage: "Une erreur système est survenue.",
loading: false, openSnack: true })
});
}
handleClose = () => {
this.setState({ openSnack: false });
}
handleSubmit = e => {
e.preventDefault();
const recaptchaValue = recaptchaRef.current.getValue();
if (this.state.mail === '' || this.state.message === '' || recaptchaValue === '')
this.setState({ success: false, error: true, warning: false,
snackMessage: "Veuillez entrer les champs necessaires et valider le Captcha.",
loading: false, openSnack: true })
else
this.setState({ success: false, error: false, warning: false, loading: true }, this.sendMessage(recaptchaValue))
}
config = (item, state) => (state === 'leave' ? [{ duration: 1000 }, spring] : [{ duration: 5000 }, spring])
async componentDidMount() {
//loading lib for client side only
let result = await import("../src/backSync.js")
back = result.default
}
render() {
const { classes } = this.props;
return (
<Layout menu={this.props.menu}>
<Head>
<title>Devis Développement PWA</title>
<meta name="description" content="Vous souhaitez créer une PWA, développer des fonctionnalités à votre site web et améliorer l'expérience utilisateur sur votre site internet? Contactez-nous pour parler de votre projet." />
<link rel="canonical" href="https://www.application.bzh/contact"/>
</Head>
<div className={classes.root}>
<div className={classes.wrapper}>
<Slide direction="right" in={true} mountOnEnter unmountOnExit timeout={800}>
<Paper elevation={4} className={classes.paper}>
{this.state.loading && (
<div className="alert alert-info">
<CircularProgress color="secondary" />Validating your inputs...
</div>
)}
{!this.state.loading &&
(<form onSubmit={this.handleSubmit}>
<TextField
id="prenom"
name="prenom"
label="Prénom"
value={this.state.prenom}
onChange={this.handleField}
className={classes.textField}
margin="normal"
/>
<TextField
id="nom"
name="nom"
label="Nom"
value={this.state.nom}
onChange={this.handleField}
className={classes.textField}
margin="normal"
/>
<TextField
id="mail"
name="mail"
label="Email*"
value={this.state.mail}
onChange={this.handleField}
className={classes.textField}
margin="normal"
error={this.state.error}
/>
<TextField
id="tel"
name="tel"
label="Tel"
value={this.state.tel}
onChange={this.handleField}
className={classes.textField}
margin="normal"
/>
<TextField
id="standard-multiline-flexible"
label="Message*"
multiline
rowsMax={10}
rows={4}
value={this.state.message}
onChange={this.handleMessage}
className={classes.textField}
margin="normal"
error={this.state.error}
/>
<div className={this.state.error ? classes.errorCaptcha : ''}>
<ReCAPTCHA
id="component-outlined"
ref={recaptchaRef}
sitekey={publicRuntimeConfig.ReCAPTCHA.siteKey}
size="normal"
/>
</div>
<Button type="submit" variant="outlined" color="secondary" className={classes.button}>
Send
</Button>
<Transition
config={this.config}
items={this.state.success}
from={{ opacity: 0 }}
enter={{ opacity: 1 }}
leave={{ opacity: 0 }}>
{show =>
show && (props => <div style={props} className={classes.spock}>🖖</div>)
}
</Transition>
</form>)
}
</Paper>
</Slide>
</div>
<br></br>
<div>
<Paper elevation={4} className={classes.website}>
<a href={publicRuntimeConfig.eDeclicUrl} >
<Typography variant="subtitle1" color="secondary" className={classes.link} >
www.e-declic.com
</Typography>
</a>
</Paper>
</div>
</div>
<Snackbar
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
open={this.state.openSnack}
onClose={this.handleClose}
autoHideDuration={6000}
TransitionComponent={(props)=><Slide {...props} direction="left" />}
ContentProps={{
'aria-describedby': 'message-id',
}}
>
<SnackbarContent
aria-describedby="message-id"
className={classes[this.state.error ? 'error' : this.state.warning ? 'warning' : 'success']}
message={<span id="message-id">{this.state.snackMessage}</span>}
/>
</Snackbar>
</Layout>
);
}
}
export default withStyles(styles)(Contact)
+62
View File
@@ -0,0 +1,62 @@
import React, { Component } from 'react'
import Layout from '../components/MyLayout.js'
import { withStyles } from "@material-ui/core/styles";
import Head from 'next/head'
const styles = theme => ({})
class edit extends Component {
state = {
data: []
}
componentDidMount()
{
console.log('edit', this.props)
}
componentWillReceiveProps(nextProps) {
this.setState({ data: nextProps.data })
}
handleSubmit = e => {
// this.setState({...this.state, loading: true });
// e.preventDefault();
// const errors = this.validate(this.state);
// this.setState({ errors });
// if (Object.keys(errors).length === 0) {
// console.log( " if (Object.keys(errors).length === 0) {", this.state);
// this.props.submit(this.state);
// }
}
render() {
const { classes, menu } = this.props;
const { data } = this.state;
return (
<Layout menu={menu}>
<Head>
<title>AG2L SAV</title>
<meta name="description" content="" />
<link rel="canonical" href="https://AG2L-pwa.bzh" />
</Head>
<div className={classes.root}>
<div className="container">
<h3>{data.Lbl_Itv}</h3>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="gameTitle">Descriptif</label>
</div>
</form>
</div>
</div>
</Layout>
)
}
}
export default withStyles(styles)(edit)
+37 -20
View File
@@ -5,6 +5,7 @@ import Head from 'next/head'
import _ from 'lodash'
import getConfig from 'next/config'
import FormatListNumbered from '@material-ui/icons/FormatListNumbered'
import Map from '@material-ui/icons/Map'
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardContent from '@material-ui/core/CardContent';
@@ -21,22 +22,25 @@ const styles = theme => ({
height: 250,
width: 400
},
icon: {
fontSize: '64px',
margin: 'auto',
textAlign: 'center'
},
innerCard:{
display:'flex',
flexDirection: 'column'
innerCard: {
display: 'flex',
flexDirection: 'column'
},
text: {
justifyContent: 'center',
textAlign:'center'
}
textAlign: 'center'
},
card:{
margin:theme.spacing.unit,
}
});
@@ -47,7 +51,7 @@ class Index extends React.Component {
async componentDidMount() {
}
@@ -63,21 +67,34 @@ class Index extends React.Component {
<div className={classes.root}>
<Card className={classes.card}>
<Link href={"listeSAV"} prefetch><a>
<CardActionArea >
<CardContent>
<div className={classes.innerCard + ' '+classes.icon}>
<FormatListNumbered className={classes.icon} />
<Typography variant='h3' className={classes.text}>Interventions</Typography>
</div>
</CardContent>
</CardActionArea></a>
</Link>
</Card>
<CardActionArea >
<CardContent>
<div className={classes.innerCard + ' ' + classes.icon}>
<FormatListNumbered className={classes.icon} />
<Typography variant='h3' className={classes.text}>Interventions</Typography>
</div>
</CardContent>
</CardActionArea></a>
</Link>
</Card>
{/* <Card className={classes.card}>
<Link href={"listeMap"} prefetch><a>
<CardActionArea >
<CardContent>
<div className={classes.innerCard + ' ' + classes.icon}>
<Map className={classes.icon} />
<Typography variant='h3' className={classes.text}>Map</Typography>
</div>
</CardContent>
</CardActionArea></a>
</Link>
</Card> */}
</div>
</Layout>
)
+120
View File
@@ -0,0 +1,120 @@
import React from 'react'
import Layout from '../components/MyLayout.js'
import { withStyles } from '@material-ui/core/styles'
import Head from 'next/head'
import _ from 'lodash'
import getConfig from 'next/config'
import MapInt from '../components/AG2L/MapInt'
import api from '../data/api'
const { publicRuntimeConfig } = getConfig()
const styles = theme => ({
root: {
margin: theme.spacing.unit,
height: 250,
width: 400
},
});
class ListeSAV extends React.Component {
state = {
data: []
};
async componentWillMount() {
}
//example requete adress:
//https://poc-api.ag2l.fr:50000/rest/server.Atbl_Adr?filter={"Id_Adr_Key":"Cli.ARMORACIER22F.112731"}
async componentDidMount() {
console.log('ListeMap componentDidMount')
if (!!localStorage.AG2LToken) {
let res = await api.intervention.fetch(1,2,'')
if (res.ok == true) {
res.headers.forEach(function (val, key) { console.log(key + ' -> ' + val); });
const data = await res.json()
console.log(data)
this.setState({ data: data["server.Qtbl_Itv_Sav"] })
}
else {
//TODO: + trycatch
}
}
}
onGetPage = (pageNumber, pageSize) => {
console.log('onGetPage')
console.log(pageNumber, pageSize)
let from = pageNumber * pageSize;
let to = from + pageSize
let requestSuffix = "?range=" + from + '-' + to
// axios.defaults.headers.common.authorization = `Bearer ${localStorage.AG2LToken}`;
// return axios.get(publicRuntimeConfig.apiUrl.Qtbl_Itv_Sav + requestSuffix)
// .then((res) => {console.log(res)
// if (res.ok == true) {
// res.headers.forEach(function (val, key) { console.log(key + ' -> ' + val); });
// return res.json()
// }
// else {
// //TODO: + trycatch
// return null
// }
// } )
return fetch(publicRuntimeConfig.apiUrl.Qtbl_Itv_Sav + requestSuffix,
{
method: "GET",
headers: {
"Accept": "application/json",
'Authorization': 'Bearer ' + localStorage.AG2LToken
}
}).then(
(res) => {
if (res.ok == true) {
res.headers.forEach(function (val, key) { console.log(key + ' -> ' + val); });
return res.json()
}
else {
//TODO: + trycatch
return null
}
}
)
}
render() {
const { classes, menu } = this.props;
const { data } = this.state;
return (
<Layout menu={menu}>
<Head>
<title>AG2L SAV</title>
<meta name="description" content="" />
<link rel="canonical" href="https://AG2L-pwa.bzh" />
</Head>
<div className={classes.root}>
<MapInt data={data}
_onGetPage={this.onGetPage}></MapInt>
</div>
</Layout>
)
}
}
export default withStyles(styles)(ListeSAV)
+86 -47
View File
@@ -5,7 +5,9 @@ import Head from 'next/head'
import _ from 'lodash'
import getConfig from 'next/config'
import Intervention from '../components/AG2L/Intervention'
import axios from "axios";
import api from '../data/api'
import { resolve } from 'bluebird';
const { publicRuntimeConfig } = getConfig()
@@ -29,58 +31,92 @@ class ListeSAV extends React.Component {
}
//example requete adress:
//https://poc-api.ag2l.fr:50000/rest/server.Atbl_Adr?filter={"Id_Adr_Key":"Cli.ARMORACIER22F.112731"}
//https://poc-api.ag2l.fr:50000/rest/server.Ttbl_Aff_Elt?filter={Id_Res_Hg:"c7edb876-15e7-4dfe-be37-2498beb6457c"}
async componentDidMount() {
axios.defaults.headers.common.authorization = `Bearer ${localStorage.AG2LToken}`;
if (!!localStorage.AG2LToken) {
const res = await fetch(publicRuntimeConfig.apiUrl.Qtbl_Itv_Sav + '?range=0-1',
{
method: "GET",
headers: {
"Accept": "application/json",
'Authorization': 'Bearer ' + localStorage.AG2LToken,
}
})
if (res.ok == true) {
res.headers.forEach(function (val, key) { console.log(key + ' -> ' + val); });
const data = await res.json()
console.log(data)
this.setState({ data: data["server.Qtbl_Itv_Sav"] })
}
else {
//TODO: + trycatch
}
}
this.onGetPage(1, 20)
}
onGetPage = (pageNumber, pageSize) => {
let from = pageNumber * pageSize;
let to = from + pageSize
let requestSuffix = "?range=" + from + '-' + to
return fetch(publicRuntimeConfig.apiUrl.Qtbl_Itv_Sav + requestSuffix,
{
method: "GET",
headers: {
"Accept": "application/json",
'Authorization': 'Bearer ' + localStorage.AG2LToken
onGetPage = (pageNumber, pageSize, filter) => {
console.log('onGetPage')
console.log(pageNumber, pageSize, filter)
return api.intervention.fetch(pageNumber, pageSize, filter).then(
(res) => {
if (res.status === 206 || res.status === 200) {
let cr = res.headers['content-range']
let crRegexp = /(\d*)-(\d*)\/(\d*)/
let ranges = cr.match(crRegexp)
let from = ranges[1] * 1//hack: to transform to a number
let size = ranges[3] * 1
this.setState({ currentPage: pageNumber, pageSize: pageSize, recordCount: size })
return res.data["server.Qtbl_Itv_Sav"]
}
}).then(
(res) => {
if (res.ok == true) {
res.headers.forEach(function (val, key) { console.log(key + ' -> ' + val); });
res.json().then(
(j) => {return j}
)
}
else {
//TODO: + trycatch
return null
}
}
else {
throw false;
}
}
)
.then((data) => {
if (!!data) {
let tab = data
let addrIds = tab.map(a => a.Id_Adr_Key)
console.log(addrIds)
return api.adresse.fetch(addrIds).then((res) => {
if (res.status === 200) {
let raw = res.data["server.Atbl_Adr"];
const merged = [...tab.concat(raw).reduce((m, o) =>
m.set(o.Id_Adr_Key, Object.assign(m.get(o.Id_Adr_Key) || {}, o))
, new Map()).values()];
console.log(merged);
this.setState({ data: merged });
return merged;
// raw.Lbc_Adr_1
// raw.Lbl_Adr_2
// raw.Lbl_Adr_3
// raw.Lbl_Adr_4
// raw.Lbc_Adr_Vil
// raw.Id_Adr_Cp
// raw.Id_Adr_Pys
// raw.Lbm_Adr
}
}).catch(() => this.setState({ data: null }))
}
})
.then((r) => {
if (!!r) {
let tab = r
let contactIds = tab.map(a => a.Id_Cct_Key)
console.log(contactIds)
api.client.fetch(contactIds).then((res) => {
if (res.status === 200) {
let raw = res.data["server.Atbl_Cct_Ent"];
const merged = [...tab.concat(raw).reduce((m, o) =>
m.set(o.Id_Cct_Key, Object.assign(m.get(o.Id_Cct_Key) || {}, o))
, new Map()).values()];
console.log(merged);
this.setState({ data: merged });
return merged;
}
}).catch(() => this.setState({ data: null }))
}
}
)
.catch(() => this.setState({ data: null }))
}
@@ -92,11 +128,14 @@ class ListeSAV extends React.Component {
<Head>
<title>AG2L SAV</title>
<meta name="description" content="" />
<link rel="canonical" href="https://AG2L-pwa.bzh" />
<link rel="canonical" href="https://ag2l.e-declic.net" />
</Head>
<div className={classes.root}>
<Intervention data={data}
currentPage={this.state.currentPage}
pageSize={this.state.pageSize}
recordCount={this.state.recordCount}
_onGetPage={this.onGetPage}></Intervention>
</div>
</Layout>
+2 -252
View File
@@ -2,13 +2,9 @@
require('dotenv').load()
const utils = require('./src/utils');
const mailer = require("./src/mailer.js")
const { google } = require('googleapis');
//const express = require('express')
const express = require('express')
const axios = require('axios')
const next = require('next')
const nextAuth = require('next-auth')
const nextAuthConfig = require('./next-auth.config')
const { join } = require('path');
const { parse } = require('url');
@@ -16,261 +12,18 @@ const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
// const passport = require('passport')
// const FacebookStrategy = require('passport-facebook').Strategy
const routes = {
admin: require('./routes/admin'),
account: require('./routes/account')
}
const bodyParser = require('body-parser');
function getAccessToken() {
return new Promise(function (resolve, reject) {
var key = require('./service-account.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
'https://www.googleapis.com/auth/firebase.messaging',
null
);
jwtClient.authorize(function (err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
// passport.use(new FacebookStrategy({
// clientID: process.env.FACEBOOK_ID,
// clientSecret: process.env.FACEBOOK_SECRET,
// callbackURL: "http://localhost:3000/auth/facebook/callback",
// enableProof: true
// },
// function (accessToken, refreshToken, profile, cb) {
// User.findOrCreate({ facebookId: profile.id }, function (err, user) {
// return cb(err, user);
// });
// console.log(profile)
// console.log("accessToken : " + accessToken);
// console.log("refreshToken" + refreshToken);//****not needed because we only use accesstoken to connect to our api*/
// return cb(null, profile);
// }
// ));
app.prepare()
.then(() => {
// Load configuration and return config object
return nextAuthConfig()
})
.then(nextAuthOptions => {
// Pass Next.js App instance and NextAuth options to NextAuth
// Note We do not pass a port in nextAuthOptions, because we want to add some
// additional routes before Express starts (if you do pass a port, NextAuth
// tells NextApp to handle default routing and starts Express automatically).
return nextAuth(app, nextAuthOptions)
})
.then(nextAuthOptions => {
const express = nextAuthOptions.express
const expressApp = nextAuthOptions.expressApp
// Add admin routes
routes.admin(expressApp)
// Add account management route - reuses functions defined for NextAuth
routes.account(expressApp, nextAuthOptions.functions)
const expressApp = express()
expressApp.use('/', express.static(__dirname + '/.next/'))
var jsonParser = bodyParser.json()
expressApp.post('/feedback', jsonParser, (req, res) => {
let message = req.body.message;
let nom = req.body.nom;
let prenom = req.body.prenom;
let tel = req.body.tel;
let mail = req.body.mail;
let recaptchaValue = req.body.recaptchaValue;
console.log(message);
if (message === '') {
res.status(400).json({
errors: { global: "Message is empty." }
}); return;
}
if (mail === '') {
res.status(400).json({
errors: { global: "email is empty." }
}); return;
}
utils.verifyCaptcha(recaptchaValue)
.then(
respo => {
if (respo.data.success == true) {
try {
mailer.sendEmail(message, nom, prenom, tel, mail);
res.status(200).send({})
} catch (err) {
res.status(500).send({ message: "Send mail fail." })
}
}
else
res.status(500).send({ message: "Captcha invalid." })
})
.catch(() =>
res.status(403).send({ message: "Captcha verification impossible." })
)
});
expressApp.post('/push/message', jsonParser, (req, res) => {
let message = req.body.payload;
if (message === '') {
res.status(400).json({
errors: { global: "Message is empty." }
}); return;
}
else {
getAccessToken().then((googleAccessToken) => {
utils.setAuthorizationHeader(googleAccessToken);
// console.log(message);
axios.post('https://fcm.googleapis.com/v1/projects/edeclicpwa/messages:send',
message
).then(r => {
console.log("message : "); console.log(r)
res.status(200).send({ message: "message sent to firebase" })
}
)
.catch(err => console.log("first catch" + err)
//res.status(500).send({ message: "Push error" })
)
}).catch(err => console.log("second catch" + err)
//err => res.status(500).send({ message: "Push error" + err })
)
}
})
expressApp.get('/push/info/:token', (req, res) => {
var token = req.params.token
if (token === '') {
res.status(498).json({
errors: { global: "No token specified." }
}); return;
}
var url = 'https://iid.googleapis.com/iid/info/' + token + '?details=true'
utils.setAuthorizationHeaderForIID(process.env.FIREBASE_API_KEY)
axios.get(url).then(r => {
if (!!r.data.rel && !!r.data.rel.topics) {
res.status(200).send({ topics: r.data.rel.topics })
}
else
res.status(200).send({ topics: null })
}).catch(err => {
console.log(err)
res.status(500).send({ message: "Push error" })
})
})
expressApp.post('/push/subscribe', jsonParser, (req, res) => {
let message = req.body;
if (message === '') {
res.status(400).json({
errors: { global: "Message is empty." }
}); return;
}
else {
if (message.topic === '' || message.token === '') {
res.status(400).json({
errors: { global: "Message isnot well formed." }
}); return;
}
var url = 'https://iid.googleapis.com/iid/v1/' + message.token + '/rel/topics/' + message.topic
utils.setAuthorizationHeaderForIID(process.env.FIREBASE_API_KEY)
axios.post(url)
.then(r => {
res.status(200).send({ message: "message sent to firebase" })
})
.catch(err => console.log(err))
}
})
expressApp.post('/push/unsubscribe', jsonParser, (req, res) => {
let message = req.body;
if (message === '') {
res.status(400).json({
errors: { global: "Message is empty." }
}); return;
}
else {
if (message.topic === '' || message.token === '') {
res.status(400).json({
errors: { global: "Message is not well formed." }
}); return;
}
message.topic = '/topics/' + message.topic
var payload = { to: message.topic, registration_tokens: [message.token] }
var url = 'https://iid.googleapis.com/iid/v1:batchRemove'
utils.setAuthorizationHeaderForIID(process.env.FIREBASE_API_KEY)
axios.post(url, payload
).then(r => {
res.status(200).send({ message: "message sent to firebase" })
}
)
.catch(err => console.log(err)
)
}
})
//https://us-central1-edeclicpwa.cloudfunctions.net/pushMessageToAllSubscribers
expressApp.post('/push/allsubscribers', jsonParser, (req, res) => {
let message = req.body.payload;
if (message === '') {
res.status(400).json({
errors: { global: "Message is empty." }
}); return;
}
else {
utils.setAuthorizationHeader(process.env.FIREBASE_API_KEY)
// console.log(message);
axios.post('https://us-central1-edeclicpwa.cloudfunctions.net/pushMessageToAllSubscribers',
{ payload: message }
).then(r => {
res.status(200).send({ message: "message sent to firebase" })
}
)
.catch(err => console.log(err))
}
})
expressApp.get('/static/js/*.js', (req, res) => {
const parsedUrl = parse(req.url, true);
const { pathname } = parsedUrl;
const filePath = join(__dirname, '.next', pathname);
@@ -278,14 +31,12 @@ app.prepare()
})
expressApp.get('/manifest.json', (req, res) => {
const parsedUrl = parse(req.url, true);
const { pathname } = parsedUrl;
const filePath = join(__dirname, '/static/', pathname);
app.serveStatic(req, res, filePath);
})
expressApp.get('*', (req, res) => {
return handle(req, res)
})
@@ -293,7 +44,6 @@ app.prepare()
expressApp.listen(process.env.PORT, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:' + process.env.PORT)
console.log('> access Token googleapi >' + getAccessToken())
})
})
.catch((ex) => {
-12
View File
@@ -1,12 +0,0 @@
{
"type": "service_account",
"project_id": "edeclicpwa",
"private_key_id": "aef80b8a8d6f9f10aa0ef0cffd46992399fac45d",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZuQVADJXtA7Hb\n2R36sZxD3fw5QOmLwvAGnIXtexf/hppz6J1mUDsbNDLF3FxlgzdKQguGE0+q6QI+\nIWEMYPVQPot8Tbs8rcL06EKkhLR3KHRI6hWRyFNnWKnig7xVD5LUNVjgtHNIx9TC\ndf+/F6kSsz7w7cjHZEFZ5k7eAeD3M15EhtGCCyJ9DM3MO/pKzW4lah1jTGeV1ZYS\nimuBSbSX9Z6k6l3dUayMYaARYjZkBu1syhUGs/KoeqS+o+bR1OJX/JiaYe0lWWAg\nuL4315c2SkuXeHX2y9BvxbY2TGfhAPuNFPSsJgt0hXE6QeSIjFZXePmWIq28tNhH\n6OR/EJFzAgMBAAECggEAZ23/YgF4mct3C19V4BnPB+ilYReGuzfkqediskIXUPMD\nXcvkNk4n/hDqi9dW53yR4AuHCO8UmjcuMxDNV0GaWEAWKHuO1tEfPBQ4UIqgZrkH\noPnfPE2j3YUf03VMm0YWNQyQx9LBr5IK70R6NbAKSFFxtafoiVyFtSz1S38t/ZB/\nqigCvK48kG6vZCyAozuKVbkLKorY8THZRbK24Mwp4rnAxRHUxG9pXCGe1/j1FvSP\nPRGPptvRSLP2kRPZXELKga7/yCPIOqT2jEoWiXAdfU2gyED4jEhby+ryBHuTsxpo\nJiaGtbUWMKjOEUOWekgTMvtgQD4T5WP5zx2AAZ5K4QKBgQDz7075SYWTy98MZSC2\nMVF6Ei2mGDMxLDJqpTnc3Oi7+eOXI8uTo5SqgaBHfM/uTywcipqSUQTseDN7C1wu\nWOb6r0shK6FfDP1GQc9l0mmNuIZ8JLkc6nB8Es5wwd4vLRhuSsy6pSz/wDovC3Q2\n5se/B7ir+kYeFlhIMAjN91rukwKBgQDkfdDpAociVjFpcjly5Zf60uL/BzIZOdpn\n1Iq/kAxFr2kuZzGDpwUgXvSU4nELivrsf452x3+R78C2SWwil9IRrD3DAYJzT3qP\nb6l4EuKuHRPtVJBqIPWaMiT6Ov6aAQpGYh5qZjFeHSIDBzXHd/OBbSzisE3zTOqm\nED3/eQq9oQKBgAD/XYdPcahlEQhv8W5NTVP+dwlS2AK/d4VQH6hzjtAV+YRItTBp\nXtZDqXAhZohG8ps7Rd6LTkXZR/yc00etPWSRCvGbyBEncHG1GzADaEMYGhSv4cHo\ng4U+XnG/mTUALjVlQOkSe9if5J0EovkGgJKbaXnqkBbXaI0DBUYyWMDZAoGBAMND\nGfLmbCFV02gvaxTbTCPXcJFMzu1r2U99/Qxzx2kN3C8BlPjTFLhzLUTGtqCMpp7Q\n6yhqmIRYhTHCURzG7YiYzzcE5TwxoaVOYV7xlLICu3LIH5nyjLC3RY5qOAXX+bXo\nR+HZbzrkXpqD4NuTkI78g609yX+wLZ64pqLaB+nBAoGATMflJNxgyWtY6b2yFYgm\nCOhMecJKMS2Rgmdl3FQGxAMPMIy18URW/t99LmbUhqQGsdFPDTcxR6V4ajWqltrj\nLkbrEtSHp7xFAQD2anUeEwxtW8PpICwhgGXJUSjXU8+2A20CaWcTwH5USnNp8kp/\n9402JWBkoe+kodILFHZ363M=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-hffwl@edeclicpwa.iam.gserviceaccount.com",
"client_id": "100436348289421185617",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-hffwl%40edeclicpwa.iam.gserviceaccount.com"
}
-57
View File
@@ -1,57 +0,0 @@
importScripts("https://www.gstatic.com/firebasejs/5.5.8/firebase-app.js")
importScripts("https://www.gstatic.com/firebasejs/5.5.8/firebase-messaging.js")
if (firebase.messaging.isSupported()) {
// Initialize Firebase
var config = {
apiKey: "AIzaSyArZab_4hUDgksQ4Dqdwv970JyRSlbGaKY",
authDomain: "edeclicpwa.firebaseapp.com",
databaseURL: "https://edeclicpwa.firebaseio.com",
projectId: "edeclicpwa",
storageBucket: "edeclicpwa.appspot.com",
messagingSenderId: "130215947970"
};
firebase.initializeApp(config);
var messaging = firebase.messaging();
/**
* Here is is the code snippet to initialize Firebase Messaging in the Service
* Worker when your app is not hosted on Firebase Hosting.
// [START initialize_firebase_in_sw]
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'YOUR-SENDER-ID'
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
// [END initialize_firebase_in_sw]
**/
// If you would like to customize notifications that are received in the
// background (Web app is closed or not in browser focus) then you should
// implement this optional method.
// [START background_handler]
messaging.setBackgroundMessageHandler(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
var notificationTitle = 'Background Message Title';
var notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
// [END background_handler]
}
+6 -57
View File
@@ -1,72 +1,21 @@
{
"name": "E-Declic PWA",
"short_name": "E-Declic",
"name": "AG2L SAV",
"short_name": "AG2L",
"lang": "fr",
"icons": [
{
"src": "/static/images/icons/e-declicIco16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco32x32.png",
"src": "/static/images/icons/AG2L32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco57x57.png",
"sizes": "57x57",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco114x114.png",
"sizes": "114x114",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco180x180.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/static/images/icons/e-declicIco512x512.png",
"sizes": "512x512",
"src": "/static/images/icons/AG2L270x270.png",
"sizes": "270x270",
"type": "image/png"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#21b8cc",
"gcm_sender_id":"103953800507"
"theme_color": "#21b8cc"
}
+9
View File
@@ -0,0 +1,9 @@
import axios from "axios";
export default (token = null) => {console.log(token)
if (token) {
axios.defaults.headers.common.authorization = `Bearer ${token}`;
} else {
delete axios.defaults.headers.common.authorization;
}
};
+25 -1
View File
@@ -873,6 +873,11 @@
resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.11.tgz#32a853fad9974cbbc9fc766ec5699a18b22ccee6"
integrity sha512-WyMXDxk/WZ+f2lOCeEvDWUce2f5Kk2sNfvArK8f+PlUnzFdy/MBzLXrmbMgyZXP7GP4ooUxYV8Sdmoh1hGk1Uw==
"@mapbox/point-geometry@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2"
integrity sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI=
"@material-ui/core@^3.2.0":
version "3.9.0"
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-3.9.0.tgz#7e74cf1979ee65f9fd388145764b3e58f48da6c6"
@@ -3943,6 +3948,11 @@ event-stream@=3.3.4:
stream-combiner "~0.0.4"
through "~2.3.1"
eventemitter3@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=
events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
@@ -4874,6 +4884,15 @@ google-auth-library@^2.0.0:
lru-cache "^5.0.0"
semver "^5.5.0"
google-map-react@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/google-map-react/-/google-map-react-1.1.2.tgz#9ec9cbee88bf043dd24ce6c5953663e636461a71"
integrity sha512-uHyhLW0db1nXMWYfazTnm6GiMzITUZ+OldFt+u0lLsC/ndSNVZWY0Hn2v8s2+vnpg88ZU0cAk7nRK5UXq0ahaA==
dependencies:
"@mapbox/point-geometry" "^0.1.0"
eventemitter3 "^1.1.0"
scriptjs "^2.5.7"
google-p12-pem@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-1.0.3.tgz#3d8acc140573339a5bca7b2f6a4b206bbea6d8d7"
@@ -8970,7 +8989,7 @@ readdirp@^2.0.0:
micromatch "^3.1.10"
readable-stream "^2.0.2"
"recompose@0.28.0 - 0.30.0":
"recompose@0.28.0 - 0.30.0", recompose@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0"
integrity sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==
@@ -9425,6 +9444,11 @@ schema-utils@^1.0.0:
ajv-errors "^1.0.0"
ajv-keywords "^3.1.0"
scriptjs@^2.5.7:
version "2.5.9"
resolved "https://registry.yarnpkg.com/scriptjs/-/scriptjs-2.5.9.tgz#343915cd2ec2ed9bfdde2b9875cd28f59394b35f"
integrity sha512-qGVDoreyYiP1pkQnbnFAUIS5AjenNwwQBdl7zeos9etl+hYKWahjRTfzAZZYBv5xNHx7vNKCmaLDQZ6Fr2AEXg==
seek-bzip@^1.0.3, seek-bzip@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"