adminforth
Advanced tools
Comparing version 1.1.70 to 1.1.71
@@ -74,3 +74,4 @@ | ||
if (t !== mustHaveType) { | ||
throw new Error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`); | ||
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`); | ||
return null; | ||
} | ||
@@ -77,0 +78,0 @@ if (pk === null) { |
@@ -73,3 +73,4 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
if (t !== mustHaveType) { | ||
throw new Error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`); | ||
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`); | ||
return null; | ||
} | ||
@@ -76,0 +77,0 @@ if (pk === null) { |
@@ -16,8 +16,14 @@ { | ||
"@iconify-prerendered/vue-flowbite": "^0.23.1714023977", | ||
"@unhead/vue": "^1.9.12", | ||
"@vueuse/core": "^10.10.0", | ||
"dayjs": "^1.11.11", | ||
"debounce": "^2.1.0", | ||
"flowbite": "^2.3.0", | ||
"flowbite-datepicker": "^1.2.6", | ||
"pinia": "^2.1.7", | ||
"unhead": "^1.9.12", | ||
"uuid": "^10.0.0", | ||
"vue": "^3.4.21", | ||
"vue-router": "^4.3.0" | ||
"vue-router": "^4.3.0", | ||
"vue-slider-component": "^4.1.0-beta.7" | ||
}, | ||
@@ -39,5 +45,5 @@ "devDependencies": { | ||
"typescript": "~5.4.0", | ||
"vite": "^5.2.12", | ||
"vite": "^5.2.13", | ||
"vue-tsc": "^2.0.11" | ||
} | ||
} |
@@ -12,3 +12,3 @@ import { createApp } from 'vue' | ||
app.use(createPinia()) | ||
@@ -15,0 +15,0 @@ app.use(router) |
import { createRouter, createWebHistory } from 'vue-router' | ||
import HomeView from '../views/HomeView.vue' | ||
import ResourceParent from '@/views/ResourceParent.vue' | ||
import ListView from '@/views/ListView.vue' | ||
import ShowView from '@/views/ShowView.vue' | ||
import EditView from '@/views/EditView.vue' | ||
import CreateView from '@/views/CreateView.vue' | ||
import { useUserStore } from '@/stores/user' | ||
/* IMPORTANT:ADMINFORTH ROUTES IMPORTS */ | ||
@@ -13,10 +10,14 @@ const router = createRouter({ | ||
{ | ||
path: '/', | ||
name: 'home', | ||
component: HomeView | ||
}, | ||
{ | ||
path: '/login', | ||
name: 'login', | ||
component: () => import('@/views/LoginView.vue') | ||
component: () => import('@/views/LoginView.vue'), | ||
meta: { title: 'login' }, | ||
beforeEnter: async (to, from, next) => { | ||
const userStore = useUserStore() | ||
if(localStorage.getItem('isAuthorized') === 'true'){ | ||
next({name: 'home'}) | ||
} else { | ||
next() | ||
} | ||
} | ||
}, | ||
@@ -30,19 +31,25 @@ { | ||
path: '', | ||
component: ListView, | ||
name: 'resource-list' | ||
component: () => import('@/views/ListView.vue'), | ||
name: 'resource-list', | ||
meta: { title: 'list',type: 'list' } | ||
}, | ||
{ | ||
path: 'show/:primaryKey', | ||
component: ShowView, | ||
name: 'resource-show' | ||
component: () => import('@/views/ShowView.vue'), | ||
name: 'resource-show', | ||
meta: { title: 'show', type: 'show'} | ||
}, | ||
{ | ||
path: 'edit/:primaryKey', | ||
component: EditView, | ||
name: 'resource-edit' | ||
component: () => import('@/views/EditView.vue'), | ||
name: 'resource-edit', | ||
meta: { title: 'edit', type: 'edit'} | ||
}, | ||
{ | ||
path: 'create', | ||
component: CreateView, | ||
name: 'resource-create' | ||
component: () => import('@/views/CreateView.vue'), | ||
name: 'resource-create', | ||
meta: { title: 'create', type: 'create'} | ||
}, | ||
@@ -55,2 +62,6 @@ ] | ||
export default router |
import { ref, computed } from 'vue' | ||
import { defineStore } from 'pinia' | ||
import { callAdminForthApi } from '@/utils'; | ||
import router from '@/router'; | ||
import type { AdminForthResource, AdminForthResourceColumn } from '@/types/AdminForthConfig'; | ||
import type { Ref } from 'vue' | ||
async function findHomepage(menu) { | ||
for (const item of menu) { | ||
if (item.homepage) { | ||
return item; | ||
} | ||
if (item.children) { | ||
const res = findHomepage(item.children); | ||
if (res) { | ||
return res; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
export const useCoreStore = defineStore('core', () => { | ||
const resourceById = ref([]); | ||
const resourceById: Ref<Object> = ref({}); | ||
const menu = ref([]); | ||
const config = ref({}); | ||
const record = ref({}); | ||
const resourceColumns = ref(null); | ||
const record: Ref<any | null> = ref({}); | ||
const resource: Ref<AdminForthResource | null> = ref(null); | ||
const resourceColumnsWithFilters = computed(() => { | ||
if (!resource.value) { | ||
return []; | ||
} | ||
return resource.value.columns.filter((col: AdminForthResourceColumn) => col.showIn?.includes('filter')); | ||
}) | ||
const resourceOptions = ref(null); | ||
const resourceColumnsError = ref(''); | ||
const resourceColumnsId = ref(null); | ||
const user = ref(null); | ||
const adminUser = ref(null); | ||
@@ -36,4 +31,7 @@ async function fetchMenuAndResource() { | ||
}); | ||
if(!resp){ | ||
return | ||
} | ||
menu.value = resp.menu; | ||
resourceById.value = resp.resources.reduce((acc, resource) => { | ||
resourceById.value = resp.resources.reduce((acc: Object, resource: AdminForthResource) => { | ||
acc[resource.resourceId] = resource; | ||
@@ -43,19 +41,4 @@ return acc; | ||
config.value = resp.config; | ||
user.value = resp.user; | ||
// find homepage:true in menu recuresively | ||
if (import.meta.env.DEV){ | ||
return | ||
} else { | ||
const homepage = await findHomepage(menu.value); | ||
if (homepage) { | ||
if (homepage.resourceId) { | ||
// redirect to homepage | ||
router.push({ name: 'resource-list', params: { resourceId: homepage.resourceId } }); | ||
} else { | ||
// redirect to path | ||
router.push(homepage.path); | ||
} | ||
} | ||
} | ||
adminUser.value = resp.user; | ||
console.log('🌍 AdminForth v', resp.version); | ||
} | ||
@@ -65,15 +48,41 @@ | ||
record.value = null; | ||
if (!resource.value) { | ||
throw new Error('Columns not fetched yet'); | ||
} | ||
record.value = await callAdminForthApi({ | ||
path: '/get_record', | ||
const respData = await callAdminForthApi({ | ||
path: '/get_resource_data', | ||
method: 'POST', | ||
body: { | ||
source: 'show', | ||
resourceId: resourceId, | ||
primaryKey: primaryKey, | ||
filters: [ | ||
{ | ||
field: resource.value.columns.find((col: AdminForthResourceColumn) => col.primaryKey).name, | ||
operator: 'eq', | ||
value: primaryKey | ||
} | ||
], | ||
sort: [], | ||
limit: 1, | ||
offset: 0 | ||
} | ||
}); | ||
if (respData.error) { | ||
window.adminforth.alert({ | ||
message: respData.error, | ||
variant: 'danger', | ||
timeout: 'unlimited' | ||
}); | ||
record.value = {}; | ||
} else { | ||
record.value = respData.data[0]; | ||
} | ||
} | ||
async function fetchColumns({ resourceId }) { | ||
if (resourceColumnsId.value === resourceId && resourceColumns.value) { | ||
async function fetchResourceFull({ resourceId }: { resourceId: string }) { | ||
if (resourceColumnsId.value === resourceId && resource.value) { | ||
// already fetched | ||
@@ -83,6 +92,5 @@ return; | ||
resourceColumnsId.value = resourceId; | ||
resourceColumns.value = null; | ||
resourceColumnsError.value = ''; | ||
const res = await callAdminForthApi({ | ||
path: '/get_resource_columns', | ||
path: '/get_resource', | ||
method: 'POST', | ||
@@ -92,8 +100,9 @@ body: { | ||
} | ||
} | ||
); | ||
}); | ||
if (res.error) { | ||
resourceColumnsError.value = res.error; | ||
} else { | ||
resourceColumns.value = res.resource.columns; | ||
resourceById.value[resourceId] = res.resource; | ||
resource.value = res.resource; | ||
resourceOptions.value = res.resource.options; | ||
} | ||
@@ -110,12 +119,7 @@ } | ||
async function logout() { | ||
await callAdminForthApi({ | ||
path: '/logout', | ||
method: 'POST', | ||
}); | ||
} | ||
const username = computed(() => { | ||
const usernameField = config.value.usernameField; | ||
return user.value && user.value[usernameField]; | ||
return adminUser.value && adminUser.value[usernameField]; | ||
}); | ||
@@ -125,3 +129,3 @@ | ||
const userFullnameField = config.value.userFullnameField; | ||
return user.value && user.value[userFullnameField]; | ||
return adminUser.value && adminUser.value[userFullnameField]; | ||
}) | ||
@@ -140,7 +144,9 @@ | ||
record, | ||
resourceColumns, | ||
fetchColumns, | ||
fetchResourceFull, | ||
resourceColumnsError, | ||
logout | ||
resourceOptions, | ||
resource, | ||
adminUser, | ||
resourceColumnsWithFilters | ||
} | ||
}) |
import { ref } from 'vue' | ||
import { defineStore } from 'pinia' | ||
import { callAdminForthApi } from '@/utils'; | ||
type ModalContentType = { | ||
title?: string; | ||
content?: string; | ||
acceptText?: string; | ||
cancelText?: string; | ||
} | ||
export const useModalStore = defineStore('modal', () => { | ||
@@ -15,2 +21,3 @@ const modalContent = ref({ | ||
const onAcceptFunction: any = ref(()=>{}); | ||
const onCancelFunction: any = ref(()=>{}); | ||
function togleModal() { | ||
@@ -22,3 +29,6 @@ isOpened.value = !isOpened.value; | ||
} | ||
function setModalContent(content: string) { | ||
function setOnCancelFunction(func: Function) { | ||
onCancelFunction.value = func; | ||
} | ||
function setModalContent(content: ModalContentType) { | ||
modalContent.value = content; | ||
@@ -38,4 +48,4 @@ } | ||
return {isOpened, setModalContent, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState} | ||
return {isOpened, setModalContent,onCancelFunction, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState,setOnCancelFunction} | ||
}) |
import { onMounted, ref, resolveComponent } from 'vue'; | ||
import type { CoreConfig } from './spa_types/core'; | ||
import router from "./router"; | ||
import { useRouter } from 'vue-router'; | ||
import { useCoreStore } from './stores/core'; | ||
import { useUserStore } from './stores/user'; | ||
@@ -16,10 +20,14 @@ export async function callApi({path, method, body=undefined} ) { | ||
if (r.status == 401) { | ||
console.log('router', router); | ||
router.push({name: 'login'}); | ||
useUserStore().unauthorize(); | ||
router.push({ name: 'login' }); | ||
return null; | ||
} | ||
} | ||
return await r.json(); | ||
} | ||
export async function callAdminForthApi({ path, method, body=undefined }) { | ||
export async function callAdminForthApi({ path, method, body=undefined }: { | ||
path: string, | ||
method: 'GET' | 'POST' | 'PUT' | 'DELETE', | ||
body?: any | ||
}) { | ||
try { | ||
@@ -33,6 +41,8 @@ return callApi({path: `/adminapi/v1${path}`, method, body} ); | ||
export function getCustomComponent({ file, meta }: { file: string, meta: any }) { | ||
const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, ''); | ||
console.log('resolving name', name); | ||
return resolveComponent(name); | ||
} | ||
export function getIcon(icon: string) { | ||
@@ -46,2 +56,49 @@ // icon format is "feather:icon-name". We need to get IconName in pascal case | ||
return resolveComponent(compName); | ||
} | ||
} | ||
export const loadFile = (file: string) => { | ||
if (file.startsWith('http')) { | ||
return file; | ||
} | ||
let path; | ||
let baseUrl = ''; | ||
if (file.startsWith('@/')) { | ||
path = file.replace('@/', ''); | ||
baseUrl = new URL(`./${path}`, import.meta.url).href; | ||
} else if (file.startsWith('@@/')) { | ||
path = file.replace('@@/', ''); | ||
baseUrl = new URL(`./custom/${path}`, import.meta.url).href; | ||
} else { | ||
baseUrl = new URL(`./${file}`, import.meta.url).href; | ||
} | ||
return baseUrl; | ||
} | ||
export function checkEmptyValues(value: any, viewType:'show' | 'list' ) { | ||
const config: CoreConfig | {} = useCoreStore().config; | ||
let emptyFieldPlaceholder = ''; | ||
if (config.emptyFieldPlaceholder) { | ||
if(typeof config.emptyFieldPlaceholder === 'string') { | ||
emptyFieldPlaceholder = config.emptyFieldPlaceholder; | ||
} else { | ||
emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || ''; | ||
} | ||
if (value === null || value === undefined || value === '') { | ||
return emptyFieldPlaceholder; | ||
} | ||
} | ||
return value; | ||
} | ||
export function checkAcessByAllowedActions(allowedActions:any, action:any ) { | ||
if (!allowedActions) { | ||
console.warn('allowedActions not set'); | ||
return; | ||
} | ||
if(allowedActions[action] === false) { | ||
console.warn(`Action ${action} is not allowed`); | ||
router.back(); | ||
} | ||
} | ||
/** @type {import('tailwindcss').Config} */ | ||
export default { | ||
content: ["./src/**/*.{vue, js}", "./node_modules/flowbite/**/*.js"], | ||
content: ["./src/**/*.{vue, js, ts, tsx}","./src/*.{vue, js, ts, tsx}", "./index.html", "./node_modules/flowbite/**/*.js"], | ||
theme: { | ||
extend: {}, | ||
extend: { | ||
/* IMPORTANT:ADMINFORTH TAILWIND STYLES */ | ||
} | ||
}, | ||
darkMode: 'class', | ||
@@ -8,0 +11,0 @@ plugins: [ |
@@ -14,2 +14,8 @@ import { fileURLToPath, URL } from 'node:url' | ||
}, | ||
warnOnce(msg: string) { | ||
console.warn('warn once', msg); | ||
if (!this.hasWarned) { | ||
this.hasWarned = true; | ||
} | ||
}, | ||
warn(msg: string) { | ||
@@ -16,0 +22,0 @@ console.warn(msg); |
{ | ||
"name": "adminforth", | ||
"version": "1.1.70", | ||
"version": "1.1.71", | ||
"description": "OpenSource Vue3 powered forth-generation admin panel", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1297117
215
22998