This commit is contained in:
2024-09-01 18:54:23 +05:00
parent 76d18365a5
commit 061f09eca1
1597 changed files with 109451 additions and 1 deletions

View 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,
},
},
},
})
}

View 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')
},
},
}

View 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)
})
},
},
}

View 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 = []
},
},
}