Files
solidtime/resources/js/app.ts
Gregor Vostrak dbc927b6a9 Add core support for extendable authentication
add support for passwordless user creation; add filament loading support
for new laravel modules namespacing;
add support for pluggable password reset and login rules
2026-06-23 18:32:54 +02:00

115 lines
3.8 KiB
TypeScript

import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp, usePage } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy';
import { createPinia } from 'pinia';
import type { User } from '@/types/models';
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query';
import { type DefineComponent } from 'vue';
import { setupPrefetching } from '@/utils/prefetch';
interface ExtensionManifest {
name?: string;
alias?: string;
}
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
const pinia = createPinia();
const queryClient = new QueryClient();
const extensionManifests = import.meta.glob('../../extensions/**/module.json', {
eager: true,
import: 'default',
}) as Record<string, ExtensionManifest>;
// BillingPortal is a Vue 2 component and must not be bundled into the Vue 3 app.
const extensionPages = import.meta.glob<DefineComponent>([
'../../extensions/**/resources/js/Pages/**/*.vue',
'!**/BillingPortal.vue',
]);
const extensionDirectories = Object.entries(extensionManifests).reduce<Record<string, string>>(
(directories, [path, manifest]) => {
const match = path.match(/^\.\.\/\.\.\/extensions\/([^/]+)\/module\.json$/);
const extensionDirectory = match?.[1];
if (extensionDirectory === undefined) {
return directories;
}
for (const key of [manifest.name, manifest.alias, extensionDirectory]) {
if (typeof key !== 'string' || key === '') {
continue;
}
directories[key] = extensionDirectory;
directories[key.toLowerCase()] = extensionDirectory;
}
return directories;
},
{}
);
function resolveExtensionDirectory(moduleName: string): string {
return (
extensionDirectories[moduleName] ??
extensionDirectories[moduleName.toLowerCase()] ??
moduleName
);
}
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => {
// "Module::Page" (both halves present) resolves to that extension's page
// directory; everything else is a host page under resources/js/Pages.
const [module, ...pageSegments] = name.split('::');
const page = pageSegments.join('::');
if (module && page) {
const extensionDirectory = resolveExtensionDirectory(module);
const pagePath = `../../extensions/${extensionDirectory}/resources/js/Pages/${page}.vue`;
return resolvePageComponent(pagePath, extensionPages);
}
return resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob<DefineComponent>('./Pages/**/*.vue')
);
},
setup({ el, App, props, plugin }) {
const app = createApp({ render: () => h(App, props) });
// currently only one vue app setup hook is supported
if (window.vueAppSetupHook) {
window.vueAppSetupHook(app);
}
window.getWeekStartSetting = function () {
const page = usePage<{
auth: {
user: User;
};
}>();
return page.props.auth.user.week_start ?? 'monday';
};
window.getTimezoneSetting = function () {
const page = usePage<{
auth: {
user: User;
};
}>();
return page.props.auth.user.timezone;
};
app.use(plugin).use(pinia).use(ZiggyVue).use(VueQueryPlugin, { queryClient }).mount(el);
// Setup Inertia prefetching to warm TanStack Query cache
setupPrefetching(queryClient);
},
progress: {
color: '#4B5563',
},
});