stall
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Router from 'next/router'
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import Card from '@material-ui/core/Card';
|
||||||
|
import CardContent from '@material-ui/core/CardContent';
|
||||||
|
import CardHeader from '@material-ui/core/CardHeader';
|
||||||
|
import CardMedia from '@material-ui/core/CardMedia';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
card: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flex: '1 0 auto',
|
||||||
|
},
|
||||||
|
cover: {
|
||||||
|
width: 151,
|
||||||
|
height: 151,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class CompanyRowComponent extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes, theme, rowData } = this.props;
|
||||||
|
|
||||||
|
const imgDefaultUrl = "https://www.talentstube.com/img/default_picture_company.svg"
|
||||||
|
|
||||||
|
const imgUrl = (rowData.presentation_video && rowData.presentation_video.thumbnails[0] && rowData.presentation_video.thumbnails[0].sizes[4]) ? rowData.presentation_video.thumbnails[0].sizes[4].link : imgDefaultUrl
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card onClick={() => Router.push(`/Company?id=${rowData.id}`)}>
|
||||||
|
<CardHeader />
|
||||||
|
<CardContent>
|
||||||
|
<CardMedia
|
||||||
|
className={classes.cover}
|
||||||
|
image={imgUrl}
|
||||||
|
title={rowData.presentation_video&&rowData.presentation_video.title}
|
||||||
|
/>
|
||||||
|
<Typography gutterBottom variant="headline" component="h3"
|
||||||
|
className={classes.details}>
|
||||||
|
{rowData.brand_name}
|
||||||
|
</Typography>
|
||||||
|
<Typography gutterBottom variant="headline" component="h4"
|
||||||
|
className={classes.details}>
|
||||||
|
{rowData.website}
|
||||||
|
</Typography>
|
||||||
|
<Typography gutterBottom variant="headline" component="h5"
|
||||||
|
className={classes.details}>
|
||||||
|
{rowData.description}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompanyRowComponent.propTypes = {
|
||||||
|
classes: PropTypes.object.isRequired,
|
||||||
|
theme: PropTypes.object.isRequired,
|
||||||
|
rowData: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles, { withTheme: true })(CompanyRowComponent);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
|
import BottomToolbar from './BottomToolbar';
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
position: 'relative',
|
||||||
|
height: '10vh'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function Footer(props) {
|
||||||
|
const { classes } = props;
|
||||||
|
return (
|
||||||
|
<BottomToolbar/>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(Footer)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
|
import PSAB from './PrimarySearchAppBar'
|
||||||
|
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
position: 'relative',
|
||||||
|
height: '10vh'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function Header(props) {
|
||||||
|
return (
|
||||||
|
<PSAB backLink={props.backLink}/>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
Header.defaultProps = {
|
||||||
|
backLink : ""
|
||||||
|
}
|
||||||
|
Header.propTypes = {
|
||||||
|
backLink : PropTypes.string
|
||||||
|
}
|
||||||
|
export default withStyles(styles)(Header)
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Router from 'next/router'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { Container, Row, Col, Nav, NavItem, Button, Form, NavLink, Collapse,
|
||||||
|
Navbar, NavbarToggler, NavbarBrand, Modal, ModalHeader, ModalBody,
|
||||||
|
ModalFooter, ListGroup, ListGroupItem } from 'reactstrap'
|
||||||
|
import Signin from './signin'
|
||||||
|
import { NextAuth } from 'next-auth/client'
|
||||||
|
import Cookies from 'universal-cookie'
|
||||||
|
|
||||||
|
export default class extends React.Component {
|
||||||
|
|
||||||
|
static propTypes() {
|
||||||
|
return {
|
||||||
|
session: React.PropTypes.object.isRequired,
|
||||||
|
providers: React.PropTypes.object.isRequired,
|
||||||
|
children: React.PropTypes.object.isRequired,
|
||||||
|
fluid: React.PropTypes.boolean,
|
||||||
|
navmenu: React.PropTypes.boolean,
|
||||||
|
signinBtn: React.PropTypes.boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
navOpen: false,
|
||||||
|
modal: false,
|
||||||
|
providers: null
|
||||||
|
}
|
||||||
|
this.toggleModal = this.toggleModal.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleModal(e) {
|
||||||
|
if (e) e.preventDefault()
|
||||||
|
|
||||||
|
// Save current URL so user is redirected back here after signing in
|
||||||
|
if (this.state.modal !== true) {
|
||||||
|
const cookies = new Cookies()
|
||||||
|
cookies.set('redirect_url', window.location.pathname, { path: '/' })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
providers: this.state.providers || await NextAuth.providers(),
|
||||||
|
modal: !this.state.modal
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
|
||||||
|
<MainBody navmenu={this.props.navmenu} fluid={this.props.fluid} container={this.props.container}>
|
||||||
|
{this.props.children}
|
||||||
|
</MainBody>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MainBody extends React.Component {
|
||||||
|
render() {
|
||||||
|
if (this.props.container === false) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{this.props.children}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
} else if (this.props.navmenu === false) {
|
||||||
|
return (
|
||||||
|
<Container fluid={this.props.fluid} style={{marginTop: '1em'}}>
|
||||||
|
{this.props.children}
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Container fluid={this.props.fluid} style={{marginTop: '1em'}}>
|
||||||
|
<Row>
|
||||||
|
<Col xs="12" md="9" lg="10">
|
||||||
|
{this.props.children}
|
||||||
|
</Col>
|
||||||
|
<Col xs="12" md="3" lg="2" style={{paddingTop: '1em'}}>
|
||||||
|
<h5 className="text-muted text-uppercase">Examples</h5>
|
||||||
|
<ListGroup>
|
||||||
|
<ListGroupItem>
|
||||||
|
<Link prefetch href="/examples/authentication"><a href="/examples/authentication" className="d-block">Auth</a></Link>
|
||||||
|
</ListGroupItem>
|
||||||
|
<ListGroupItem>
|
||||||
|
<Link prefetch href="/examples/async"><a href="/examples/async" className="d-block">Async</a></Link>
|
||||||
|
</ListGroupItem>
|
||||||
|
<ListGroupItem>
|
||||||
|
<Link prefetch href="/examples/layout"><a href="/examples/layout" className="d-block">Layout</a></Link>
|
||||||
|
</ListGroupItem>
|
||||||
|
<ListGroupItem>
|
||||||
|
<Link prefetch href="/examples/routing"><a href="/examples/routing" className="d-block">Routing</a></Link>
|
||||||
|
</ListGroupItem>
|
||||||
|
<ListGroupItem>
|
||||||
|
<Link prefetch href="/examples/styling"><a href="/examples/styling" className="d-block">Styling</a></Link>
|
||||||
|
</ListGroupItem>
|
||||||
|
</ListGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserMenu extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.handleSignoutSubmit = this.handleSignoutSubmit.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleSignoutSubmit(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
// Save current URL so user is redirected back here after signing out
|
||||||
|
const cookies = new Cookies()
|
||||||
|
cookies.set('redirect_url', window.location.pathname, { path: '/' })
|
||||||
|
|
||||||
|
await NextAuth.signout()
|
||||||
|
Router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.session && this.props.session.user) {
|
||||||
|
// If signed in display user dropdown menu
|
||||||
|
const session = this.props.session
|
||||||
|
return (
|
||||||
|
<Nav className="ml-auto" navbar>
|
||||||
|
{/*<!-- Uses .nojs-dropdown CSS to for a dropdown that works without client side JavaScript ->*/}
|
||||||
|
<div tabIndex="2" className="dropdown nojs-dropdown">
|
||||||
|
<div className="nav-item">
|
||||||
|
<span className="dropdown-toggle nav-link d-none d-md-block">
|
||||||
|
<span className="icon ion-md-contact" style={{fontSize: '2em', position: 'absolute', top: -5, left: -25}}></span>
|
||||||
|
</span>
|
||||||
|
<span className="dropdown-toggle nav-link d-block d-md-none">
|
||||||
|
<span className="icon ion-md-contact mr-2"></span>
|
||||||
|
{session.user.name || session.user.email}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="dropdown-menu">
|
||||||
|
<Link prefetch href="/account">
|
||||||
|
<a href="/account" className="dropdown-item"><span className="icon ion-md-person mr-1"></span> Your Account</a>
|
||||||
|
</Link>
|
||||||
|
<AdminMenuItem {...this.props}/>
|
||||||
|
<div className="dropdown-divider d-none d-md-block"/>
|
||||||
|
<div className="dropdown-item p-0">
|
||||||
|
<Form id="signout" method="post" action="/auth/signout" onSubmit={this.handleSignoutSubmit}>
|
||||||
|
<input name="_csrf" type="hidden" value={this.props.session.csrfToken}/>
|
||||||
|
<Button type="submit" block className="pl-4 rounded-0 text-left dropdown-item"><span className="icon ion-md-log-out mr-1"></span> Sign out</Button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Nav>
|
||||||
|
)
|
||||||
|
} if (this.props.signinBtn === false) {
|
||||||
|
// If not signed in, don't display sign in button if disabled
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
// If not signed in, display sign in button
|
||||||
|
return (
|
||||||
|
<Nav className="ml-auto" navbar>
|
||||||
|
<NavItem>
|
||||||
|
{/**
|
||||||
|
* @TODO Add support for passing current URL path as redirect URL
|
||||||
|
* so that users without JavaScript are also redirected to the page
|
||||||
|
* they were on before they signed in.
|
||||||
|
**/}
|
||||||
|
<a href="/auth?redirect=/" className="btn btn-outline-primary" onClick={this.props.toggleModal}><span className="icon ion-md-log-in mr-1"></span> Sign up / Sign in</a>
|
||||||
|
</NavItem>
|
||||||
|
</Nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminMenuItem extends React.Component {
|
||||||
|
render() {
|
||||||
|
if (this.props.session.user && this.props.session.user.admin === true) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Link prefetch href="/admin">
|
||||||
|
<a href="/admin" className="dropdown-item"><span className="icon ion-md-settings mr-1"></span> Admin</a>
|
||||||
|
</Link>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return(<div/>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SigninModal extends React.Component {
|
||||||
|
render() {
|
||||||
|
if (this.props.providers === null) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={this.props.modal} toggle={this.props.toggleModal} style={{maxWidth: 700}}>
|
||||||
|
<ModalHeader>Sign up / Sign in</ModalHeader>
|
||||||
|
<ModalBody style={{padding: '1em 2em'}}>
|
||||||
|
<Signin session={this.props.session} providers={this.props.providers}/>
|
||||||
|
</ModalBody>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default class extends React.Component {
|
||||||
|
render() {
|
||||||
|
if (this.props.fullscreen) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<style jsx global>{`
|
||||||
|
.circle-loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 50%;
|
||||||
|
z-index: 100;
|
||||||
|
text-align: center;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-loader .circle {
|
||||||
|
fill: transparent;
|
||||||
|
stroke: rgba(0,0,0,0.2);
|
||||||
|
stroke-width: 4px;
|
||||||
|
animation: dash 2s ease infinite, rotate 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash {
|
||||||
|
0% {
|
||||||
|
stroke-dasharray: 1,95;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
stroke-dasharray: 85,95;
|
||||||
|
stroke-dashoffset: -25;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dasharray: 85,95;
|
||||||
|
stroke-dashoffset: -93;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
0% {transform: rotate(0deg); }
|
||||||
|
100% {transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<span className="circle-loader">
|
||||||
|
<svg className="circle" width="60" height="60" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="30" cy="30" r="15"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<style jsx global>{`
|
||||||
|
.circle-loader {
|
||||||
|
display: block;
|
||||||
|
text-center;
|
||||||
|
padding-left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-loader .circle {
|
||||||
|
position: relative;
|
||||||
|
left: -30px;
|
||||||
|
fill: transparent;
|
||||||
|
stroke: rgba(0,0,0,0.2);
|
||||||
|
stroke-width: 4px;
|
||||||
|
animation: dash 2s ease infinite, rotate 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash {
|
||||||
|
0% {
|
||||||
|
stroke-dasharray: 1,95;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
stroke-dasharray: 85,95;
|
||||||
|
stroke-dashoffset: -25;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dasharray: 85,95;
|
||||||
|
stroke-dashoffset: -93;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
0% {transform: rotate(0deg); }
|
||||||
|
100% {transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<span className="circle-loader">
|
||||||
|
<svg className="circle" width="60" height="60" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="30" cy="30" r="15"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Layout from './layoutAuth'
|
||||||
|
import { NextAuth } from 'next-auth/client'
|
||||||
|
|
||||||
|
export default class extends React.Component {
|
||||||
|
|
||||||
|
static async getInitialProps({req}) {
|
||||||
|
return {
|
||||||
|
session: await NextAuth.init({req}),// Add this.props.session to all pages
|
||||||
|
lang: 'en'// Add a lang property for accessibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adminAcccessOnly() {
|
||||||
|
return (
|
||||||
|
<Layout {...this.props} navmenu={false}>
|
||||||
|
<div className="text-center pt-5 pb-5">
|
||||||
|
<h1 className="display-4 mb-5">Access Denied</h1>
|
||||||
|
<p className="lead">You must be signed in as an administrator to access this page.</p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Router from 'next/router'
|
||||||
|
import { Row, Col, Form, Input, Label, Button } from 'reactstrap'
|
||||||
|
import Cookies from 'universal-cookie'
|
||||||
|
import { NextAuth } from 'next-auth/client'
|
||||||
|
|
||||||
|
export default class extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
email: '',
|
||||||
|
session: this.props.session,
|
||||||
|
providers: this.props.providers,
|
||||||
|
submitting: false
|
||||||
|
}
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this)
|
||||||
|
this.handleEmailChange = this.handleEmailChange.bind(this)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEmailChange(event) {
|
||||||
|
this.setState({
|
||||||
|
email: event.target.value.trim()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
if (!this.state.email) return
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
submitting: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Save current URL so user is redirected back here after signing in
|
||||||
|
const cookies = new Cookies()
|
||||||
|
cookies.set('redirect_url', window.location.pathname, { path: '/' })
|
||||||
|
|
||||||
|
NextAuth.signin(this.state.email)
|
||||||
|
.then(() => {
|
||||||
|
Router.push(`/auth/check-email?email=${this.state.email}`)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Router.push(`/auth/error?action=signin&type=email&email=${this.state.email}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.session.user) {
|
||||||
|
return(<div/>)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<p className="text-center" style={{marginTop: 10, marginBottom: 30}}>{`If you don't have an account, one will be created when you sign in.`}</p>
|
||||||
|
<Row>
|
||||||
|
<Col xs={12} md={6}>
|
||||||
|
<SignInButtons providers={this.props.providers}/>
|
||||||
|
</Col>
|
||||||
|
<Col xs={12} md={6}>
|
||||||
|
<Form id="signin" method="post" action="/auth/email/signin" onSubmit={this.handleSubmit}>
|
||||||
|
<Input name="_csrf" type="hidden" value={this.state.session.csrfToken}/>
|
||||||
|
<p>
|
||||||
|
<Label htmlFor="email">Email address</Label><br/>
|
||||||
|
<Input name="email" disabled={this.state.submitting} type="text" placeholder="j.smith@example.com" id="email" className="form-control" value={this.state.email} onChange={this.handleEmailChange}/>
|
||||||
|
</p>
|
||||||
|
<p className="text-right">
|
||||||
|
<Button id="submitButton" disabled={this.state.submitting} outline color="dark" type="submit">
|
||||||
|
{this.state.submitting === true && <span className="icon icon-spin ion-md-refresh mr-2"/>}
|
||||||
|
Sign in with email
|
||||||
|
</Button>
|
||||||
|
</p>
|
||||||
|
</Form>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignInButtons extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{
|
||||||
|
Object.keys(this.props.providers).map((provider, i) => {
|
||||||
|
if (!this.props.providers[provider].signin) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p key={i}>
|
||||||
|
<a className="btn btn-block btn-outline-secondary" href={this.props.providers[provider].signin}>
|
||||||
|
Sign in with {provider}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,5 +49,40 @@ export default {
|
|||||||
return show.data;
|
return show.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
companies:{
|
||||||
|
fetchCompany: async (id) => {
|
||||||
|
const res = await fetch(`http://localhost/app_dev.php/api/companies/${id}`)
|
||||||
|
const company = await res.json()
|
||||||
|
|
||||||
|
return { company: company.data }
|
||||||
|
},
|
||||||
|
fetchCompanies: async (limit = "", order = "", keyword = "", offset = 1) => {
|
||||||
|
var obj = {
|
||||||
|
method: 'GET',
|
||||||
|
//mode : 'no-cors',
|
||||||
|
headers: {
|
||||||
|
// 'Access-Control-Request-Headers': 'Authorization',
|
||||||
|
// 'Authorization': 'Basic amFzcGVyYWRtaW46amFzcGVyYWRtaW4=',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Origin': ''
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = `http://localhost/app_dev.php/api/companies?offset=${offset}`;
|
||||||
|
if (limit != "") {
|
||||||
|
url = url + `&limit=${limit}`;
|
||||||
|
}
|
||||||
|
if (order != "") {
|
||||||
|
url = url + `&order=${order}`;
|
||||||
|
}
|
||||||
|
if (keyword != "") {
|
||||||
|
url = url + `&keyword=${keyword}`;
|
||||||
|
}
|
||||||
|
const res = await fetch(url, obj);
|
||||||
|
const show = await res.json();
|
||||||
|
return show.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
// port: process.env.PORT || 3000,
|
||||||
|
// Secret used to encrypt session data on the server.
|
||||||
|
sessionSecret: 'fkjpeozr',
|
||||||
|
// 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,
|
||||||
|
sessionResave : true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
/**
|
||||||
|
* 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: process.env.EMAIL_SECURE || true,
|
||||||
|
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
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// Email Sign In
|
||||||
|
//
|
||||||
|
// Accounts are created automatically, as when signing in via oAuth.
|
||||||
|
// Users are sent one-time use sign in tokens in links. This avoids
|
||||||
|
// storing user supplied passwords anywhere, preventing password re-use.
|
||||||
|
//
|
||||||
|
// To disable this option, do not set sendSignInEmail (or set it to null).
|
||||||
|
sendSignInEmail: ({email, url, req}) => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// next.config.js
|
// next.config.js
|
||||||
const withPlugins = require('next-compose-plugins');
|
const withPlugins = require('next-compose-plugins');
|
||||||
const optimizedImages = require('next-optimized-images');
|
const optimizedImages = require('next-optimized-images');
|
||||||
|
const webpack = require("webpack");
|
||||||
|
|
||||||
module.exports = withPlugins([
|
module.exports = withPlugins([
|
||||||
[optimizedImages, {
|
[optimizedImages, {
|
||||||
@@ -8,5 +9,44 @@ module.exports = withPlugins([
|
|||||||
}],
|
}],
|
||||||
|
|
||||||
// your other plugins here
|
// your other plugins here
|
||||||
|
// {
|
||||||
|
// webpack: (config, { isServer }) => {
|
||||||
|
|
||||||
|
// // config.module.noParse = /jpg/;
|
||||||
|
|
||||||
|
// // config.module.rules.push(
|
||||||
|
// // {
|
||||||
|
// // test: /\.js$/,
|
||||||
|
// // loader: 'babel-loader',
|
||||||
|
// // exclude: /node_modules/,
|
||||||
|
|
||||||
|
// // },
|
||||||
|
// // {
|
||||||
|
// // test: /\.css$/,
|
||||||
|
// // loader: 'style!css'
|
||||||
|
// // },
|
||||||
|
// // {
|
||||||
|
// // test: /\.html$/,
|
||||||
|
// // loader: 'raw'
|
||||||
|
// // }
|
||||||
|
// // )
|
||||||
|
|
||||||
|
// // const videojsPlugin = new webpack.ProvidePlugin({
|
||||||
|
// // videojs: "video.js/dist/video.cjs.js",
|
||||||
|
// // RecordRTC: "recordrtc",
|
||||||
|
// // MediaStreamRecorder: ["recordrtc", "MediaStreamRecorder"]
|
||||||
|
// // });
|
||||||
|
// // const videojsAlias = {
|
||||||
|
// // videojs: "video.js",
|
||||||
|
// // WaveSurfer: "wavesurfer.js",
|
||||||
|
// // RecordRTC: "recordrtc"
|
||||||
|
// // };
|
||||||
|
|
||||||
|
|
||||||
|
// // config.resolve.alias = { ...config.resolve.alias, ...videojsAlias };
|
||||||
|
// // config.plugins.push(videojsPlugin);
|
||||||
|
|
||||||
|
// return config
|
||||||
|
// }
|
||||||
|
|
||||||
]);
|
]);
|
||||||
+21
-3
@@ -2,29 +2,47 @@
|
|||||||
"name": "nextjs",
|
"name": "nextjs",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"resolutions": {
|
||||||
|
"terser": "3.14.1"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "latest",
|
"@material-ui/core": "latest",
|
||||||
"@material-ui/icons": "^3.0.1",
|
"@material-ui/icons": "^3.0.1",
|
||||||
|
"connect-mongo": "^2.0.1",
|
||||||
|
"cross-env": "^5.2.0",
|
||||||
|
"dotenv": "^6.0.0",
|
||||||
"eslint": "^5.6.0",
|
"eslint": "^5.6.0",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"griddle-react": "^1.13.1",
|
"griddle-react": "^1.13.1",
|
||||||
"isomorphic-unfetch": "^3.0.0",
|
"isomorphic-unfetch": "^3.0.0",
|
||||||
"jss": "latest",
|
"jss": "latest",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"next": "latest",
|
"mongodb": "^3.1.6",
|
||||||
|
"nedb": "^1.8.0",
|
||||||
|
"next": "8.1.0",
|
||||||
|
"next-auth": "^1.11.0",
|
||||||
"next-compose-plugins": "^2.1.1",
|
"next-compose-plugins": "^2.1.1",
|
||||||
"next-optimized-images": "^1.4.1",
|
"next-optimized-images": "^1.4.1",
|
||||||
|
"nodemailer": "^4.6.8",
|
||||||
|
"nodemailer-direct-transport": "^3.3.2",
|
||||||
|
"nodemailer-smtp-transport": "^2.7.4",
|
||||||
|
"passport-facebook": "^2.1.1",
|
||||||
|
"passport-linkedin-oauth2": "^1.5.0",
|
||||||
"prop-types": "latest",
|
"prop-types": "latest",
|
||||||
"react": "latest",
|
"react": "latest",
|
||||||
"react-dom": "latest",
|
"react-dom": "latest",
|
||||||
"react-jss": "latest",
|
"react-jss": "latest",
|
||||||
"react-plyr": "^2.1.1",
|
"react-plyr": "^2.1.1",
|
||||||
"video.js": "^7.2.2"
|
"reactstrap": "^6.4.0",
|
||||||
|
"terser": "3.14",
|
||||||
|
"universal-cookie": "^3.0.4",
|
||||||
|
"video.js": "^7.2.2",
|
||||||
|
"videojs-record": "^3.6.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node server.js",
|
"dev": "node server.js",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "NODE_ENV=production node server.js"
|
"start": "cross-env NODE_ENV=production node server.js"
|
||||||
},
|
},
|
||||||
"eslint.packageManager": "yarn"
|
"eslint.packageManager": "yarn"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
import Grid from "../components/templates/Grid"
|
||||||
|
import LayoutGrid from "../components/templates/LayoutGrid"
|
||||||
|
import { Typography } from '@material-ui/core';
|
||||||
|
import CompanyRow from "../components/organisms/CompanyRow"
|
||||||
|
|
||||||
|
import api from '../data/api'
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
position: 'relative',
|
||||||
|
textAlign: 'center',
|
||||||
|
paddingTop: theme.spacing.unit * 20,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
class Companies extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutGrid backLink="/">
|
||||||
|
<div className={classes.container}>
|
||||||
|
<Grid CustomRowComponent={CompanyRow} dataFetcher={api.companies.fetchCompanies}>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</LayoutGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Companies.propTypes = {
|
||||||
|
classes: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(Companies);
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import fetch from 'isomorphic-unfetch'
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import Plyr from 'react-plyr';
|
||||||
|
import _ from "lodash/string";
|
||||||
|
import Ar from "lodash/array";
|
||||||
|
|
||||||
|
import api from "../data/api"
|
||||||
|
import LayoutDetail from "../components/templates/LayoutDetail";
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
position: 'relative',
|
||||||
|
textAlign: 'center',
|
||||||
|
paddingTop: theme.spacing.unit * 20,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '20vh',
|
||||||
|
},
|
||||||
|
video: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '33vh',
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
class Company extends React.Component {
|
||||||
|
onVimeoError = (err) => {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { classes, company } = this.props;
|
||||||
|
console.log(company);
|
||||||
|
return (
|
||||||
|
<LayoutDetail backLink="/Companies">
|
||||||
|
<div className={classes.video}>
|
||||||
|
<Plyr autoplay
|
||||||
|
type="vimeo" // or "vimeo"
|
||||||
|
videoId={Ar.last(_.split(company.presentation_video.cdn_url, '/'))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.container}>
|
||||||
|
<Typography gutterBottom variant="title" >
|
||||||
|
{company.brand_name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div className={classes.container}>
|
||||||
|
<Typography gutterBottom variant="subheading" >
|
||||||
|
{company.description}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
{/* TODO:list of ads */}
|
||||||
|
</LayoutDetail>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Company.getInitialProps = async function (context) {
|
||||||
|
const { id } = context.query
|
||||||
|
|
||||||
|
return api.companies.fetchCompany(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Company.propTypes = {
|
||||||
|
classes: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(Company);
|
||||||
@@ -8,6 +8,8 @@ import LayoutGrid from "../components/templates/LayoutGrid";
|
|||||||
import Grid from "../components/templates/Grid"
|
import Grid from "../components/templates/Grid"
|
||||||
import OffreRow from "../components/organisms/OffreRow"
|
import OffreRow from "../components/organisms/OffreRow"
|
||||||
|
|
||||||
|
import api from '../data/api'
|
||||||
|
|
||||||
const styles = theme => ({
|
const styles = theme => ({
|
||||||
root: {
|
root: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@@ -26,21 +28,19 @@ const styles = theme => ({
|
|||||||
class Offres extends React.Component {
|
class Offres extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes, ads } = this.props;
|
const { classes } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutGrid backLink="/">
|
<LayoutGrid backLink="/">
|
||||||
<div className={classes.container}>
|
<div className={classes.container}>
|
||||||
<Grid CustomRowComponent={OffreRow} >
|
<Grid CustomRowComponent={OffreRow} dataFetcher={api.ads.fetchAds}>
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
</LayoutGrid>
|
</LayoutGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Offres.propTypes = {
|
Offres.propTypes = {
|
||||||
classes: PropTypes.object.isRequired,
|
classes: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class MyDocument extends Document {
|
|||||||
return (
|
return (
|
||||||
<html lang="en" dir="ltr">
|
<html lang="en" dir="ltr">
|
||||||
<Head>
|
<Head>
|
||||||
<title>My page</title>
|
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
{/* Use minimum-scale=1 to enable GPU rasterization */}
|
{/* Use minimum-scale=1 to enable GPU rasterization */}
|
||||||
<meta
|
<meta
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import Router from 'next/router'
|
||||||
|
import Cookies from 'universal-cookie'
|
||||||
|
import { NextAuth } from 'next-auth/client'
|
||||||
|
import Loader from '../../components/templates/loader'
|
||||||
|
|
||||||
|
export default class extends React.Component {
|
||||||
|
|
||||||
|
static async getInitialProps({req}) {
|
||||||
|
const session = await NextAuth.init({force: true, req: req})
|
||||||
|
|
||||||
|
const cookies = new Cookies((req && req.headers.cookie) ? req.headers.cookie : null)
|
||||||
|
|
||||||
|
// If the user is signed in, we look for a redirect URL cookie and send
|
||||||
|
// them to that page, so that people signing in end up back on the page they
|
||||||
|
// were on before signing in. Defaults to '/'.
|
||||||
|
let redirectTo = '/'
|
||||||
|
if (session.user) {
|
||||||
|
// Read redirect URL to redirect to from cookies
|
||||||
|
redirectTo = cookies.get('redirect_url') || redirectTo
|
||||||
|
|
||||||
|
// Allow relative paths only - strip protocol/host/port if they exist.
|
||||||
|
redirectTo = redirectTo.replace( /^[a-zA-Z]{3,5}\:\/{2}[a-zA-Z0-9_.:-]+\//, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
session: session,
|
||||||
|
redirectTo: redirectTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
// Get latest session data after rendering on client *then* redirect.
|
||||||
|
// The ensures client state is always updated after signing in or out.
|
||||||
|
// (That's why we use a callback page)
|
||||||
|
const session = await NextAuth.init({force: true})
|
||||||
|
Router.push(this.props.redirectTo || '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// Provide a link for clients without JavaScript as a fallback.
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Head>
|
||||||
|
<meta charSet="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"/>
|
||||||
|
</Head>
|
||||||
|
<a href={this.props.redirectTo}>
|
||||||
|
<Loader fullscreen={true}/>
|
||||||
|
</a>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Router from 'next/router'
|
||||||
|
import Page from '../../components/templates/page'
|
||||||
|
import Layout from '../../components/templates/layout'
|
||||||
|
import { NextAuth } from 'next-auth/client'
|
||||||
|
|
||||||
|
export default class extends Page {
|
||||||
|
|
||||||
|
static async getInitialProps({req, res, query}) {
|
||||||
|
let props = await super.getInitialProps({req})
|
||||||
|
props.session = await NextAuth.init({force: true, req: req})
|
||||||
|
|
||||||
|
// If signed in already, instead of displaying message send to callback page
|
||||||
|
// which should redirect them to whatever page it normally sends clients to
|
||||||
|
if (props.session.user) {
|
||||||
|
if (req) {
|
||||||
|
res.redirect('/auth/callback')
|
||||||
|
} else {
|
||||||
|
Router.push('/auth/callback')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
props.email = query.email
|
||||||
|
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Layout {...this.props} navmenu={false} signinBtn={false}>
|
||||||
|
<div className="text-center pt-5 pb-5">
|
||||||
|
<h1 className="display-4">Check your email</h1>
|
||||||
|
<p className="lead">
|
||||||
|
A sign in link has been sent to { (this.props.email) ? <span className="font-weight-bold">{this.props.email}</span> : <span>your inbox</span> }.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Router from 'next/router'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { NextAuth } from 'next-auth/client'
|
||||||
|
|
||||||
|
export default class extends React.Component {
|
||||||
|
|
||||||
|
static async getInitialProps({req}) {
|
||||||
|
return {
|
||||||
|
session: await NextAuth.init({req}),
|
||||||
|
linkedAccounts: await NextAuth.linked({req}),
|
||||||
|
providers: await NextAuth.providers({req})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
session: this.props.session
|
||||||
|
}
|
||||||
|
this.handleEmailChange = this.handleEmailChange.bind(this)
|
||||||
|
this.handlePasswordChange = this.handlePasswordChange.bind(this)
|
||||||
|
this.handleSignInSubmit = this.handleSignInSubmit.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
if (this.props.session.user) {
|
||||||
|
Router.push(`/auth/`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEmailChange(event) {
|
||||||
|
this.setState({
|
||||||
|
email: event.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePasswordChange(event) {
|
||||||
|
this.setState({
|
||||||
|
password: event.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSignInSubmit(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
// An object passed NextAuth.signin will be passed to your signin() function
|
||||||
|
NextAuth.signin({
|
||||||
|
email: this.state.email,
|
||||||
|
password: this.state.password
|
||||||
|
})
|
||||||
|
.then(authenticated => {
|
||||||
|
Router.push(`/auth/callback`)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert("Authentication failed.")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.session.user) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="display-4 mt-3 mb-3">NextAuth With Credentials</h1>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-sm-6 mr-auto ml-auto">
|
||||||
|
<p>
|
||||||
|
If you need password based sign in, two factor authentication
|
||||||
|
or another sign in method, you can use a signin() function
|
||||||
|
in <strong>next-auth.functions.js</strong>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You can pass in any properties you need – e.g. username and password,
|
||||||
|
a PIN or 2FA Token – as properties of the object passed to
|
||||||
|
NextAuth.signin() in the front end and they will be passed
|
||||||
|
through to your signin() function.
|
||||||
|
</p>
|
||||||
|
<div className="card mt-3 mb-3">
|
||||||
|
<h4 className="card-header">Sign In</h4>
|
||||||
|
<div className="card-body pb-0">
|
||||||
|
<p className="text-italic text-muted text-center small">
|
||||||
|
<strong>Important!</strong> Enable the signin() function in <strong>next-auth.functions.js</strong> first.
|
||||||
|
</p>
|
||||||
|
<form id="signin" method="post" action="/auth/signin" onSubmit={this.handleSignInSubmit}>
|
||||||
|
<input name="_csrf" type="hidden" value={this.state.session.csrfToken}/>
|
||||||
|
<p>
|
||||||
|
<label htmlFor="email">Email address</label><br/>
|
||||||
|
<input name="email" type="text" placeholder="j.smith@example.com" id="email" className="form-control" value={this.state.email} onChange={this.handleEmailChange}/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label htmlFor="password">Password</label><br/>
|
||||||
|
<input name="password" type="password" placeholder="" id="password" className="form-control" value={this.state.password} onChange={this.handlePasswordChange}/>
|
||||||
|
</p>
|
||||||
|
<p className="text-right">
|
||||||
|
<button id="submitButton" type="submit" className="btn btn-outline-primary">Sign in</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-center">
|
||||||
|
<Link href="/auth"><a>Back</a></Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import Page from '../../components/templates/page'
|
||||||
|
import Layout from '../../components/templates/layoutAuth'
|
||||||
|
|
||||||
|
export default class extends Page {
|
||||||
|
|
||||||
|
static async getInitialProps({req, query}) {
|
||||||
|
let props = await super.getInitialProps({req})
|
||||||
|
props.action = query.action || null
|
||||||
|
props.type = query.type || null
|
||||||
|
props.service = query.service || null
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.action == 'signin' && this.props.type == 'oauth') {
|
||||||
|
return(
|
||||||
|
<Layout {...this.props} navmenu={false}>
|
||||||
|
<div className="text-center mb-5">
|
||||||
|
<h1 className="display-4 mt-5 mb-3">Unable to sign in</h1>
|
||||||
|
<p className="lead">An account associated with your email address already exists.</p>
|
||||||
|
<p className="lead"><Link href="/auth"><a>Sign in with email or another service</a></Link></p>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-sm-8 mr-auto ml-auto mb-5">
|
||||||
|
<div className="text-muted">
|
||||||
|
<h4 className="mb-2">Why am I seeing this?</h4>
|
||||||
|
<p className="mb-2">
|
||||||
|
It looks like you might have already signed up using another service.
|
||||||
|
</p>
|
||||||
|
<p className="mb-3">
|
||||||
|
To protect your account, if you have perviously signed up
|
||||||
|
using another service you must link accounts before you
|
||||||
|
can use a different service to sign in.
|
||||||
|
</p>
|
||||||
|
<h4 className="mb-2">How do I fix this?</h4>
|
||||||
|
<p className="mb-0">
|
||||||
|
To sign in using another service, first sign in using your email address then link accounts.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
} else if (this.props.action == 'signin' && this.props.type == 'token-invalid') {
|
||||||
|
return(
|
||||||
|
<Layout {...this.props} navmenu={false}>
|
||||||
|
<div className="text-center mb-5">
|
||||||
|
<h1 className="display-4 mt-5 mb-2">Link not valid</h1>
|
||||||
|
<p className="lead">This sign in link is no longer valid.</p>
|
||||||
|
<p className="lead"><Link href="/auth"><a>Get a new sign in link</a></Link></p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return(
|
||||||
|
<Layout {...this.props} navmenu={false}>
|
||||||
|
<div className="text-center mb-5">
|
||||||
|
<h1 className="display-4 mt-5">Error signing in</h1>
|
||||||
|
<p className="lead">An error occured while trying to sign in.</p>
|
||||||
|
<p className="lead"><Link href="/auth"><a>Sign in with email or another service</a></Link></p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,12 +95,12 @@ class Index extends React.Component {
|
|||||||
<Card className={classes.card} >
|
<Card className={classes.card} >
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
<CardMedia
|
<CardMedia
|
||||||
onClick={() => Router.push('/Entreprises')}
|
onClick={() => Router.push('/Companies')}
|
||||||
component="img"
|
component="img"
|
||||||
className={classes.media}
|
className={classes.media}
|
||||||
image={require('../images/667855903_640x360.jpg')}
|
image={require('../images/667855903_640x360.jpg')}
|
||||||
title="entreprises"
|
title="Companies"
|
||||||
/><Typography className={classes.title + ' ' + classes.white} variant="headline" >Entreprises</Typography>
|
/><Typography className={classes.title + ' ' + classes.white} variant="headline" >Companies</Typography>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
</Card>
|
</Card>
|
||||||
</Grow>
|
</Grow>
|
||||||
|
|||||||
+30
-15
@@ -1,5 +1,7 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const next = require('next')
|
const next = require('next')
|
||||||
|
const nextAuth = require('next-auth')
|
||||||
|
const nextAuthConfig = require('./next-auth.config')
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV !== 'production'
|
const dev = process.env.NODE_ENV !== 'production'
|
||||||
const app = next({ dev })
|
const app = next({ dev })
|
||||||
@@ -7,26 +9,39 @@ const handle = app.getRequestHandler()
|
|||||||
|
|
||||||
app.prepare()
|
app.prepare()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const server = express()
|
// Load configuration and return config object
|
||||||
|
return nextAuthConfig()
|
||||||
|
})
|
||||||
|
.then(nextAuthOptions => {
|
||||||
|
// Pass Next.js App instance and NextAuth options to NextAuth
|
||||||
|
return nextAuth(app, nextAuthOptions)
|
||||||
|
})
|
||||||
|
.then(nextAuthOptions => {
|
||||||
|
// Get Express and instance of Express from NextAuth
|
||||||
|
const express = nextAuthOptions.express
|
||||||
|
const expressApp = nextAuthOptions.expressApp
|
||||||
|
|
||||||
// server.get('/Offre?:id', (req, res) => {
|
expressApp.get('/Offre?:id', (req, res) => {
|
||||||
// const actualPage = '/Offre'
|
const actualPage = '/Offre'
|
||||||
// const queryParams = { id: req.params.id }
|
const queryParams = { id: req.params.id }
|
||||||
// app.render(req, res, actualPage, queryParams)
|
app.render(req, res, actualPage, queryParams)
|
||||||
// })
|
})
|
||||||
// server.get('/Offre/:id', (req, res) => {
|
|
||||||
// const actualPage = '/Offre'
|
expressApp.get('/Offre/:id', (req, res) => {
|
||||||
// const queryParams = { id: req.params.id }
|
const actualPage = '/Offre'
|
||||||
// app.render(req, res, actualPage, queryParams)
|
const queryParams = { id: req.params.id }
|
||||||
// })
|
app.render(req, res, actualPage, queryParams)
|
||||||
server.get('*', (req, res) => {
|
})
|
||||||
|
|
||||||
|
expressApp.get('*', (req, res) => {
|
||||||
return handle(req, res)
|
return handle(req, res)
|
||||||
})
|
})
|
||||||
server.listen(3000, (err) => {
|
|
||||||
|
expressApp.listen(3000, (err) => {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
console.log('> Ready on http://localhost:3000')
|
console.log('> Ready on '+process.env.SERVER_URL)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((ex) => {
|
.catch((ex) => {
|
||||||
console.error(ex.stack)
|
console.error(ex.stack)
|
||||||
|
|||||||
+1347
-658
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user