...
 
Commits (5)
window.env = {
// MEDIA
// Online Simcaa.it
"PathImages": "http://www.simcaa.it/demo/media/symbols/",
"CustomImage": "http://www.simcaa.it/demo2/media/custom/",
// "ApiSymbolUpload": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/imageuploadtmp",
// "PathImages": "http://www.simcaa.it/media/symbols/",
// "WaterImages": "http://www.simcaa.it/media/specialchar/",
// "CustomImage": "http://www.simcaa.it/demo/media/custom/",
// "ApiImageUpload": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/imageupload",
"MediaImage": "http://www.simcaa.it/demo2/media/images/",
"TmpImage": "http://www.simcaa.it/demo2/media/tmp/",
"ApiImageUpload": "http://www.simcaa.it/demo2/apisimcaa-graphql/public/graphql/imageupload",
"ApiSymbolUpload": "http://www.simcaa.it/demo2/apisimcaa-graphql/public/graphql/imageuploadtmp",
// "ApiSymbolUpload": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/imageuploadtmp",
// "MediaImage": "http://www.simcaa.it/demo/media/images/",
// "TmpImage": "http://www.simcaa.it/demo/media/tmp/",
// Local
//"PathImages": "http://10.0.0.132/media/symbols/",
// "CustomImage": "http://10.0.0.132/media/custom/",
// "ApiImageUpload": "http://10.0.0.132:8085/graphql/imageupload",
// "ApiSymbolUpload": "http://10.0.0.132:8085/graphql/imageuploadtmp",
// "MediaImage": "http://10.0.0.132/media/images/",
// "TmpImage": "http://10.0.0.132/media/tmp/",
"PathImages": "http://10.0.0.132/media/symbols/",
"WaterImages": "http://10.0.0.132/media/watermark/",
"CustomImage": "http://10.0.0.132/media/custom/",
"ApiImageUpload": "http://10.0.0.132:8085/graphql/imageupload",
"ApiSymbolUpload": "http://10.0.0.132:8085/graphql/imageuploadtmp",
"MediaImage": "http://10.0.0.132/media/images/",
"TmpImage": "http://10.0.0.132/media/tmp/",
// TEMP: DEMO2 (DELETE LATER)
// API SERVER
// Online Simcaa.it
"GraphQLServer": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/query",
"GraphQLServerNoAuth": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/query/noauth",
"GraphQLLogin": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/login",
"GraphQLCurrentUser": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/me",
"GraphQLRefreshToken": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/jwtrefresh",
"RestApiCard": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/card",
// "GraphQLServer": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/query",
// "GraphQLServerNoAuth": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/query/noauth",
// "GraphQLLogin": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/login",
// "GraphQLCurrentUser": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/me",
// "GraphQLRefreshToken": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/jwtrefresh",
// "RestApiCard": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/card",
// "RestApiDuplicateProject": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/duplicateproject",
// "RestApiDuplicateChapter": "http://www.simcaa.it/demo/apisimcaa-graphql/public/graphql/duplicatechapter",
// Local
// "GraphQLServer": "http://10.0.0.132:8085/graphql/query",
// "GraphQLServerNoAuth": "http://10.0.0.132:8085/graphql/query/noauth",
// "GraphQLLogin": "http://10.0.0.132:8085/graphql/login",
// "GraphQLCurrentUser": "http://10.0.0.132:8085/graphql/me",
// "GraphQLRefreshToken": "http://10.0.0.132:8085/graphql/jwtrefresh",
// "RestApiCard": "http://10.0.0.132:8085/graphql/card",
"GraphQLServer": "http://10.0.0.132:8085/graphql/query",
"GraphQLServerNoAuth": "http://10.0.0.132:8085/graphql/query/noauth",
"GraphQLLogin": "http://10.0.0.132:8085/graphql/login",
"GraphQLCurrentUser": "http://10.0.0.132:8085/graphql/me",
"GraphQLRefreshToken": "http://10.0.0.132:8085/graphql/jwtrefresh",
"RestApiCard": "http://10.0.0.132:8085/graphql/card",
"RestApiDuplicateProject": "http://10.0.0.132:8085/graphql/duplicateproject",
"RestApiDuplicateChapter": "http://10.0.0.132:8085/graphql/duplicatechapter",
// CSS - remote
// "SemanticCSS": "http://10.0.0.132/includes/css/semantic.min.css",
......@@ -49,6 +51,10 @@ window.env = {
// "SemanticCSS": "%PUBLIC_URL%/semantic.min.css",
// "StyleCSS": "%PUBLIC_URL%/style.css",
// Responsive Voice URL
"responsiveVoiceUrl": "http://code.responsivevoice.org/responsivevoice.js",
// "responsiveVoiceUrl": "",
// HOMEPAGE MESSAGE
"blue_text_header": "To test this Demo",
"blue_text_content": "<p>Username: user</p>Password: user",
......
......@@ -22,7 +22,7 @@
<!-- <link rel="stylesheet" href="%PUBLIC_URL%/semantic.min.css"></link>
<link rel="stylesheet" href="%PUBLIC_URL%/style.css"></link> -->
<script src="http://code.responsivevoice.org/responsivevoice.js"></script>
<!-- <script src="http://code.responsivevoice.org/responsivevoice.js"></script> -->
<script src="%PUBLIC_URL%/env.js"></script>
<script src="%PUBLIC_URL%/language.js"></script>
......@@ -30,19 +30,23 @@
// document.write('<link rel="stylesheet" href="' + window.env.SemanticCSS + '" />');
// document.write('<link rel="stylesheet" href="' + window.env.StyleCSS + '" />');
var sNew = document.createElement("link")
sNew.rel="stylesheet"
sNew.type="text/css"
sNew.href = window.env.SemanticCSS
var s0 = document.getElementsByTagName('link')[0]
s0.parentNode.insertBefore(sNew, s0.nextSibling)
var SemanticCSS = document.createElement("link")
SemanticCSS.rel="stylesheet"
SemanticCSS.type="text/css"
SemanticCSS.href = window.env.SemanticCSS
var link1 = document.getElementsByTagName('link')[0]
link1.parentNode.insertBefore(SemanticCSS, link1.nextSibling)
var sNew2 = document.createElement("link")
sNew2.rel="stylesheet"
sNew2.type="text/css"
sNew2.href = window.env.StyleCSS
var s1 = document.getElementsByTagName('link')[1]
s1.parentNode.insertBefore(sNew2, s1.nextSibling)
var StyleCSS = document.createElement("link")
StyleCSS.rel="stylesheet"
StyleCSS.type="text/css"
StyleCSS.href = window.env.StyleCSS
var link2 = document.getElementsByTagName('link')[1]
link2.parentNode.insertBefore(StyleCSS, link2.nextSibling)
var responsiveVoiceScript = document.createElement('script');
responsiveVoiceScript.setAttribute('src',window.env.responsiveVoiceUrl);
document.head.appendChild(responsiveVoiceScript);
</script>
<title>SimCAA</title>
......
......@@ -29,6 +29,7 @@ window.language = {
"MAIN_FRM_SHARE": "Progetto pubblico o privato",
"MAIN_FRM_PRIVATE": "Rendi questo progetto privato",
"MAIN_FRM_NOTES": "Note del Progetto",
"MAIN_FRM_CURRENTPROFILE": "Profilo corrente",
"MAIN_BTN_CREATE": "Crea il Progetto",
"MAIN_BTN_UPDATE": "Aggiorna il Progetto",
"MAIN_BTN_CLOSE": "Annulla",
......@@ -55,8 +56,9 @@ window.language = {
"HEAD_BTN_OPTIONS": "Opzioni",
"HEAD_BTN_EXPORT": "Esporta",
"HEAD_BTN_SAVE": "Salva",
"HEAD_BTN_SAVECLOSE": "Salva e Chiudi",
"HEAD_BTN_SAVECLOSE": "Salva e chiudi",
"HEAD_BTN_CLOSE": "Chiudi",
"HEAD_BTN_CLOSENOSAVE": "Chiudi senza salvare",
"HEAD_BTN_VIEW": "View",
"HEAD_BTN_EDIT": "Edit",
"HEAD_BTN_TYPO": "typo",
......@@ -152,7 +154,7 @@ window.language = {
// PROJECT
"PRJ_MNU_BACK": "Torna alla Home",
"PRJ_MNU_OPTIONS": "Opzioni",
"PRJ_MNU_OPTIONS": "Modifica profilo",
"PRJ_MNU_ADDPROFILE": "Aggiungi Profilo",
"PRJ_BTN_EDIT": "Gestisci",
"PRJ_BTN_VIEW": "Visualizza",
......@@ -166,6 +168,7 @@ window.language = {
"PRJ_FRM_ERROR_PROFILE": "Selezionare il profilo da usare per il progetto",
"PRJ_FRM_LAYOUT": "Layout del capitolo",
"PRJ_BTN_CREATE": "Crea Capitolo",
"PRJ_BTN_UPDATE": "Aggiorna Capitolo",
"PRJ_BTN_CLOSE": "Chiudi",
//MISC
......@@ -194,6 +197,9 @@ window.language = {
"TTS_FRM_HIGHLIGHTER": "Evidenzioatore",
"TTS_FRM_HIGHLIGHTER_CARD": "Sulla Card",
"TTS_FRM_HIGHLIGHTER_INPUT": "Sul Testo",
"TTS_PLAY": "Play audio",
"TTS_OPTIONS": "Opzioni della sintesi vocale",
"TTS_SELECT": "Selezione le card da leggere",
// INIT APP
"INIT_FETCH_PROJECT": "Sto caricando i progetti",
......@@ -201,8 +207,9 @@ window.language = {
"INIT_FETCH_LAYOUT": "Sto caricando i layout",
"INIT_FETCH_CLASS": "Sto caricando le classi dei lemmi",
"INIT_FETCH_STYLE": "Sto caricando gli stili",
"INIT_FETCH_SIMSTYLE": "Sto caricando gli stili dei simboli",
"INIT_FETCH_SYMSTYLE": "Sto caricando gli stili dei simboli",
"INIT_FETCH_TEAM": "Sto caricando i team",
"INIT_FETCH_WATERMARK": "Sto caricando i watermark",
"TMSEL_DROP_PLACEHOLDER": "Scegli un team",
"TMSEL_MSG_ERROR": "Nessun team selezionato",
"TMSEL_HEADER": "Sono stati rilevati più team per il tuo utente, scegli un team con cui procedere",
......@@ -271,10 +278,10 @@ window.language = {
"UPSY_IMGSTYLE": "Stile immagine",
"UPSY_IMGSTYLE_PLACEHOLDER": "Seleziona",
"UPSY_OPENDIALOG": "Apri il File Dialog",
"UPSY_WIDTH": "Larghezza: ",
"UPSY_HEIGHT": "Altezza: ",
"UPSY_SIZE": "Grandezza: ",
"UPSY_CUSTOMIMG": "Immagine personalizzata ",
"UPSY_WIDTH": "Larghezza originale:",
"UPSY_HEIGHT": "Altezza originale:",
"UPSY_SIZE": "Grandezza:",
"UPSY_CUSTOMIMG": "Immagine personalizzata",
//Popup
"POPUP_IMPORT": "Importa immagine",
......@@ -285,15 +292,19 @@ window.language = {
"POPUP_SHOW": "Mostra immagini alternative",
"POPUP_LOCK": "Blocca/Sblocca carte",
"POPUP_COPY": "Copia carta selezionata",
"POPUP_PASTE": "Incolla carta selezionata",
"POPUP_SEARCH": "Cerca",
"POPUP_ADD_SX": "Aggiungi carta prima",
"POPUP_ADD_DX": "Aggiungi carta dopo",
"POPUP_JUMP_ROW": "Vai a capo",
"POPUP_DELETE": "Elimina carta",
// ERROR
"ERR_FILESIZE_EXCEED": "Le dimensioni dell'immagine sono troppo alte (Max: 5Mb)",
"ERR_FILESIZE_EXCEED_FORMAT": "Le dimensioni dell'immagine sono troppo alte (Max: 5Mb) o l'immagine non è un .png o un .jpg",
"ERR_DUPLICATE_RECORD": "Record doppi trovati",
"ERR_UPLOAD_SYMBOL": "Errore nell'immagine o nel lemma",
"ERR_UPLOAD_SYMBOL": "Il campo \"Parola\" non può essere vuoto",
"ERR_UPLOAD_SYMBOL_HEADER": "Errore!!!",
"ERR_UPDATE_CHAPTER": "Il titolo del capitolo non può essere vuoto",
// Close Button Confirm
"CLS_MESSAGE": "Vuoi veramente uscire senza salvare? Ogni cambiamento non salvato andrà perso.",
......@@ -339,6 +350,7 @@ window.language = {
"MAIN_FRM_SHARE": "Project private or public",
"MAIN_FRM_PRIVATE": "Make this project private",
"MAIN_FRM_NOTES": "Project notes",
"MAIN_FRM_CURRENTPROFILE": "Current profile",
"MAIN_BTN_CREATE": "Create Project",
"MAIN_BTN_UPDATE": "Update Project",
"MAIN_BTN_CLOSE": "Close",
......@@ -367,6 +379,7 @@ window.language = {
"HEAD_BTN_SAVE": "Save",
"HEAD_BTN_SAVECLOSE": "Save and Close",
"HEAD_BTN_CLOSE": "Close",
"HEAD_BTN_CLOSENOSAVE": "Exit without saving",
"HEAD_BTN_VIEW": "View",
"HEAD_BTN_EDIT": "Edit",
"HEAD_BTN_TYPO": "typo",
......@@ -462,7 +475,7 @@ window.language = {
// PROJECT
"PRJ_MNU_BACK": "Turn to Homepage",
"PRJ_MNU_OPTIONS": "Options",
"PRJ_MNU_OPTIONS": "Edit profile",
"PRJ_MNU_ADDPROFILE": "Add Profile",
"PRJ_BTN_EDIT": "Edit",
"PRJ_BTN_VIEW": "View",
......@@ -476,6 +489,7 @@ window.language = {
"PRJ_FRM_ERROR_PROFILE": "Select a profile for the current project",
"PRJ_FRM_LAYOUT": "Chapter Layout",
"PRJ_BTN_CREATE": "Create Chapter",
"PRJ_BTN_UPDATE": "Edit Chapter",
"PRJ_BTN_CLOSE": "Close",
//MISC
......@@ -504,6 +518,9 @@ window.language = {
"TTS_FRM_HIGHLIGHTER": "Highlighter",
"TTS_FRM_HIGHLIGHTER_CARD": "on Card",
"TTS_FRM_HIGHLIGHTER_INPUT": "on Text",
"TTS_PLAY": "Play audio",
"TTS_OPTIONS": "Options of the text to speech",
"TTS_SELECT": "Select cards to read",
// INIT APP
"INIT_FETCH_PROJECT": "Fetching projetcs",
......@@ -511,8 +528,9 @@ window.language = {
"INIT_FETCH_LAYOUT": "Fetching layouts",
"INIT_FETCH_CLASS": "Fetching classes",
"INIT_FETCH_STYLE": "Fetching styles",
"INIT_FETCH_SIMSTYLE": "Fetching symbol styles",
"INIT_FETCH_SYMSTYLE": "Fetching symbol styles",
"INIT_FETCH_TEAM": "Fetching teams",
"INIT_FETCH_WATERMARK": "Fetching watermarks",
"TMSEL_DROP_PLACEHOLDER": "Select a team",
"TMSEL_MSG_ERROR": "No team has been selected",
"TMSEL_HEADER": "Your user has more than one team, please choose one to continue",
......@@ -570,7 +588,7 @@ window.language = {
//UPLOAD Symbol
"UPSY_SELECTIMG": "Select an Image",
"UPSY_WORD": "Word: ",
"UPSY_WORD": "Word:",
"UPSY_CLASS": "Class",
"UPSY_IMGTYPE": "Image Type",
"UPSY_PRIVATEIMG": "This image is private",
......@@ -580,10 +598,10 @@ window.language = {
"UPSY_IMGSTYLE": "Image Style",
"UPSY_IMGSTYLE_PLACEHOLDER": "Select",
"UPSY_OPENDIALOG": "Open File Dialog",
"UPSY_WIDTH": "Width: ",
"UPSY_HEIGHT": "Height: ",
"UPSY_SIZE": "Size: ",
"UPSY_CUSTOMIMG": "Custom Image ",
"UPSY_WIDTH": "Original width:",
"UPSY_HEIGHT": "Original height:",
"UPSY_SIZE": "Size:",
"UPSY_CUSTOMIMG": "Custom Image",
//Popup
"POPUP_IMPORT": "Import image",
......@@ -594,15 +612,19 @@ window.language = {
"POPUP_SHOW": "Show alternatives images",
"POPUP_LOCK": "Lock/Unlock cards",
"POPUP_COPY": "Copy current card",
"POPUP_PASTE": "Paste current card",
"POPUP_SEARCH": "Search",
"POPUP_ADD_SX": "Add a card before",
"POPUP_ADD_DX": "Add a card after",
"POPUP_JUMP_ROW": "Jump to next row",
"POPUP_DELETE": "Delete card",
// ERROR
"ERR_FILESIZE_EXCEED": "The size of the image is too high (Max: 5Mb)",
"ERR_FILESIZE_EXCEED_FORMAT": "The size of the image is too high (Max: 5Mb) or the image extension is not a .png or .jpg",
"ERR_DUPLICATE_RECORD": "Duplicate record found",
"ERR_UPLOAD_SYMBOL": "Error in image or word",
"ERR_UPLOAD_SYMBOL": "Form field \"Word\" can't be empty",
"ERR_UPLOAD_SYMBOL_HEADER": "Error detected",
"ERR_UPDATE_CHAPTER": "Chapter title cannot be empty",
// Close Button Confirm
"CLS_MESSAGE": "Do you really want to exit without save? Every change not saved will be lost",
......
......@@ -156,6 +156,17 @@ body {
width: 100%;
}
/* Watermark input */
.input-water {
visibility: hidden;
}
.cardUI-water, .uicardlayout-water {
background-color: transparent !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
/* Typo rules */
.uicardlayout {
min-height: 100% !important;
......@@ -171,6 +182,11 @@ body {
min-width: 100%;
}
/* Margin Hover on custom img of the typo */
.customImgTypo:hover {
border: 5px solid red;
}
/* Center the lemma inside che input of the layout-card */
.uicardlayout input {
text-align: center !important;
......@@ -324,3 +340,33 @@ input {
.ui.header > .icon {
font-size: 2em;
}
/* Fix for nav icon margin */
.nav-icon-margin {
margin: 0 0 0 .5em !important;
}
/* Card Content extra alpha 1 */
.extra-alpha-1 {
color: rgba(0, 0, 0, 1) !important;
}
/* Utils float right */
.float-right {
float: right !important;
}
/* Utils float left */
.float-left {
float: left !important;
}
/* Utils no padding */
.no-padding {
padding: 0px !important;
}
/* Utils no margin */
.no-margin {
margin: 0px !important;
}
......@@ -9,35 +9,27 @@ import Login from './containers/Login'
import InitProgress from './containers/InitProgress'
import SelectTeam from './containers/SelectTeam'
import ReloginModal from './HOC/ReloginModal'
// Import CSS files
import './css/grid_styles.css'
import './css/resizable_styles.css'
import './css/react-draft-wysiwyg.css'
class App extends Component {
isLogged(Component) {
let jwt = sessionStorage.getItem('jwt')
return !jwt ? (
<Redirect to="/login"/>
) : (
<Component />
)
}
render() {
return (
<HashRouter>
<div>
<Route exact path="/" render={() => (<Redirect to="/home" />)}/>
<Route path="/home" render={this.isLogged.bind(this, RootComponent)}/>
<Route path="/basic/:mode?/:projectid?/:chapterid?" render={this.isLogged.bind(this, BasicProject)} />
<Route path="/project/:projectid?" render={this.isLogged.bind(this, Project)} />
<Route path="/layout/:mode?/:projectid?/:chapterid?" render={this.isLogged.bind(this, LayoutExport)} />
<Route path="/home" component={ReloginModal(RootComponent)}/>
<Route path="/basic/:mode?/:projectid?/:chapterid?" component={ReloginModal(BasicProject)} />
<Route path="/project/:projectid?" component={ReloginModal(Project)} />
<Route path="/layout/:mode?/:projectid?/:chapterid?" component={ReloginModal(LayoutExport)} />
<Route path="/login" render={() => (<Login />)}/>
<Route path="/admin" render={this.isLogged.bind(this, Admin)} />
<Route path="/init" render={this.isLogged.bind(this, InitProgress)} />
<Route path="/teamselect" render={this.isLogged.bind(this, SelectTeam)} />
<Route path="/admin" component={ReloginModal(Admin)} />
<Route path="/init" component={ReloginModal(InitProgress)} />
<Route path="/teamselect" component={ReloginModal(SelectTeam)} />
</div>
</HashRouter>
)
......
import React, { Component } from 'react'
import { Button, Modal } from 'semantic-ui-react'
import { Redirect } from 'react-router-dom'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { jwtFetchData, jwtExpiredOrInvalid } from '../store/actions/JWTActions'
import LoginForm from '../components/LoginForm'
const ReloginModal = (WrappedComponent) => {
return class ReloginModal extends Component {
constructor(props) {
super(props)
this.state= {
toLogin: false,
}
}
componentDidUpdate(prevProps) {
if (prevProps.ReloginUser.id !== this.props.ReloginUser.id) {
this.toLogin()
}
}
toLogin() {
sessionStorage.removeItem('state')
this.setState({toLogin: true})
}
relogin(user, password) {
this.props.fetchJWT(window.env.GraphQLLogin, user, password, false)
}
render() {
if (this.state.toLogin === true || (!sessionStorage.getItem('jwt') && !sessionStorage.getItem('state'))) {
return (
<Redirect to="/login"/>
)
} else {
const message = { text: 'Fill the form to relogin into the App', view: this.props.jwtHasErrored, header: 'Welcome back to SimCAA'}
return (
<div>
<Modal open={this.props.jwtExpiredOrInvalid}>
<Modal.Header>
Session time out
</Modal.Header>
<Modal.Content>
<LoginForm
login={this.relogin.bind(this)}
message={message}
loading={this.props.jwtIsLoading}
noMessage
/>
</Modal.Content>
<Modal.Actions>
<Button negative onClick={this.toLogin.bind(this)}>Exit the app</Button>
</Modal.Actions>
</Modal>
<WrappedComponent {...this.props} />
</div>
)
}
}
}
}
const mapStateToProps = (state) => {
return {
jwtExpiredOrInvalid: state.jwtExpiredOrInvalid,
jwtHasErrored: state.jwtHasErrored,
jwtIsLoading: state.jwtIsLoading,
ReloginUser: state.user,
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchJWT: (url, user, password, reLogin) => dispatch(jwtFetchData(url, user, password, reLogin)),
openModal: (value) => dispatch(jwtExpiredOrInvalid(value)),
}
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
ReloginModal
)
import React, { Component } from 'react'
import { Button, Segment, Dropdown } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
class LanguageSwitcher extends Component {
render() {
......@@ -20,7 +20,7 @@ class LanguageSwitcher extends Component {
</Dropdown>
)
}
return (
<Segment basic>
<Button onClick={() => changeLanguage('it')}>Italiano</Button>
......
import React, { Component } from 'react'
import { withApolloFetch } from '../withApolloFetch'
import { withRouter, Route, Redirect, HashRouter } from 'react-router-dom'
import { withCurrentUser} from '../withCurrentUser'
import { translate, Trans } from 'react-i18next'
import { Container, Menu, Segment, Grid } from 'semantic-ui-react'
import { withRouter, Route, Redirect } from 'react-router-dom'
import { translate } from 'react-i18next'
import { Grid } from 'semantic-ui-react'
import {connect} from 'react-redux'
import {saveTeams,saveRoles,saveUserTeams} from '../store/actions/AdministrationActions'
......@@ -13,6 +11,8 @@ import RootModal from './containers/RootModal'
import RootComponent from './containers/RootComponent'
import Helper from './components/Helper'
import ReloginModal from '../HOC/ReloginModal'
class Admin extends Component{
isLogged(Component) {
let jwt = sessionStorage.getItem('jwt')
......@@ -28,6 +28,8 @@ class Admin extends Component{
this.props.saveUserTeamsToStore
}
render(){
// <Route path="/admin/home" render={this.isLogged.bind(this, Helper)}/>
// <Route path="/admin/overview" render={this.isLogged.bind(this, RootComponent)} />
return (
<div>
<AdminMenu/>
......@@ -37,8 +39,8 @@ class Admin extends Component{
<Route exact path="/admin" render={() => (
<Redirect to="/admin/home" />
)}/>
<Route path="/admin/home" render={this.isLogged.bind(this, Helper)}/>
<Route path="/admin/overview" render={this.isLogged.bind(this, RootComponent)} />
<Route path="/admin/home" component={ReloginModal(Helper)}/>
<Route path="/admin/overview" component={ReloginModal(RootComponent)} />
</Grid.Column>
</Grid.Row>
</Grid>
......
import React, { Component } from 'react'
import { Modal, Button, Icon, Form, Message, Popup, Transition } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import { withApolloFetch } from '../../withApolloFetch'
import { withRouter} from 'react-router-dom'
......@@ -108,9 +108,9 @@ class AddUserModal extends Component{
this.setState({missingData: false});
}
render(){
const { t, i18n } = this.props
const { t } = this.props
return(
<Modal trigger={<Popup trigger={<Button circular floated="right" onClick={()=>this.setState({open:true})} icon={<Icon circular name="add user" size="big"/>} onClick={()=>this.setState({open:true})}/>} content={t("POPUP_ADD")}/>} open={this.state.open} onClose={()=>this.close()}>
<Modal trigger={<Popup trigger={<Button circular floated="right" onClick={()=>this.setState({open:true})} icon={<Icon circular name="add user" size="big"/>} />} content={t("POPUP_ADD")}/>} open={this.state.open} onClose={()=>this.close()}>
<Modal.Header>{t("ALLGROUPS_ADDUSR_H")}</Modal.Header>
<Modal.Content>
<p>{t("ADDUSR_HELPER")}</p>
......
import React, { Component } from 'react'
import {Header, Dimmer, Segment, Loader, Message} from 'semantic-ui-react'
import { translate } from 'react-i18next'
import {withApolloFetch} from '../../withApolloFetch'
import UsrTable from './UsrTable'
import AddUserModal from './AddUserModal'
class AllGroups extends Component{
constructor(props){
super(props)
this.update = this.update.bind(this)
this.showError = this.showError.bind(this)
this.state={
lock: 0,
users: [],
teams: [],
groups: [],
interror: false
}
}
update(){
this.componentWillMount();
}
showError(){
this.setState({interror: true});
}
componentWillMount(){
let query = `
query getAllUsers {
caa_users {
data{
id
name
user
email
organization
link_web
role_id
}
}
roles{
data {
id
role_desc
}
}
user_teams{
data{
userteam_id
user_id
team_id
}
}
team{
data {
id
name
}
}
}
`
this.props.apolloFetch({query})
.then((data)=>{
this.setState({
lock:1,
users: data.data.caa_users.data,
roles: data.data.roles.data,
teams: data.data.team.data,
user_teams: data.data.user_teams.data
})
})
.catch((error)=>{
console.log(error);
})
}
cancelError(){
this.setState({
interror: false
})
}
findUser(user_id){
return this.state.users[this.state.users.findIndex((usr)=>usr.id===user_id)]
}
render(){
const { t } = this.props
if(this.state.lock===0){
return(
<Dimmer active>
<Loader size='massive'>{t("LOADING")}</Loader>
</Dimmer>
)
}else{
let teamTables = this.state.teams.map((team, idx)=>{
let usrs = []
//creo lista di utenti appartenenti a quel gruppo
for(let i=0; i<this.state.user_teams.length; i++){
if(this.state.user_teams[i].team_id === team.id){
usrs.push(this.findUser(this.state.user_teams[i].user_id))
}
}
let margin = ((idx===0) ? '10px' : '65px')
//aggiungo tabella di questo team
return(
<Segment style={{marginTop: margin}} key={idx}>
<Header as="h4">{team.name}</Header>
<UsrTable update={this.update} action='delete' users={usrs} userTeam={this.state.user_teams} team={team.id} roles={this.state.roles} showError={this.showError}/>
<AddUserModal team={team.id} update={this.update.bind(this)} userTeam={this.state.user_teams} showError={this.showError.bind(this)}/>
</Segment>
)
});
//renderizzo tutte le tabelle
return(
<div>
{teamTables}
<Message negative compact style={{position: 'absolute', left:'50%', transform: 'translate(-50%)'}} hidden={!this.state.interror} onDismiss={()=>this.cancelError()}>
<Message.Header> {t("INTERROR_HEADER")} </Message.Header>
<p>{t("INTERROR_BODY")}</p>
</Message>
</div>
)
}
}
}
export default withApolloFetch(translate('translations') (AllGroups))
import React, { Component } from 'react'
import {Table, Button, Popup, Icon, Confirm} from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import {withApolloFetch} from '../../withApolloFetch'
class UsrTable extends Component{
......@@ -74,7 +74,7 @@ class UsrTable extends Component{
render(){
console.log(this.props);
const { t, i18n } = this.props
const { t } = this.props
let tableLayout = this.props.users.map((item)=>{
let actionLayout= <div/>
if(this.props.action==='add'){
......
import React, { Component } from 'react'
import { Modal, Button, Icon, Form, Select, Message, Popup } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import { withApolloFetch } from '../../withApolloFetch'
class NewUserForm extends Component{
......@@ -110,7 +110,7 @@ class NewUserForm extends Component{
}
}
render(){
const { t, i18n } = this.props
const { t } = this.props
if(this.state.interror){
//messaggio di errore interno
return(
......
import React, { Component } from 'react'
import {Table, Dimmer, Loader, Button, Icon, Input, Modal, Message} from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import {withApolloFetch} from '../../withApolloFetch'
import {Table, Button, Icon, Input, Modal, Message} from 'semantic-ui-react'
import { translate } from 'react-i18next'
import { withApolloFetch } from '../../withApolloFetch'
import { withRouter } from 'react-router-dom'
class UsrConfig extends Component{
......@@ -32,25 +32,25 @@ class UsrConfig extends Component{
switch(which){
case 0:
paramToUpdate = "name"
value = ((this.state.newName != '') ? this.state.newName : '-1')
value = ((this.state.newName !== '') ? this.state.newName : '-1')
break;
case 1:
paramToUpdate = "email"
value = ((this.state.newEmail != '') ? this.state.newEmail : '-1')
value = ((this.state.newEmail !== '') ? this.state.newEmail : '-1')
break;
case 2:
paramToUpdate = "organization"
value = ((this.state.newOrganization != '') ? this.state.newOrganization : '-1')
value = ((this.state.newOrganization !== '') ? this.state.newOrganization : '-1')
break;
case 3:
paramToUpdate = "link_web"
value = ((this.state.newWeb != '') ? this.state.newWeb : '-1')
value = ((this.state.newWeb !== '') ? this.state.newWeb : '-1')
break;
default:
break;
}
//faccio query solo se il valore da inserire non è vuoto
if(value != '-1'){
if(value !== '-1'){
let query = `
mutation updateUser {
updateCaaUser(id: ${this.props.id},
......@@ -124,7 +124,7 @@ class UsrConfig extends Component{
})
}
render(){
const { t, i18n } = this.props
const { t } = this.props
return(
<Modal trigger={this.props.trigger} onClose={()=>{this.setState({interror: false, missingData: false})}}>
<Modal.Header>{t("USR_CNFG_H")}</Modal.Header>
......
import React, { Component } from 'react'
import { Form, Select, Message, Popup } from 'semantic-ui-react'
import { Form, Select } from 'semantic-ui-react'
import {apolloFetch} from '../../store/apolloFetchUtils'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
class CreateUserForm extends Component {
constructor(props) {
super(props)
}
componentWillMount(){
let tmpRoles = this.props.roles.map((role)=>{
return({
......@@ -21,7 +17,7 @@ class CreateUserForm extends Component {
});
}
render(){
const { t, i18n } = this.props
const { t } = this.props
return(
<Form>
<Form.Field required error={this.props.nameError}>
......
......@@ -2,9 +2,6 @@ import React,{Component} from 'react'
import { Message } from 'semantic-ui-react'
class ErrorMessage extends Component{
constructor(props){
super(props)
}
render(){
return(
<Message negative hidden={this.props.hidden}>
......
import React, { Component } from 'react'
import {Container, Header} from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
class Helper extends Component{
render(){
......
import React, { Component } from 'react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import { Menu } from 'semantic-ui-react'
import { withRouter, Link } from 'react-router-dom'
import LanguageSwitcher from '../../LanguageSwitcher'
......@@ -10,11 +10,9 @@ class AdminMenu extends Component{
this.props.history.push('/')
}
render(){
const { t, i18n } = this.props
const style={
const { t } = this.props
}
return(
return(
<Menu id='navbar'>
<Menu.Item name='header'>
<h3><b>Pannello di Amministrazione</b></h3>
......
......@@ -2,9 +2,6 @@ import React,{Component} from 'react'
import { Message } from 'semantic-ui-react'
class PositiveMessage extends Component{
constructor(props){
super(props)
}
render(){
return(
<Message positive hidden={this.props.hidden}>
......
import React from 'react'
import PropTypes from 'prop-types'
import { Table, Popup, Button, Icon } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
class UsersTable extends React.Component {
constructor(props) {
......@@ -22,7 +19,7 @@ class UsersTable extends React.Component {
this.props.modUser(id)
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let rows = this.props.users.map((item)=>{
return(
<Table.Row key={item.id} onClick={()=>{this.props.selectable ? this.clicked(item.id) : null }}>
......
......@@ -4,7 +4,7 @@ import { Modal,Button } from 'semantic-ui-react'
import ErrorMessage from '../components/ErrorMessage'
import PositiveMessage from '../components/PositiveMessage'
import CreateUserForm from '../components/CreateUserForm'
import {apolloFetch, apolloFetchNoAuth} from '../../store/apolloFetchUtils'
import {apolloFetch} from '../../store/apolloFetchUtils'
class CreateUserModal extends Component {
constructor(props){
......
import React, { Component } from 'react'
import { Modal,Button,Message } from 'semantic-ui-react'
import { Modal, Button } from 'semantic-ui-react'
import PositiveMessage from '../components/PositiveMessage'
import NegativeMessage from '../components/ErrorMessage'
......
import React, { Component } from 'react'
import {Header, Dimmer, Segment, Loader, Message, Card, Button} from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { withRouter } from 'react-router-dom'
import {apolloFetch} from '../../store/apolloFetchUtils'
import {Message, Card, Button} from 'semantic-ui-react'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import {openModalCreateUser, openModalAddUser, openModalManageTeam} from '../../store/actions/AdministrationActions'
......@@ -38,7 +36,7 @@ class RootComponent extends Component{
this.props.openModalManageTeam(team_id)
}
render(){
const { t, i18n } = this.props
const { t } = this.props
let teamsLayout = this.props.teams.map((team, idx)=>{
//aggiungo tabella di questo team
return(
......
import React, { Component } from 'react'
import {Modal, Button} from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import {closeModal} from '../../store/actions/AdministrationActions'
......@@ -10,9 +9,6 @@ import AddUserModal from '../containers/AddUserModal'
import ManageTeamModal from '../containers/ManageTeamModal'
class RootModal extends Component{
constructor(props){
super(props)
}
render(){
switch(this.props.content){
case "create_usr":
......
......@@ -5,7 +5,7 @@ import UsersTable from '../components/UsersTable'
import ErrorMessage from '../components/ErrorMessage'
import {Pagination, Loader, Dimmer} from 'semantic-ui-react'
import {apolloFetch, apolloFetchNoAuth} from '../../store/apolloFetchUtils'
import {apolloFetch} from '../../store/apolloFetchUtils'
class UsersPagination extends Component {
constructor(props) {
......@@ -25,7 +25,7 @@ class UsersPagination extends Component {
}
componentDidMount(){
//fetch first users page
if(this.state.activePage == 1) this.queryNewPage()
if(this.state.activePage === 1) this.queryNewPage()
}
queryNewPage(){
let query=`
......
......@@ -71,11 +71,11 @@ class CardLayout extends Component {
let srcImg = ''
if (this.props.Card.custom !== undefined && this.props.Card.custom === true) {
srcImg = window.env.CustomImage
srcImg = window.env.CustomImage + this.state.card.img
} else if (this.props.urlImg) {
srcImg = this.props.urlImg
srcImg = this.props.urlImg + this.state.card.img
} else {
srcImg = window.env.PathImages
srcImg = window.env.PathImages + this.state.card.img
}
let styles = {}
......@@ -90,12 +90,18 @@ class CardLayout extends Component {
styles = this.state.borderCard[this.state.card.codClass]
}
let classNameInput
if (this.state.card.water) {
classNameInput = this.props.formatInput + ' ' + this.props.weightInput + ' ' + this.props.decorationInput + ' ' + this.props.fontStyleInput + ' colorTextInput colorBackgroundInput input-water'
} else {
classNameInput = this.props.formatInput + ' ' + this.props.weightInput + ' ' + this.props.decorationInput + ' ' + this.props.fontStyleInput + ' colorTextInput colorBackgroundInput'
}
let cardInput = <Card.Content>
<Input
size= {this.props.sizeInput}
disabled={this.props.mode}
transparent={inputBorder}
className={this.props.formatInput + ' ' + this.props.weightInput + ' ' + this.props.decorationInput + ' ' + this.props.fontStyleInput + ' colorTextInput colorBackgroundInput'}
className={classNameInput}
id = {'textLayout-' + this.state.card.id}
value = {this.state.card.lemma}
/>
......@@ -103,18 +109,19 @@ class CardLayout extends Component {
let cardImage = <Card.Content className={this.props.imgPadding}>
<Image
src={srcImg + this.state.card.img}
src={this.state.card.water ? window.env.WaterImages + this.state.card.img : srcImg}
className={imgFullWidth}
size={this.props.imgSize}
/>
</Card.Content>
let classNameCard = this.state.card.water ? 'uicardlayout uicardlayout-water' : 'uicardlayout'
if (this.props.posInput === 'bottom' && this.props.isTypo === true) {
return(
<Card
id={this.props.isTitle ? null : 'layout-' + this.state.card.id}
style={{...styles}}
className='uicardlayout'
className={classNameCard}
onClick={this.props.onClick}
>
{cardImage}
......@@ -127,7 +134,7 @@ class CardLayout extends Component {
<Card
id={this.props.isTitle ? null : 'layout-' + this.state.card.id}
style={{...styles}}
className="uicardlayout"
className={classNameCard}
onClick={this.props.onClick}
>
{cardInput}
......
import React, { Component } from 'react'
import { Button, Form, Segment, Grid, Message, Image } from 'semantic-ui-react'
import PropTypes from 'prop-types'
import { Button, Form, Segment, Grid, Message } from 'semantic-ui-react'
import logo from '../assets/simcaa.png'
// import logo from '../assets/simcaa.png'
class LoginForm extends Component {
constructor(props) {
......@@ -40,6 +41,14 @@ class LoginForm extends Component {
disable = true
}
// TODO: DELETE WHEN MESSAGE NOT NEEDED
let style
if (this.props.noMessage) {
style = {'display': 'none'}
} else {
style = {'maxWidth': '50%', 'left': '50%', 'transform': 'translateX(-50%)'}
}
return (
<div>
<Grid
......@@ -91,7 +100,7 @@ class LoginForm extends Component {
</Grid.Column>
</Grid>
<Segment style={{'maxWidth': '50%', 'left': '50%', 'transform': 'translateX(-50%)'}}>
<Segment style={style}>
<Message
color='blue'
size='massive'
......@@ -118,4 +127,10 @@ class LoginForm extends Component {
}
LoginForm.propTypes = {
login: PropTypes.func.isRequired,
message: PropTypes.object.isRequired,
loading: PropTypes.bool.isRequired,
}
export default LoginForm
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Table, Icon } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import NewProfileForm from '../../containers/homepage/NewProfileForm'
......@@ -13,7 +13,7 @@ class HomepageLayoutTable extends Component {
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let allProfiles = this.props.profile
allProfiles = allProfiles.map((item, index) => {
......
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Menu, Dropdown } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import { Link, withRouter } from 'react-router-dom'
import UploadSymbol from '../../containers/UploadSymbol'
......@@ -9,24 +10,40 @@ import NewProfileForm from '../../containers/homepage/NewProfileForm'
import TeamSwitcher from '../../containers/switcher/TeamSwitcher'
class HomepageProject extends Component {
constructor(props) {
super(props)
this.beforeUnloadFunction = this.beforeUnloadFunction.bind(this)
}
// Logout...cancella il jwt dal session storage e redirige sulla login
Logout() {
window.removeEventListener("beforeunload", this.beforeUnloadFunction)
sessionStorage.removeItem('state')
sessionStorage.removeItem('jwt')
this.props.history.push('/')
}
componentDidMount() {
// TODO: Da attivare in fase finale per evitare refresh o chiusura della scheda del browser
window.addEventListener("beforeunload", this.beforeUnloadFunction)
}
beforeUnloadFunction(e) {
e.preventDefault()
e.returnValue = ''
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let dropdownOptions = ''
if (this.props.user.role_id === 1) {
dropdownOptions = <Dropdown.Item as={Link} to="/admin">Admin</Dropdown.Item>
}
return (
<Menu>
<Menu.Menu position='right'>
<LanguageSwitcher type='dropdown' />
<TeamSwitcher type='menu' />
<Dropdown item text={t("HOME_NAVBAR_MANAGE")}>
<Dropdown.Menu>
<UploadSymbol type='dropdown' user={this.props.user} />
......@@ -37,9 +54,12 @@ class HomepageProject extends Component {
/>
</Dropdown.Menu>
</Dropdown>
<Dropdown item text={t("HOME_NAVBAR_USER")}>
<TeamSwitcher type='menu' />
<Dropdown item text={this.props.user.user}>
<Dropdown.Menu>
<Dropdown.Item>{t("HOME_NAVBAR_USER_PROFILE")}</Dropdown.Item>
{
// <Dropdown.Item>{t("HOME_NAVBAR_USER_PROFILE")}</Dropdown.Item>
}
{dropdownOptions}
<Dropdown.Item onClick={this.Logout.bind(this)}>{t("HOME_NAVBAR_USER_LOGOUT")}</Dropdown.Item>
</Dropdown.Menu>
......@@ -50,4 +70,8 @@ class HomepageProject extends Component {
}
}
HomepageProject.propTypes = {
user: PropTypes.object.isRequired
}
export default translate('translations')(withRouter(HomepageProject))
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Table, Pagination, Input, Icon, Button, List, Radio } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { Table, Pagination, Input, Icon, Button, List, Radio, Loader } from 'semantic-ui-react'
import { translate } from 'react-i18next'
import { Link } from 'react-router-dom'
import NewProjectForm from '../../containers/homepage/NewProjectForm'
......@@ -86,23 +86,28 @@ class HomepageProjectTable extends Component {
this.props.confirm(id)
}
// MAIN RENDER
render() {
const { t, i18n } = this.props
const { t } = this.props
let numColumnsTable = 7
// body table for the projects
let allProjects = this.state.projects
allProjects = allProjects.map((item, index) => {
return (
<Table.Row key={index}>
<Table.Cell collapsing>{item.proj_id}</Table.Cell>
<Table.Cell collapsing>
<Link to={'/project/' + item.proj_id}>{item.proj_name}</Link>
</Table.Cell>
<Table.Cell collapsing><Link to={'/project/' + item.proj_id}>{item.proj_name}</Link></Table.Cell>
<Table.Cell>{item.updated_at}</Table.Cell>
<Table.Cell collapsing>{item.proj_share === 1 ? t("MAIN_TBL_PRIVATE") : t("MAIN_TBL_PUBLIC")}</Table.Cell>
<Table.Cell collapsing textAlign='center'>{item.user_name}</Table.Cell>
<Table.Cell collapsing textAlign='center'>{item.team_name}</Table.Cell>
<Table.Cell collapsing textAlign='right' className='fix-display'>
<Icon name='clone'
size='big'
className='icon-pointer'
onClick={this.props.duplicateProject.bind(this, item)}
/>
<NewProjectForm
className='icon-pointer'
size='big'
......@@ -124,6 +129,13 @@ class HomepageProjectTable extends Component {
)
})
// Loader for the project table
let loadingTableBody = <Table.Row>
<Table.HeaderCell colSpan={numColumnsTable}>
<Loader active inline='centered' size='massive' />
</Table.HeaderCell>
</Table.Row>
return (
<Table celled striped color='blue' sortable>
<Table.Header>
......@@ -177,8 +189,8 @@ class HomepageProjectTable extends Component {
</Table.Row>
<Table.Row>
<Table.HeaderCell
sorted={this.state.column === 'id' ? this.state.direction : null}
onClick={this.handleSort.bind(this, 'id')}
sorted={this.state.column === 'proj_id' ? this.state.direction : null}
onClick={this.handleSort.bind(this, 'proj_id')}
>
ID
</Table.HeaderCell>
......@@ -212,7 +224,7 @@ class HomepageProjectTable extends Component {
</Table.Header>
<Table.Body>
{allProjects}
{this.props.loading ? loadingTableBody : allProjects}
</Table.Body>
<Table.Footer>
......@@ -236,11 +248,14 @@ HomepageProjectTable.propTypes = {
confirm: PropTypes.func.isRequired,
changePage: PropTypes.func.isRequired,
searchProject: PropTypes.func.isRequired,
duplicateProject: PropTypes.func.isRequired,
resetSearch: PropTypes.func.isRequired,
project: PropTypes.array.isRequired,
total: PropTypes.number.isRequired,
page: PropTypes.number.isRequired,
loading: PropTypes.bool,
user: PropTypes.object.isRequired,
teamID: PropTypes.number.isRequired,
optionsLayouts: PropTypes.array,
optionsProfiles: PropTypes.array,
checkedRadioFilter: PropTypes.string.isRequired
......
......@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import { Form, Dropdown, Grid, Divider, Button } from 'semantic-ui-react'
import { ChromePicker } from 'react-color';
import CardLayout from '../CardLayout'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
class CardOptions extends Component {
constructor(props) {
......@@ -46,7 +46,7 @@ class CardOptions extends Component {
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let card = {id:0 ,
lemma: 'React',
......
......@@ -2,7 +2,7 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Form, Dropdown, Grid } from 'semantic-ui-react'
import CardLayout from '../CardLayout'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
class ImageOptions extends Component {
constructor(props) {
......@@ -31,7 +31,7 @@ class ImageOptions extends Component {
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let card = {id:0 ,
lemma: 'React',
......
......@@ -2,7 +2,7 @@ import React, { Component } from 'react'
import { Form, Dropdown, Segment } from 'semantic-ui-react'
import { ChromePicker } from 'react-color';
import CardLayout from '../CardLayout'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import PropTypes from 'prop-types'
class InputOptions extends Component {
......@@ -47,7 +47,7 @@ class InputOptions extends Component {
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let card = {id:0 ,
lemma: 'React',
......
import React, { Component } from 'react'
import { Form, Dropdown, Segment, Icon, Loader } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { Form, Dropdown, Segment, Icon } from 'semantic-ui-react'
import { translate } from 'react-i18next'
import { withApolloFetch } from '../../withApolloFetch'
import PropTypes from 'prop-types'
......@@ -38,7 +38,7 @@ class ImageOptions extends Component {
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let priorityList = this.props.order
priorityList = priorityList.map((item, index) => {
......
......@@ -2,7 +2,9 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import { Icon, Button, Card, Confirm } from 'semantic-ui-react'
import { translate, Trans } from 'react-i18next'
import { translate } from 'react-i18next'
import NewChapterForm from './NewChapterForm'
class ChapterUI extends Component {
constructor(props) {
......@@ -22,12 +24,18 @@ class ChapterUI extends Component {
this.props.deleteChapter(this.props.chapter.id)
}
// Force unlock of the chapter
forceUnlockChapter(id, event) {
this.props.forceUnlockChapter(id)
}
// Duplicate the chapter
duplicateChapter(id, e) {
this.props.duplicateChapter(id)
}
render() {
const { t, i18n } = this.props
const { t } = this.props
let ifBlocked = this.props.chapter.chapt_user_block === 0 ? false : true
let contentButton
......@@ -51,7 +59,24 @@ class ChapterUI extends Component {
return (
<Card style={{'textAlign': 'center', 'width': 'auto'}}>
<Card.Content header={'Chapt id: ' + this.props.chapter.id}/>
<Card.Content extra className='extra-alpha-1'>
<Icon name='clone' size='big'