All plugins in your project are called from your main.js file with the registerPlugins() method.
src/main.js
// Components
import App from "./App.vue";
// Composables
import { createApp } from "vue";
// Plugins
import { registerPlugins } from "@/plugins";
const app = createApp(App);
registerPlugins(app);
app.mount("#app");
The Admin plugin allows the Olobase Admin main library to be included in your project and the basic functions in your project to be configured and run. The following example shows an example of the admin plugin:
src/plugins/admin.js
import OlobaseAdmin from "olobase-admin";
import router from "@/router";
import i18n from "../i18n";
import store from "@/store";
import config from "@/_config";
import routes from "@/router/admin";
import PageNotFound from "@/views/Error404.vue";
import {
jsonServerDataProvider,
jwtAuthProvider,
} from "olobase-admin/src/providers";
import { en, tr } from "olobase-admin/src/locales";
let admin = new OlobaseAdmin(import.meta.env);
/**
* Install admin plugin
*/
export default {
install: (app, http, resources) => {
admin.setOptions({
app,
router,
resources,
store,
i18n,
dashboard: "dashboard",
downloadUrl: "/files/findOneById/",
readFileUrl: "/files/readOneById/",
title: "demo",
routes,
locales: { en, tr },
dataProvider: jsonServerDataProvider(http),
authProvider: jwtAuthProvider(http),
http,
canAction: null,
// canAction: ({ resource, action, can }) => {
// if (can(["admin"])) {
// return true;
// }
// // any other custom actions on given resource and action...
// },
config: config,
});
admin.init();
OlobaseAdmin.install(app); // install layouts & components
app.provide("i18n", i18n);
app.provide("router", router);
app.provide("admin", admin); // make injectable
app.component("PageNotFound", PageNotFound);
},
};
The main tasks of the Admin plugin are as follows:
The OlobaseAdmin object requires the src/_config.js object located on your application side for configuration.
export default {
//
// va-form component global settings
//
form: {
disableExitWithoutSave: false,
},
dateFormat: "shortFormat",
//
// va-list provider global settings
//
list: {
disableGlobalSearch: false,
disableItemsPerPage: true,
itemsPerPage: 10,
itemsPerPageOptions: [100],
},
/*
...
*/
}
The OlobaseAdmin constructor method needs all the parameters listed below to work:
Key | Type | Description |
---|---|---|
router | VueRouter | Vue Router instance that can contain all your public custom routes. |
store | Vuex.Store | Vue Store instance that can contain all your custom modules for automatic source API modules hyperlink registration. |
i18n | VueI18n | Vue I18n instance that can contain all your custom localized tags for full localization support. |
dashboard | string | It determines the route of your application's home page. If you want to differentiate the route of your home page, you should give this value an existing route name. |
downloadUrl | string | Determines the file download api route. |
readFileUrl | string | Determines the file reading api route. |
title | string | Your app's title will be shown after the page title in the app bar title and document title. |
routes | object | List of authenticated routes that should be inherited from the admin layout. CRUD pages of all resource routes will be saved as children here. |
locales | object | At least one Olobase Admin locale must be provided; Only en and tr languages are 100% supported. |
authProvider | object | The authentication provider that must implement the authentication contract. |
dataProvider | object | Data provider who must implement the data contract. |
resources | array | The array of resources that contains all the resources to be managed |
http | object | Optionally added custom HTTP client, available via this.admin.http. |
config | object | Some general configuration and options for fields or entries. |
canAction | null|function | Customizable authorization function for every action of any resource. |
Olobase Admin flow chart.
The main VueRouter should only have public pages (or any other pages not related to vuetify-admin). These pages are completely free of any Admin Layout, so you can use your own layout. Olobase Admin will need the Vue Router to add the CRUD source routes it creates via VueRouter.addRoute().
src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
import i18n from "../i18n";
import Member from "@/layouts/Member.vue";
import Login from "@/views/Login.vue";
import ForgotPassword from "@/views/ForgotPassword";
import ResetPassword from "@/views/ResetPassword";
const routes = [
{
path: "/",
redirect: "/login",
component: Member,
children: [
{
path: "/login",
name: "login",
component: Login,
meta: {
title: i18n.global.t("routes.login"),
},
},
{
path: "/forgotPassword",
name: "forgotPassword",
component: ForgotPassword,
meta: {
title: i18n.global.t("routes.forgotPassword"),
},
},
{
path: "/resetPassword",
name: "resetPassword",
component: ResetPassword,
meta: {
title: i18n.global.t("routes.resetPassword"),
},
},
],
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
By default you should have a login page like above, which may include any registration or password resets.
You can put all your custom store modules here. You're free to use them wherever you want, whether on your custom pages or resource pages. Olobase Admin will need fully instantiated Vuex to register the API resources modules it creates via RegisterModule() .
Here's a basic example:
src/store/index.js
import { createStore } from "vuex";
import axios from 'axios';
const store = createStore({
state: {
locale: "en",
showDialog: false,
confirm: false,
resolve: null,
reject: null,
},
actions: {
setLocale({ commit }, locale) {
commit("SET_LOCALE", locale);
},
openDialog({ commit, state }) {
commit("TOGGLE_DIALOG", true);
return new Promise((resolve, reject) => {
state.resolve = resolve;
state.reject = reject;
});
},
},
getters: {
dialog(state) {
return state.showDialog;
},
getLocale(state) {
return state.locale
}
},
mutations: {
SET_LOCALE(state, locale) {
state.locale = locale;
axios.defaults.headers.common['Client-Locale'] = locale;
},
TOGGLE_DIALOG(state, bool) {
state.showDialog = bool;
},
TOGGLE_DIALOG(state, bool) {
state.showDialog = bool;
},
TOGGLE_AGREE(state) {
state.resolve(true);
state.showDialog = false;
},
TOGGLE_CANCEL(state) {
state.resolve(false);
state.showDialog = false;
},
},
modules: {},
});
export default store;
Olobase Admin needs a base route format object separate from the above generic routes that will contain all authenticated routes as children. The main reason for this departure is due to Vue Router sub registration limitation. So it needs to be manually injected into the OlobaseAdmin constructor, at least for now. This is where you can view all your authenticated private pages like dashboard, profile page, etc. This is where you can place it.
src/router/admin.js
import AdminLayout from "@/layouts/Admin";
import Dashboard from "@/views/Dashboard";
import Account from "@/views/Account";
import Password from "@/views/Password";
import Error404 from "@/views/Error404";
import i18n from "../i18n";
export default {
path: "",
component: AdminLayout,
meta: {
title: i18n.global.t("routes.home"),
},
children: [
{
path: "/dashboard",
name: "dashboard",
component: Dashboard,
meta: {
title: i18n.global.t("routes.dashboard"),
},
},
{
path: "/account",
name: "account",
component: Account,
meta: {
title: i18n.global.t("routes.account"),
},
},
{
path: "/password",
name: "password",
component: Password,
meta: {
title: i18n.global.t("routes.password"),
},
},
{
path: "*",
component: Error404,
meta: {
title: i18n.global.t("routes.notFound"),
},
},
],
};
Page requests that cannot be resolved by Olobase Admin are directed to the src/views/Error404.vue component. It is used as the default 404 page and can be customized.
The Loader plugin, as its name suggests, includes basic loading tasks. Listed below are these tasks for your libraries.
import "./vuetify";
import Trix from "trix";
import "trix/dist/trix.css";
import "@mdi/font/css/materialdesignicons.css";
import PortalVue from "portal-vue";
import Modal from "../components/Modal";
import camelCase from "lodash/camelCase";
import upperFirst from "lodash/upperFirst";
/**
* Autoload resources
*/
const modules = import.meta.glob('@/resources/*/*.vue', { eager: true })
/**
* Dynamic vuetify components
*/
import {
VAutocomplete,
VCombobox,
} from "vuetify/components";
export default {
install: (app) => {
/**
* Register portal-vue
*/
app.use(PortalVue);
/**
* Register global modal
*/
app.component('modal', Modal);
/**
* Explicit registering of this components because dynamic
*/
app.component("VAutocomplete", VAutocomplete);
app.component("VCombobox", VCombobox);
/**
* Register application resources automatically
*/
for (let fileName in modules) {
const componentConfig = modules[fileName];
fileName = fileName
.replace(/^\.\//, "")
.replace(/\//, "")
.replace(/\.\w+$/, "");
const pathArray = fileName.split("/").slice(-2);
const componentName = upperFirst(camelCase(pathArray[0].toLowerCase() + pathArray[1]));
// register component
app.component(
componentName,
componentConfig.default || componentConfig
);
}
// end app resources
},
};
The useHttp(axios) method is used to obtain an admin client from an axios instance. In other words, since the useHttp() method is disabled, the following functions will not work in your application below.
To put it clearly, the useHttp() method assigns application-specific axios interceptors to the axios instance.
The primary axios instance of the application is created in the plugins/index.js file and the useHttp() method is run there.
src/plugins/index.js
import { useHttp } from "../plugins/useHttp";
import axios from "axios";
/**
* Set default global http configuration
*/
axios.defaults.timeout = 10000;
axios.defaults.baseURL = import.meta.env.VITE_API_URL;
axios.defaults.headers.common['Content-Type'] = "application/json";
axios.defaults.headers.common['Client-Locale'] = i18n.global.locale.value;
axios.interceptors.request.use(
function (config) {
let token = cookies.get(cookieKey.token);
if (typeof token == "undefined" || token == "undefined" || token == "") {
return config;
}
config.headers["Authorization"] = "Bearer " + token;
return config;
},
function (error) {
return Promise.reject(error);
}
);
useHttp(axios);
src/plugins/useHttp.js
import i18n from "../i18n";
import store from "@/store";
import eventBus from "vuetify-admin/src/utils/eventBus";
let isRefreshing = false;
let failedQueue = [];
//
// https://github.com/Godofbrowser/axios-refresh-multiple-request/tree/master
//
const processQueue = (error, token = null) => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
};
/**
* Http global response settings
*/
const useHttp = function (axios) {
let axiosInstance = axios;
axiosInstance.interceptors.response.use(
(response) => {
const statusOk = (response && response["status"] && response.status === 200) ? true : false;
const configUrlSegments = response.config.url.split('/');
if (
(statusOk && configUrlSegments.includes("create")) ||
(statusOk && configUrlSegments.includes("update"))
) {
eventBus.emit("last-dialog", false) // close edit modal window if it's opened
store.commit("messages/show", { type: 'success', message: i18n.global.t("form.saved") });
}
if (statusOk &&
cookies.get(cookieKey.token) &&
response.config.url == "/auth/session") {
let config = response.config;
config._retry = false; // retry value every must be false
const delayRetryRequest = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, import.meta.env.VITE_SESSION_UPDATE_TIME * 60 * 1000); // every x minutes
});
return delayRetryRequest.then(() => axiosInstance(config));
}
return response;
},
/* etc .... */
}
//
// This is an example and this part of the coding has been deleted
// so it doesn't take up much space.
//
/**
* Export useHttp method
*/
export { useHttp };
Vuetify eklentisi, VuetifyJs kütüphanesine özgü konfigürasyonlarınızı içerir.
// Styles
import "vuetify/styles";
// Translations provided by Vuetify
import { en, tr } from "vuetify/locale";
import Trans from "@/i18n/translation";
const defaultLang = Trans.guessDefaultLocale();
// Composables
import { createVuetify } from "vuetify";
const defaultTheme = {
dark: false,
colors: {
background: "#FFFFFF",
surface: "#FFFFFF",
primary: localStorage.getItem("themeColor")
? localStorage.getItem("themeColor")
: "#0a7248",
secondary: "#eeeeee",
error: "#ed0505",
info: "#00CAE3",
// success: '#4CAF50',
// warning: '#FB8C00',
},
};
// Vuetify
export default createVuetify({
locale: {
locale: Trans.supportedLocales.includes(defaultLang) ? defaultLang : import.meta.env.VITE_DEFAULT_LOCALE,
fallback: "en",
messages: { tr, en },
},
theme: {
defaultTheme: "defaultTheme",
themes: {
defaultTheme,
},
},
});