With this guide I couldn't complete my routes and layout :
https://pixinvent.com/demo/vuexy-react-admin-dashboard-template-old/documentation/docs/development/routing
When I want to add or open new routes always shows an error
utils.js:90 Uncaught TypeError: Cannot read properties of undefined (reading 'can')
at utils.js:90:22
at Array.some (<anonymous>)
at checkForVisibleChild (utils.js:86:14)
at canViewMenuGroup (utils.js:98:47)
at VerticalNavMenuItems.js:25:14
at Array.map (<anonymous>)
at VerticalMenuNavItems (VerticalNavMenuItems.js:22:38)
at renderWithHooks (react-dom.development.js:16305:18)
at mountIndeterminateComponent (react-dom.development.js:20074:13)
at beginWork (react-dom.development.js:21587:16)
utils.js
export const resolveHorizontalNavMenuItemComponent = item => {
if (item.children) return 'HorizontalNavMenuGroup'
return 'HorizontalNavMenuLink'
}
export const hasActiveChild = (item, currentUrl) => {
const { children } = item
if (!children) {
return false
}
for (const child of children) {
if (child.children) {
if (hasActiveChild(child, currentUrl)) {
return true
}
}
// Check if the child has a link and is active
if (child && child.navLink && currentUrl && (child.navLink === currentUrl || currentUrl.includes(child.navLink))) {
return true
}
}
return false
}
const checkForVisibleChild = (arr, ability) => {
return arr.some(i => {
if (i.children) {
return checkForVisibleChild(i.children, ability)
} else {
return ability.can(i.action, i.resource)
}
})
}
Dashboards.js Routes
import { lazy } from 'react'
const DashboardAnalytics = lazy(() => import('../../views/dashboard/analytics'))
const DashboardEcommerce = lazy(() => import('../../views/dashboard/ecommerce'))
const DashboardRoutes = [
{
path: '/dashboard/analytics',
element: <DashboardAnalytics />
},
{
path: '/dashboard/ecommerce',
element: <DashboardEcommerce />
}
]
export default DashboardRoutes
Default roots
// ** Router imports
import { lazy } from 'react'
// ** Router imports
import { useRoutes, Navigate } from 'react-router-dom'
// ** Layouts
import BlankLayout from '#layouts/BlankLayout'
import HorizontalLayout from '#layouts/HorizontalLayout'
// ** Hooks Imports
import { useLayout } from '#hooks/useLayout'
// ** Utils
import { getUserData, getHomeRouteForLoggedInUser } from '../utility/Utils'
// ** GetRoutes
import { getRoutes } from './routes'
// ** Components
const Error = lazy(() => import('../views/pages/misc/Error'))
const Login = lazy(() => import('../views/pages/authentication/Login'))
const NotAuthorized = lazy(() => import('../views/pages/misc/NotAuthorized'))
// ** Pages
const Home = lazy(() => import('../views/dashboard/ecommerce'))
const Router = () => {
// ** Hooks
const { layout } = useLayout()
const allRoutes = getRoutes(layout)
const getHomeRoute = () => {
const user = getUserData()
if (user) {
return getHomeRouteForLoggedInUser(user.role)
} else {
return '/login'
}
}
const routes = useRoutes([
{
path: '/',
index: true,
element: <Navigate replace to={getHomeRoute()} />
},
{
path: '/login',
element: <BlankLayout />,
children: [{ path: '/login', element: <Login /> }]
},
{
path: '/auth/not-auth',
element: <BlankLayout />,
children: [{ path: '/auth/not-auth', element: <NotAuthorized /> }],
meta: {
menuCollapsed: true,
publicRoute: true
}
},
{
path: '*',
element: <BlankLayout />,
children: [{ path: '*', element: <Error /> }]
},
...allRoutes
])
return routes
}
export default Router
When I add another layout except for BlankLayout it shows this error
Can't add HorizontalLayout
Tried routes for sample
Add HorizontalLayout but as it uses setlocalstorage cant do it
Related
This is my first time using useRouter and i really dont know how it works.
I want to take props.indexUrl and added dynamiacally into the url below, i have done several ways but always getting error.
import { useRouter } from "next/router";
const Component = () => {
const router = useRouter();
const {
query: { indexUrl },
} = router;
const props = {
indexUrl,
};
return props.indexUrl;
};
export const urls = [
{
url:
"click.myyellowlocal.com/k.php?ai=19202&url=http%3a%2f%2f" +
Component,
},
{
url: "cse.google.vu/url?q=https%3A%2F%2",
},
{
url: "sazhs.co.kr/member/login.html?noMemberOrder=&returnUrl=http%3a%2f%2",
},
];
I have try like this before
import { useRouter } from "next/router";
const router = useRouter();
const {
query: { indexUrl },
} = router;
const props = {
indexUrl,
};
export const urls = [
{
url:
"click.myyellowlocal.com/k.php?ai=19202&url=http%3a%2f%2f" +
props.indexUrl,
},
{
url: "cse.google.vu/url?q=https%3A%2F%2",
},
{
url: "sazhs.co.kr/member/login.html?noMemberOrder=&returnUrl=http%3a%2f%2",
},
];
and give an error Cannot read properties of null (reading 'useContext')
so i make like this
const Component = () => {
const router = useRouter();
const {
query: { indexUrl },
} = router;
const props = {
indexUrl,
};
};
but i dont know how to get props.indexUrl to url like in the top code.
more detail, the router indexUrl is from other file
function sendProps() {
Router.push({
// pathname: "/processUrl",
pathname: "/urlList",
query: { indexUrl },
});
}
how to get the value of props.indexUrl and add it into the list of url dynamiacally ??
I am trying to get a dynamic title for useMeta with composition API but it does not work.
<script setup>
import { computed } from 'vue'
import { POST } from '#/constants/blog'
import { useQuery, useResult } from "#vue/apollo-composable";
import { useRoute } from 'vue-router'
import { useMeta } from "vue-meta";
const route = useRoute();
const variables = computed(() => ({
slug: route.params.slug,
}));
const { result, loading, error } = useQuery(
POST, variables
);
const post = useResult(result, null, data => data.post.data );
const metaTitle = computed(() => ({
title: post.attributes.title,
}));
useMeta(metaTitle);
</script>
here is the response
{
"data": {
"post": {
"data": {
"id": 4,
"attributes": {
"title": "This is the post title"
}
}
}
}
}
Please help me understand what is wrong here!
Maybe It's too late to answer this question.
This module is for vue2. After many searches, I found version 3 of this module, But It's at the alpha stage, now.
I found an alternative solution that doesn't need any dependency.
Create a new file somewhere in your project directory(utils.js) and put the below code in it:
const changeMetaTags = (meta) => {
document.title = `${meta.title} - YOUR PROJECT NAME`;
// document.querySelector('meta[name="og:title"]').setAttribute("content", meta['og:title']);
// document.querySelector('meta[name="description"]').setAttribute("content", meta.description);
// document.querySelector('meta[name="og:description"]').setAttribute("content", meta['og:description']);
// document.querySelector('meta[name="keywords"]').setAttribute("content", meta.keywords);
}
export { changeMetaTags }
Caution: You have to have the above code on your index.html file.
and for your use case just import it and use:
<script setup>
import { computed } from 'vue'
import { POST } from '#/constants/blog'
import { useQuery, useResult } from "#vue/apollo-composable";
import { useRoute } from 'vue-router'
import { useMeta } from "vue-meta";
import { changeMetaTags } from "#/infrastructures/seo/utils"; // <----- this
const route = useRoute();
const variables = computed(() => ({
slug: route.params.slug,
}));
const { result, loading, error } = useQuery(
POST, variables
);
const post = useResult(result, null, data => data.post.data );
const metaTitle = computed(() => ({
title: post.attributes.title,
}));
changeMetaTags(metaTitle.value); // <---- this
</script>
I use it in the router file (router/index.js) as well
const router = createRouter({
routes: [
{
path: "/",
component: () => import("#/layouts/MainLayout.vue"),
children: [
{
path: "",
name: "Home",
meta: { // <-------- add this
title: "Home",
description:
"your description",
"og:title": `YOUR PROJECT NAME home page`,
"og:description":
"og description",
keywords:
`your, keywords`,
},
component: () => import("#/views/HomeView.vue"),
},
...
]
})
router.beforeEach((to, from) => {
changeMetaTags(to.meta); // <----- and this
...
})
I'm fairly new to React testing library and am using a function within a useEffect to decode a user token from keycloak when they sign up, to determine what type of user they are and render different Menu's based on that. This new Jwt function I created though, is causing a lot of my previously working tests within other files to fail as it throws an error like below:
I'm sure how to deal with this error on the testing side, should I be mocking this decoder function within the test file?
This is my main file:
import React, {useEffect, useState, useMemo, useCallback} from "react";
import {withTranslation} from "react-i18next";
import "./index.scss";
import {historyObject} from "historyObject";
import {Heading} from "#xriba/ui";
import { keycloak } from "utils/keycloak";
export const Menu = (props) => {
const {t} = props;
const [userHasCommunities, setUserHasCommunities] = useState(false);
useEffect(() => {
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
if (JSON.parse(jsonPayload).communities?.length > 0){
setUserHasCommunities(true)
}
return JSON.parse(jsonPayload);
};
parseJwt(keycloak.token);
}, [userHasCommunities])
const buildItem = useCallback((item) => {
const isCurrent = historyObject.location.pathname.includes(item.path);
return <li key={item.path} className={isCurrent ? "selected" : ""} onClick={() => historyObject.push(item.path)}>
<Heading level={"4"}>{t(item.title)}</Heading>
</li>
}, [t])
const renderMenu = useMemo(() => {
let menuItems = []
if (userHasCommunities){
menuItems = [
{
path: "/portfolio",
title: "pages.portfolio.menu_entry"
},
{
path: "/transactions",
title: "pages.transactions.menu_entry"
},
{
path: "/community",
title: "pages.community.menu_entry"
},
{
path: "/reports",
title: "pages.reports.menu_entry"
}
]
} else {
menuItems = [
{
path: "/portfolio",
title: "pages.portfolio.menu_entry"
},
{
path: "/transactions",
title: "pages.transactions.menu_entry"
},
{
path: "/reports",
title: "pages.reports.menu_entry"
}
]
}
return menuItems.map(i => buildItem(i))
}, [userHasCommunities, buildItem])
return <div className={"menu"}>
<ul>
{renderMenu}
</ul>
</div>
}
export default withTranslation()(Menu)
And my current test file:
import React from "react";
import {shallow} from "enzyme";
import {Menu} from "features/layouts/logged-in/menu/index";
import {historyObject} from "historyObject";
import { keycloak } from "utils/keycloak";
jest.mock("utils/keycloak");
jest.mock("historyObject")
describe("Menu test", () => {
const props = {
t: jest.fn(),
};
beforeEach(() => {
jest.resetAllMocks();
jest.spyOn(React, "useEffect").mockImplementationOnce(x => x()).mockImplementationOnce(x => x()).mockImplementationOnce(x => x()).mockImplementationOnce(x => x());
});
it('should render Menu', () => {
const wrapper = shallow(<Menu {...props} />);
expect(wrapper).toBeDefined();
wrapper.find('li').at(0).prop('onClick')();
expect(historyObject.push).toHaveBeenCalled()
});
it('should render Menu with an object as current', () => {
historyObject.location.pathname = "/portfolio"
const wrapper = shallow(<Menu {...props} />);
expect(wrapper).toBeDefined();
expect(wrapper.find('.selected')).toHaveLength(1);
});
});
Thanks in advance for any advice!
I'm using Strapi as a Headless CMS and building my frontend with Gatsby + Graphql. I have a "blocks renderer" component that is rendering any of the dynamic zones in strapi.
import React from "react"
import { graphql } from "gatsby"
import BlockHero from "./block-hero"
import BlockParagraph from "./block-paragraph"
import BlockSplitFeature from "./block-split-feature"
const componentsMap = {
// STRAPI__COMPONENT_LAYOUT_ELEMENTS_MULTIPLE_CALLOUT: blockMultipleCallout,
STRAPI__COMPONENT_LAYOUT_ELEMENTS_SIMPLE_PARAGRAPH: BlockParagraph,
STRAPI__COMPONENT_LAYOUT_ELEMENTS_SPLIT_FEATURE: BlockSplitFeature,
STRAPI__COMPONENT_MEDIA_ELEMENT_HERO: BlockHero,
// STRAPI__COMPONENT_META_DATA_DEFAULT_SEO: blockSeo
}
const Block = ({ block }) => {
const Component = componentsMap[block.__typename]
if(!Component) {
return null
}
return <Component data={block} />
}
const BlocksRenderer = ({ blocks }) => {
return (
<div>
{blocks.map((block, index) => (
<Block key={`${index}${block.__typename}`} block={block} />
))}
</div>
)
}
export const query = graphql`
fragment Blocks on STRAPI__COMPONENT_LAYOUT_ELEMENTS_CTASTRAPI__COMPONENT_LAYOUT_ELEMENTS_MULTIPLE_CALLOUTSTRAPI__COMPONENT_LAYOUT_ELEMENTS_SIMPLE_PARAGRAPHSTRAPI__COMPONENT_LAYOUT_ELEMENTS_SPLIT_FEATURESTRAPI__COMPONENT_MEDIA_ELEMENT_HEROUnion {
__typename
... on STRAPI__COMPONENT_LAYOUT_ELEMENTS_MULTIPLE_CALLOUT {
id
MultipleCalloutItem {
id
Heading
Description
}
}
... on STRAPI__COMPONENT_LAYOUT_ELEMENTS_SIMPLE_PARAGRAPH {
id
Text
}
... on STRAPI__COMPONENT_LAYOUT_ELEMENTS_SPLIT_FEATURE {
id
Heading
Description
mediaAlignment
Media {
id
mime
localFile {
childImageSharp {
gatsbyImageData
}
}
alternativeText
}
}
... on STRAPI__COMPONENT_MEDIA_ELEMENT_HERO {
id
Heading
Description
Media {
id
mime
alternativeText
localFile {
url
}
alternativeText
}
}
}
`
export default BlocksRenderer
Then I have my page layout file to generate a page layout (side note, the "Layout" element is just for the navigation & footer. This will be rewritten once I have this page layout file issue fixed)>
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"
import Seo from "../components/seo"
import BlocksRenderer from "../components/blocks-renderer"
const PageLayout = () => {
const { allStrapiPage } = useStaticQuery(graphql`
query {
allStrapiPage {
edges {
node {
id
Name
Slug
Blocks {
...Blocks
}
}
}
}
}
`)
const { Blocks } = allStrapiPage
return (
<Layout>
<div>{allStrapiPage.id}</div>
<h1>{allStrapiPage.Name}</h1>
<BlocksRenderer blocks={allStrapiPage.Blocks} />
</Layout>
)
}
export default PageLayout
I'm dynamically creating pages with a gatsby-node.js file. When I try to access one of the dynamically created slugs, I get an error in the blocks-renderer file that says can't access property "map", blocks is undefined. Anyone have any ideas?
EDIT: Added the additional files mentioned.
gatsby-config.js file below:
/**
* Configure your Gatsby site with this file.
*
* See: https://www.gatsbyjs.com/docs/gatsby-config/
*/
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
module.exports = {
/* Your site config here */
plugins: [
"gatsby-plugin-gatsby-cloud",
"gatsby-plugin-postcss",
"gatsby-plugin-sass",
"gatsby-plugin-image",
"gatsby-plugin-sharp",
"gatsby-transformer-sharp",
"gatsby-transformer-remark",
{
resolve: "gatsby-source-strapi",
options: {
apiURL: process.env.STRAPI_API_URL || "http://localhost:1337",
accessToken: process.env.STRAPI_TOKEN,
collectionTypes: [
"drink",
"category",
{
singularName: "page",
queryParams: {
populate: {
Blocks: {
populate: "*",
MultipleCalloutItem: {
populate: "*",
},
},
PageMeta: {
populate: "*",
},
ParentPage: {
populate: "*",
},
},
},
},
],
singleTypes: [
{
singularName: "global",
queryParams: {
populate: {
DefaultSeo: {
populate: "*",
},
Favicon: {
populate: "*",
},
},
},
},
{
singularName: "homepage",
queryParams: {
populate: {
Blocks: {
populate: "*",
},
},
},
},
],
queryLimit: 1000,
}
},
],
}
home.js (which works as intended).
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"
import Seo from "../components/seo"
import BlocksRenderer from "../components/blocks-renderer"
const HomePage = () => {
const { strapiHomepage } = useStaticQuery(graphql`
query {
strapiHomepage {
Blocks {
...Blocks
}
}
}
`)
const { Blocks } = strapiHomepage
// const seo = {
// metaTitle: title,
// metaDescription: title
// }
return (
<Layout>
<BlocksRenderer blocks={Blocks} />
</Layout>
)
}
export default HomePage
This is the gatsby-node.js file I'm using to generate the pages with the page-layout.js file. Note that I can generate the pages and content, minus the Blocks query.
const path = require('path')
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(
`
query {
allStrapiPage {
edges {
node {
Slug
Name
ParentPage {
Slug
}
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL Query`)
return
}
const pageTemplate = path.resolve(`./src/layouts/page-layout.js`)
result.data.allStrapiPage.edges.forEach(({ node }) => {
const path = node.Slug
createPage({
path,
component: pageTemplate,
context: {
pagePath: path
},
})
})
}
The problem is here:
<BlocksRenderer blocks={allStrapiPage.Blocks} />
You can't access directly to Blocks because you node is an array inside edges property. From what I see, the loop is done in BlocksRenderer hence you need to provide it an array of blocks. Without knowing exactly your data structure and what returns it's difficult to guess but try something like:
<BlocksRenderer blocks={allStrapiPage.edges.node[0].Blocks} />
I have a Home.js file that is using a different property
(allStrapiHomepage) and BlockRenderer is working as expected
If your Home.js query is using a page query instead of a static query they cane be triggered and hydrated in different runtimes and build times, so one can fail if the other doesn't. This leads me to think that maybe the query is ok, but the logic isn't. You can easily check it by adding a simple condition like:
<BlocksRenderer blocks={allStrapiPage?.Blocks} />
Or:
{allStrapiPage.Blocks && <BlocksRenderer blocks={allStrapiPage.Blocks} />}
Thanks to #Ferran I was pointed in the right direction and solved this issue.
Two changes needed to be made for this to work properly. First, I needed to be passing pageContext from the gatsby-node.js file. Here I'm passing the page slug to the template
const { resolve } = require('path')
const path = require('path')
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(
`
query {
allStrapiPage {
edges {
node {
Slug
Name
ParentPage {
Slug
}
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL Query`)
return
}
const pageTemplate = path.resolve('./src/layouts/page-layout.js')
result.data.allStrapiPage.edges.forEach(edge => {
const path = `${edge.node.Slug}`
const parentPath = `${edge.node.ParentPage.Slug}`
createPage({
path,
component: pageTemplate,
context: {
Slug: edge.node.Slug
},
})
resolve()
})
}
Then in the page-layout.js file, I needed to get the pageContext from gatsby-node.js, map all of my page nodes, in the graphql query, and pass the page Slug from gatsby-node.js as a variable in the graphql query.
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"
import Seo from "../components/seo"
import BlocksRenderer from "../components/blocks-renderer"
const PageLayout = ({ data, pageContext }) => {
const { Slug } = pageContext
console.log(Slug)
return (
<Layout>
{
data.allStrapiPage.nodes.map(node => {
return (
<div key={node.id}>
<h1>{node.Name}</h1>
{node.Blocks &&
<BlocksRenderer blocks={node.Blocks} />
}
</div>
)
})
}
</Layout>
)
}
export const query = graphql`
query GetPage($Slug: String) {
allStrapiPage(filter: { Slug: {in: [$Slug]} }) {
nodes {
id
Name
Blocks {
...Blocks
}
}
}
}
`
export default PageLayout
Now I can dynamically create pages with Strapi and the "blocks" I made using dynamic zones.
I am in trouble...
After registration in my application the user need's to create a workspace, I need to oblige the workspace creation otherwise the API will not work, but I am unable to lock it on the workspace creation page.
When I try to check if the user already has a workspace on the first load of the application vuex is still empty because the axios request has not yet been finalized...
How can I be sure that the vue router will wait for axios to receive the api data?
router.js
import Vue from 'vue'
import Router from 'vue-router'
import store from './../store'
import auth from './modules/auth'
import api from './modules/api'
import common from './modules/common'
import projects from './modules/projects'
import wabas from './modules/wabas'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
...auth,
...api,
...common,
...projects,
...wabas,
]
})
router.beforeEach((to, from, next) => {
store.dispatch('auth/setToken').then(() => {
// will return an empty array
console.log(store.getters['workspaces/workspaces'])
if (!store.state.workspaces.workspaces.length && to.name !== 'welcome_a' && to.name !== 'welcome_b' && to.name !== 'welcome_c') {
next({
name: 'welcome_a'
})
} else {
if (to.meta.visitor || to.name === 'welcome_a' || to.name === 'welcome_b' || to.name === 'welcome_c') {
next({
name: 'home'
})
return
}
next()
}
}).catch(() => {
if (to.meta.auth) {
next({
name: 'login'
})
return
}
next()
})
})
export default router
router/modules/common.js
const routes = [
{
path: '/',
name: 'home',
component: () => import('../../pages/Home'),
meta: {
auth: true
}
},
{
path: '/bem-vindo',
name: 'welcome_a',
component: () => import('../../pages/WelcomeA'),
meta: {
auth: true
}
},
{
path: '/finalizar-cadastro',
name: 'welcome_b',
component: () => import('../../pages/WelcomeB'),
meta: {
auth: true
}
},
{
path: '/area-de-trabalho',
name: 'welcome_c',
component: () => import('../../pages/WelcomeC'),
meta: {
auth: true
}
}
]
export default routes
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import localforage from "localforage";
localforage.config({
driver: localforage.LOCALSTORAGE,
storeName: 'positus'
})
Vue.config.productionTip = false
window._ = require('lodash')
import components from './components'
components.forEach(component => {
Vue.component(component.name, component);
});
import helpersMixin from './support/mixins/helpers'
Vue.mixin(helpersMixin)
import notifications from './support/notifications'
Vue.use(notifications)
import bus from './support/bus'
Vue.use(bus)
import VueClipboard from 'vue-clipboard2'
Vue.use(VueClipboard)
import http from './support/http'
Vue.use(http)
store.dispatch('auth/setToken').then(() => {
store.dispatch('auth/fetchSystemData').catch(() => {
store.dispatch('auth/clearAuth')
})
}).catch(() => {
store.dispatch('auth/clearAuth')
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
store/modules/auth.js
import Vue from 'vue'
import authApi from '../../api/auth'
import {setHttpToken} from '../../support/http'
import axios from 'axios'
import localforage from 'localforage'
import router from '../../router'
const state = {
user: {}
}
const getters = {
user(state) {
return state.user
}
}
const actions = {
fetchData({commit}) {
return axios.all([
authApi().user.get()
]).then(axios.spread((user) => {
commit('SET_USER_DATA', user.data.data)
})).catch(error => console.error(error))
},
fetchSystemData({dispatch}) {
return Promise.all([
dispatch('fetchData'),
dispatch('workspaces/fetchData', null, {root: true})
]).finally(() => {
dispatch('app/setProcessing', false, {root: true})
})
},
authenticateUser({commit, dispatch}, data) {
dispatch('app/setProcessing', true, {root: true})
return authApi().login(data)
.then(({data}) => {
dispatch('setToken', data).then(() => {
dispatch('fetchSystemData').then(() => {
router.push({
name: 'home'
})
}).catch(() => {
Vue.$n('Ocorreu um erro ao receber os dados da sua conta, tente novamente mais tarde.', 'error')
})
})
}).catch(() => {
dispatch('app/setProcessing', false, {root: true})
Vue.$n('Algum erro ocorreu na tentativa de acessar sua conta.', 'error')
})
},
setToken({dispatch}, token) {
if (_.isEmpty(token)) {
return dispatch('checkTokenExists').then((token) => {
setHttpToken(token)
})
}
dispatch('setTokenLocalStorage', token)
setHttpToken(token)
return token
},
setTokenLocalStorage({commit}, token) {
if (_.isEmpty(token)) {
localforage.removeItem('token', token)
return
}
localforage.setItem('token', token)
},
checkTokenExists() {
return localforage.getItem('token').then((token) => {
if (_.isEmpty(token)) {
return Promise.reject('NO_STORAGE_TOKEN')
}
return Promise.resolve(token)
})
},
clearAuth({dispatch}) {
Promise.all([
dispatch('setTokenLocalStorage', null)
]).finally(() => {
setHttpToken(null)
router.push({
name: 'login'
})
})
},
updateActiveWorkspace({commit}, data) {
commit('UPDATE_ACTIVE_WORKSPACE', data)
}
}
const mutations = {
SET_USER_DATA(state, user) {
state.user = user
},
UPDATE_ACTIVE_WORKSPACE(state, data) {
state.user.active_workspace = data
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
store/modules/workspaces.js
import workspacesApi from "../../api/workspaces"
import axios from "axios"
const state = {
workspaces: []
}
const getters = {
workspace(state) {
return (id) => {
return _.find(state.workspaces, (workspace) => {
return workspace.id === id
})
}
},
workspaces(state) {
return state.workspaces
}
}
const actions = {
fetchData({commit}) {
return axios.all([
workspacesApi().get()
]).then(axios.spread((workspaces) => {
commit('SET_WORKSPACES', workspaces.data.data)
})).catch(error => console.error(error))
},
setWorkspaces({commit}, data) {
commit('SET_WORKSPACES', data)
},
setWorkspace({commit, state}, data) {
let index = _.findIndex(state.workspaces, (space) => {
return space.id === data.id
})
if (index >= 0) {
commit('UPDATE_WORKSPACE', {
index: index,
data: data
})
} else {
commit('SET_WORKSPACE', data)
}
}
}
const mutations = {
SET_WORKSPACES(state, bool) {
state.workspaces = bool
},
SET_WORKSPACE(state, data) {
state.workspaces.push(data)
},
UPDATE_WORKSPACE(state, data) {
state.workspaces.splice(data.index, 1, data.data);
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
from the title of this question, you can add a hook in vue component that will run when before the route is changed
beforeRouteLeave (to, from, next) {
// called when the route that renders this component is about to
// be navigated away from.
// has access to `this` component instance.
}
if you want user to move to the next route on which he clicked, you will call next() inside this hook and he will be navigated to the next page.
if you want to lock him and not navigate, pass false in this next() method like next(false)
Why don't you just create a middleware server side that redirects the user to the workspace creation page if the user didn't have one yet? Something like that in laravel
class RedirectIfWorkspaceNotCreated
{
public function handle($request, Closure $next, $guard = null)
{
$user = Auth::guard($guard)->user();
if ($user && !checkForUserWorkspace) {
return redirect('/your/workspace/creation/page');
}
return $next($request);
}
}