My directory structure is like this:
.
├─ README.md
├─ contact.md
├─ about.md
├─ foo/
| ├─ test/
| | ├─ README.md
| | ├─ three.md
| | └─ four.md
│ ├─ README.md
│ ├─ one.md
│ └─ two.md
└─ bar/
├─ README.md
└─ five.md
How do I update configuration to define my sidebar for each section and folder?
You can read full post here.
The Setup
$ npm install -g vue-cli
$ vue init webpack <project-name>
$ npm install --save vuex gsap
Vuex module
const types = {
TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR'
}
// initial state
const state = {
sidebarOpen: false
}
// getters
const getters = {
sidebarOpen: state => state.sidebarOpen
}
// actions
const actions = {
toggleSidebar ({ commit, state }) {
commit(types.TOGGLE_SIDEBAR)
}
}
// mutations
const mutations = {
[types.TOGGLE_SIDEBAR] (state) {
state.sidebarOpen = !state.sidebarOpen
}
}
export default {
state,
getters,
actions,
mutations
}
Store
import Vue from 'vue'
import Vuex from 'vuex'
import ui from './modules/ui'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules: {
ui
},
strict: debug
})
You can see we import the UI module and add it as a module in the
exported Vuex.Store object. Now, all we have to do is add it to our
Vue instance.
import Vue from 'vue'
import App from './App'
import store from './store/index.js'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
template: '<App/>',
components: { App }
})
Components (Files) ...
APP.vue
<template>
<div id="app">
<div :class="$style.container">
</div>
<sidebar/>
<sidebar-toggle/>
</div>
</template>
<script>
import Sidebar from '#/components/sidebar.vue'
import SidebarToggle from '#/components/sidebarToggle.vue'
export default {
name: 'app',
components: {
Sidebar, SidebarToggle
}
}
</script>
<style>
:root{
--accent-color: #FFCB08;
--primary-color: #820263;
--dark-color: #2E294E;
}
*{
box-sizing: border-box;
}
</style>
<style module>
.container{
position: fixed;
left: 0;
top: 0;
height: 100vh;
width: 100vw;
background-color: var(--primary-color);
}
</style>
sidebar toogle
<template>
<button :class="[open ? $style.active : '', $style.button]" #click="handleClick">
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
</svg>
</button>
</template>
<script>
import {TweenMax, Power4} from 'gsap'
export default {
name: 'sidebar-toggle',
computed: {
open () {
return this.$store.state.ui.sidebarOpen
}
},
methods: {
handleClick () {
this.$store.dispatch('toggleSidebar')
}
}
}
</script>
sidebar
<template>
<div :class="$style.sidebar"/>
</template>
<script>
import {TweenMax, Power4} from 'gsap'
export default {
name: 'sidebar',
mounted () {
TweenMax.set(this.$el, {
x: this.$el.offsetWidth
})
},
computed: {
open () {
return this.$store.state.ui.sidebarOpen
}
},
watch: {
open: function (open) {
const dX = open ? 0 : this.$el.offsetWidth
TweenMax.to(this.$el, 0.6, {
x: dX,
ease: Power4.easeOut
})
}
}
}
</script>
Demo Structure for routes
{//For Dropdown
"name":"Settings",
"route":undefined,
"icon":"/assets/icons/ic_settings_white_24px.svg",//"/assets/icons/ic_settings_black_24px.svg"
"children":[
{
"name":"Generate Code",
"route":"settings/code"
},
{
"name":"Subscription Layout",
"route":"settings/layout"
},
{
"name":"Plans",
"route":"settings/plans"
}
]
}
{//For Routes
"name":"Settings",
"route":"/settings",
"icon":"/assets/icons/ic_settings_white_24px.svg",//"/assets/icons/ic_settings_black_24px.svg"
"children":[]
}
You can see an example of how to achieve this in the docs here - https://vuepress.vuejs.org/theme/default-theme-config.html#multiple-sidebars
Related
I'm having quite a tussle with my code. I've deployed FullCalendar v5 as a Vue 3 component inside a laravel 9 site using Breeze for authentication and Inertia for speedy rendering. I have the Calendar/Pages/Index.vue displaying the calendar and updating events on the calendar with modals from the example, but I cannot manage to get the modals to create events in the database, nor get the events from the database table to display on the calendar. I'm trying to avoid Ajax and use axios to retrieve a JSON feed.
Here's the part of my controller that creates the JSON feed:
public function showEvents(Request $request) {
$event = Event::get(['title','acronym','city','venue','value','start','end']);
return response()->json(["events" => $event]);
}
Here's what the JSON feed returns:
{"events":[{"title":"Test","acronym":"TST","city":"Denver","venue":"Big Venue","value":"$0","start":"2022-10-25 00:00:00","end":"2022-10-28 00:00:00"}]}
And here's my rather massive Calendar/Index.vue:
<template>
<head title="Dashboard" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Resource Calendar Timeline
</h2>
</template>
<div class="py-12">
<div class="max-w-10xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-12 bg-white border-b border-gray-200">
<!--start calendar-->
<div class='demo-app'>
<div class='demo-app-main'>
<FullCalendar
class='demo-app-calendar'
:events="calendarEvents"
:options='calendarOptions'>
<template v-slot:eventContent='arg'>
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
</template>
</FullCalendar>
</div>
</div>
<!--end calendar-->
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
<!--start calendar-->
<script setup lang='ts'>
import BreezeAuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { head, Link } from '#inertiajs/inertia-vue3';
import '#fullcalendar/core/vdom'; // solves problem with Vite
import { defineComponent } from 'vue';
import FullCalendar, { CalendarOptions, EventApi, DateSelectArg, EventClickArg } from '#fullcalendar/vue3';
import dayGridPlugin from '#fullcalendar/daygrid';
import timeGridPlugin from '#fullcalendar/timegrid';
import listPlugin from '#fullcalendar/list';
import interactionPlugin from '#fullcalendar/interaction';
import axios from 'axios';
</script>
<script lang='ts'>
const Demo = defineComponent({
components: {
FullCalendar,
},
data() {
return {
// trying to pull events from DB json -->>
eventSources: [{
url: 'https://l9-v3-breeze-crud.ddev.site/show-events', // use the `url` property
color: 'yellow', // an option!
textColor: 'black' // an option!
}],
calendarEvents: [{
events(title, start, end, callback) {
axios.get('https://l9-v3-breeze-crud.ddev.site/show-events').then(res => {
callback(res.data.events)
})
},
failure: function() {
alert('there was an error while fetching events!');
},
color: 'red',
textColor: 'white',
}],
//end events pull from DB -->
calendarOptions: {
plugins: [
dayGridPlugin,
timeGridPlugin,
listPlugin,
interactionPlugin // needed for dateClick
],
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
},
initialView: 'dayGridMonth',
events: this.getEvents,
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
weekends: true,
select: this.handleDateSelect,
eventClick: this.handleEventClick,
eventsSet: this.handleEvents
} as CalendarOptions,
currentEvents: [] as EventApi[],
}
},
methods: {
getEvents(info, successCallback, failureCallback) {
events(start, end, timezone, callback) {
axios.get('http://localhost:8000/show-events').then(res => {
callback(res.data.eventList)
})
})
},
handleWeekendsToggle() {
this.calendarOptions.weekends = !this.calendarOptions.weekends // update a property
},
handleDateSelect(selectInfo: DateSelectArg) {
let title = prompt('Please enter a new title for your event')
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if (title) {
calendarApi.addEvent({
id: createEventId(),
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay
})
}
},
handleEventClick(clickInfo: EventClickArg) {
if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove()
}
},
handleEvents(events: EventApi[]) {
this.currentEvents = events
},
}
})
export default Demo
</script>
<style lang='css'>
h2 {
margin: 0;
font-size: 16px;
}
ul {
margin: 0;
padding: 0 0 0 1.5em;
}
li {
margin: 1.5em 0;
padding: 0;
}
b { /* used for event dates/times */
margin-right: 3px;
}
.demo-app {
display: flex;
min-height: 100%;
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
.demo-app-sidebar {
width: 300px;
line-height: 1.5;
background: #eaf9ff;
border-right: 1px solid #d3e2e8;
}
.demo-app-sidebar-section {
padding: 2em;
}
.demo-app-main {
flex-grow: 1;
padding: 3em;
}
.fc { /* the calendar root */
max-width: 1200px;
margin: 0 auto;
}
.fc-day-today
{
background-color: var(--fc-today-bg-color, rgba(255, 220, 40, 0.15));
}
.fc-day-sat, .fc-day-sun
{
background-color: var(--fc-today-bg-color, rgba(255, 100, 40, 0.15));
}
</style>
I'm stumped! Either I pull an empty array for events with
events[]
or I get a 500 error denoting that "Module source URI is not allowed in this document: “http://[::1]:5173/resources/js/Pages/Calendar/Index.vue" when I try the axios.get URL
and, not matter what I try, I can't seem to get it to pull the 'calendarEvents' options from
calendarEvents: [{
events(title, start, end, callback) {
axios.get('https://l9-v3-breeze-crud.ddev.site/show-events').then(res => {
callback(res.data.events)
})
},
failure: function() {
alert('there was an error while fetching events!');
},
color: 'red',
textColor: 'white',
}],
Additional Information:
I've been continuing to look for a solution and found several online demo's that resolve the issue at hand, but none with my particular project dependencies. Being a newbie at this, I'm just not certain on how to merge the code. In particular, the structure of the Calendar/Index.vue file taken from the http://fullcalendar.io Vue3 and TypeScript demo here https://github.com/fullcalendar/fullcalendar-example-projects/tree/master/vue3-typescript And in the app.js file from this demo https://www.positronx.io/how-to-display-events-in-calendar-with-laravel-vue-js/
My app.js is complicated by the installation of Ziggy and Inertia and I'm not certain how to construct the Vue rather than a blade. Here's my app.js:
import './bootstrap';
import '../css/app.css';
//import { ZiggyVue } from 'ziggy-vue';
//import route from 'ziggy';
import { createApp, h } from 'vue';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });
Ok, I figured out how to use axios to retrieve events from the JSON feed. The issue wasn't in the app.js routes, but rather in the actual Vue component itself.
Previously unmentioned, here is the route that constructs the JSON feed of events:
Route::get('show-events', [CalendarController::class, 'showEvents']);
Here's the part of my controller that generates that JSON feed for events from the database:
public function showEvents(Request $request) {
$event = Event::get(['title','acronym','city','venue','value','start','end']);
return response()->json(["events" => $event]);
And here's the method part of the Vue component that retrieves events from the JSON feel and publishes them to the calendar:
methods: {
getEvents() {
axios.get('show-events')
.then(response => {
this.calendarOptions.events = response.data.events;
});
},
This is api for maps I would like to use in laravel in a vue component.
app.js
import Vue from 'vue';
import ExampleComponent from "./components/ExampleComponent";
import YmapPlugin from 'vue-yandex-maps';
import YandexComponent from "./components/YandexComponent";
require('./bootstrap');
Vue.use(YmapPlugin)
const app = new Vue({
el: '#app',
components: {
YandexComponent
}
});
YandexComponent.vue
<template>
<yandex-map :coords="coords">
<ymap-marker
marker-id="123"
:coords="coords"
:marker-events="['click']"
></ymap-marker>
</yandex-map>
</template>
<script>
export default {
name: 'YandexComponent',
setup() {
return {
coords: [54, 39],
}
}
}
</script>
<style>
.ymap-container {
height: 600px;
}
</style>
How to use api for laravel/vue https://vue-yandex-maps.github.io/en/
It doesn't want to work...
I am trying to build vue js applications but I am getting following errors .
Severity Code Description Project File Line Suppression State
Warning TS1005 (JS) ':' expected. VuejsApp JavaScript Content Files C:\Users\Khundokar Nirjor\Documents\Visual Studio 2017\Projects\VuejsApp\VuejsApp\src\App.vue 19 Active
This is Home.vue code
<template>
<div id="databinding">
<div id="counter-event-example">
<p style="font-size:25px;">Language displayed : <b>{{ languageclicked }}</b></p>
<button-counter v-for="(item, index) in languages"
v-bind:item="item"
v-bind:index="index"
v-on:showlanguage="languagedisp"></button-counter>
</div>
</div>
</template>
<script>
// import Home from './components/Home.vue';
// import component1 from './components/component1.vue';
export default {
name: 'app',
Vue.components('button-counter', {
template: '<button v-on:click = "displayLanguage(item)"><span style = "font-size:25px;">{{ item }}</span></button>',
data: function () {
return {
counter: 0
}
},
props: ['item'],
methods: {
displayLanguage: function (lng) {
console.log(lng);
this.$emit('showlanguage', lng);
}
},
});
new Vue({
el: '#databinding',
data: {
languageclicked: "",
languages: ["Java", "PHP", "C++", "C", "Javascript", "C#", "Python", "HTML"]
},
methods: {
languagedisp: function (a) {
this.languageclicked = a;
}
}
})
};
</script>
<style>
</style>
I want to display list of buttons and when i clicked the any of them button , I want to display the message that button is clicked.
I believe your error is related to the component. First, the function name is wrong. The correct name is Vue.component and it is Vue.components. Second, your component declaration is not correct.
I created this codepen where you can see how to declare the component globally and locally.
const buttonCounter = {
template:
`<button #click="displayLanguage(item)">
<span style="font-size:25px;">{{ item }}</span>
</button>`,
props: ["item"],
methods: {
displayLanguage: function(lng) {
this.$emit("show-language", lng);
}
}
}
// Declare your component globally, will be able to access it in any another component in the application
Vue.component("button-counter", buttonCounter );
new Vue({
el: "#databinding",
// declarete your component locally, it only will be available inside this context
components:{
'button-counter-2' : buttonCounter
},
data: function() {
return {
languageclicked: "",
languages: ["Java", "PHP", "C++", "C", "Javascript", "C#", "Python", "HTML"]
};
},
methods: {
languageDisp: function(a) {
this.languageclicked = a;
}
}
});
body {
margin: 20px;
}
.btn-wrapper {
display: flex;
margin-bottom: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="databinding">
<div id="counter-event-example">
<p style="font-size:25px;">Language displayed : <b>{{ languageclicked }}</b></p>
<div class="btn-wrapper">
<button-counter v-for="(item, index) in languages" :item="item" :key="item" #show-language="languageDisp"/>
</div>
<button-counter-2 v-for="(item, index) in languages" :item="item" :key="item" #show-language="languageDisp"/>
</div>
</div>
I am having big problems applying classes to my ul elements in React using SCSS and Webpack 4. I have upgraded my project to Webpack 4 ( #yesiamstupid )
If I taget a type of element (ul) it works.
My own class "navMenu" is never applied, though.
I can see the class in the web developer tools --> styles.scss
I expect the text background to be blue in the navigation.
[ app.js ]
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import AppRouter from './routers/AppRouter';
import 'normalize.css/normalize.css';
import './styles/styles.scss';
const jsx = (
<Provider>
<AppRouter />
</Provider>
);
ReactDOM.render(jsx, document.getElementById('app'));
[ AppRouter.js ]
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import StartPage from '../components/StartPage';
import Header from '../components/Header';
const AppRouter = () => (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route path="/" component={StartPage} exact={true} />
</Switch>
</div>
</BrowserRouter>
);
export default AppRouter;
[ StartPage.js ]
import React from 'react';
const StartPage = () => (
<div>
Hello
</div>
);
export default StartPage;
[ Header.js ]
import React from 'react';
import { NavLink } from 'react-router-dom';
const Header = () => (
<header>
<h1>Test WP4</h1>
<ul className="navMenu">
<li><NavLink to="/" activeClassName="is-active" exact={true}>Here we are</NavLink></li>
<li><NavLink to="/undefined" activeClassName="is-active" exact={true}>This route is undefined</NavLink></li>
</ul>
</header>
);
export default Header;
[_base.scss]
html {
font-size: 62.5%;
}
body {
font-family: Helvetica, Arial, sans-serif;
font-size: $m-size;
}
button {
cursor: pointer;
}
button:disabled {
cursor: default;
}
.is-active {
font-weight: bold;
}
[ _settings.scss ]
// Colors
// Spacing
$s-size: 1.2rem;
$m-size: 1.6rem;
$l-size: 3.2rem;
$xl-size: 4.8rem;
$desktop-breakpoint: 45rem;
[ _header.scss ]
ul {
list-style-type: circle;
}
.navMenu {
text-align:center;
background:blue;
padding-top:400px;
}
[ styles.scss ]
#import './base/settings';
#import './base/base';
#import './components/header';
[ webpack.config.js ]
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = () => {
return {
watch: true,
//mode: 'development',
entry: ['babel-polyfill', './src/app.js'],
output: {
path: path.join(__dirname, 'public', 'dist'), //absolute path
filename: 'bundle.js' //name is whatever you want
},
module: {
rules: [{
loader: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/
}, {
test: /\.svg$/,
loader: 'raw-loader'
}, {
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
}
]
},
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "style.css",
//chunkFilename: "chunk.css"
})
],
devtool: 'inline-source-map',
devServer: {
contentBase: path.join(__dirname, 'public'), //absolute path
historyApiFallback: true, //go to index if path not found
publicPath: '/dist' //specify where bundle files liveY
}
};
}
I recommend you to split your tests for css and scss files.
In your code you are using sass-loader for css. Instead use something like this:
{
test: /\.css$/,
include: 'path-to-css-files',
use: [
MiniCssExtractPlugin.loader,
'css-loader'
],
},
{
test: /\.(sa|sc)ss$/,
exclude: 'path-to-css-files',
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
],
},
How to enable the circular progress when user clicks on submit on the login page? I can able to see the loader symbol in the app bar on other pages but I'm not able to activate it on the login page.
We need to add Custom reducer for login page. I did it in the following way.
1.1. Create a new login page. Just copy and paste the admin-on-rest login page code.
1.2. Update the propTypes like below
Login.propTypes = {
...propTypes,
authClient: PropTypes.func,
previousRoute: PropTypes.string,
theme: PropTypes.object.isRequired,
translate: PropTypes.func.isRequired,
userLogin: PropTypes.func.isRequired,
isLogging: PropTypes.bool.isRequired,
};
1.3. Add the below line
function mapStateToProps(state, props) {
return {
isLogging: state.loginReducer > 0
};
}
1.4. Update the login page with below code.
const enhance = compose(
translate,
reduxForm({
form: 'signIn',
validate: (values, props) => {
const errors = {};
const { translate } = props;
if (!values.username) errors.username = translate('aor.validation.required');
if (!values.password) errors.password = translate('aor.validation.required');
return errors;
},
}),
connect(mapStateToProps, { userLogin: userLoginAction }),
);
export default enhance(Login);
1.5. Replace the submit button code
<CardActions>
<RaisedButton type="submit" primary disabled={isLogging} icon={isLogging && <CircularProgress size={25} thickness={2} />} label={translate('aor.auth.sign_in')} fullWidth />
</CardActions>
1.6 The complete code for the login page is
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { propTypes, reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { Card, CardActions } from 'material-ui/Card';
import Avatar from 'material-ui/Avatar';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
import CircularProgress from 'material-ui/CircularProgress';
import { cyan500, pinkA200, white } from 'material-ui/styles/colors';
import defaultTheme, {translate, Notification, userLogin as userLoginAction } from 'admin-on-rest';
const styles = {
main: {
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'center',
},
card: {
minWidth: 300,
},
avatar: {
margin: '1em',
textAlign: 'center ',
},
avatarText:{
verticalAlign:'middle',
fontSize:20,
},
form: {
padding: '0 1em 1em 1em',
},
input: {
display: 'flex',
},
};
function getColorsFromTheme(theme) {
if (!theme) return { primary1Color: cyan500, accent1Color: pinkA200 };
const {
palette: {
primary1Color,
accent1Color,
},
} = theme;
return { primary1Color, accent1Color };
}
// see http://redux-form.com/6.4.3/examples/material-ui/
const renderInput = ({ meta: { touched, error } = {}, input: { ...inputProps }, ...props }) =>
<TextField
errorText={touched && error}
{...inputProps}
{...props}
fullWidth
/>;
class Login extends Component {
login = (auth) => this.props.userLogin(auth, this.props.location.state ? this.props.location.state.nextPathname : '/');
render() {
const { handleSubmit, submitting, theme, translate, isLogging } = this.props;
const muiTheme = getMuiTheme(theme);
const { primary1Color } = getColorsFromTheme(muiTheme);
return (
<MuiThemeProvider muiTheme={muiTheme}>
<div style={{ ...styles.main, backgroundColor: primary1Color }}>
<Card style={styles.card}>
<div style={styles.avatar}>
<div>
<Avatar backgroundColor={white} src="EnsembleGreenLogo.png" size={45} />
</div>
<div>
<span style={styles.avatarText}>Ensemble SmartWAN Manager</span>
</div>
</div>
<form onSubmit={handleSubmit(this.login)}>
<div style={styles.form}>
<div style={styles.input} >
<Field
name="username"
component={renderInput}
floatingLabelText={translate('aor.auth.username')}
disabled={submitting}
/>
</div>
<div style={styles.input}>
<Field
name="password"
component={renderInput}
floatingLabelText={translate('aor.auth.password')}
type="password"
disabled={submitting}
/>
</div>
</div>
<CardActions>
<RaisedButton
type="submit"
primary
disabled={isLogging}
icon={isLogging && <CircularProgress size={25} thickness={2} />}
label={translate('aor.auth.sign_in')}
fullWidth
/>
</CardActions>
</form>
</Card>
<Notification />
</div>
</MuiThemeProvider>
);
}
}
Login.propTypes = {
...propTypes,
authClient: PropTypes.func,
previousRoute: PropTypes.string,
theme: PropTypes.object.isRequired,
translate: PropTypes.func.isRequired,
userLogin: PropTypes.func.isRequired,
isLogging: PropTypes.bool.isRequired,
};
Login.defaultProps = {
theme: defaultTheme,
};
function mapStateToProps(state, props) {
return {
isLogging: state.loginReducer > 0
};
}
const enhance = compose(
translate,
reduxForm({
form: 'signIn',
validate: (values, props) => {
const errors = {};
const { translate } = props;
if (!values.username) errors.username = translate('aor.validation.required');
if (!values.password) errors.password = translate('aor.validation.required');
return errors;
},
}),
connect(mapStateToProps, { userLogin: userLoginAction }),
);
export default enhance(Login);
2.1. Add a new file (src/loginReducer.js) in src folder with the below content
import { USER_LOGIN_LOADING, USER_LOGIN_SUCCESS, USER_LOGIN_FAILURE, USER_CHECK } from 'admin-on-rest';
export default (previousState = 0, { type }) => {
switch (type) {
case USER_LOGIN_LOADING:
return previousState + 1;
case USER_LOGIN_SUCCESS:
case USER_LOGIN_FAILURE:
case USER_CHECK:
return Math.max(previousState - 1, 0);
default:
return previousState;
}
};
3.1 Update the app.js admin tag.
<Admin
menu={createMenus}
loginPage={Login}
dashboard={Dashboard}
appLayout={Layout}
customReducers={{ loginReducer }}
>
3.2 import the login page and login reducers in app.js
import loginReducer from './loginReducer';
import Login from "./Login";