add nova
This commit is contained in:
17
nova/resources/js/store/index.js
Normal file
17
nova/resources/js/store/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createStore } from 'vuex'
|
||||
import nova from './nova'
|
||||
import notifications from './notifications'
|
||||
|
||||
export function createNovaStore() {
|
||||
return createStore({
|
||||
...nova,
|
||||
modules: {
|
||||
nova: {
|
||||
namespaced: true,
|
||||
modules: {
|
||||
notifications,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
56
nova/resources/js/store/notifications.js
Normal file
56
nova/resources/js/store/notifications.js
Normal file
@@ -0,0 +1,56 @@
|
||||
export default {
|
||||
state: () => ({
|
||||
notifications: [],
|
||||
notificationsShown: false,
|
||||
unreadNotifications: false,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
notifications: s => s.notifications,
|
||||
notificationsShown: s => s.notificationsShown,
|
||||
unreadNotifications: s => s.unreadNotifications,
|
||||
},
|
||||
|
||||
mutations: {
|
||||
toggleNotifications(state) {
|
||||
state.notificationsShown = !state.notificationsShown
|
||||
localStorage.setItem('nova.mainMenu.open', state.notificationsShown)
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
async fetchNotifications({ state }) {
|
||||
const {
|
||||
data: { notifications, unread },
|
||||
} = await Nova.request().get(`/nova-api/nova-notifications`)
|
||||
|
||||
state.notifications = notifications
|
||||
state.unreadNotifications = unread
|
||||
},
|
||||
|
||||
async markNotificationAsUnread({ state, dispatch }, id) {
|
||||
await Nova.request().post(`/nova-api/nova-notifications/${id}/unread`)
|
||||
dispatch('fetchNotifications')
|
||||
},
|
||||
|
||||
async markNotificationAsRead({ state, dispatch }, id) {
|
||||
await Nova.request().post(`/nova-api/nova-notifications/${id}/read`)
|
||||
dispatch('fetchNotifications')
|
||||
},
|
||||
|
||||
async deleteNotification({ state, dispatch }, id) {
|
||||
await Nova.request().delete(`/nova-api/nova-notifications/${id}`)
|
||||
dispatch('fetchNotifications')
|
||||
},
|
||||
|
||||
async deleteAllNotifications({ state, dispatch }, id) {
|
||||
await Nova.request().delete(`/nova-api/nova-notifications`)
|
||||
dispatch('fetchNotifications')
|
||||
},
|
||||
|
||||
async markAllNotificationsAsRead({ state, dispatch }, id) {
|
||||
await Nova.request().post(`/nova-api/nova-notifications/read-all`)
|
||||
dispatch('fetchNotifications')
|
||||
},
|
||||
},
|
||||
}
|
||||
191
nova/resources/js/store/nova.js
Normal file
191
nova/resources/js/store/nova.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import { usePage } from '@inertiajs/inertia-vue3'
|
||||
import { Inertia } from '@inertiajs/inertia'
|
||||
import forEach from 'lodash/forEach'
|
||||
import filled from '@/util/filled'
|
||||
|
||||
export default {
|
||||
state: () => ({
|
||||
baseUri: '/nova',
|
||||
currentUser: null,
|
||||
mainMenu: [],
|
||||
userMenu: [],
|
||||
breadcrumbs: [],
|
||||
resources: [],
|
||||
version: '4.x',
|
||||
mainMenuShown: false,
|
||||
canLeaveForm: true,
|
||||
canLeaveModal: true,
|
||||
pushStateWasTriggered: false,
|
||||
validLicense: true,
|
||||
queryStringParams: {},
|
||||
compiledQueryStringParams: '',
|
||||
}),
|
||||
|
||||
getters: {
|
||||
currentUser: s => s.currentUser,
|
||||
currentVersion: s => s.version,
|
||||
mainMenu: s => s.mainMenu,
|
||||
userMenu: s => s.userMenu,
|
||||
breadcrumbs: s => s.breadcrumbs,
|
||||
mainMenuShown: s => s.mainMenuShown,
|
||||
canLeaveForm: s => s.canLeaveForm,
|
||||
canLeaveFormToPreviousPage: s => s.canLeaveForm && !s.pushStateWasTriggered,
|
||||
canLeaveModal: s => s.canLeaveModal,
|
||||
validLicense: s => s.validLicense,
|
||||
queryStringParams: s => s.queryStringParams,
|
||||
},
|
||||
|
||||
mutations: {
|
||||
allowLeavingForm(state) {
|
||||
state.canLeaveForm = true
|
||||
},
|
||||
|
||||
preventLeavingForm(state) {
|
||||
state.canLeaveForm = false
|
||||
},
|
||||
|
||||
allowLeavingModal(state) {
|
||||
state.canLeaveModal = true
|
||||
},
|
||||
|
||||
preventLeavingModal(state) {
|
||||
state.canLeaveModal = false
|
||||
},
|
||||
|
||||
triggerPushState(state) {
|
||||
Inertia.pushState(Inertia.page)
|
||||
Inertia.ignoreHistoryState = true
|
||||
state.pushStateWasTriggered = true
|
||||
},
|
||||
|
||||
resetPushState(state) {
|
||||
state.pushStateWasTriggered = false
|
||||
},
|
||||
|
||||
toggleMainMenu(state) {
|
||||
state.mainMenuShown = !state.mainMenuShown
|
||||
localStorage.setItem('nova.mainMenu.open', state.mainMenuShown)
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
async login({ commit, dispatch }, { email, password, remember }) {
|
||||
await Nova.request().post(Nova.url('/login'), {
|
||||
email,
|
||||
password,
|
||||
remember,
|
||||
})
|
||||
},
|
||||
|
||||
async logout({ state }, customLogoutPath) {
|
||||
let response = null
|
||||
|
||||
if (!Nova.config('withAuthentication') && customLogoutPath) {
|
||||
response = await Nova.request().post(customLogoutPath)
|
||||
} else {
|
||||
response = await Nova.request().post(Nova.url('/logout'))
|
||||
}
|
||||
|
||||
return response?.data?.redirect || null
|
||||
},
|
||||
|
||||
async startImpersonating({}, { resource, resourceId }) {
|
||||
let response = null
|
||||
|
||||
response = await Nova.request().post(`/nova-api/impersonate`, {
|
||||
resource,
|
||||
resourceId,
|
||||
})
|
||||
|
||||
let redirect = response?.data?.redirect || null
|
||||
|
||||
if (redirect !== null) {
|
||||
location.href = redirect
|
||||
return
|
||||
}
|
||||
|
||||
Nova.visit('/')
|
||||
},
|
||||
|
||||
async stopImpersonating({}) {
|
||||
let response = null
|
||||
|
||||
response = await Nova.request().delete(`/nova-api/impersonate`)
|
||||
|
||||
let redirect = response?.data?.redirect || null
|
||||
|
||||
if (redirect !== null) {
|
||||
location.href = redirect
|
||||
return
|
||||
}
|
||||
|
||||
Nova.visit('/')
|
||||
},
|
||||
|
||||
async assignPropsFromInertia({ state, dispatch }) {
|
||||
let config = usePage().props.value.novaConfig || Nova.appConfig
|
||||
let { resources, base, version, mainMenu, userMenu } = config
|
||||
|
||||
let user = usePage().props.value.currentUser
|
||||
let validLicense = usePage().props.value.validLicense
|
||||
let breadcrumbs = usePage().props.value.breadcrumbs
|
||||
|
||||
Nova.appConfig = config
|
||||
state.breadcrumbs = breadcrumbs || []
|
||||
state.currentUser = user
|
||||
state.validLicense = validLicense
|
||||
state.resources = resources
|
||||
state.baseUri = base
|
||||
state.version = version
|
||||
state.mainMenu = mainMenu
|
||||
state.userMenu = userMenu
|
||||
|
||||
dispatch('syncQueryString')
|
||||
},
|
||||
|
||||
async fetchPolicies({ state, dispatch }) {
|
||||
await dispatch('assignPropsFromInertia')
|
||||
},
|
||||
|
||||
async syncQueryString({ state }) {
|
||||
let searchParams = new URLSearchParams(window.location.search)
|
||||
|
||||
state.queryStringParams = Object.fromEntries(searchParams.entries())
|
||||
state.compiledQueryStringParams = searchParams.toString()
|
||||
},
|
||||
|
||||
async updateQueryString({ state }, value) {
|
||||
let searchParams = new URLSearchParams(window.location.search)
|
||||
let page = Inertia.page
|
||||
|
||||
forEach(value, (v, i) => {
|
||||
if (!filled(v)) {
|
||||
searchParams.delete(i)
|
||||
} else {
|
||||
searchParams.set(i, v || '')
|
||||
}
|
||||
})
|
||||
|
||||
if (state.compiledQueryStringParams !== searchParams.toString()) {
|
||||
if (page.url !== `${window.location.pathname}?${searchParams}`) {
|
||||
page.url = `${window.location.pathname}?${searchParams}`
|
||||
|
||||
window.history.pushState(
|
||||
page,
|
||||
'',
|
||||
`${window.location.pathname}?${searchParams}`
|
||||
)
|
||||
}
|
||||
|
||||
state.compiledQueryStringParams = searchParams.toString()
|
||||
}
|
||||
|
||||
Nova.$emit('query-string-changed', searchParams)
|
||||
state.queryStringParams = Object.fromEntries(searchParams.entries())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(searchParams)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
206
nova/resources/js/store/resources.js
Normal file
206
nova/resources/js/store/resources.js
Normal file
@@ -0,0 +1,206 @@
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import each from 'lodash/each'
|
||||
import find from 'lodash/find'
|
||||
import filter from 'lodash/filter'
|
||||
import map from 'lodash/map'
|
||||
import reduce from 'lodash/reduce'
|
||||
import { escapeUnicode } from '@/util/escapeUnicode'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
||||
state: () => ({
|
||||
filters: [],
|
||||
originalFilters: [],
|
||||
}),
|
||||
|
||||
getters: {
|
||||
/**
|
||||
* The filters for the resource
|
||||
*/
|
||||
filters: state => state.filters,
|
||||
|
||||
/**
|
||||
* The original filters for the resource
|
||||
*/
|
||||
originalFilters: state => state.originalFilters,
|
||||
|
||||
/**
|
||||
* Determine if there are any filters for the resource.
|
||||
*/
|
||||
hasFilters: state => Boolean(state.filters.length > 0),
|
||||
|
||||
/**
|
||||
* The current unencoded filter value payload
|
||||
*/
|
||||
currentFilters: (state, getters) => {
|
||||
return map(filter(state.filters), f => {
|
||||
return {
|
||||
[f.class]: f.currentValue,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the current filters encoded to a string.
|
||||
*/
|
||||
currentEncodedFilters: (state, getters) =>
|
||||
btoa(escapeUnicode(JSON.stringify(getters.currentFilters))),
|
||||
|
||||
/**
|
||||
* Determine whether any filters are applied
|
||||
*/
|
||||
filtersAreApplied: (state, getters) => getters.activeFilterCount > 0,
|
||||
|
||||
/**
|
||||
* Return the number of filters that are non-default
|
||||
*/
|
||||
activeFilterCount: (state, getters) => {
|
||||
return reduce(
|
||||
state.filters,
|
||||
(result, f) => {
|
||||
const originalFilter = getters.getOriginalFilter(f.class)
|
||||
const originalFilterCloneValue = JSON.stringify(
|
||||
originalFilter.currentValue
|
||||
)
|
||||
const currentFilterCloneValue = JSON.stringify(f.currentValue)
|
||||
return currentFilterCloneValue == originalFilterCloneValue
|
||||
? result
|
||||
: result + 1
|
||||
},
|
||||
0
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a single filter from the list of filters.
|
||||
*/
|
||||
getFilter: state => filterKey => {
|
||||
return find(state.filters, filter => {
|
||||
return filter.class == filterKey
|
||||
})
|
||||
},
|
||||
|
||||
getOriginalFilter: state => filterKey => {
|
||||
return find(state.originalFilters, filter => {
|
||||
return filter.class == filterKey
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the options for a single filter.
|
||||
*/
|
||||
getOptionsForFilter: (state, getters) => filterKey => {
|
||||
const filter = getters.getFilter(filterKey)
|
||||
return filter ? filter.options : []
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the current value for a given filter at the provided key.
|
||||
*/
|
||||
filterOptionValue: (state, getters) => (filterKey, optionKey) => {
|
||||
const filter = getters.getFilter(filterKey)
|
||||
|
||||
return find(filter.currentValue, (value, key) => key == optionKey)
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* Fetch the current filters for the given resource name.
|
||||
*/
|
||||
async fetchFilters({ commit, state }, options) {
|
||||
let { resourceName, lens = false } = options
|
||||
let { viaResource, viaResourceId, viaRelationship, relationshipType } =
|
||||
options
|
||||
let params = {
|
||||
params: {
|
||||
viaResource,
|
||||
viaResourceId,
|
||||
viaRelationship,
|
||||
relationshipType,
|
||||
},
|
||||
}
|
||||
|
||||
const { data } = lens
|
||||
? await Nova.request().get(
|
||||
'/nova-api/' + resourceName + '/lens/' + lens + '/filters',
|
||||
params
|
||||
)
|
||||
: await Nova.request().get(
|
||||
'/nova-api/' + resourceName + '/filters',
|
||||
params
|
||||
)
|
||||
|
||||
commit('storeFilters', data)
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the default filter state to the original filter settings.
|
||||
*/
|
||||
async resetFilterState({ commit, getters }) {
|
||||
each(getters.originalFilters, filter => {
|
||||
commit('updateFilterState', {
|
||||
filterClass: filter.class,
|
||||
value: filter.currentValue,
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the current filter values from the decoded query string.
|
||||
*/
|
||||
async initializeCurrentFilterValuesFromQueryString(
|
||||
{ commit, getters },
|
||||
encodedFilters
|
||||
) {
|
||||
if (encodedFilters) {
|
||||
const initialFilters = JSON.parse(atob(encodedFilters))
|
||||
each(initialFilters, filter => {
|
||||
if (
|
||||
filter.hasOwnProperty('class') &&
|
||||
filter.hasOwnProperty('value')
|
||||
) {
|
||||
commit('updateFilterState', {
|
||||
filterClass: filter.class,
|
||||
value: filter.value,
|
||||
})
|
||||
} else {
|
||||
for (let key in filter) {
|
||||
commit('updateFilterState', {
|
||||
filterClass: key,
|
||||
value: filter[key],
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mutations: {
|
||||
updateFilterState(state, { filterClass, value }) {
|
||||
const filter = find(state.filters, f => f.class == filterClass)
|
||||
|
||||
if (filter !== undefined && filter !== null) {
|
||||
filter.currentValue = value
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Store the mutable filter settings
|
||||
*/
|
||||
storeFilters(state, data) {
|
||||
state.filters = data
|
||||
state.originalFilters = cloneDeep(data)
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the filters for this resource
|
||||
*/
|
||||
clearFilters(state) {
|
||||
state.filters = []
|
||||
state.originalFilters = []
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user