mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-04-04 15:56:16 +02:00
Merge 56f49fe892 into 8dcbf7dbcf
This commit is contained in:
commit
d97f895fa3
|
|
@ -13,6 +13,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|||
import NotificationsProvider from '~/providers/NotificationProvider'
|
||||
import { ThemeProvider } from '~/providers/ThemeProvider'
|
||||
import { UsePageProps } from '../../types/system'
|
||||
import '../i18n'
|
||||
|
||||
const appName = import.meta.env.VITE_APP_NAME || 'Project N.O.M.A.D.'
|
||||
const queryClient = new QueryClient()
|
||||
|
|
@ -40,11 +41,16 @@ createInertiaApp({
|
|||
createRoot(el).render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeProvider>
|
||||
<TransmitProvider baseUrl={window.location.origin} enableLogging={environment === 'development'}>
|
||||
<TransmitProvider
|
||||
baseUrl={window.location.origin}
|
||||
enableLogging={environment === 'development'}
|
||||
>
|
||||
<NotificationsProvider>
|
||||
<ModalsProvider>
|
||||
<App {...props} />
|
||||
{showDevtools && <ReactQueryDevtools initialIsOpen={false} buttonPosition='bottom-left' />}
|
||||
{showDevtools && (
|
||||
<ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-left" />
|
||||
)}
|
||||
</ModalsProvider>
|
||||
</NotificationsProvider>
|
||||
</TransmitProvider>
|
||||
|
|
|
|||
31
admin/inertia/components/LanguageSwitcher.tsx
Normal file
31
admin/inertia/components/LanguageSwitcher.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const languages = [
|
||||
{ code: 'en', name: 'English', flag: '🇺🇸' },
|
||||
{ code: 'zh', name: '中文', flag: '🇨🇳' },
|
||||
]
|
||||
|
||||
export default function LanguageSwitcher() {
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-500">Language:</span>
|
||||
<div className="flex gap-1">
|
||||
{languages.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`px-3 py-1 rounded text-sm transition-colors ${
|
||||
i18n.language === lang.code
|
||||
? 'bg-desert-green text-white'
|
||||
: 'bg-gray-200 hover:bg-gray-300 text-gray-700'
|
||||
}`}
|
||||
>
|
||||
{lang.flag} {lang.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
29
admin/inertia/i18n/index.ts
Normal file
29
admin/inertia/i18n/index.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
|
||||
import en from './locales/en.json'
|
||||
import zh from './locales/zh.json'
|
||||
|
||||
const resources = {
|
||||
en: { translation: en },
|
||||
zh: { translation: zh },
|
||||
}
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'en',
|
||||
supportedLngs: ['en', 'zh'],
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
detection: {
|
||||
order: ['localStorage', 'navigator'],
|
||||
caches: ['localStorage'],
|
||||
},
|
||||
})
|
||||
|
||||
export default i18n
|
||||
144
admin/inertia/i18n/locales/en.json
Normal file
144
admin/inertia/i18n/locales/en.json
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"common": {
|
||||
"home": "Home",
|
||||
"settings": "Settings",
|
||||
"docs": "Docs",
|
||||
"chat": "Chat",
|
||||
"maps": "Maps",
|
||||
"search": "Search",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"install": "Install",
|
||||
"uninstall": "Uninstall",
|
||||
"start": "Start",
|
||||
"stop": "Stop",
|
||||
"restart": "Restart",
|
||||
"loading": "Loading...",
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
"warning": "Warning",
|
||||
"info": "Info",
|
||||
"confirm": "Confirm",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"unknown": "Unknown",
|
||||
"back": "Back",
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
"close": "Close",
|
||||
"submit": "Submit",
|
||||
"reset": "Reset"
|
||||
},
|
||||
"home": {
|
||||
"title": "Command Center",
|
||||
"subtitle": "Your offline knowledge and education hub",
|
||||
"updateAvailable": "An update is available for Project N.O.M.A.D.!",
|
||||
"goToSettings": "Go to Settings",
|
||||
"startHere": "Start here!",
|
||||
"poweredBy": "Powered by",
|
||||
"easySetupDesc": "Not sure where to start? Use the setup wizard to quickly configure your N.O.M.A.D.!",
|
||||
"installAppsDesc": "Not seeing your favorite app? Install it here!",
|
||||
"docsDesc": "Read Project N.O.M.A.D. manuals and guides",
|
||||
"settingsDesc": "Configure your N.O.M.A.D. settings"
|
||||
},
|
||||
"menu": {
|
||||
"maps": "Maps",
|
||||
"easySetup": "Easy Setup",
|
||||
"installApps": "Install Apps",
|
||||
"docs": "Docs",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"maps": {
|
||||
"title": "Offline Maps",
|
||||
"viewOffline": "View offline maps",
|
||||
"search": "Search locations...",
|
||||
"noResults": "No results found",
|
||||
"loadingMap": "Loading map...",
|
||||
"offline": "Offline map - no internet required"
|
||||
},
|
||||
"chat": {
|
||||
"title": "AI Assistant",
|
||||
"placeholder": "Ask me anything...",
|
||||
"thinking": "Thinking...",
|
||||
"noMessages": "Start a conversation with the AI assistant",
|
||||
"uploadDocument": "Upload Document",
|
||||
"knowledgeBase": "Knowledge Base"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"system": "System",
|
||||
"apps": "Apps",
|
||||
"models": "Models",
|
||||
"maps": "Maps",
|
||||
"mapsManager": "Maps Manager",
|
||||
"benchmark": "Benchmark",
|
||||
"update": "Update",
|
||||
"checkUpdates": "Check for Updates",
|
||||
"legal": "Legal",
|
||||
"support": "Support",
|
||||
"supportProject": "Support the Project",
|
||||
"contentExplorer": "Content Explorer",
|
||||
"contentManager": "Content Manager",
|
||||
"serviceLogs": "Service Logs & Metrics"
|
||||
},
|
||||
"system": {
|
||||
"title": "System Settings",
|
||||
"hostname": "Hostname",
|
||||
"version": "Version",
|
||||
"uptime": "Uptime",
|
||||
"storage": "Storage",
|
||||
"memory": "Memory",
|
||||
"cpu": "CPU",
|
||||
"network": "Network",
|
||||
"theme": "Theme",
|
||||
"language": "Language",
|
||||
"darkMode": "Dark Mode",
|
||||
"lightMode": "Light Mode"
|
||||
},
|
||||
"apps": {
|
||||
"title": "Install Apps",
|
||||
"installed": "Installed",
|
||||
"notInstalled": "Not Installed",
|
||||
"installing": "Installing...",
|
||||
"noApps": "No apps available"
|
||||
},
|
||||
"models": {
|
||||
"title": "AI Models",
|
||||
"download": "Download",
|
||||
"downloading": "Downloading...",
|
||||
"downloaded": "Downloaded",
|
||||
"delete": "Delete Model",
|
||||
"noModels": "No models installed"
|
||||
},
|
||||
"easySetup": {
|
||||
"title": "Easy Setup",
|
||||
"welcome": "Welcome to Project N.O.M.A.D.!",
|
||||
"getStarted": "Let's get you started with the basics.",
|
||||
"next": "Next",
|
||||
"skip": "Skip",
|
||||
"complete": "Complete"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentation",
|
||||
"searchDocs": "Search documentation...",
|
||||
"noResults": "No documentation found"
|
||||
},
|
||||
"about": {
|
||||
"title": "About Project N.O.M.A.D.",
|
||||
"description": "Project N.O.M.A.D. (Node for Offline Media, Archives, and Data) is an offline-first knowledge and education server.",
|
||||
"version": "Version",
|
||||
"license": "License",
|
||||
"github": "GitHub",
|
||||
"website": "Website"
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "Page Not Found",
|
||||
"serverError": "Server Error",
|
||||
"goHome": "Go to Home",
|
||||
"tryAgain": "Try Again"
|
||||
}
|
||||
}
|
||||
144
admin/inertia/i18n/locales/zh.json
Normal file
144
admin/inertia/i18n/locales/zh.json
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"common": {
|
||||
"home": "首页",
|
||||
"settings": "设置",
|
||||
"docs": "文档",
|
||||
"chat": "聊天",
|
||||
"maps": "地图",
|
||||
"search": "搜索",
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"install": "安装",
|
||||
"uninstall": "卸载",
|
||||
"start": "启动",
|
||||
"stop": "停止",
|
||||
"restart": "重启",
|
||||
"loading": "加载中...",
|
||||
"error": "错误",
|
||||
"success": "成功",
|
||||
"warning": "警告",
|
||||
"info": "信息",
|
||||
"confirm": "确认",
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"enabled": "已启用",
|
||||
"disabled": "已禁用",
|
||||
"unknown": "未知",
|
||||
"back": "返回",
|
||||
"next": "下一步",
|
||||
"previous": "上一步",
|
||||
"close": "关闭",
|
||||
"submit": "提交",
|
||||
"reset": "重置"
|
||||
},
|
||||
"home": {
|
||||
"title": "控制中心",
|
||||
"subtitle": "您的离线知识与教育中心",
|
||||
"updateAvailable": "Project N.O.M.A.D. 有可用更新!",
|
||||
"goToSettings": "前往设置",
|
||||
"startHere": "从这里开始!",
|
||||
"poweredBy": "技术支持",
|
||||
"easySetupDesc": "不确定从哪里开始?使用设置向导快速配置您的 N.O.M.A.D.!",
|
||||
"installAppsDesc": "没有看到您想要的应用?在这里安装!",
|
||||
"docsDesc": "阅读 Project N.O.M.A.D. 手册和指南",
|
||||
"settingsDesc": "配置您的 N.O.M.A.D. 设置"
|
||||
},
|
||||
"menu": {
|
||||
"maps": "地图",
|
||||
"easySetup": "快速设置",
|
||||
"installApps": "安装应用",
|
||||
"docs": "文档",
|
||||
"settings": "设置"
|
||||
},
|
||||
"maps": {
|
||||
"title": "离线地图",
|
||||
"viewOffline": "查看离线地图",
|
||||
"search": "搜索位置...",
|
||||
"noResults": "未找到结果",
|
||||
"loadingMap": "加载地图中...",
|
||||
"offline": "离线地图 - 无需互联网"
|
||||
},
|
||||
"chat": {
|
||||
"title": "AI 助手",
|
||||
"placeholder": "问我任何问题...",
|
||||
"thinking": "思考中...",
|
||||
"noMessages": "开始与 AI 助手对话",
|
||||
"uploadDocument": "上传文档",
|
||||
"knowledgeBase": "知识库"
|
||||
},
|
||||
"settings": {
|
||||
"title": "设置",
|
||||
"system": "系统",
|
||||
"apps": "应用",
|
||||
"models": "模型",
|
||||
"maps": "地图",
|
||||
"mapsManager": "地图管理器",
|
||||
"benchmark": "基准测试",
|
||||
"update": "更新",
|
||||
"checkUpdates": "检查更新",
|
||||
"legal": "法律",
|
||||
"support": "支持",
|
||||
"supportProject": "支持项目",
|
||||
"contentExplorer": "内容浏览器",
|
||||
"contentManager": "内容管理器",
|
||||
"serviceLogs": "服务日志和指标"
|
||||
},
|
||||
"system": {
|
||||
"title": "系统设置",
|
||||
"hostname": "主机名",
|
||||
"version": "版本",
|
||||
"uptime": "运行时间",
|
||||
"storage": "存储",
|
||||
"memory": "内存",
|
||||
"cpu": "处理器",
|
||||
"network": "网络",
|
||||
"theme": "主题",
|
||||
"language": "语言",
|
||||
"darkMode": "深色模式",
|
||||
"lightMode": "浅色模式"
|
||||
},
|
||||
"apps": {
|
||||
"title": "安装应用",
|
||||
"installed": "已安装",
|
||||
"notInstalled": "未安装",
|
||||
"installing": "安装中...",
|
||||
"noApps": "没有可用的应用"
|
||||
},
|
||||
"models": {
|
||||
"title": "AI 模型",
|
||||
"download": "下载",
|
||||
"downloading": "下载中...",
|
||||
"downloaded": "已下载",
|
||||
"delete": "删除模型",
|
||||
"noModels": "没有已安装的模型"
|
||||
},
|
||||
"easySetup": {
|
||||
"title": "快速设置",
|
||||
"welcome": "欢迎使用 Project N.O.M.A.D.!",
|
||||
"getStarted": "让我们从基础设置开始。",
|
||||
"next": "下一步",
|
||||
"skip": "跳过",
|
||||
"complete": "完成"
|
||||
},
|
||||
"docs": {
|
||||
"title": "文档",
|
||||
"searchDocs": "搜索文档...",
|
||||
"noResults": "未找到文档"
|
||||
},
|
||||
"about": {
|
||||
"title": "关于 Project N.O.M.A.D.",
|
||||
"description": "Project N.O.M.A.D.(离线媒体、档案和数据节点)是一个离线优先的知识与教育服务器。",
|
||||
"version": "版本",
|
||||
"license": "许可证",
|
||||
"github": "GitHub",
|
||||
"website": "网站"
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "页面未找到",
|
||||
"serverError": "服务器错误",
|
||||
"goHome": "返回首页",
|
||||
"tryAgain": "重试"
|
||||
}
|
||||
}
|
||||
|
|
@ -9,47 +9,67 @@ import {
|
|||
IconSettings,
|
||||
IconTerminal2,
|
||||
IconWand,
|
||||
IconZoom
|
||||
IconZoom,
|
||||
} from '@tabler/icons-react'
|
||||
import { usePage } from '@inertiajs/react'
|
||||
import StyledSidebar from '~/components/StyledSidebar'
|
||||
import { getServiceLink } from '~/lib/navigation'
|
||||
import useServiceInstalledStatus from '~/hooks/useServiceInstalledStatus'
|
||||
import { SERVICE_NAMES } from '../../constants/service_names'
|
||||
import LanguageSwitcher from '~/components/LanguageSwitcher'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function SettingsLayout({ children }: { children: React.ReactNode }) {
|
||||
const { t } = useTranslation()
|
||||
const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props
|
||||
const aiAssistantInstallStatus = useServiceInstalledStatus(SERVICE_NAMES.OLLAMA)
|
||||
|
||||
const navigation = [
|
||||
...(aiAssistantInstallStatus.isInstalled ? [{ name: aiAssistantName, href: '/settings/models', icon: IconWand, current: false }] : []),
|
||||
{ name: 'Apps', href: '/settings/apps', icon: IconTerminal2, current: false },
|
||||
{ name: 'Benchmark', href: '/settings/benchmark', icon: IconChartBar, current: false },
|
||||
{ name: 'Content Explorer', href: '/settings/zim/remote-explorer', icon: IconZoom, current: false },
|
||||
{ name: 'Content Manager', href: '/settings/zim', icon: IconFolder, current: false },
|
||||
{ name: 'Maps Manager', href: '/settings/maps', icon: IconMapRoute, current: false },
|
||||
...(aiAssistantInstallStatus.isInstalled
|
||||
? [{ name: aiAssistantName, href: '/settings/models', icon: IconWand, current: false }]
|
||||
: []),
|
||||
{ name: t('settings.apps'), href: '/settings/apps', icon: IconTerminal2, current: false },
|
||||
{
|
||||
name: 'Service Logs & Metrics',
|
||||
name: t('settings.benchmark'),
|
||||
href: '/settings/benchmark',
|
||||
icon: IconChartBar,
|
||||
current: false,
|
||||
},
|
||||
{
|
||||
name: t('settings.contentExplorer'),
|
||||
href: '/settings/zim/remote-explorer',
|
||||
icon: IconZoom,
|
||||
current: false,
|
||||
},
|
||||
{ name: t('settings.contentManager'), href: '/settings/zim', icon: IconFolder, current: false },
|
||||
{ name: t('settings.mapsManager'), href: '/settings/maps', icon: IconMapRoute, current: false },
|
||||
{
|
||||
name: t('settings.serviceLogs'),
|
||||
href: getServiceLink('9999'),
|
||||
icon: IconDashboard,
|
||||
current: false,
|
||||
target: '_blank',
|
||||
},
|
||||
{
|
||||
name: 'Check for Updates',
|
||||
name: t('settings.checkUpdates'),
|
||||
href: '/settings/update',
|
||||
icon: IconArrowBigUpLines,
|
||||
current: false,
|
||||
},
|
||||
{ name: 'System', href: '/settings/system', icon: IconSettings, current: false },
|
||||
{ name: 'Support the Project', href: '/settings/support', icon: IconHeart, current: false },
|
||||
{ name: 'Legal Notices', href: '/settings/legal', icon: IconGavel, current: false },
|
||||
{ name: t('settings.system'), href: '/settings/system', icon: IconSettings, current: false },
|
||||
{ name: t('settings.support'), href: '/settings/support', icon: IconHeart, current: false },
|
||||
{ name: t('settings.legal'), href: '/settings/legal', icon: IconGavel, current: false },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-row bg-surface-secondary/90">
|
||||
<StyledSidebar title="Settings" items={navigation} />
|
||||
{children}
|
||||
<StyledSidebar title={t('settings.title')} items={navigation} />
|
||||
<div className="flex-1 flex flex-col">
|
||||
<div className="p-4 border-b border-desert-stone-light bg-desert-white">
|
||||
<LanguageSwitcher />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,57 +15,57 @@ import { useUpdateAvailable } from '~/hooks/useUpdateAvailable'
|
|||
import { useSystemSetting } from '~/hooks/useSystemSetting'
|
||||
import Alert from '~/components/Alert'
|
||||
import { SERVICE_NAMES } from '../../constants/service_names'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
// Maps is a Core Capability (display_order: 4)
|
||||
const MAPS_ITEM = {
|
||||
label: 'Maps',
|
||||
const getMapsItem = (t: (key: string) => string) => ({
|
||||
label: t('menu.maps'),
|
||||
to: '/maps',
|
||||
target: '',
|
||||
description: 'View offline maps',
|
||||
description: t('maps.viewOffline'),
|
||||
icon: <IconMapRoute size={48} />,
|
||||
installed: true,
|
||||
displayOrder: 4,
|
||||
poweredBy: null,
|
||||
}
|
||||
})
|
||||
|
||||
// System items shown after all apps
|
||||
const SYSTEM_ITEMS = [
|
||||
const getSystemItems = (t: (key: string) => string) => [
|
||||
{
|
||||
label: 'Easy Setup',
|
||||
label: t('menu.easySetup'),
|
||||
to: '/easy-setup',
|
||||
target: '',
|
||||
description:
|
||||
'Not sure where to start? Use the setup wizard to quickly configure your N.O.M.A.D.!',
|
||||
description: t('home.easySetupDesc'),
|
||||
icon: <IconBolt size={48} />,
|
||||
installed: true,
|
||||
displayOrder: 50,
|
||||
poweredBy: null,
|
||||
},
|
||||
{
|
||||
label: 'Install Apps',
|
||||
label: t('menu.installApps'),
|
||||
to: '/settings/apps',
|
||||
target: '',
|
||||
description: 'Not seeing your favorite app? Install it here!',
|
||||
description: t('home.installAppsDesc'),
|
||||
icon: <IconPlus size={48} />,
|
||||
installed: true,
|
||||
displayOrder: 51,
|
||||
poweredBy: null,
|
||||
},
|
||||
{
|
||||
label: 'Docs',
|
||||
label: t('menu.docs'),
|
||||
to: '/docs/home',
|
||||
target: '',
|
||||
description: 'Read Project N.O.M.A.D. manuals and guides',
|
||||
description: t('home.docsDesc'),
|
||||
icon: <IconHelp size={48} />,
|
||||
installed: true,
|
||||
displayOrder: 52,
|
||||
poweredBy: null,
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
label: t('menu.settings'),
|
||||
to: '/settings/system',
|
||||
target: '',
|
||||
description: 'Configure your N.O.M.A.D. settings',
|
||||
description: t('home.settingsDesc'),
|
||||
icon: <IconSettings size={48} />,
|
||||
installed: true,
|
||||
displayOrder: 53,
|
||||
|
|
@ -89,15 +89,18 @@ export default function Home(props: {
|
|||
services: ServiceSlim[]
|
||||
}
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const items: DashboardItem[] = []
|
||||
const updateInfo = useUpdateAvailable();
|
||||
const updateInfo = useUpdateAvailable()
|
||||
const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props
|
||||
|
||||
// Check if user has visited Easy Setup
|
||||
const { data: easySetupVisited } = useSystemSetting({
|
||||
key: 'ui.hasVisitedEasySetup'
|
||||
key: 'ui.hasVisitedEasySetup',
|
||||
})
|
||||
const shouldHighlightEasySetup = easySetupVisited?.value ? String(easySetupVisited.value) !== 'true' : false
|
||||
const shouldHighlightEasySetup = easySetupVisited?.value
|
||||
? String(easySetupVisited.value) !== 'true'
|
||||
: false
|
||||
|
||||
// Add installed services (non-dependency services only)
|
||||
props.system.services
|
||||
|
|
@ -105,7 +108,10 @@ export default function Home(props: {
|
|||
.forEach((service) => {
|
||||
items.push({
|
||||
// Inject custom AI Assistant name if this is the chat service
|
||||
label: service.service_name === SERVICE_NAMES.OLLAMA && aiAssistantName ? aiAssistantName : (service.friendly_name || service.service_name),
|
||||
label:
|
||||
service.service_name === SERVICE_NAMES.OLLAMA && aiAssistantName
|
||||
? aiAssistantName
|
||||
: service.friendly_name || service.service_name,
|
||||
to: service.ui_location ? getServiceLink(service.ui_location) : '#',
|
||||
target: '_blank',
|
||||
description:
|
||||
|
|
@ -123,38 +129,36 @@ export default function Home(props: {
|
|||
})
|
||||
|
||||
// Add Maps as a Core Capability
|
||||
items.push(MAPS_ITEM)
|
||||
items.push(getMapsItem(t))
|
||||
|
||||
// Add system items
|
||||
items.push(...SYSTEM_ITEMS)
|
||||
items.push(...getSystemItems(t))
|
||||
|
||||
// Sort all items by display order
|
||||
items.sort((a, b) => a.displayOrder - b.displayOrder)
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<Head title="Command Center" />
|
||||
{
|
||||
updateInfo?.updateAvailable && (
|
||||
<div className='flex justify-center items-center p-4 w-full'>
|
||||
<Alert
|
||||
title="An update is available for Project N.O.M.A.D.!"
|
||||
type="info-inverted"
|
||||
variant="solid"
|
||||
className="w-full"
|
||||
buttonProps={{
|
||||
variant: 'primary',
|
||||
children: 'Go to Settings',
|
||||
icon: 'IconSettings',
|
||||
onClick: () => router.visit('/settings/update'),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<Head title={t('home.title')} />
|
||||
{updateInfo?.updateAvailable && (
|
||||
<div className="flex justify-center items-center p-4 w-full">
|
||||
<Alert
|
||||
title={t('home.updateAvailable')}
|
||||
type="info-inverted"
|
||||
variant="solid"
|
||||
className="w-full"
|
||||
buttonProps={{
|
||||
variant: 'primary',
|
||||
children: t('home.goToSettings'),
|
||||
icon: 'IconSettings',
|
||||
onClick: () => router.visit('/settings/update'),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
|
||||
{items.map((item) => {
|
||||
const isEasySetup = item.label === 'Easy Setup'
|
||||
const isEasySetup = item.label === t('menu.easySetup')
|
||||
const shouldHighlight = isEasySetup && shouldHighlightEasySetup
|
||||
|
||||
const tileContent = (
|
||||
|
|
@ -166,13 +170,17 @@ export default function Home(props: {
|
|||
style={{ animationDuration: '1.5s' }}
|
||||
></span>
|
||||
<span className="relative inline-flex items-center rounded-full px-2.5 py-1 bg-desert-orange-light text-xs font-semibold text-white shadow-sm">
|
||||
Start here!
|
||||
{t('home.startHere')}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
<div className="flex items-center justify-center mb-2">{item.icon}</div>
|
||||
<h3 className="font-bold text-2xl">{item.label}</h3>
|
||||
{item.poweredBy && <p className="text-sm opacity-80">Powered by {item.poweredBy}</p>}
|
||||
{item.poweredBy && (
|
||||
<p className="text-sm opacity-80">
|
||||
{t('home.poweredBy')} {item.poweredBy}
|
||||
</p>
|
||||
)}
|
||||
<p className="xl:text-lg mt-2">{item.description}</p>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
91
admin/package-lock.json
generated
91
admin/package-lock.json
generated
|
|
@ -47,6 +47,8 @@
|
|||
"edge.js": "^6.2.1",
|
||||
"fast-xml-parser": "^5.5.7",
|
||||
"fuse.js": "^7.1.0",
|
||||
"i18next": "^26.0.3",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"jszip": "^3.10.1",
|
||||
"luxon": "^3.6.1",
|
||||
"maplibre-gl": "^4.7.1",
|
||||
|
|
@ -61,6 +63,7 @@
|
|||
"react": "^19.1.0",
|
||||
"react-adonis-transmit": "^1.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^17.0.2",
|
||||
"react-map-gl": "^8.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
|
|
@ -1096,6 +1099,14 @@
|
|||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
|
|
@ -9814,6 +9825,14 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||
"dependencies": {
|
||||
"void-elements": "3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-url-attributes": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
||||
|
|
@ -9917,6 +9936,44 @@
|
|||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "26.0.3",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.3.tgz",
|
||||
"integrity": "sha512-1571kXINxHKY7LksWp8wP+zP0YqHSSpl/OW0Y0owFEf2H3s8gCAffWaZivcz14rMkOvn3R/psiQxVsR9t2Nafg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.locize.com/i18next"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.locize.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.29.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5 || ^6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-browser-languagedetector": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.1.tgz",
|
||||
"integrity": "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
||||
|
|
@ -14049,6 +14106,32 @@
|
|||
"react": "^19.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.2.tgz",
|
||||
"integrity": "sha512-shBftH2vaTWK2Bsp7FiL+cevx3xFJlvFxmsDFQSrJc+6twHkP0tv/bGa01VVWzpreUVVwU+3Hev5iFqRg65RwA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.29.2",
|
||||
"html-parse-stringify": "^3.0.1",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"i18next": ">= 26.0.1",
|
||||
"react": ">= 16.8.0",
|
||||
"typescript": "^5 || ^6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
|
|
@ -16513,6 +16596,14 @@
|
|||
"vite": "^2.9.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vt-pbf": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@
|
|||
"edge.js": "^6.2.1",
|
||||
"fast-xml-parser": "^5.5.7",
|
||||
"fuse.js": "^7.1.0",
|
||||
"i18next": "^26.0.3",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"jszip": "^3.10.1",
|
||||
"luxon": "^3.6.1",
|
||||
"maplibre-gl": "^4.7.1",
|
||||
|
|
@ -114,6 +116,7 @@
|
|||
"react": "^19.1.0",
|
||||
"react-adonis-transmit": "^1.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^17.0.2",
|
||||
"react-map-gl": "^8.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user