156 lines
4.0 KiB
TypeScript
156 lines
4.0 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
import * as appstate from './appstate.js';
|
|
|
|
const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
|
|
|
|
export const validViews = [
|
|
'dashboard',
|
|
'organizations',
|
|
'packages',
|
|
'tokens',
|
|
'settings',
|
|
'admin',
|
|
] as const;
|
|
|
|
export type TValidView = typeof validViews[number];
|
|
|
|
class AppRouter {
|
|
private router: InstanceType<typeof SmartRouter>;
|
|
private initialized = false;
|
|
private suppressStateUpdate = false;
|
|
|
|
constructor() {
|
|
this.router = new SmartRouter({ debug: false });
|
|
}
|
|
|
|
public init(): void {
|
|
if (this.initialized) return;
|
|
this.setupRoutes();
|
|
this.setupStateSync();
|
|
this.handleInitialRoute();
|
|
this.initialized = true;
|
|
}
|
|
|
|
private setupRoutes(): void {
|
|
for (const view of validViews) {
|
|
this.router.on(`/${view}`, async () => {
|
|
this.updateViewState(view);
|
|
});
|
|
}
|
|
|
|
// Root redirect
|
|
this.router.on('/', async () => {
|
|
this.navigateTo('/dashboard');
|
|
});
|
|
}
|
|
|
|
private setupStateSync(): void {
|
|
appstate.uiStatePart.select((s) => s.activeView).subscribe((activeView) => {
|
|
if (this.suppressStateUpdate) return;
|
|
|
|
const currentPath = window.location.pathname;
|
|
const expectedPath = `/${activeView}`;
|
|
|
|
if (currentPath !== expectedPath) {
|
|
this.suppressStateUpdate = true;
|
|
this.router.pushUrl(expectedPath);
|
|
this.suppressStateUpdate = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
private handleInitialRoute(): void {
|
|
const path = window.location.pathname;
|
|
|
|
// Handle OAuth callback
|
|
if (path === '/oauth-callback') {
|
|
this.handleOAuthCallback();
|
|
return;
|
|
}
|
|
|
|
if (!path || path === '/') {
|
|
this.router.pushUrl('/dashboard');
|
|
} else {
|
|
const segments = path.split('/').filter(Boolean);
|
|
const view = segments[0];
|
|
|
|
if (validViews.includes(view as TValidView)) {
|
|
this.updateViewState(view as TValidView);
|
|
// If there's a sub-path, store the entity ID
|
|
if (segments[1]) {
|
|
const currentState = appstate.uiStatePart.getState();
|
|
appstate.uiStatePart.setState({
|
|
...currentState,
|
|
activeEntityId: segments[1],
|
|
});
|
|
}
|
|
} else {
|
|
this.router.pushUrl('/dashboard');
|
|
}
|
|
}
|
|
}
|
|
|
|
private handleOAuthCallback(): void {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const accessToken = params.get('accessToken');
|
|
const refreshToken = params.get('refreshToken');
|
|
const sessionId = params.get('sessionId');
|
|
|
|
if (accessToken && refreshToken && sessionId) {
|
|
// Store tokens and redirect to dashboard
|
|
// The app shell will pick up the identity from loginStatePart
|
|
appstate.handleOAuthCallback(accessToken, refreshToken, sessionId);
|
|
}
|
|
|
|
// Redirect to dashboard
|
|
this.navigateTo('/dashboard');
|
|
}
|
|
|
|
private updateViewState(view: string): void {
|
|
this.suppressStateUpdate = true;
|
|
const currentState = appstate.uiStatePart.getState();
|
|
if (currentState.activeView !== view) {
|
|
appstate.uiStatePart.setState({
|
|
...currentState,
|
|
activeView: view,
|
|
activeEntityId: undefined,
|
|
});
|
|
}
|
|
this.suppressStateUpdate = false;
|
|
}
|
|
|
|
public navigateTo(path: string): void {
|
|
this.router.pushUrl(path);
|
|
}
|
|
|
|
public navigateToView(view: string): void {
|
|
const normalized = view.toLowerCase().replace(/\s+/g, '-');
|
|
if (validViews.includes(normalized as TValidView)) {
|
|
this.navigateTo(`/${normalized}`);
|
|
} else {
|
|
this.navigateTo('/dashboard');
|
|
}
|
|
}
|
|
|
|
public navigateToEntity(view: string, entityId: string): void {
|
|
const currentState = appstate.uiStatePart.getState();
|
|
appstate.uiStatePart.setState({
|
|
...currentState,
|
|
activeView: view,
|
|
activeEntityId: entityId,
|
|
});
|
|
this.router.pushUrl(`/${view}/${entityId}`);
|
|
}
|
|
|
|
public getCurrentView(): string {
|
|
return appstate.uiStatePart.getState().activeView;
|
|
}
|
|
|
|
public destroy(): void {
|
|
this.router.destroy();
|
|
this.initialized = false;
|
|
}
|
|
}
|
|
|
|
export const appRouter = new AppRouter();
|