Compare commits

...

28 Commits

Author SHA1 Message Date
llaniau 0eed896aec next config 2020-08-18 10:40:41 +02:00
llaniau e8ae224414 VARIABILISATION DES URL EN DUR (CHANGEMENT DE NOM DE DOMAINE) 2020-08-17 11:28:32 +02:00
llaniau fa3dcd8fd2 suppression d'un attribut (job) pour jobposting 2020-08-13 18:14:56 +02:00
llaniau 2c0c0a2632 Mise en place Job Posting 2020-08-13 17:12:45 +02:00
llaniau 501ee57430 referencement index.js 2020-08-13 12:43:28 +02:00
llaniau 0f6ec17fef OgHead 2020-08-13 11:40:03 +02:00
llaniau 112541275b Modif Referencement 2020-08-13 11:36:41 +02:00
llaniau e08b879691 gestion adresse json webapp 2020-08-12 15:43:53 +02:00
llaniau 527351c232 video de pres Web app V2 2020-08-11 17:30:55 +02:00
llaniau 0ed8da11e1 package.json 2020-07-22 12:11:35 +02:00
llaniau f522e4389e bug 2020-07-22 11:55:33 +02:00
llaniau efb6527b9b v2.0.1 2020-07-22 08:23:55 +02:00
llaniau ba43bcddc7 mise en place modif profile 2020-07-20 14:38:00 +02:00
llaniau 3523cc95ee Design update profile et video 2020-07-16 17:10:27 +02:00
llaniau c0d32cee12 Mise en place uplaod cv papier 2020-07-15 16:05:33 +02:00
llaniau 3b6aa331db after merge 2020-07-09 15:27:59 +02:00
llaniau fe7932e5ae commit 2020-07-09 15:20:43 +02:00
llaniau 05b64ac44c Merge branch 'Saga' of https://git.e-declic.net/talents-tube/app into Saga 2020-07-09 15:18:57 +02:00
llaniau 446e13ceba Enregistrement video APP V2 2020-07-09 14:54:55 +02:00
llaniau 6570e28bbd Mise en place upload video presentation cote react/nextjs 2020-07-08 19:52:04 +02:00
llaniau d146da7ab3 package.json de V2 2020-07-07 18:00:00 +02:00
llaniau ae06e730ad edit profile web app 2020-07-06 14:08:15 +02:00
llaniau f386b93dd2 Web app V2.0.1 avec gestion du profil (step) 2020-07-06 10:37:31 +02:00
Sébastien cebdf4d1d2 Feat: test install préprod 2020-06-30 15:50:56 +02:00
Sébastien e6738a1565 Feat: test conseils 2020-06-30 15:22:41 +02:00
Sébastien ce9b8b6031 Fix: Too much space 2020-06-30 15:19:23 +02:00
Huaoe 00eac45913 liste de correctif MEP
tt-app/app/Saga There was a failure building this commit
2019-11-06 14:13:40 +01:00
Grégoire 89719a8bbc config webpush 2019-11-05 17:13:04 +01:00
80 changed files with 19849 additions and 85 deletions
+10 -9
View File
@@ -5,7 +5,7 @@
Download the example [or clone the repo](https://git.e-declic.net/talents-tube/app.git):
```bash
git clone
git clone
```
Install it and run:
@@ -27,10 +27,11 @@ https://redux-saga.js.org/docs/basics/ErrorHandling.html
is a library that aims to make application side effect asynchronous things like data fetching and impure things like accessing the browser cache
## Integration
### Naming conventions
style classes name syntax : Camel case
Try to name without too much precision
### Naming conventions
style classes name syntax : Camel case
Try to name without too much precision
### Material UI
@@ -52,16 +53,16 @@ sudo docker exec -it frontnginx.nginx bash
nginx -t && nginx -s reload
## Test
### Cypress
[Gmail-tester]
https://github.com/levz0r/gmail-tester
## Continuous Integration
### Jenkins
>in case of emergency
make docker-restart ENV=dev
adduser jenkins root
> in case of emergency
> make docker-restart ENV=dev
> adduser jenkins root
View File
+38 -2
View File
@@ -1,7 +1,14 @@
import {
UPLOAD_MEDIA_REQUEST,
UPLOAD_MEDIA_FAILURE,
UPLOAD_MEDIA_SUCCESS
UPLOAD_MEDIA_SUCCESS,
UPLOAD_MEDIA_PRES_REQUEST,
UPLOAD_MEDIA_PRES_FAILURE,
UPLOAD_MEDIA_PRES_SUCCESS,
UPLOAD_MEDIA_CV_REQUEST,
UPLOAD_MEDIA_CV_FAILURE,
UPLOAD_MEDIA_CV_SUCCESS,
} from "../types";
export const uploadMedia = payload => ({
@@ -18,6 +25,35 @@ import {
type: UPLOAD_MEDIA_SUCCESS,
res
});
export const uploadMediaPres = payload => ({
type: UPLOAD_MEDIA_PRES_REQUEST,
payload
});
export const uploadMediaPresFailure = errors => ({
type: UPLOAD_MEDIA_PRES_FAILURE,
errors
});
export const uploadMediaPresSuccess = res => ({
type: UPLOAD_MEDIA_PRES_SUCCESS,
res
});
export const uploadMediaCv = payload => ({
type: UPLOAD_MEDIA_CV_REQUEST,
payload
});
export const uploadMediaCvFailure = errors => ({
type: UPLOAD_MEDIA_CV_FAILURE,
errors
});
export const uploadMediaCvSuccess = res => ({
type: UPLOAD_MEDIA_CV_SUCCESS,
res
});
+34
View File
@@ -0,0 +1,34 @@
import React from 'react'
import { JobPostingJsonLd } from 'next-seo';
const JobPosting = props => {
return (
<JobPostingJsonLd
datePosted="2020-01-06T03:37:40Z"
description={props.job_description}
hiringOrganization={{
name: props.job_brand,
sameAs: props.job_brand_link,
}}
jobLocation={{
addressLocality: props.job_location,
}}
title={props.job_title}
validThrough="2020-01-06"
applicantLocationRequirements="FR"
/>
)
}
JobPosting.propTypes = {
// seo_title: PropTypes.string.isRequired,
// seo_description: PropTypes.string.isRequired,
// seo_url: PropTypes.string.isRequired,
// image: PropTypes.string.isRequired,
}
export default JobPosting
+11 -4
View File
@@ -6,8 +6,9 @@ const OgHead = props => {
return (
<Head>
<title key="title">{props.seo_title}</title>
<meta name="description" key="description" content={props.seo_description} />
<meta name="robots" content="noindex, nofollow"></meta>
<meta name="description" kenoindexy="description" content={props.seo_title} />
<meta name="keywords" content={props.seo_key}/>
<meta key="og:url" property="og:url" content={props.seo_url} />
<meta key="og:type" property="og:type" content="article" />
<meta key="og:title" property="og:title" content={props.seo_title} />
@@ -16,10 +17,16 @@ const OgHead = props => {
property="og:description"
content={props.seo_description}
/>
<meta name="keywords" content={props.seo_key}/>
<meta key="og:image" property="og:image" content={props.image} />
<link rel="canonical" href={"https://www.application.bzh/" + props.seo_url} />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:site" content="@TalentsTube"/>
<meta name="twitter:title" content={props.seo_title}/>
<meta name="twitter:description" content={props.seo_title}/>
<meta name="twitter:image" content={props.image}/>
<link rel="canonical" href={ props.seo_url} />
</Head>
)
}
+34 -2
View File
@@ -30,6 +30,32 @@ const useStyles = makeStyles(theme => ({
height: '100%',
},
},
},
videoPresentation: {
display: 'flex',
flexDirection: 'column',
position: 'relative',
paddingBottom: '56%', /* 16:9 58%*/
'& iframe': {
position: 'absolute',
top: 0,
left: 'calc(50% - 50vw)',
width: '100vw',
height: '100%',
},
[theme.breakpoints.up('md')]: {
display: 'flex',
flexDirection: 'column',
position: 'relative',
paddingBottom: '56.5%', /* 16:9 */
'& iframe': {
position: 'absolute',
top: 0,
left: 'calc(50% - 32vw)',
width: '64vw',
height: '100%',
},
},
}
}));
@@ -37,14 +63,20 @@ export default function VideoVimeo(props) {
const {videoId, title, options, classesOverride, autoplay} = props;
const classes = useStyles();
let className = classes.video;
if (classesOverride) {
className = classes.videoPresentation;
}
return (
<section className={classes.video +' '+classesOverride}>
<section className={className +' '+classesOverride}>
<iframe className='player'
src={"https://player.vimeo.com/video/"+ videoId + options+"?autoplay="+autoplay}
frameBorder="0"
title={title}
webkitallowfullscreen="true" mozallowfullscreen="true" allowFullScreen></iframe>
</section>
)
}
+5 -4
View File
@@ -2,14 +2,13 @@ import React, { Component, Fragment } from 'react'
import PropTypes from 'prop-types'
import { connect } from "react-redux";
import { subscribeUserToPushRequest } from '../../actions/user'
import getConfig from 'next/config'
import { urlB64ToUint8Array, payloadFromSubscription } from './Utility'
var swRegistration = null
//TODO: put in config
const applicationServerPublicKey = 'BNFyiyPiLH97Dgc0m1vXluUNaCN6y3Hkgd4YcqS2lVkP8my3tq2D5gM5g7cY6R4VxC7GljSzkLRu+k+oO+2TtjU=';
const WebPushContext = React.createContext()
function WebPushConsumer(props) {
@@ -26,6 +25,8 @@ function WebPushConsumer(props) {
</WebPushContext.Consumer>
)
}
const { publicRuntimeConfig } = getConfig()
class WebPush extends Component {
static Consumer = WebPushConsumer;
@@ -91,7 +92,7 @@ class WebPush extends Component {
if (isSubscribed) {
this.onUpdateSubscriptionOnServer(subscription)
} else {
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey)
const applicationServerKey = urlB64ToUint8Array(publicRuntimeConfig.applicationServerPublicKey)
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
@@ -0,0 +1,478 @@
import React, { useState, Component, Fragment } from 'react'
import { withStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import { Alert, AlertTitle } from '@material-ui/lab';
import Snackbar from '@material-ui/core/Snackbar';
import { connect } from 'react-redux'
import { updateUserRequest } from '../../actions/user'
import getConfig from 'next/config'
import Validator from "validator";
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
const styles = theme => ({
container: {
width: '85%',
},
form: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '24px',
paddingBottom: theme.spacing(8),
marginBottom: theme.spacing(8),
'&>div': {
marginTop: theme.spacing(1.5),
marginBottom: theme.spacing(1.5),
}
},
textField: {
width: '85%',
'&:disabled': {
color: theme.palette.secondary.dark,
},
'& label': {
color: theme.palette.primary.dark,
},
'& fieldset:hover': {
borderColor: theme.palette.primary.main + ' !important',
},
},
textFieldSmall: {
width: '32%',
'&:disabled': {
color: theme.palette.secondary.dark,
},
'& label': {
color: theme.palette.primary.dark,
},
'& fieldset:hover': {
borderColor: theme.palette.primary.main + ' !important',
},
padding: '5px',
},
});
const { publicRuntimeConfig } = getConfig();
//https://xd.adobe.com/view/ba98331a-9145-4d67-7e27-25a36e519ef4-cca9/screen/a96779d7-bdb3-410a-8f19-e6987acb98ee/Candidature-Step-3
class Accountdetails extends Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handleChange = this.handleChange.bind(this);
this.handleClose = this.handleClose.bind(this);
this.onBlur = this.onBlur.bind(this)
this.onSubmit = this.onSubmit.bind(this);
}
state = {
file: null,
firstname: '',
lastname: '',
email:'',
phone: '',
currentJob: '',
address1:'',
address2 : '',
zipCode : '',
city : '',
country : '',
linkedin: '',
twitter: '',
website: '',
mediaId: null,
companyId: null,
};
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
//console.log(this.refs.myTextField.input.value);
//this.soumettre(e);
}
handleClose = (e, reason) => {
if (reason === 'clickaway') {
return;
}
//setOpen(false);
};
onBlur(e) {
console.log("blur")
this.setState({ [e.target.name]: e.target.value });
this.soumettre();
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
firstname: nextProps.user.firstname,
lastname: nextProps.user.lastname,
phone: nextProps.user.phone,
email: nextProps.user.email,
currentJob: nextProps.user.currentJob,
address1 : nextProps.user.address1,
address2 : nextProps.user.address2,
zipCode : nextProps.user.zipCode,
city : nextProps.user.city,
country : nextProps.user.country,
linkedin: nextProps.user.linkedin,
twitter: nextProps.user.twitter,
website: nextProps.user.website,
companyId: nextProps.companyId,
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
})
}
componentDidMount() {
this.setState({
firstname: this.props.user.firstname,
lastname: this.props.user.lastname,
phone: this.props.user.phone,
email: this.props.user.email,
address1: this.props.user.address1,
address2 : this.props.user.address2,
zipCode : this.props.user.zipCode,
city : this.props.user.city,
country : this.props.user.country,
currentJob: this.props.user.currentJob,
linkedin: this.props.user.linkedin,
twitter: this.props.user.twitter,
website: this.props.user.website,
})
}
onSubmit = (e) => {
e.preventDefault();
this.soumettre(e);
}
soumettre(e){
const errors = this.validate(this.state);
const success = {};
this.setState({ errors });
if (!this.state.errors || !this.state.errors.message){
success.message ="Profil mis à jour";
this.setState({ success });
const data = new FormData()
data.append('firstname', this.state.firstname)
data.append('lastname', this.state.lastname)
data.append('phone', this.state.phone)
//data.append('email', this.state.email)
data.append('currentJob', this.state.currentJob)
data.append('address1', this.state.address1)
data.append('address2', this.state.address2)
data.append('zipCode', this.state.zipCode)
data.append('city', this.state.city)
data.append('country', this.state.country)
data.append('linkedin', this.state.linkedin)
data.append('twitter', this.state.twitter)
data.append('website', this.state.website)
//TODO:maybe add this two in a single transactionnal saga
this.props.updateUserRequest(data)
}
}
validate = data => {
const errors = {};
errors.message ="";
//if (!Validator.isEmail(data.currentJob)) errors.message += "Le poste actuel doit etre un email\n";
if (!data.firstname || data.firstname==="") errors.message += "Veuillez saisir votre prénom<br />";
if (!data.lastname || data.lastname==="") errors.message += " Veuillez saisir saisir votre nom<br />";
if (!data.phone || data.phone==="") errors.message += "Veuillez saisir un numéro de téléphone valide<br />";
if (!data.currentJob || data.currentJob==="") errors.message += "Veuillez saisir votre intitulé de poste actuel ou poste recherché<br />";
if ((data.zipCode && data.zipCode!="") && !Validator.isPostalCode(data.zipCode, "FR")) errors.message += "Le code postal n'est pas correct<br />";
if ((data.website && data.website!="") && !Validator.isURL(data.website)) errors.message += " Veuillez ajouter un site internet avec une url valide<br />";
if (!Validator.isMobilePhone(data.phone, "fr-FR") && !Validator.isNumeric(data.phone, "fr-FR")) errors.message += "Le téléphone n'est pas correct<br />";
//console.log(errors.email);
return errors;
};
AfficherErreurs() {
if (!this.state.errors || !this.state.errors.message){
return "";
}else if (this.state.errors.length != 0 && this.state.errors.message!="") {
return (
<Alert severity="error">
{ ReactHtmlParser(this.state.errors.message)}
</Alert>
);
}
return "";
}
AfficherSuccess() {
if (this.state.success && this.state.success.length != 0 && this.state.success.message != "") {
return (
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
<Alert onClose={() => this.setState({open: false})} severity="success">
{ReactHtmlParser(this.state.success.message)}
</Alert>
</Snackbar>
);
}
return "";
}
inputFilesystemChange = (e) => {
console.log("inputFilesystemChange");
var file = e.target.files[0]
this.setState({ picture: file })
const data = new FormData()
data.append('file', file)
this.props.updateUserPictureRequest(data)
}
render() {
const { classes, user } = this.props
const logoUrl = user.picture;
return (
<section className={classes.container}>
<form className={classes.form}
autoComplete="off" focused="true" name="monForm" ref="formToSubmit">
{/* {!!logoUrl && <Avatar className={classes.logo}
classes={{ img: classes.MuiAvatarImg }}
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl} alt="candidatLogo" />} */}
{/* {!!user.pictureLoading && <Loader />}
{!!!user.pictureLoading && !!!logoUrl &&
(<Fragment><input
type="file" accept="image/*" capture="filesystem"
className={classes.input}
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="raised-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg'
className={classes.iconButton} />
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> ma photo de profil
</Typography>
<KeyboardArrowRightIcon className={classes.InputKeyboardArrowRightIcon} />
</Button>
</label></Fragment>)
} */}
{this.AfficherErreurs()}
{this.AfficherSuccess()}
<TextField
name="firstname"
required
id="outlined-required"
label="Prénom"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.firstname}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="lastname"
required
id="outlined-required"
label="Nom"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.lastname}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
disabled
name="email"
id="outlined"
label="E-mail"
required
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.email}
onChange={this.handleChange}
type="mail"
onBlur={this.onBlur}
/>
<TextField
name="phone"
id="outlined"
label="Téléphone"
required
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.phone ? this.state.phone: ''}
onChange={this.handleChange}
type="tel"
onBlur={this.onBlur}
/>
<TextField
name="currentJob"
id="outlined"
label="Poste actuel"
required
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.currentJob && this.state.currentJob !='null' ? this.state.currentJob : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="address1"
id="outlined"
label="Adresse"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.address1 && this.state.address1 !='null' ? this.state.address1 : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="address2"
id="outlined"
label="Adresse complémentaire"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.address2 && this.state.address2 !='null' ? this.state.address2 : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<div className={classes.container}>
<TextField
name="zipCode"
id="outlined"
label="Code postal"
className={classes.textField}
classes={{ root: classes.textFieldSmall }}
variant="outlined"
value={this.state.zipCode && this.state.zipCode !='null' ? this.state.zipCode : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="city"
id="outlined"
label="Ville"
className={classes.textField}
classes={{ root: classes.textFieldSmall }}
variant="outlined"
value={this.state.city && this.state.city !='null' ? this.state.city : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="country"
id="outlined"
label="Pays"
className={classes.textField}
classes={{ root: classes.textFieldSmall }}
variant="outlined"
value={this.state.country && this.state.country !='null' ? this.state.country : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
</div>
<TextField
name="linkedin"
id="outlined"
label="Linkedin"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://www.linkedin.com/in/"
value={this.state.linkedin && this.state.linkedin !='null' ? this.state.linkedin : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="twitter"
id="outlined"
label="Twitter"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://twitter.com/.."
value={this.state.twitter && this.state.twitter !='null' ? this.state.twitter : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="website"
id="outlined"
label="Site internet"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://monsite.fr"
value={this.state.website && this.state.website !='null' ? this.state.website : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
</form>
{/* <div id="F" className={classes.foot}>
<Button variant="contained" className={classes.button + ' cypButtonSendApplication'} onClick={this.onSubmit}>Mettre à jour!</Button>
</div> */}
</section>
)
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user ,
loading: !!state.user.loading,
pictureLoading: !!state.user.pictureLoading,
errors: state.user.errors || ownProps.errors,
mediaId: state.media.message && state.media.message.mediaId,
};
}
export default withStyles(styles)(connect(mapStateToProps, { updateUserRequest })(Accountdetails))
@@ -0,0 +1,317 @@
import React, {Fragment} from 'react'
import {connect} from "react-redux";
import {makeStyles} from '@material-ui/styles';
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import {Button, Typography} from '@material-ui/core';
import dynamic from 'next/dynamic'
import {withStyles} from '@material-ui/core/styles'
import {uploadMediaPres} from '../../actions/media'
import {errorMessageSelector} from '../../reducers/media'
import {fetchCompanyRequest} from '../../actions/companies'
import {fetchCurrentUserRequest} from '../../actions/user'
import VideoVimeo from '../atoms/VideoVimeo'
import Router from 'next/router'
import Loader from '../templates/loader'
import Ar from "lodash/array"
import Snackbar from '@material-ui/core/Snackbar';
import { Alert, AlertTitle } from '@material-ui/lab';
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
import Grid from '@material-ui/core/Grid';
import Container from '@material-ui/core/Container';
import Hidden from '@material-ui/core/Hidden';
const styles = theme => ({
container: {
width: '100%',
},
root: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-start',
margin: theme.spacing(1),
flexWrap: 'wrap',
overflow: 'hidden',
backgroundColor: theme.palette.background.paper,
},
recorderContainer: {
//display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%'
},
rootButton: {
textTransform: 'inherit'
},
underlined: {
textDecoration: 'underline'
},
textButton: {
fontWeight: '700',
fontSize: '1.15em',
paddingLeft: theme.spacing(2)
},
videoPresentation: {
width: "100%",
height: "100%"
},
conseilButton: {
color: "#ffffff",
backgroundColor: "#25D89A",
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
width: "100%",
textAlign: "right",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
'&:hover': {
color: "#ffffff",
backgroundColor: "#25D89A",
}
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
width: "100%",
textAlign: "right",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
'&:hover': {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
}
},
InputKeyboardArrowDownIconBlank: {
fontSize: '2.5em',
color: "#ffffff",
textAlign: "right"
},
InputKeyboardArrowDownIcon: {
fontSize: '2.5em',
color: "#1689FB",
textAlign: "right"
}
});
const Recorder = dynamic(() => import ('../organisms/Recorder'), {ssr: false})
class Etape2 extends React.Component {
state = {
loading: true,
activeStep: 0,
data: {},
openSnack: false,
errors: {},
success : {}
};
componentDidMount() {
localStorage.setItem('loginRedirect', window.location)
if (!!!localStorage.getItem('ttJWT')) {
Router.push("/Login")
} else
this.setState({loading: false, firstname: this.props.user.firstname, videoPres: this.props.user.videoPres, video: this.props.user.presentation_video_id})
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
loading: nextProps.loading,
openSnack: !!!_.isEmpty(nextProps.errors),
errors: !!!nextProps.errors
? {
errors: {
message: ""
}
}
: nextProps.errors,
videoPres: nextProps.videoPres,
video: nextProps.video
})
//window.location.reload(false);
if (nextProps.media.status === 'success') {
const success = {};
success.message ="Vidéo mis à jour";
this.setState({ success });
this.setActiveStep(1);
}
}
setActiveStep = (step) => {
this.setState({activeStep: step})
}
inputFilesystemChange = (e) => {
this.uploadMediaPres(e)
}
inputCamRecorderChange = (e) => {
this.uploadMediaPres(e)
}
uploadMediaPres = (e) => {
var file = e.target.files[0]
const data = new FormData()
data.append('upload', file)
//this.setState({ videoPres: videoPres })
this
.props
.uploadMediaPres(data)
}
AfficherSuccess() {
if (!this.state.success || !this.state.success.message) {
} else if (this.state.success.length != 0 && this.state.success.message != "") {
return (
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
<Alert onClose={() => this.setState({open: false})} severity="success">
{ReactHtmlParser(this.state.success.message)}
</Alert>
</Snackbar>
);
}
return "";
}
handleClose = (event, reason) => {
this.setState({openSnack: false});
}
render() {
const {classes, company} = this.props;
const {loading, activeStep} = this.state;
let video;
if (this.state.videoPres) {
video = <VideoVimeo videoId={Ar.last(_.split(this.state.videoPres, '/'))} autoplay='1' classesOverride="videoPresentation"/>;
} else {
video = "";
}
return (
<section className={classes.container}>
<Fragment>
{this.AfficherSuccess()}
{ loading ? <Loader/> : <div></div> }
<div className={classes.root}>
<Hidden xsUp>
&nbsp;
</Hidden>
<Container maxWidth="md">
<Grid container
justify="center"
alignItems="center"
spacing={2}>
<Grid item xs={12} sm={12} md={12} lg={6} lg={6}>
<div className={classes.recorderContainer}>
<Recorder uploader={this.props.uploadMediaPres} titreBouton="Remplacer ma vidéo"/>
</div>
</Grid>
<Grid item xs={12} sm={12} md={12} lg={6} xl={6}>
<div>
<label htmlFor="import-conseil">
<Button
component="span"
className={classes.conseilButton}
classes={{
root: classes.rootButton}}
onClick={() => Router.push(`/Conseils`)}>
<img
alt="talents-tube"
src='/statique/images/conseils-talentstube.svg'
className={classes.iconButton}/>
<Typography align="left" className={classes.textButton}>
<span className={classes.underlined}>Comment</span> créer ma vidéo
</Typography>
<KeyboardArrowDownIcon className={classes.InputKeyboardArrowDownIconBlank}/>
</Button>
</label>
</div>
<div>
<input
type="file"
accept="video/*"
className={classes.input + ' cypButtonImportVid'}
style={{display: 'none'}}
id="import-button-file"
onChange={this.inputFilesystemChange}/>
<label htmlFor="import-button-file">
<Button
component="span"
className={classes.importButton}
classes={{
root: classes.rootButton}}>
<img
alt="talents-tube"
src='/statique/images/Import.svg'
className={classes.iconButton}/>
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> ma vidéo de réponse
</Typography>
<KeyboardArrowDownIcon className={classes.InputKeyboardArrowDownIcon}/>
</Button>
</label>
</div>
</Grid>
<Hidden xsUp>
&nbsp;
</Hidden>
<Grid item xs={12} sm={12} md={12} lg={12} xl={12}>
<div className={classes.videoPresentation}>
{video}
</div>
</Grid>
</Grid>
</Container>
</div>
{/* <div className={classes.textContainer}>
</div> */}
</Fragment>
</section>
);
}
}
Etape2.getInitialProps = function (context) {
const {companyId} = context.query
const {res} = context
//TODO :really needed ?
context
.store
.dispatch(fetchCurrentUserRequest())
if (!!companyId)
context.store.dispatch(fetchCompanyRequest(companyId))
return {}; //leave this please
}
function mapStateToProps(state) {
return {
loading: !!state.media.loading,
media: state.media,
offre: state.ad,
//errors: errorMessageSelector(state),
user: state.user,
company: state.company
};
}
export default withStyles(styles)(connect(mapStateToProps, {fetchCurrentUserRequest, fetchCompanyRequest, uploadMediaPres})(Etape2));
@@ -0,0 +1,233 @@
import React, { Fragment } from 'react';
import { connect } from "react-redux";
import { fetchCurrentUserRequest } from '../../actions/user'
import { fetchCompanyRequest } from '../../actions/companies'
import { uploadMediaCv } from '../../actions/media'
import { withStyles } from '@material-ui/core/styles'
import { makeStyles } from '@material-ui/styles';
import { Button, Typography, Divider, Hidden } from '@material-ui/core';
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import Loader from '../../components/templates/loader'
import Snack from '../../components/atoms/snack';
import Snackbar from '@material-ui/core/Snackbar';
import { Alert, AlertTitle } from '@material-ui/lab';
import ReactHtmlParser from 'react-html-parser';
import getConfig from 'next/config'
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
const styles = theme => ({
root: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-start',
margin: theme.spacing(1),
},
rootButton: {
textTransform: 'inherit',
},
underlined: {
textDecoration: 'underline',
},
textButton: {
fontWeight: '700',
fontSize: '1.15em',
paddingLeft: theme.spacing(2),
},
InputKeyboardArrowDownIcon: {
fontSize: '2.5em',
color: theme.palette.primary.main,
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
width: "400px",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
'&:hover': {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
}
},
});
const { publicRuntimeConfig } = getConfig();
class Etape3 extends React.Component {
state = {
loading: true,
openSnack: false,
cv:false,
errors: {},
success : {}
};
componentDidMount() {
localStorage.setItem('loginRedirect', window.location)
if(!!!localStorage.getItem('ttJWT'))
{
Router.push("/Login")
}
else
this.setState({
loading: false,
cvLink: this.props.user.cv
})
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
cvLink: nextProps.cvLink,
loading: nextProps.loading,
openSnack: !!!_.isEmpty(nextProps.errors),
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
})
this.state.cvLink = nextProps.cvLink;
if (nextProps.loading) {
const success = {};
success.message ="CV mis à jour";
console.log("CV mis à jour!!!")
if(this.state.cvLink) console.log(this.state.cvLink)
this.setState({ success });
}
}
inputFilesystemChange = (e) => {
this.uploadMediaCv(e)
}
uploadMediaCv = (e) => {
var file = e.target.files[0]
const data = new FormData()
data.append('upload', file)
this.props.uploadMediaCv(data)
}
AfficherSuccess() {
if (!this.state.success || !this.state.success.message) {
console.log("Erreur Upload CV");
return "";
} else if (this.state.success.length != 0 && this.state.success.message != "") {
return (
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
<Alert onClose={() => this.setState({open: false})} severity="success">
{ReactHtmlParser(this.state.success.message)}
</Alert>
</Snackbar>
);
}
return "";
}
AfficherLienCv() {
console.log("afficher lien");
if (this.props.user.cv !="") {
return (
<Fragment>
<Hidden>&nbsp;</Hidden>
<Hidden>&nbsp;</Hidden>
<Divider style={{width:400+"px"}}/>
<Hidden>&nbsp;</Hidden>
<Card variant="outlined" style={{width:400+"px"}}>
<CardActions>
<Button size="small">Mon CV numérique</Button>
</CardActions>
<CardContent>
<Typography>
<a href={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentCv + this.state.cvLink} target="_blank">Lien vers le cv téléchargé</a><br />
{/* <a href="https://www.permatheque.fr/PDF/mansobufukuoka-larevolutiondunseulbrindepaille.pdf" target="_blank">lien</a> */}
</Typography>
</CardContent>
</Card>
</Fragment>
);
}
return "";
}
render() {
const { classes, company } = this.props;
const { loading } = this.state;
return (
<Fragment>
{ loading ?
<Loader /> :
<Fragment>
{this.AfficherSuccess()}
<input
type="file"
accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document, .pdf, .odt"
className={classes.input+' cypButtonImportVid'}
style={{ display: 'none' }}
id="import-button-file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="import-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/statique/images/Import-cv-candidat.svg' className={classes.iconButton} />
<Typography className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> mon CV au format PDF
</Typography>
<KeyboardArrowDownIcon className={ classes. InputKeyboardArrowDownIcon }/>
</Button>
</label>
{this.AfficherLienCv()}
</Fragment>
}
</Fragment>
);
}
}
Etape3.getInitialProps = function (context) {
const { adId, companyId } = context.query
const {res} = context
//TODO :really needed ?
context.store.dispatch(fetchCurrentUserRequest())
if(!!adId)
context.store.dispatch(fetchAdRequest(adId))
else if(!!companyId)
context.store.dispatch(fetchCompanyRequest(companyId))
return {};//leave this please
}
function mapStateToProps(state) {
// console.log("mapStateToProps")
// console.log(state.loading)
// console.log(state.user)
// console.log(state.media.message)
return {
user: state.user ,
cvLink : state.media.message,
loading: !!state.media.loading,
cv : true,
//errors: errorMessageSelector(state),
user: state.user,
company: state.company
};
}
export default withStyles(styles)(connect(mapStateToProps, { fetchCurrentUserRequest, fetchCompanyRequest, uploadMediaCv })(Etape3));
@@ -0,0 +1,403 @@
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import AccountStep from "../../components/organisms/AccountStep";
import AccountEtape1 from '../organisms/AccountEtape1';
import AccountEtape2 from '../organisms/AccountEtape2';
import AccountEtape3 from '../organisms/AccountEtape3';
import {withStyles} from '@material-ui/core/styles';
import Switch from '@material-ui/core/Switch';
import PhoneIcon from '@material-ui/icons/Phone';
import MailIcon from '@material-ui/icons/Mail';
import PublicIcon from '@material-ui/icons/Public';
import LinkedInIcon from '@material-ui/icons/LinkedIn';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import {updateUserRequest, updateUserPictureRequest} from '../../actions/user'
import {makeStyles} from '@material-ui/styles';
import {connect} from 'react-redux'
import getConfig from 'next/config'
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';
import RefreshIcon from '@material-ui/icons/Refresh';
import FavoriteIcon from '@material-ui/icons/Favorite';
import LocationOnIcon from '@material-ui/icons/LocationOn';
import Box from '@material-ui/core/Box';
import {
Card,
CardActions,
CardContent,
Avatar,
Typography,
Divider,
Button,
LinearProgress,
Stepper,
Step,
StepLabel,
StepContent,
StepButton,
Paper,
Grid
} from '@material-ui/core';
const styles = theme => ({
container: {
width: '100%'
},
button: {
textTransform: 'inherit',
marginLeft: theme.spacing(8),
marginRight: theme.spacing(8),
padding: theme.spacing(1.2, 2),
color: theme.palette.secondary.main,
backgroundColor: theme.palette.primary.important
},
stickToBottom: {
width: '100%',
position: 'fixed',
bottom: 0
}
});
const useStyles = makeStyles(theme => ({
root: {},
details: {
display: 'flex'
},
avatar: {
marginLeft: 'auto',
height: 110,
width: 100,
flexShrink: 0,
flexGrow: 0
},
progress: {
marginTop: theme.spacing(2)
},
uploadButton: {
marginRight: theme.spacing(2)
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1)
},
actionsContainer: {
marginBottom: theme.spacing(2)
},
resetContainer: {
padding: theme.spacing(3)
}
}));
function getSteps() {
return ['Informations personnelles', 'Vidéo de présentation', 'Expériences professionnelles'];
}
function getStepContent(step) {
switch (step) {
case 0:
return <AccountEtape1 ></AccountEtape1>;
case 1:
return <AccountEtape2></AccountEtape2>;
case 2:
return <AccountEtape3></AccountEtape3>;
default:
return 'Unknown step';
}
}
const AccountProfile = props => {
const {
className,
user,
...rest
} = props;
const logoUrl = user.picture;
const classes = useStyles();
// const user = { name: 'Laurent Laniau', city: 'Questembert', country:
// 'France', timezone: '', avatar: '/images/avatars/avatar_11.png' };
const {publicRuntimeConfig} = getConfig()
const [activeStep,
setActiveStep] = React.useState(0);
const [completed,
setCompleted] = React.useState(new Set());
const [skipped,
setSkipped] = React.useState(new Set());
const [state,
setState] = React.useState({checkedA: true, checkedB: true});
const steps = getSteps();
const totalSteps = () => {
return getSteps().length;
};
const isStepOptional = (step) => {
return step === 4;
};
const handleSkip = () => {
if (!isStepOptional(activeStep)) {
// You probably want to guard against something like this it should never occur
// unless someone's actively trying to break something.
throw new Error("You can't skip a step that isn't optional.");
}
setActiveStep((prevActiveStep) => prevActiveStep + 1);
setSkipped((prevSkipped) => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
};
const skippedSteps = () => {
return skipped.size;
};
const completedSteps = () => {
return completed.size;
};
const allStepsCompleted = () => {
return completedSteps() === totalSteps() - skippedSteps();
};
const isLastStep = () => {
return activeStep === totalSteps() - 1;
};
const handleNext = () => {
const newActiveStep = isLastStep() && !allStepsCompleted()
? // It's the last step, but not all steps have been completed
// find the first step that has been completed
steps.findIndex((step, i) => !completed.has(i))
: activeStep + 1;
setActiveStep(newActiveStep);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleStep = (step) => () => {
console.log("step");
console.log(step);
setActiveStep(step);
};
const handleComplete = () => {
const newCompleted = new Set(completed);
newCompleted.add(activeStep);
setCompleted(newCompleted);
/**
* Sigh... it would be much nicer to replace the following if conditional with
* `if (!this.allStepsComplete())` however state is not set when we do this,
* thus we have to resort to not being very DRY.
*/
if (completed.size !== totalSteps() - skippedSteps()) {
handleNext();
}
};
const handleReset = () => {
setActiveStep(0);
setCompleted(new Set());
setSkipped(new Set());
};
const isStepSkipped = (step) => {
return skipped.has(step);
};
const handleChange = (event) => {
setState({
...state,
[event.target.name]: event.target.checked
});
};
function isStepComplete(step) {
return completed.has(step);
}
function refresh (){
document.location.reload(true);
console.log("refresh")
}
return (
<Grid
container
spacing={4}
direction="row"
justify="space-between"
alignItems="flex-start">
<Grid item lg={3} md={6} xl={4} xs={12}>
<Card {...rest} className={clsx(classes.root, className)}>
<CardContent>
<div className={classes.details}>
<div>
<Typography gutterBottom variant="h5">
{user.lastname} &nbsp;
{user.firstname} <a href="#" onClick={refresh}><RefreshIcon className={classes.menuButton}/></a>
</Typography>
<Typography
className={classes.locationText}
color="textSecondary"
variant="body1">
<MailIcon className={classes.menuButton}/> : {user.email}<br/>
<PhoneIcon className={classes.menuButton}/> : {user.phone}<br/>
<LinkedInIcon className={classes.menuButton}/> : {(user.linkedin && user.linkedin !='' && user.linkedin != 'null') ? <a href={user.linkedin} target="_blank">{user.linkedin}</a> : 'Non renseigné'}<br/>
<PublicIcon className={classes.menuButton}/> : {(user.website && user.website !='' && user.website != 'null') ? <a href={user.website} target="_blank">{user.website}</a> : 'Non renseigné'}
</Typography>
<Typography className={classes.dateText} color="textSecondary" variant="body1"></Typography>
</div>
<Avatar
className={classes.avatar}
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl}/>
</div>
{/* <div className={classes.progress}>
<Typography variant="body1">Etat du Profil: 70%</Typography>
<LinearProgress value={70} variant="determinate"/>
</div> */}
</CardContent>
<Box m={2} />
<Divider/>
<Box m={2} />
{/* <CardActions>
<Button className={classes.uploadButton} color="primary" variant="text">
Remplacer une photo de profil
</Button>
<Button variant="text">Supprimer photo de profil</Button>
</CardActions> */}
<Box m={1} />
<Divider/>
<Box m={1} />
{/* <CardContent>
<Typography variant="body1"></Typography>
<FormControlLabel
value="end"
control={< Switch checked = {
state.checkedA
}
onChange = {
handleChange
}
name = "checkedA" color = "primary" />}
label="Je souhaite afficher mon profil sur la CV Vidéothèque Talents Tube"
labelPlacement="start"/>
</CardContent> */}
<Box m={3} />
<Divider/>
<CardActions>
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
const stepProps = {};
const buttonProps = {};
if (isStepOptional(index)) {
buttonProps.optional = <Typography variant="caption">Optional</Typography>;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} className={classes.actionsContainer} {...stepProps}>
<StepButton
onClick={handleStep(index)}
completed={isStepComplete(index)}
{...buttonProps}>
{label}
</StepButton>
</Step>
);
})}
</Stepper>
</CardActions>
<Divider/>
<div>
<div>
<div>
{/* {isStepOptional(activeStep) && !completed.has(activeStep) && (
<Button
variant="contained"
color="primary"
onClick={handleSkip}
className={classes.button}
>
Skip
</Button>
)} */}
{/* {activeStep !== steps.length &&
(completed.has(activeStep) ? (
<Typography variant="caption" className={classes.completed}>
Step {activeStep + 1} already completed
</Typography>
) : (
<Button variant="contained" color="primary" onClick={handleComplete}>
{completedSteps() === totalSteps() - 1 ? 'Finish' : 'Complete Step'}
</Button>
))} */}
</div>
</div>
</div>
</Card>
<Box m={2} />
<CardContent>
<BottomNavigation className={classes.stickToBottom}>
<Button
disabled={activeStep === 0}
onClick={handleBack}
className={classes.button}>
Etape précédente
</Button>
<Button
variant="contained"
color="primary"
onClick={handleNext}
className={classes.button}>
Etape suivante
</Button>
{/* <BottomNavigationAction label="Recents" icon={< RestoreIcon />}/>
<BottomNavigationAction label="Favorites" icon={< FavoriteIcon />}/>
<BottomNavigationAction label="Nearby" icon={< LocationOnIcon />}/> */}
</BottomNavigation>
</CardContent>
</Grid>
<Grid item lg={9} md={6} xl={8} xs={12}>
{getStepContent(activeStep)}
</Grid>
</Grid>
);
};
AccountProfile.propTypes = {
className: PropTypes.string
};
function mapStateToProps(state, ownProps) {
return {
user: state.user,
loading: !!state.user.loading,
errors: state.user.errors || ownProps.errors
};
}
export default withStyles(styles)(connect(mapStateToProps, {updateUserRequest})(AccountProfile))
+147
View File
@@ -0,0 +1,147 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepButton from '@material-ui/core/StepButton';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import AccountEtape1 from '../organisms/AccountEtape1';
import AccountEtape2 from '../organisms/AccountEtape2';
import AccountEtape3 from '../organisms/AccountEtape3';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
button: {
marginRight: theme.spacing(1),
},
completed: {
display: 'inline-block',
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
}));
function getSteps() {
return ['Informations personnelles', 'Vidéo de présentation', 'Expériences professionnelles'];
}
function getStepContent(step) {
switch (step) {
case 0:
return <AccountEtape1 ></AccountEtape1>;
case 1:
return <AccountEtape2></AccountEtape2>;
case 2:
return <AccountEtape3></AccountEtape3>;
default:
return 'Unknown step';
}
}
export default function HorizontalNonLinearStepper() {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [completed, setCompleted] = React.useState({});
const steps = getSteps();
const totalSteps = () => {
return steps.length;
};
const completedSteps = () => {
return Object.keys(completed).length;
};
const isLastStep = () => {
return activeStep === totalSteps() - 1;
};
const allStepsCompleted = () => {
return completedSteps() === totalSteps();
};
const handleNext = () => {
const newActiveStep =
isLastStep() && !allStepsCompleted()
? // It's the last step, but not all steps have been completed,
// find the first step that has been completed
steps.findIndex((step, i) => !(i in completed))
: activeStep + 1;
setActiveStep(newActiveStep);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleStep = (step) => () => {
setActiveStep(step);
};
const handleComplete = () => {
const newCompleted = completed;
newCompleted[activeStep] = true;
setCompleted(newCompleted);
handleNext();
};
const handleReset = () => {
setActiveStep(0);
setCompleted({});
};
return (
<div className={classes.root}>
<Stepper alternativeLabel nonLinear activeStep={activeStep}>
{steps.map((label, index) => (
<Step key={label}>
<StepButton onClick={handleStep(index)} completed={completed[index]}>
{label}
</StepButton>
</Step>
))}
</Stepper>
<div>
{allStepsCompleted() ? (
<div>
{/* <Typography className={classes.instructions}>
Toutes les étapes sont terminées -
</Typography>
<Button onClick={handleReset}>Annuler</Button> */}
</div>
) : (
<div>
<Typography className={classes.instructions}>{getStepContent(activeStep)}</Typography>
<div>
{/* <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Précédent
</Button>
<Button
variant="contained"
color="primary"
onClick={handleNext}
className={classes.button}
>
Suivant
</Button> */}
{/* {activeStep !== steps.length &&
(completed[activeStep] ? (
<Typography variant="caption" className={classes.completed}>
Step {activeStep + 1} already completed
</Typography>
) : (
<Button variant="contained" color="primary" onClick={handleComplete}>
{completedSteps() === totalSteps() - 1 ? 'Etape valide' : 'Processus terminé'}
</Button>
))} */}
</div>
</div>
)}
</div>
</div>
);
}
@@ -0,0 +1,42 @@
import React, { useState } from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import Link from 'next/link';
import ListItem from '@material-ui/core/ListItem';
import Box from '@material-ui/core/Box';
import {
Card,
CardHeader,
CardContent,
CardActions,
Divider,
Grid,
Button,
TextField
} from '@material-ui/core';
const useStyles = makeStyles(() => ({
root: {}
}));
const AccountVideoPres = props => {
const { className, ...rest } = props;
const classes = useStyles();
return (
<Card
{...rest}
className={clsx(classes.root, className)}
>
</Card>
);
};
AccountVideoPres.propTypes = {
className: PropTypes.string
};
export default AccountVideoPres;
+17 -22
View File
@@ -20,6 +20,8 @@ import WorkOutlineIcon from '@material-ui/icons/WorkOutline';
import AccountCircleOutlinedIcon from '@material-ui/icons/AccountCircleOutlined';
import FolderSharedIcon from '@material-ui/icons/FolderShared';
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
import PermContactCalendar from '@material-ui/icons/PermContactCalendar';
import VideoCam from '@material-ui/icons/Videocam';
import SettingsIcon from '@material-ui/icons/Settings';
import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew';
import ArrowForward from '@material-ui/icons/ArrowForward';
@@ -37,6 +39,7 @@ import { withRouter } from "next/router";
import ActiveLink from "./../atoms/Activelink";
import axios from 'axios';
import setAuthorizationHeader from "../../data/setAuthorizationHeader";
import getConfig from 'next/config'
const styles = theme => ({
root: {
@@ -327,7 +330,7 @@ class Header extends React.Component {
render() {
const { classes } = this.props;
const { publicRuntimeConfig } = getConfig();
return (
@@ -344,7 +347,7 @@ class Header extends React.Component {
<Link href="/" >
<a className={classes.darkMenuItem}><ListItem classes={{ root: classes.rootListItem }}>
<img alt="talents-tube" src='/static/images/logos/TalentsTube_logo-color.svg' className={classes.menuLogo} />
<img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-color.svg' className={classes.menuLogo} />
</ListItem></a>
</Link>
@@ -381,18 +384,9 @@ class Header extends React.Component {
</ListItem></a>
</ActiveLink>
</div>
<div>
<div className={classes.videothequeLinkGroup}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<VideoIcon className={classes.icon} />
<ListItemText classes={{ primary: classes.ListItemText }} primary='CV vidéothèque'>
</ListItemText>
</ListItem>
</a>
</div>
<div className={classes.blogLinkGroup}>
<a href="https://blog.talentstube.com" className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<a href={publicRuntimeConfig.blogUrl} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<NotesIcon className={classes.icon} />
<ListItemText classes={{ primary: classes.ListItemText }} primary='Blog'>
</ListItemText>
@@ -400,7 +394,7 @@ class Header extends React.Component {
</a>
</div>
<div className={classes.recruteurLinkGroup}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<a href={publicRuntimeConfig.symfonyUrl + "faire-offre-emploi-video"} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<SupervisorAccountIcon className={classes.icon} />
<ListItemText classes={{ primary: classes.ListItemText }} primary='Recruteurs'>
</ListItemText>
@@ -430,7 +424,7 @@ class Header extends React.Component {
<List>
<Link href="/" >
<a className={classes.darkMenuItem + ' cypFrontPage'}><ListItem classes={{ root: classes.rootListItemIcon }}>
<img alt="talents-tube" src='/static/images/logos/TalentsTube_logo-blanc.svg' className={classes.menuLogo} />
<img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-blanc.svg' className={classes.menuLogo} />
</ListItem></a>
</Link>
@@ -458,7 +452,7 @@ class Header extends React.Component {
<Divider light={true} />
<div className={classes.linkGroup}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.lightMenuItem}><ListItem classes={{ primary: classes.ListItemText }} classes={{ root: classes.rootListItemIcon }}>
<a href={publicRuntimeConfig.symfonyUrl + "/faire-offre-emploi-video"} className={classes.lightMenuItem}><ListItem classes={{ primary: classes.ListItemText }} classes={{ root: classes.rootListItemIcon }}>
<WorkOutlineIcon className={classes.icon} />
<ListItemText primary='Connexion recruteurs'>
</ListItemText>
@@ -470,7 +464,7 @@ class Header extends React.Component {
{!!!this.props.loading && !!this.props.fetched &&
<Fragment>
{this.props.user.picture ?
<img alt="talents-tube" src={`http://localhost/uploads/img/${this.props.user.picture}`} className={classes.menuAvatar} />
<img alt="talents-tube" src={publicRuntimeConfig.symfonyUrl +publicRuntimeConfig.postedContentUrl+ this.props.user.picture} className={classes.menuAvatar} />
:
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg' className={classes.menuAvatar} />
}
@@ -494,17 +488,18 @@ class Header extends React.Component {
</ListItemText>
</ListItem></a>
</Link>
<Link href="" >
<a onClick={this.onRedirectAccount} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<Link href="/profileStep" >
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<AccountCircleIcon className={classes.icon} />
<ListItemText primary='Mon compte'>
</ListItemText>
</ListItem></a>
</Link>
<Link href="/Parametres" >
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<SettingsIcon className={classes.icon} />
<ListItemText primary='Paramètres'>
<ListItemText primary='Paramètres de notification'>
</ListItemText>
</ListItem></a>
</Link>
@@ -530,7 +525,7 @@ class Header extends React.Component {
<Link href="/" >
<a className={classes.darkMenuItem}>
<ListItem classes={{ root: classes.rootListItemDesktop }}>
<img alt="talents-tube" src='/static/images/logos/TalentsTube_logo-color.svg' className={classes.desktopMenuLogo} />
<img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-color.svg' className={classes.desktopMenuLogo} />
</ListItem>
</a>
</Link>
@@ -610,7 +605,7 @@ class Header extends React.Component {
</section>
<section className={classes.appBarDesktopSection + ' ' + classes.appBarDesktopSection3}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.desktopMenuItem}>
<a href={publicRuntimeConfig.symfonyUrl + "/faire-offre-emploi-video"} className={classes.desktopMenuItem}>
<ListItemText primary='Espace recruteurs'>
</ListItemText>
<ArrowForward className={classes.icon} />
@@ -628,7 +623,7 @@ class Header extends React.Component {
</IconButton>
<div className={classes.grow} />
<Link href="/" >
<a><img alt="talents-tube" src='/static/images/logos/TalentsTube_logo-color.svg' className={classes.menuLogo} /></a>
<a><img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-color.svg' className={classes.menuLogo} /></a>
</Link>
<div className={classes.grow} />
<IconButton
+14 -3
View File
@@ -18,6 +18,8 @@ import Link from 'next/link'
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
import VideoVimeo from '../atoms/VideoVimeo'
import OgHead from '../atoms/OgHead'
import JobPosting from '../atoms/JobPosting'
import Moment from 'react-moment';
const styles = theme => ({
primary: {
@@ -304,11 +306,20 @@ function OffreDetail(props) {
return (
<Fragment>
<OgHead seo_title={offre.title}
seo_description={offre.description}
seo_url={socialShareUrl}
image={imgUrlDesktop}
></OgHead>
<JobPosting job_title={offre.title}
job_description={offre.description}
job_brand= {offre.company.brand_name && offre.company.brand_name}
job_brand_link= {entrepriseLink}
job_title = {offre.title}
job_location = {offre.location.gmap_address.formatted_address}
job_date = {offre.published_at}
></JobPosting>
{offre.video && (
<Fragment>
<Hidden smDown>
@@ -319,13 +330,13 @@ function OffreDetail(props) {
<div className={classes.logoContainer}>
<Avatar className={classes.logo} classes={{ img: classes.MuiAvatarImg }}
src={logoUrl} alt="companyLogo" />
src={logoUrl} alt={offre.company.brand_name && offre.company.brand_name} />
</div>
</a>
<div className={classes.infoContainer}>
<Typography gutterBottom className={classes.offreTitle}>
{offre.title}
{offre.title} {/*- publié le <Moment format="DD/MM/YYYY" date={offre.published_at} /> */}
</Typography>
<a href={entrepriseLink} >
<Typography gutterBottom variant="subtitle1" className={classes.companyName}>
@@ -413,7 +424,7 @@ function OffreDetail(props) {
variant="contained"
className={classes.button}
>Postuler ! </Button>
>Postuler</Button>
</Link>
<Typography className={classes.contentCondition}>
ou
+10 -2
View File
@@ -130,7 +130,15 @@ class Recorder extends Component {
}
render() {
const { classes } = this.props;
const { classes, titreBouton } = this.props;
let titreButtonLabel= "Envoyer";
if (titreBouton) {
titreButtonLabel = titreBouton;
console.log("titre");
console.log(titreButtonLabel);
}
return (
<div>
<div data-vjs-player>
@@ -140,7 +148,7 @@ class Recorder extends Component {
{ this.state.canSend && <Button type="submit" variant="outlined" color="primary"
className={classes.button}
onClick={this.sendBlob}>
Envoyer
{titreButtonLabel}
</Button>}
</div>
</div>
+17 -1
View File
@@ -120,6 +120,22 @@ export default {
withCredentials: true
}).then(res => res)
},
uploadMediaPres: (payload) => {
return axios.post("/api/mediaPres", payload, {
headers: {
'Content-Type': `multipart/form-data;`
},
withCredentials: true
}).then(res => res)
},
uploadMediaCv: (payload) => {
return axios.post("/api/mediaCv", payload, {
headers: {
'Content-Type': `multipart/form-data;`
},
withCredentials: true
}).then(res => res)
}
},
stats: {
countAdView: id => {
@@ -135,4 +151,4 @@ export default {
}
};
};
+7 -4
View File
@@ -33,21 +33,24 @@ module.exports = withPlugins(
}
],
importScripts:
['/static/js/Edeclic-service-worker.js',
'/static/js/backgroundSync-sw.js'
['/statique/js/Edeclic-service-worker.js',
'/statique/js/backgroundSync-sw.js'
]
},
}],
[{
publicRuntimeConfig: {
symphonyUrl: 'https://www.talentstube.com',
frontServerUrl: 'https://pp.app.talentstube.com',
adListDefaultUrl: '/static/images/default/default_picture_company.svg',
blogUrl: 'https://blog.talentstube.com',
frontServerUrl: 'https://app.talentstube.com',
adListDefaultUrl: '/statique/images/default/default_picture_company.svg',
cachedMediaUrl: '/media/cache/profile_thumb/uploads/img/',
postedContentUrl: '/uploads/img/',
postedContentCv: '/uploads/cv/',
ReCAPTCHA: {
"siteKey": "6LdyqLwUAAAAADKI5uC7d5W9VqVgzd8VEmMU2Yi-"
},
applicationServerPublicKey: 'BC9XGBLNGm0RWczR4IjEPWQ9_P1BmlvT-oaVgr_GJTI2OZtT85olmi3xzQhgZF4-Bi0rPafpa8vqt5pudQ4Na6w'
}
}]
],
+16912
View File
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -7,8 +7,9 @@
},
"dependencies": {
"@blunck/next-alias": "^1.0.0",
"@material-ui/core": "^4.2.0",
"@material-ui/icons": "^3.0.1",
"@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"@ostai/next-css": "^1.0.2",
"axios": "^0.18.0",
"connect-mongo": "^2.0.1",
@@ -33,6 +34,7 @@
"next-optimized-images": "^1.4.1",
"next-redux-saga": "^4.0.2",
"next-redux-wrapper": "^3.0.0-alpha.2",
"next-seo": "^4.7.3",
"next-transpile-modules": "^2.3.1",
"node-mailjet": "^3.3.1",
"nodemailer": "^4.6.8",
@@ -44,10 +46,13 @@
"passport-oauth2": "https://github.com/Huaoe/passport-oauth2.git",
"prop-types": "latest",
"react": "^16.9.0",
"react-ad-block-detect": "^1.0.1",
"react-dom": "^16.9.0",
"react-ga": "^2.5.7",
"react-google-recaptcha": "^2.0.1",
"react-html-parser": "^2.0.2",
"react-jss": "latest",
"react-moment": "^0.9.7",
"react-redux": "^7.0.3",
"react-swipeable-views": "^0.13.3",
"reactstrap": "^6.4.0",
+1 -1
View File
@@ -178,7 +178,7 @@ class Confidentialite extends Component {
<a href="http://windows.microsoft.com/fr-fr/internet-explorer/delete-manage-cookies#ie=ie-11" className={classes.link} target="_blank">Internet Explorer</a><br />
`
Pour plus dinformations sur les cookies et la façon de les gérer, nous vous recommandons de consulter le site `} <a href="http://www.youronlinechoices.com/fr/" className={classes.link} target="_blank">www.youronlinechoices.com</a>{`, développé par lorganisme EDAA (European Interactive Digital Advertising Alliance).
Pour plus dinformations sur les cookies et la façon de les gérer, nous vous recommandons de consulter le site ` <a href="http://www.youronlinechoices.com/fr/" className={classes.link} target="_blank">www.youronlinechoices.com</a>{`, développé par lorganisme EDAA (European Interactive Digital Advertising Alliance).
`
}
</Typography>
+5 -5
View File
@@ -12,7 +12,7 @@ const styles = theme => ({
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-start',
padding: theme.spacing(3,2),
padding: theme.spacing(3, 2),
width: '100%',
},
vimeoVideo: {
@@ -26,17 +26,17 @@ class Conseils extends React.Component {
const { classes } = this.props
return (
<LayoutBack text="Quelques conseils !">
<LayoutBack text="Quelques conseils !">
<Head>
<title>Quelques conseils !</title>
<meta name="description" content="Quelques conseils !" />
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
<link rel="canonical" href="https://app.talentstube.com" />
</Head>
<section className={ classes.container }>
<VideoVimeo videoId='356411947' autoplay='1'/>
<section className={classes.container}>
<VideoVimeo videoId='356411947' autoplay='1' />
</section>
</LayoutBack>
</LayoutBack>
)
}
+2 -2
View File
@@ -357,9 +357,9 @@ class Entreprise extends React.Component {
render() {
const { classes, company } = this.props;
const logoUrl = (company.logo ? publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + company.logo : "https://www.talentstube.com/img/default_picture_svg")
const logoUrl = (company.logo ? publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + company.logo : publicRuntimeConfig.symfonyUrl + "/img/default_picture_svg")
const DefaultUrl = "https://www.talentstube.com/img/default_picture_svg"
const DefaultUrl = publicRuntimeConfig.symfonyUrl + "/img/default_picture_svg"
const imgUrl = (company.presentation_video && company.presentation_video.thumbnails[0] && company.presentation_video.thumbnails[0].sizes[2]) ? company.presentation_video.thumbnails[0].sizes[2].link : DefaultUrl
const offre = (company.count_ads == 1) ? company.count_ads + ' offre' : company.count_ads + ' offres'
+33 -3
View File
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import getConfig from 'next/config'
import { withStyles } from '@material-ui/core/styles';
import _ from "lodash"
import Grid from "../components/templates/Grid"
@@ -11,6 +12,7 @@ import { fetchCompaniesRequest } from '../actions/companies'
import { CompaniesSelector } from '../reducers/companies'
import { connect } from "react-redux";
import Head from 'next/head'
import OgHead from '../components/atoms/OgHead';
const styles = theme => ({
root: {
@@ -64,13 +66,41 @@ class Entreprises extends React.Component {
render() {
const { classes } = this.props;
const { publicRuntimeConfig } = getConfig();
const socialShareUrl = typeof window !== 'undefined' ? window.location
: publicRuntimeConfig.frontServerUrl + `/Entreprises`;
const DefaultUrl = publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg"
const imgUrl = DefaultUrl ;
return (
<LayoutGrid backLink="/">
<Head>
{/* <Head>
<title key="title">Entreprises qui recrutent Talents Tube</title>
<meta name="description" content="Les entreprises en vidéo." />
</Head>
<meta name="twitter:card" content="summary"/>
<meta name="twitter:site" content="@TalentsTube"/>
<meta name="twitter:title" content="Les entreprises qui recrutent en vidéo | TalentsTube"/>
<meta name="twitter:description" content="Découvrez les entreprises qui recrutent en vidéo, elles dévoilent leurs marques employeur et leurs offres d'emploi en vidéo. Postulez rapidement à l'offre qui vous convient."/>
<meta name="twitter:image" content=""/>
<meta property="og:title" content="Les entreprises qui recrutent en vidéo | TalentsTube"/>
<meta property="og:type" content="article"/>
<meta property="og:image" content=""/>
<meta property="og:description"
content="Découvrez les entreprises qui recrutent en vidéo, elles dévoilent leurs marques employeur et leurs offres d'emploi en vidéo. Postulez rapidement à l'offre qui vous convient."/>
<meta property="og:site_name" content="Talents Tube"/>
<meta property="og:url"
content=""/>
</Head> */}
<OgHead seo_title="Entreprises qui recrutent Talents Tube"
seo_description="Les entreprises en vidéo."
seo_url={socialShareUrl}
image={imgUrl}
seo_key="Recrutement, video"
></OgHead>
<Typography gutterBottom component="h1"
className={classes.title}>
Les entreprises en <span className={classes.primary}>vidéo</span>
+3 -10
View File
@@ -120,7 +120,7 @@ function Help(props) {
<img alt="talents-tube" src='/static/images/help/image-aide-web-app.svg' className={classes.teamworkingImg} />
<Typography gutterBottom component="h2"
className={classes.subTitle}>
Découvrez les offres d'emploi en vidéo
Comment postuler à une offre demploi en vidéo
</Typography>
<VideoVimeo videoId='356411947' classesOverride={classes.video} autoplay='0'/>
<Typography
@@ -128,15 +128,8 @@ function Help(props) {
gutterBottom>
Suivez nos conseils pour postuler simplement aux offres demploi. La vidéo de motivation vous permet de dévoiler votre personnalité, vos compétences, votre parcours, vos valeurs, vos soft-skills, votre ambition,. et bien plus encore.
</Typography>
<Typography gutterBottom component="h2"
className={classes.subTitle}>
Vous possédez déjà un CV vidéo ?
</Typography>
<Typography
className={classes.text}
gutterBottom>
Vous n'avez pas trouvé votre bonheur dans nos offres d'emploi, mais vous souhaitez diffuser votre <span className={classes.bold}>CV vidéo à l'ensemble de notre communauté de talents.</span> <br></br> Découvrez les vidéos de nos talents sur notre CV vidéothèque.
</Typography>
</section>
<Footer />
+1
View File
@@ -90,6 +90,7 @@ class Offres extends React.Component {
<Head>
<title key="title">Offres demploi en vidéo Talents Tube</title>
<meta name="description" content="Les offres d'emploi en vidéo." />
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
+15 -5
View File
@@ -15,6 +15,8 @@ import { isEmpty } from "lodash"
import NoResult from './../components/atoms/NoResult';
import Head from 'next/head'
import dynamic from 'next/dynamic'
import OgHead from '../components/atoms/OgHead';
import getConfig from 'next/config'
const VideoSlider = dynamic(() => import('../components/organisms/ShortVideoHooker'), {
ssr: false
@@ -34,7 +36,7 @@ const TileOffre = dynamic(() => import('../components/organisms/TileOffre'), {
const Footer = dynamic(() => import('../components/organisms/Footer'), {
ssr: false
});
const { publicRuntimeConfig } = getConfig();
const styles = theme => ({
root: {
position: 'relative',
@@ -154,12 +156,20 @@ class Index extends React.Component {
}
render() {
const { classes } = this.props;
const socialShareUrl = typeof window !== 'undefined' ? window.location
: publicRuntimeConfig.frontServerUrl ;
const DefaultUrl = publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg"
const imgUrl = DefaultUrl
return (
<Layout>
<Head>
<title key="title"> Talents tube - La recherche d'emploi en vidéo</title>
<meta name="description" content="Talents Tube est la première plateforme réservée aux offres d'emploi et aux CV vidéos. Découvrez un nouvelle méthode de recrutement ! Installez notre application." />
</Head>
<OgHead seo_title="Entreprises qui recrutent Talents Tube"
seo_description="Les entreprises en vidéo."
seo_url={socialShareUrl}
image={imgUrl}
seo_key="Recrutement, video"
></OgHead>
<div className={classes.container}>
<VideoSlider />
+54
View File
@@ -0,0 +1,54 @@
import React from 'react';
import { makeStyles } from '@material-ui/styles';
import { Grid } from '@material-ui/core';
import Layout from "../components/templates/Layout";
import AccountProfile from "../components/organisms/AccountProfile";
import AccountVideoPres from "../components/organisms/AccountVideoPres";
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(4)
}
}));
const Account = () => {
const classes = useStyles();
return (
<Layout>
<div className={classes.root}>
<Grid
container
spacing={4}
direction="row"
justify="space-between"
alignItems="flex-start"
>
<Grid
item
lg={4}
md={6}
xl={4}
xs={12}
>
<AccountProfile />
<div><br/></div>
<AccountVideoPres />
</Grid>
<Grid
item
lg={8}
md={6}
xl={8}
xs={12}
>
</Grid>
</Grid>
</div>
</Layout>
);
};
export default Account;
+43
View File
@@ -0,0 +1,43 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Layout from "../components/templates/Layout";
import AccountProfile from "../components/organisms/AccountProfile";
import Head from 'next/head'
// import AdBlockDetect from 'react-ad-block-detect';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
button: {
marginRight: theme.spacing(1),
},
completed: {
display: 'inline-block',
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
}));
export default function HorizontalNonLinearStepper() {
const classes = useStyles();
return (
<Layout>
<Head>
<title key="title">Profile Talents Tube</title>
</Head>
<div >
<AccountProfile />
</div>
{/* <AdBlockDetect>
<p>AdBlocks a été détecté. Pour que Talents Tube puisse fonctionner correctement, veuillez désactiver Ad Blocks</p>
</AdBlockDetect> */}
</Layout>
);
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+73
View File
@@ -0,0 +1,73 @@
{
"name": "Talents Tube",
"short_name": "Talents Tube",
"lang": "fr",
"orientation":"portrait",
"icons": [
{
"src": "/static/images/icons/talentstubeIco16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco57x57.png",
"sizes": "57x57",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco114x114.png",
"sizes": "114x114",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco180x180.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#1689FB",
"theme_color": "#1689FB",
"gcm_sender_id":"103953800507"
}
Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,35 @@
<svg xmlns="http://www.w3.org/2000/svg" width="73.762" height="69.644" viewBox="0 0 73.762 69.644">
<g id="Import" transform="translate(0 13.219)">
<g id="Groupe_7895" data-name="Groupe 7895" transform="translate(53.502 2.911) rotate(131)">
<rect id="Rectangle_316" data-name="Rectangle 316" width="37.465" height="23.87" transform="translate(0.976 1.838)"/>
<rect id="Rectangle_321" data-name="Rectangle 321" width="4.656" height="4.656" transform="translate(2.783 18.724)" fill="#fff"/>
<rect id="Rectangle_398" data-name="Rectangle 398" width="1.656" height="12.656" transform="translate(2.781 4.725)" fill="#fff"/>
<rect id="Rectangle_399" data-name="Rectangle 399" width="1.656" height="8.656" transform="translate(5.781 8.725)" fill="#fff"/>
<rect id="Rectangle_400" data-name="Rectangle 400" width="1.656" height="18.656" transform="translate(10.781 4.725)" fill="#fff"/>
<path id="Tracé_396" data-name="Tracé 396" d="M0-3H1.656V8.656H0Z" transform="translate(13.781 14.724)" fill="#fff"/>
<rect id="Rectangle_401" data-name="Rectangle 401" width="1.656" height="18.656" transform="translate(17.781 4.725)" fill="#fff"/>
<path id="Tracé_397" data-name="Tracé 397" d="M0-3H1.656V15.656H0Z" transform="translate(20.781 7.725)" fill="#fff"/>
<path id="Tracé_398" data-name="Tracé 398" d="M0-3H1.656V10.788H0Z" transform="translate(23.781 12.593)" fill="#fff"/>
<rect id="Rectangle_402" data-name="Rectangle 402" width="1.656" height="18.656" transform="translate(27.781 4.725)" fill="#fff"/>
<path id="Tracé_399" data-name="Tracé 399" d="M0,9H1.656v6.656H0Z" transform="translate(33.781 7.725)" fill="#fff"/>
<path id="Tracé_400" data-name="Tracé 400" d="M0-3H1.656V10.788H0Z" transform="translate(30.781 12.593)" fill="#fff"/>
</g>
<path id="Tracé_279" data-name="Tracé 279" d="M217.367,282.859l-6.93,6.93-48.507-6.93L155,275.93l6.93-6.93h55.437Z" transform="translate(-155 -233.363)" fill="#155ee4"/>
<path id="Tracé_280" data-name="Tracé 280" d="M173,269v6.93h48.507l6.93-6.93Z" transform="translate(-166.07 -233.363)" opacity="0.25"/>
<g id="Groupe_7897" data-name="Groupe 7897" transform="translate(28.442 19.099)">
<g id="Groupe_7896" data-name="Groupe 7896" transform="translate(3.551)">
<path id="Tracé_281" data-name="Tracé 281" d="M239.065,257.45a.488.488,0,0,1-.231-.039.939.939,0,0,1-.693-1.155,50.925,50.925,0,0,0,1.463-12.088,51.857,51.857,0,0,0-.346-6.006.969.969,0,1,1,1.925-.231,55.915,55.915,0,0,1,.346,6.237,52.807,52.807,0,0,1-1.5,12.55A.952.952,0,0,1,239.065,257.45Z" transform="translate(-238.104 -232.827)" fill="#25d89a"/>
<path id="Tracé_282" data-name="Tracé 282" d="M240.5,229.337a1.021,1.021,0,0,1-.962-.808c-.077-.462-.154-.885-.27-1.347a.944.944,0,0,1,1.848-.385c.077.462.192.924.269,1.386a.969.969,0,0,1-.77,1.116A.141.141,0,0,1,240.5,229.337Z" transform="translate(-238.804 -226.042)" fill="#25d89a"/>
</g>
<path id="Tracé_283" data-name="Tracé 283" d="M233.084,279.525l-4.042-6.237a.972.972,0,1,1,1.617-1.078l3,4.62,4.62-3a.972.972,0,0,1,1.078,1.617Z" transform="translate(-228.879 -254.171)" fill="#25d89a"/>
</g>
<path id="Tracé_284" data-name="Tracé 284" d="M316.393,300.545h-9.008a.385.385,0,1,1,0-.77h8.7l5.505-5.659a.381.381,0,1,1,.539.539Z" transform="translate(-248.483 -248.739)" fill="#25d89a"/>
<path id="Tracé_285" data-name="Tracé 285" d="M317.93,299.2h-6.545a.385.385,0,0,1,0-.77h6.237l4.2-4.312a.381.381,0,1,1,.539.539Z" transform="translate(-250.943 -248.739)" fill="#fa6362"/>
<g id="Groupe_7899" data-name="Groupe 7899" transform="translate(30.144 40.641)">
<g id="Groupe_7898" data-name="Groupe 7898" transform="translate(0 0)">
<path id="Tracé_286" data-name="Tracé 286" d="M233.3,282l2.5,3.811,5.89-3.811h-3.542l-.654.423a1.636,1.636,0,0,0,.077-.423H233.3Z" transform="translate(-233.3 -282)" fill="#21b77e"/>
</g>
</g>
<rect id="Rectangle_325" data-name="Rectangle 325" width="55.437" height="13.859" transform="translate(0 42.566)" fill="#1689fb"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="73.762" height="56.426" viewBox="0 0 73.762 56.426"><defs><style>.a{fill:#fff;}.b{fill:#155ee4;}.c{opacity:0.25;}.d{fill:#25d89a;}.e{fill:#fa6362;}.f{fill:#21b77e;}.g{fill:#1689fb;}</style></defs><g transform="translate(0 0)"><g transform="translate(9.499)"><rect width="42.463" height="23.869" transform="translate(0 3.769) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(1.401 6.158) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(1.866 11.472) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(2.327 16.747) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(2.826 22.019) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(38.67 2.848) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(39.131 8.123) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(39.633 13.433) rotate(-5.093)"/><rect class="a" width="2.656" height="2.656" transform="translate(40.094 18.708) rotate(-5.093)"/><path class="a" d="M229.4,202.9l6.275,3.311-5.582,4.35Z" transform="translate(-210.257 -192.71)"/></g><path class="b" d="M217.367,282.859l-6.93,6.93-48.507-6.93L155,275.93l6.93-6.93h55.437Z" transform="translate(-155 -233.363)"/><path class="c" d="M173,269v6.93h48.507l6.93-6.93Z" transform="translate(-166.07 -233.363)"/><g transform="translate(28.442 19.099)"><g transform="translate(3.551)"><path class="d" d="M239.065,257.45a.488.488,0,0,1-.231-.039.939.939,0,0,1-.693-1.155,50.925,50.925,0,0,0,1.463-12.088,51.857,51.857,0,0,0-.346-6.006.969.969,0,0,1,1.925-.231,55.915,55.915,0,0,1,.346,6.237,52.807,52.807,0,0,1-1.5,12.55A.952.952,0,0,1,239.065,257.45Z" transform="translate(-238.104 -232.827)"/><path class="d" d="M240.5,229.337a1.021,1.021,0,0,1-.962-.808c-.077-.462-.154-.885-.27-1.347a.944.944,0,0,1,1.848-.385c.077.462.192.924.269,1.386a.969.969,0,0,1-.77,1.116A.141.141,0,0,1,240.5,229.337Z" transform="translate(-238.804 -226.042)"/></g><path class="d" d="M233.084,279.525l-4.042-6.237a.972.972,0,1,1,1.617-1.078l3,4.62,4.62-3a.972.972,0,0,1,1.078,1.617Z" transform="translate(-228.879 -254.171)"/></g><path class="d" d="M316.393,300.545h-9.008a.385.385,0,1,1,0-.77h8.7l5.505-5.659a.381.381,0,1,1,.539.539Z" transform="translate(-248.483 -248.739)"/><path class="e" d="M317.93,299.2h-6.545a.385.385,0,0,1,0-.77h6.237l4.2-4.312a.381.381,0,1,1,.539.539Z" transform="translate(-250.943 -248.739)"/><g transform="translate(30.144 40.641)"><g transform="translate(0 0)"><path class="f" d="M233.3,282l2.5,3.811,5.89-3.811h-3.542l-.654.423a1.636,1.636,0,0,0,.077-.423H233.3Z" transform="translate(-233.3 -282)"/></g></g><rect class="g" width="55.437" height="13.859" transform="translate(0 42.566)"/></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="65.694" height="55" viewBox="0 0 65.694 55"><defs><style>.a{fill:#fff;}.b{fill:#fa6362;}.c{fill:#25d89a;}.d{fill:#d0e7fe;}.e{fill:#f8fcff;}</style></defs><g transform="translate(6.99 12.604)"><rect class="a" width="0.382" height="6.302" transform="translate(8.135)"/><rect class="b" width="0.382" height="6.302" transform="translate(8.25 10.503)"/><rect class="b" width="6.302" height="0.382" transform="translate(0 8.288)"/><rect class="a" width="6.302" height="0.382" transform="translate(10.503 8.135)"/><rect class="a" width="0.382" height="4.125" transform="translate(4.324 4.686) rotate(-45)"/><rect class="a" width="0.382" height="4.125" transform="translate(9.254 9.481) rotate(-45)"/><rect class="b" width="4.125" height="0.382" transform="translate(4.392 12.164) rotate(-45)"/><rect class="a" width="4.125" height="0.382" transform="translate(9.186 7.288) rotate(-45)"/></g><path class="c" d="M249.806,254.569H236.819A3.831,3.831,0,0,1,233,250.75v-9.931A3.831,3.831,0,0,1,236.819,237h12.986a3.831,3.831,0,0,1,3.819,3.819v9.931A3.831,3.831,0,0,1,249.806,254.569Z" transform="translate(-206.646 -214.465)"/><path class="d" d="M235.875,281.75,229,288.625l-48.125-6.875L174,274.875,180.875,268h55Z" transform="translate(-170.181 -233.625)"/><rect class="a" width="55" height="13.75" transform="translate(3.819 41.25)"/><path class="d" d="M185.58,296.5h-9.549a3.748,3.748,0,0,1-2.483-.917c-.8-.687.382-8.441.382-9.587H189.4v6.684A3.831,3.831,0,0,1,185.58,296.5Z" transform="translate(-169.73 -244.75)"/><path d="M177.368,258.556h-9.549A3.831,3.831,0,0,1,164,254.736V231.819A3.831,3.831,0,0,1,167.819,228h9.549a3.831,3.831,0,0,1,3.819,3.819v22.917A3.831,3.831,0,0,1,177.368,258.556Z" transform="translate(-164 -208.903)"/><path class="a" d="M178.533,259.028h-9.778c-2.1,0-3.056-1.719-3.056-3.819V233.819c0-2.1.955-3.819,3.056-3.819h9.778c2.1,0,3.056,1.719,3.056,3.819v21.389C181.589,257.309,180.634,259.028,178.533,259.028Z" transform="translate(-165.051 -210.139)"/><circle class="c" cx="6.875" cy="6.875" r="6.875" transform="translate(29.792 6.875)"/><path class="c" d="M180.042,279h-1.3A3.749,3.749,0,0,1,175,275.254v-6.111a3.749,3.749,0,0,1,3.743-3.743h1.3a3.749,3.749,0,0,1,3.743,3.743v6.111A3.749,3.749,0,0,1,180.042,279Z" transform="translate(-170.799 -232.018)"/><circle class="c" cx="2.941" cy="2.941" r="2.941" transform="translate(5.653 26.736)"/><path d="M181.589,282.1v5.309c0,2.1-.955,3.819-3.056,3.819h-9.778c-2.1,0-3.056-1.719-3.056-3.819V282.1Z" transform="translate(-165.051 -242.34)"/><circle class="a" cx="1.91" cy="1.91" r="1.91" transform="translate(6.875 42.396)"/><circle class="b" cx="1.26" cy="1.26" r="1.26" transform="translate(7.524 43.045)"/><g transform="translate(48.15)"><path class="e" d="M303.981,180.368a8.149,8.149,0,0,0-12.069,10.924,3.012,3.012,0,0,1-1.3,1.49.77.77,0,0,0,.229,1.451,2.89,2.89,0,0,0,.573.038h0a5.282,5.282,0,0,0,2.941-.955,8.018,8.018,0,0,0,3.858.955,8.183,8.183,0,0,0,5.767-2.368,7.961,7.961,0,0,0,2.368-5.767A8.183,8.183,0,0,0,303.981,180.368Zm-.649,10.885a7.129,7.129,0,0,1-5.118,2.1,7.236,7.236,0,0,1-3.667-.993.543.543,0,0,0-.229-.076.552.552,0,0,0-.267.076,4.7,4.7,0,0,1-2.559.955,4.748,4.748,0,0,0,1.375-1.91.48.48,0,0,0-.076-.5,7.219,7.219,0,0,1,.306-9.892,7.129,7.129,0,0,1,5.118-2.1,7.219,7.219,0,0,1,5.118,12.337Z" transform="translate(-290.064 -178)"/><path class="e" d="M308.557,192.3h-6.8a.458.458,0,1,0,0,.917h6.8a.458.458,0,1,0,0-.917Z" transform="translate(-297.009 -186.838)"/><path class="e" d="M308.557,198.1h-6.8a.458.458,0,1,0,0,.917h6.8a.458.458,0,1,0,0-.917Z" transform="translate(-297.009 -190.423)"/><path class="e" d="M306.265,203.9h-4.507a.458.458,0,1,0,0,.917h4.507a.441.441,0,0,0,.458-.458A.464.464,0,0,0,306.265,203.9Z" transform="translate(-297.009 -194.008)"/></g></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -0,0 +1,12 @@
<svg id="Groupe_8099" data-name="Groupe 8099" xmlns="http://www.w3.org/2000/svg" width="65.497" height="40.618" viewBox="0 0 65.497 40.618">
<path id="Tracé_401" data-name="Tracé 401" d="M0,0H62.62V35.2H0Z" transform="translate(0 5.559) rotate(-5.093)"/>
<rect id="Rectangle_317" data-name="Rectangle 317" width="3.917" height="3.917" transform="translate(2.066 9.081) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_318" data-name="Rectangle 318" width="3.917" height="3.917" transform="translate(2.751 16.917) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_319" data-name="Rectangle 319" width="3.917" height="3.917" transform="translate(3.431 24.696) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_320" data-name="Rectangle 320" width="3.917" height="3.917" transform="translate(4.167 32.47) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_321" data-name="Rectangle 321" width="3.917" height="3.917" transform="translate(57.023 4.199) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_322" data-name="Rectangle 322" width="3.917" height="3.917" transform="translate(57.703 11.978) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_323" data-name="Rectangle 323" width="3.917" height="3.917" transform="translate(58.444 19.809) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_324" data-name="Rectangle 324" width="3.917" height="3.917" transform="translate(59.124 27.588) rotate(-5.093)" fill="#fff"/>
<text id="_" data-name="?" transform="matrix(0.996, -0.087, 0.087, 0.996, 26.298, 31.189)" fill="#fff" font-size="29" font-family="Roboto-Bold, Roboto" font-weight="700"><tspan x="0" y="0">?</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F3F9FF;}
.st1{fill:#1689FB;}
</style>
<g>
<circle class="st0" cx="200.32" cy="200.12" r="194.12"/>
<circle class="st1" cx="200.32" cy="144.75" r="69.37"/>
<path class="st1" d="M265.84,233.39H134.8c-21.2,0-38.54,17.34-38.54,38.54v92.09c30.08,19.14,65.77,30.22,104.06,30.22
s73.98-11.09,104.06-30.22v-92.09C304.38,250.73,287.04,233.39,265.84,233.39z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 755 B

@@ -0,0 +1,42 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 1920 1080">
<defs>
<radialGradient id="radial-gradient" cx="0.403" cy="0.395" r="1.057" gradientTransform="translate(0.146 -0.002) scale(0.637 1.004)" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#155ee4"/>
<stop offset="1" stop-color="#8dc6ff"/>
</radialGradient>
<radialGradient id="radial-gradient-2" cx="0.319" cy="0.609" r="1.639" gradientTransform="matrix(0.358, 0.411, -0.161, 0.912, 0.303, -0.077)" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#1689fb"/>
<stop offset="1" stop-color="#54d7e6"/>
</radialGradient>
<filter id="Tracé_105" x="781.117" y="346.457" width="822.06" height="723.468" filterUnits="userSpaceOnUse">
<feOffset dy="-27" input="SourceAlpha"/>
<feGaussianBlur stdDeviation="43" result="blur"/>
<feFlood flood-opacity="0.161"/>
<feComposite operator="in" in2="blur"/>
<feComposite in="SourceGraphic"/>
</filter>
<radialGradient id="radial-gradient-3" cx="0.524" cy="0.704" r="0.76" gradientTransform="matrix(-0.323, -0.938, 0.872, -0.347, 0.079, 1.439)" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#1689fb" stop-opacity="0.6"/>
<stop offset="1" stop-color="#54d7e6" stop-opacity="0.6"/>
</radialGradient>
<radialGradient id="radial-gradient-4" cx="0.447" cy="0.416" r="1.057" gradientTransform="translate(0.162 -0.002) scale(0.637 1.004)" xlink:href="#radial-gradient-2"/>
<radialGradient id="radial-gradient-5" cx="0.915" cy="0.609" r="1.568" gradientTransform="matrix(0.184, 0.259, -0.081, 1.582, 0.795, -0.591)" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#fff" stop-opacity="0.475"/>
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
</radialGradient>
<clipPath id="clip-Youtube_4">
<rect width="1920" height="1080"/>
</clipPath>
</defs>
<g id="Youtube_4" data-name="Youtube 4" clip-path="url(#clip-Youtube_4)">
<rect width="1920" height="1080" fill="#fff"/>
<path id="Tracé_106" data-name="Tracé 106" d="M12369.043,2300.3s-141.664-1034.519,765.283-1034.519c214.947,0,511.67,196.543,697.428,58.283,408.648-304.154,352.379-1089.441,934.215-1425.363,574.066-331.437,1213.727-72.058,1566.926,55.155s-251.332,2385.215-251.332,2385.215" transform="translate(-14153.795 -949.145)" fill="url(#radial-gradient)"/>
<path id="Tracé_101" data-name="Tracé 101" d="M11762.942,398.383s55.409-341.747,332.711-280.328,290.146,146.232,543.427,0,251.155-350.119,595.736-131.889,887.4,587.9,856.8,724.362-2328.67,85.529-2328.67,85.529Z" transform="translate(-11803 322)" fill="url(#radial-gradient-2)"/>
<g transform="matrix(1, 0, 0, 1, 0, 0)" filter="url(#Tracé_105)">
<path id="Tracé_105-2" data-name="Tracé 105" d="M12760.129,408.192c118.317,8.328,220.3,90.749,319.038-145.828s198.01,114.584,198.01,114.584l-493.051,268.977S12641.812,399.864,12760.129,408.192Z" transform="translate(-11803 322)" fill="#fff"/>
</g>
<path id="Tracé_102" data-name="Tracé 102" d="M534.949,72.539C818.824,72.539,1028,230.126,1028,514s-230.126,514-514,514S0,797.874,0,514,251.075,72.539,534.949,72.539Z" transform="translate(-200 201)" fill="url(#radial-gradient-3)"/>
<path id="Tracé_103" data-name="Tracé 103" d="M12364.285,780.991s-57.706-421.407,311.735-421.407c87.558,0,208.426,80.061,284.094,23.741,166.461-123.9,143.54-443.779,380.548-580.615,233.844-135.01,494.406-29.353,638.281,22.467s-102.379,971.607-102.379,971.607" transform="translate(-11803 322)" fill="url(#radial-gradient-4)"/>
<path id="Tracé_107" data-name="Tracé 107" d="M11779.612,537.613s427.922-55.028,1287.072,146.785,1121.613,162.176,1983.9-332.047,198.711,587.6,198.711,587.6L11739.1,909.484Z" transform="translate(-12748.872 179.263)" fill="url(#radial-gradient-5)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="277.043" height="328" viewBox="0 0 277.043 328">
<g id="Groupe_7859" data-name="Groupe 7859" transform="translate(-4.489 -11.849)">
<circle id="Ellipse_105" data-name="Ellipse 105" cx="40.594" cy="40.594" r="40.594" transform="translate(119.489 270.847) rotate(-90.438)"/>
<circle id="Ellipse_106" data-name="Ellipse 106" cx="40.594" cy="40.594" r="40.594" transform="translate(119.554 163.329) rotate(-90.438)"/>
<circle id="Ellipse_107" data-name="Ellipse 107" cx="48.799" cy="48.799" r="48.799" transform="translate(5.344 225.063) rotate(-90.438)"/>
<circle id="Ellipse_108" data-name="Ellipse 108" cx="48.799" cy="48.799" r="48.799" transform="matrix(-0.008, -1, 1, -0.008, 5.253, 110.195)"/>
<circle id="Ellipse_109" data-name="Ellipse 109" cx="48.799" cy="48.799" r="48.799" transform="translate(5.235 339.849) rotate(-90.438)"/>
<circle id="Ellipse_110" data-name="Ellipse 110" cx="33.901" cy="33.901" r="33.901" transform="translate(213.73 210.028) rotate(-90.438)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+144
View File
@@ -0,0 +1,144 @@
///
/// Thomas Berrod
/// A generic service worker
/// inspired by https://github.com/gauntface/web-push-book
///
; (function () {
"use strict";
self.importScripts('/static/js/analytics-helper.js');
function focusWindow(event) {
const url = event.notification.data["link"]; //TODO : there are 2 web server now...
const urlToOpen = new URL(url, self.location.origin).href;
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true
})
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
event.waitUntil(promiseChain);
}
function isClientFocused() {
return clients.matchAll({
type: 'window',
includeUncontrolled: true
})
.then((windowClients) => {
let clientIsFocused = false;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.focused) {
clientIsFocused = true;
break;
}
}
return clientIsFocused;
});
}
function demoMustShowNotificationCheck(event) {
const promiseChain = isClientFocused()
.then((clientIsFocused) => {
if (clientIsFocused) {
//could alert
demoSendMessageToPage(event);
return;
}
// Client isn't focused, we need to show a notification.
return self.registration.showNotification(
event.data.json().notification.title,
event.data.json().notification);
});
event.waitUntil(promiseChain);
}
function demoSendMessageToPage(event) {
const promiseChain = isClientFocused()
.then((clientIsFocused) => {
if (clientIsFocused) {
clients.matchAll({
type: 'window',
includeUncontrolled: true
}).then((windowClients) => {
let clientIsFocused = false;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.focused) {
console.info('Posting message to page');
windowClient.postMessage(event.data.json().notification);
}
}
});
} else {
return self.registration.showNotification('No focused windows', {
body: 'Had to show a notification instead of messaging each page.'
});
}
});
event.waitUntil(promiseChain);
}
self.addEventListener('push', function (event) {
demoMustShowNotificationCheck(event);
});
self.addEventListener('notificationclick', function (event) {
event.waitUntil(
sendAnalyticsEvent('click', 'Push', event.notification.title)
);
event.notification.close();
switch (event.notification.tag) {
case 'open-window':
focusWindow(event);
break;
default:
//
break;
}
});
const notificationCloseAnalytics = () => {
return Promise.resolve();
};
self.addEventListener('notificationclose', function (event) {
const dismissedNotification = event.notification;
event.waitUntil(
sendAnalyticsEvent('close', 'Push', event.notification.title)
);
const promiseChain = notificationCloseAnalytics();
event.waitUntil(promiseChain);
});
})()
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

+84
View File
@@ -0,0 +1,84 @@
//
// INFO : this file is required to track push event inside the serviceworker
//
// Set this to your tracking ID
var trackingId = 'UA-98246968-6';
function sendAnalyticsEvent(eventAction, eventCategory, eventLabel) {
'use strict';
console.info('Sending analytics event: ' + eventCategory + '/' + eventAction);
if (!trackingId) {
console.error('You need your tracking ID in analytics-helper.js');
console.error('Add this code:\nvar trackingId = \'UA-XXXXXXXX-X\';');
// We want this to be a safe method, so avoid throwing unless absolutely necessary.
return Promise.resolve();
}
if (!eventAction && !eventCategory && !eventLabel) {
console.warn('sendAnalyticsEvent() called with no eventAction or ' +
'eventCategory or '+
'eventLabel');
// We want this to be a safe method, so avoid throwing unless absolutely necessary.
return Promise.resolve();
}
return self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription === null) {
throw new Error('No subscription currently available.');
}
// Create hit data
var payloadData = {
// Version Number
v: 1,
// Client ID
cid: subscription.endpoint,
// Tracking ID
tid: trackingId,
// Hit Type
t: 'event',
// Event Category
ec: eventCategory,
// Event Action
ea: eventAction,
// Event Label
el: eventLabel
};
// Format hit data into URI
var payloadString = Object.keys(payloadData)
.filter(function(analyticsKey) {
return payloadData[analyticsKey];
})
.map(function(analyticsKey) {
return analyticsKey + '=' + encodeURIComponent(payloadData[analyticsKey]);
})
.join('&');
// Post to Google Analytics endpoint
return fetch('https://www.google-analytics.com/collect', {
method: 'post',
body: payloadString
});
})
.then(function(response) {
if (!response.ok) {
return response.text()
.then(function(responseText) {
throw new Error(
'Bad response from Google Analytics:\n' + response.status
);
});
} else {
console.info(eventCategory + '/' + eventAction +
'hit sent, check the Analytics dashboard');
}
})
.catch(function(err) {
console.warn('Unable to send the analytics event', err);
});
}
+42
View File
@@ -0,0 +1,42 @@
/*
Copyright 2018 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js');
if (workbox) {
console.info(`Yay! Workbox from backgroundSync-sw.js is loaded 🎉`);
workbox.precaching.precacheAndRoute([]);
// const showNotification = (z) => {
// console.log(z);
// self.registration.showNotification('🔄 Votre message a été envoyé !', {
// body: '🎉`🎉`🎉`'
// });
// };
const bgSyncPlugin = new workbox.backgroundSync.Plugin(
'dashboardr-queue'
);
const networkWithBackgroundSync = new workbox.strategies.NetworkOnly({
plugins: [bgSyncPlugin],
});
workbox.routing.registerRoute(
/\/feedback/,
networkWithBackgroundSync,
'POST'
);
} else {
console.info(`Boo! Workbox from backgroundSync-sw.js didn't load 😬`);
}
+70
View File
@@ -0,0 +1,70 @@
function promptInstall() {
if (deferredPrompt == null) {
console.info('beforeinstallprompt has not been triggerred yet');
return;
}
// never show it if the app was launched standalone
if (navigator.standalone) {
return;
}
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.info('User accepted the A2HS prompt');
} else {
console.info('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
}
var deferredPrompt;
/// cf https://developers.google.com/web/fundamentals/app-install-banners/
///
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
});
window.addEventListener('appinstalled', (evt) => {
console.info('a2hs installed');
});
const isInStandaloneMode = () =>
(window.matchMedia('(display-mode: standalone)').matches) || (window.navigator.standalone);
console.info('EdeclicLib loaded');
// window.addEventListener('error', function (e) {
// var errorText = [
// e.message,
// 'URL: ' + e.filename,
// 'Line: ' + e.lineno + ', Column: ' + e.colno,
// 'Stack: ' + (e.error && e.error.stack || '(no stack trace)')
// ].join('');
// console.log(errorText);
// // Example: log errors as visual output into the host page.
// // Note: you probably dont want to show such errors to users, or
// // have the errors get indexed by Googlebot; however, it may
// // be a useful feature while actively debugging the page.
// var DOM_ID = 'rendering-debug-pre';
// if (!document.getElementById(DOM_ID)) {
// var log = document.createElement('pre');
// log.id = DOM_ID;
// log.style.whiteSpace = 'pre-wrap';
// log.textContent = errorText;
// if (!document.body) document.body = document.createElement('body');
// document.body.insertBefore(log, document.body.firstChild);
// } else {
// document.getElementById(DOM_ID).textContent += ' ' + errorText;
// }
// });
+73
View File
@@ -0,0 +1,73 @@
{
"name": "Talents Tube",
"short_name": "Talents Tube",
"lang": "fr",
"orientation":"portrait",
"icons": [
{
"src": "/static/images/icons/talentstubeIco16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco57x57.png",
"sizes": "57x57",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco114x114.png",
"sizes": "114x114",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco180x180.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#1689FB",
"theme_color": "#1689FB",
"gcm_sender_id":"103953800507"
}
+40
View File
@@ -2,6 +2,12 @@ import {
UPLOAD_MEDIA_REQUEST,
UPLOAD_MEDIA_SUCCESS,
UPLOAD_MEDIA_FAILURE,
UPLOAD_MEDIA_PRES_REQUEST,
UPLOAD_MEDIA_PRES_FAILURE,
UPLOAD_MEDIA_PRES_SUCCESS,
UPLOAD_MEDIA_CV_REQUEST,
UPLOAD_MEDIA_CV_FAILURE,
UPLOAD_MEDIA_CV_SUCCESS,
} from "../types";
export function media(state = {
@@ -24,6 +30,40 @@ export function media(state = {
errors: action.errors,
loading: false
};
case UPLOAD_MEDIA_PRES_REQUEST:
return {
...state,
loading: true
};
case UPLOAD_MEDIA_PRES_SUCCESS:
return {
...state,
...action.res.data,
loading: false
};
case UPLOAD_MEDIA_PRES_FAILURE:
return {
...state,
errors: action.errors,
loading: false
};
case UPLOAD_MEDIA_CV_REQUEST:
return {
...state,
loading: true
};
case UPLOAD_MEDIA_CV_SUCCESS:
return {
...state,
...action.res.data,
loading: false
};
case UPLOAD_MEDIA_CV_FAILURE:
return {
...state,
errors: action.errors,
loading: false
};
default:
return state;
}
+6 -2
View File
@@ -11,7 +11,7 @@ import {
} from './sagas/userSaga.js'
import { requestAdsSaga, requestAdSaga, requestEmphasisAdsSaga, countAdViewRequestSaga } from './sagas/offreSaga.js'
import { requestCompaniesSaga, requestCompanySaga, requestEmphasisCompaniesSaga, countCompanyViewRequestSaga } from './sagas/companySaga.js'
import { postAnswerMessageSaga, postAdAnswerRequestSaga, uploadMediaSaga, fetchAdsAnsweredRequestSaga, fetchAnswerRequestSaga, fetchAnswerMessagesRequestSaga } from './sagas/answerSaga.js'
import { postAnswerMessageSaga, postAdAnswerRequestSaga, uploadMediaSaga, uploadMediaPresSaga, uploadMediaCvSaga, fetchAdsAnsweredRequestSaga, fetchAnswerRequestSaga, fetchAnswerMessagesRequestSaga } from './sagas/answerSaga.js'
import {
REQUEST_LOGIN,
@@ -29,6 +29,8 @@ import {
ACCOUNT_CONFIRMATION_REQUEST,
POST_AD_ANSWER_REQUEST,
UPLOAD_MEDIA_REQUEST,
UPLOAD_MEDIA_PRES_REQUEST,
UPLOAD_MEDIA_CV_REQUEST,
UPDATE_USER_REQUEST,
FETCH_ADS_ANSWER_REQUEST,
FETCH_ANSWER_REQUEST,
@@ -65,6 +67,8 @@ function* rootSaga() {
takeLatest(ACCOUNT_CONFIRMATION_REQUEST, requestConfirmationAccountSaga),
takeLatest(POST_AD_ANSWER_REQUEST, postAdAnswerRequestSaga),
takeLatest(UPLOAD_MEDIA_REQUEST, uploadMediaSaga),
takeLatest(UPLOAD_MEDIA_PRES_REQUEST, uploadMediaPresSaga),
takeLatest(UPLOAD_MEDIA_CV_REQUEST, uploadMediaCvSaga),
takeLatest(FETCH_ADS_ANSWER_REQUEST, fetchAdsAnsweredRequestSaga),
takeLatest(FETCH_ANSWER_REQUEST, fetchAnswerRequestSaga),
takeLatest(VERIFY_USER_REQUEST, verifyUserSaga),
@@ -79,4 +83,4 @@ function* rootSaga() {
])
}
export default rootSaga
export default rootSaga
+45 -1
View File
@@ -23,7 +23,11 @@ import {
import {
uploadMediaSuccess,
uploadMediaFailure
uploadMediaFailure,
uploadMediaPresSuccess,
uploadMediaPresFailure,
uploadMediaCvSuccess,
uploadMediaCvFailure
} from "../actions/media";
import AuthRouter from "../pages/AuthRouter";
@@ -70,6 +74,46 @@ export function* uploadMediaSaga(action) {
}
}
export function* uploadMediaPresSaga(action) {
try {
const res = yield call(api.medias.uploadMediaPres, action.payload);
if (!!res.data)
yield put(uploadMediaPresSuccess(res));
else
yield put(uploadMediaPresFailure(res));
} catch (err) {
if (_.includes(err, "401") || err.response.status === 401) {
localStorage.setItem('loginRedirect', window.location)
Router.push("/Login");
}
yield put(uploadMediaPresFailure(err.response));
}
}
export function* uploadMediaCvSaga(action) {
try {
const res = yield call(api.medias.uploadMediaCv, action.payload);
if (!!res.data)
yield put(uploadMediaCvSuccess(res));
else
yield put(uploadMediaCvFailure(res));
} catch (err) {
if (_.includes(err, "401") || err.response.status === 401) {
localStorage.setItem('loginRedirect', window.location)
Router.push("/Login");
}
yield put(uploadMediaCvFailure(err.response));
}
}
export function* fetchAdsAnsweredRequestSaga(action) {
try {
const res = yield call(api.answers.fetchAdsAnswered, action.payload);
+9 -1
View File
@@ -112,4 +112,12 @@ export const COUNT_COMPANY_VIEW = "COUNT_COMPANY_VIEW";
export const UPLOAD_MEDIA_REQUEST = "UPLOAD_MEDIA_REQUEST";
export const UPLOAD_MEDIA_SUCCESS = "UPLOAD_MEDIA_SUCCESS";
export const UPLOAD_MEDIA_FAILURE = "UPLOAD_MEDIA_FAILURE";
export const UPLOAD_MEDIA_FAILURE = "UPLOAD_MEDIA_FAILURE";
export const UPLOAD_MEDIA_PRES_REQUEST = "UPLOAD_MEDIA_PRES_REQUEST";
export const UPLOAD_MEDIA_PRES_SUCCESS = "UPLOAD_MEDIA_PRES_SUCCESS";
export const UPLOAD_MEDIA_PRES_FAILURE = "UPLOAD_MEDIA_PRES_FAILURE";
export const UPLOAD_MEDIA_CV_REQUEST = "UPLOAD_MEDIA_CV_REQUEST";
export const UPLOAD_MEDIA_CV_SUCCESS = "UPLOAD_MEDIA_CV_SUCCESS";
export const UPLOAD_MEDIA_CV_FAILURE = "UPLOAD_MEDIA_CV_FAILURE";
+1
View File
@@ -171,6 +171,7 @@ class Contact extends React.Component {
<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"/>
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div className={classes.root}>
<div className={classes.wrapper}>
+1
View File
@@ -57,6 +57,7 @@ class Index extends React.Component {
<title>Boilerplate 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://pwa-boiler.bzh" />
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div className={classes.root}>