98 lines
3.3 KiB
JavaScript
98 lines
3.3 KiB
JavaScript
/**
|
|
* SettingsPage - Settings page component
|
|
* Platform default: lists registered fragment routes under the settings base path
|
|
* and mounts each with {@code settingsFragmentPath} so fragments can resolve their own meta.
|
|
*/
|
|
|
|
import React, { useMemo } from 'react';
|
|
import { Page } from '../components/Page.jsx';
|
|
import { YStack, Input, Text } from 'tamagui';
|
|
import { useRouter, useRoute } from '../components/Router.jsx';
|
|
import { getRootItem } from '../../platform/menu.js';
|
|
import { securityService, useSecurityState } from '../../security/runtime/security-service.js';
|
|
|
|
export function SettingsPage() {
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const securityState = useSecurityState();
|
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
|
|
const basePath = useMemo(() => {
|
|
if (route.path) {
|
|
const currentPath = route.fragment_path || route.path;
|
|
const segments = currentPath.split('/').filter((s) => s.length > 0);
|
|
return segments.length > 0 ? `/${segments[0]}` : '/settings';
|
|
}
|
|
return '/settings';
|
|
}, [route.path, route.fragment_path]);
|
|
|
|
const childRoutes = useMemo(() => {
|
|
const allRoutes = router.getRoutes();
|
|
const settingsRoot = getRootItem('settings');
|
|
const settingsItems = settingsRoot ? Array.from(settingsRoot.items.values()) : [];
|
|
const security = {
|
|
...securityState,
|
|
isPermitted: (rights, resourcePath, options = {}) => securityService.isPermitted(rights, resourcePath, options)
|
|
};
|
|
const routes = [];
|
|
|
|
for (const [path, routeData] of allRoutes.entries()) {
|
|
if (path.startsWith(`${basePath}/`) && path !== basePath) {
|
|
if (routeData.is_fragment) {
|
|
const matchingMenuItem = settingsItems.find((item) => item.invoke_target === path);
|
|
if (matchingMenuItem && !matchingMenuItem.isRenderable(security)) {
|
|
continue;
|
|
}
|
|
routes.push({
|
|
path,
|
|
component: routeData.component,
|
|
options: routeData.options || {}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
return routes;
|
|
}, [router, basePath, router.currentRoute, securityState]);
|
|
|
|
const filteredRoutes = useMemo(() => {
|
|
if (!searchQuery.trim()) {
|
|
return childRoutes;
|
|
}
|
|
const query = searchQuery.toLowerCase();
|
|
return childRoutes.filter((r) => {
|
|
const segments = r.path.split('/').filter((s) => s.length > 0);
|
|
const routeName = segments[segments.length - 1] || '';
|
|
return routeName.toLowerCase().includes(query);
|
|
});
|
|
}, [childRoutes, searchQuery]);
|
|
|
|
return (
|
|
<Page icon="settings" title="Settings" headerRight={[]}>
|
|
<YStack gap="$4" width="100%">
|
|
<Input
|
|
placeholder="Search settings…"
|
|
value={searchQuery}
|
|
onChangeText={setSearchQuery}
|
|
size="$4"
|
|
/>
|
|
{filteredRoutes.length > 0 ? (
|
|
filteredRoutes.map((routeItem) => {
|
|
const RouteComponent = routeItem.component;
|
|
return (
|
|
<RouteComponent key={routeItem.path} settingsFragmentPath={routeItem.path} />
|
|
);
|
|
})
|
|
) : (
|
|
<Text fontSize="$4" color="$textMuted">
|
|
{searchQuery.trim() ? 'No settings found matching your search.' : 'No settings available.'}
|
|
</Text>
|
|
)}
|
|
</YStack>
|
|
</Page>
|
|
);
|
|
}
|
|
|
|
export default SettingsPage;
|