lots of improvements

This commit is contained in:
root
2026-02-10 17:47:48 -05:00
parent 6d0c5584bd
commit b87b5d9052
65 changed files with 3445 additions and 1577 deletions

View File

@@ -30,18 +30,17 @@ class InitController extends ControllerAbstract
// modules - filter by permissions
$configuration['modules'] = [];
foreach ($this->moduleManager->list() as $module) {
if ($module instanceof ModuleBrowserInterface === false) {
continue;
}
// Check if user has permission to view this module
// Allow access if user has: {module_handle}, {module_handle}.*, or * permission
$handle = $module->handle();
if (!$this->hasModuleViewPermission($handle)) {
continue;
}
$configuration['modules'][$handle] = $module->registerBI();
$integrations = $module->registerBI();
if ($integrations !== null) {
$configuration['modules'][$handle] = $integrations;
}
}
// tenant

View File

@@ -252,6 +252,29 @@ class ModuleManager
$this->repository->deposit($moduleEntry);
}
/**
* Boot all enabled modules (must be called after container is ready).
*/
public function modulesBoot(): void
{
// Only load modules that are enabled in the database
$modules = $this->list();
$this->logger->debug('Booting enabled modules', ['count' => count($modules)]);
foreach ($modules as $module) {
$handle = $module->handle();
try {
$module->boot();
$this->logger->debug('Module booted', ['handle' => $handle]);
} catch (Exception $e) {
$this->logger->error('Module boot failed: ' . $handle, [
'exception' => $e,
'message' => $e->getMessage(),
'code' => $e->getCode(),
]);
}
}
}
/**
* Scan filesystem for module directories and return module instances
*
@@ -315,29 +338,6 @@ class ModuleManager
return $modules;
}
/**
* Boot all enabled modules (must be called after container is ready).
*/
public function modulesBoot(): void
{
// Only load modules that are enabled in the database
$modules = $this->list();
$this->logger->debug('Booting enabled modules', ['count' => count($modules)]);
foreach ($modules as $module) {
$handle = $module->handle();
try {
$module->boot();
$this->logger->debug('Module booted', ['handle' => $handle]);
} catch (Exception $e) {
$this->logger->error('Module boot failed: ' . $handle, [
'exception' => $e,
'message' => $e->getMessage(),
'code' => $e->getCode(),
]);
}
}
}
public function moduleInstance(string $handle, ?string $namespace = null): ?ModuleInstanceInterface
{

View File

@@ -4,6 +4,8 @@ namespace KTXC\Module;
use JsonSerializable;
use KTXC\Module\Store\ModuleEntry;
use KTXF\Module\ModuleBrowserInterface;
use KTXF\Module\ModuleConsoleInterface;
use KTXF\Module\ModuleInstanceInterface;
/**
@@ -97,6 +99,11 @@ class ModuleObject implements JsonSerializable
return '0.0.0';
}
public function permissions(): array
{
return $this->instance?->permissions() ?? [];
}
// ===== Computed properties =====
public function needsUpgrade(): bool
@@ -153,14 +160,20 @@ class ModuleObject implements JsonSerializable
$this->instance?->upgrade();
}
public function bootUi(): array | null
public function registerBI(): array | null
{
return $this->instance?->bootUi() ?? null;
if ($this->instance instanceof ModuleBrowserInterface) {
return $this->instance->registerBI();
}
return null;
}
public function permissions(): array
public function registerCI(): array | null
{
return $this->instance?->permissions() ?? [];
if ($this->instance instanceof ModuleConsoleInterface) {
return $this->instance->registerCI();
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/**
* Simple snackbar/toast notification composable
* Uses Vuetify's snackbar component
*/
import { ref } from 'vue'
export interface SnackbarOptions {
message: string
color?: 'success' | 'error' | 'warning' | 'info' | string
timeout?: number
}
const snackbarVisible = ref(false)
const snackbarMessage = ref('')
const snackbarColor = ref<string>('info')
const snackbarTimeout = ref(3000)
export function useSnackbar() {
const showSnackbar = (options: SnackbarOptions) => {
snackbarMessage.value = options.message
snackbarColor.value = options.color || 'info'
snackbarTimeout.value = options.timeout || 3000
snackbarVisible.value = true
}
const hideSnackbar = () => {
snackbarVisible.value = false
}
return {
snackbarVisible,
snackbarMessage,
snackbarColor,
snackbarTimeout,
showSnackbar,
hideSnackbar,
}
}

View File

@@ -13,9 +13,21 @@ export interface ModuleObject {
export type ModuleCollection = Record<string, ModuleObject>;
// Module-side integration types (before prefixing by the loader)
export interface ModuleIntegrationItem {
// Module integration types
/**
* Base interface for module integrations
*/
export interface ModuleIntegrationBase {
id: string;
[key: string]: any;
}
/**
* UI Navigation integration item
* Represents a single clickable entry in a menu or navigation structure
*/
export interface ModuleIntegrationItem extends ModuleIntegrationBase {
type?: 'item';
label: string;
caption?: string;
@@ -30,8 +42,11 @@ export interface ModuleIntegrationItem {
meta?: Record<string, any>;
}
export interface ModuleIntegrationGroup {
id: string;
/**
* UI Navigation integration group
* Represents a group of related navigation items, potentially with its own label and icon
*/
export interface ModuleIntegrationGroup extends ModuleIntegrationBase {
type: 'group';
label?: string;
caption?: string;
@@ -47,5 +62,6 @@ export type ModuleIntegrationEntry = ModuleIntegrationItem | ModuleIntegrationGr
export interface ModuleIntegrations {
system_menu?: ModuleIntegrationEntry[];
user_menu?: ModuleIntegrationEntry[];
[key: string]: ModuleIntegrationEntry[] | undefined;
[key: string]: ModuleIntegrationEntry[] | ModuleIntegrationBase[] | undefined;
}