diff --git a/client/src/App.tsx b/client/src/App.tsx index 5a93ba4..080d605 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,3 +1,4 @@ +import { lazy, Suspense } from 'react'; import { NavLink, Navigate, Route, Routes } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Globe, Monitor, ClipboardList, Activity, KeyRound, Braces } from 'lucide-react'; @@ -5,29 +6,81 @@ import type { LucideIcon } from 'lucide-react'; import styles from './App.module.css'; import { SidePanel, ThemeSwitcher } from './ui'; import liqaLogo from './assets/liqa.svg'; -import { EnvironmentsPage } from './pages/environment/EnvironmentsPage'; -import { EnvironmentDetailPage } from './pages/environment/EnvironmentDetailPage'; -import { CreateEnvironmentPage } from './pages/environment/CreateEnvironmentPage'; -import { EditEnvironmentPage } from './pages/environment/EditEnvironmentPage'; -import { SessionsPage } from './pages/session/SessionsPage'; -import { SessionDetailPage } from './pages/session/SessionDetailPage'; -import { ScenariosPage } from './pages/scenario/ScenariosPage'; -import { ScenarioDetailPage } from './pages/scenario/ScenarioDetailPage'; -import { CreateScenarioPage } from './pages/scenario/CreateScenarioPage'; -import { EditScenarioPage } from './pages/scenario/EditScenarioPage'; -import { CreateStepPage } from './pages/scenario/CreateStepPage'; -import { EditStepPage } from './pages/scenario/EditStepPage'; -import { RunsPage } from './pages/run/RunsPage'; -import { RunDetailPage } from './pages/run/RunDetailPage'; -import { AllRunsPage } from './pages/run/AllRunsPage'; -import { CredentialsPage } from './pages/credential/CredentialsPage'; -import { CreateCredentialPage } from './pages/credential/CreateCredentialPage'; -import { EditCredentialPage } from './pages/credential/EditCredentialPage'; -import { CredentialDetailPage } from './pages/credential/CredentialDetailPage'; -import { SnippetsPage } from './pages/snippet/SnippetsPage'; -import { CreateSnippetPage } from './pages/snippet/CreateSnippetPage'; -import { EditSnippetPage } from './pages/snippet/EditSnippetPage'; -import { SnippetDetailPage } from './pages/snippet/SnippetDetailPage'; + +const EnvironmentsPage = lazy(() => + import('./pages/environment/EnvironmentsPage').then((m) => ({ default: m.EnvironmentsPage })), +); +const EnvironmentDetailPage = lazy(() => + import('./pages/environment/EnvironmentDetailPage').then((m) => ({ default: m.EnvironmentDetailPage })), +); +const CreateEnvironmentPage = lazy(() => + import('./pages/environment/CreateEnvironmentPage').then((m) => ({ default: m.CreateEnvironmentPage })), +); +const EditEnvironmentPage = lazy(() => + import('./pages/environment/EditEnvironmentPage').then((m) => ({ default: m.EditEnvironmentPage })), +); + +const SessionsPage = lazy(() => + import('./pages/session/SessionsPage').then((m) => ({ default: m.SessionsPage })), +); +const SessionDetailPage = lazy(() => + import('./pages/session/SessionDetailPage').then((m) => ({ default: m.SessionDetailPage })), +); + +const ScenariosPage = lazy(() => + import('./pages/scenario/ScenariosPage').then((m) => ({ default: m.ScenariosPage })), +); +const ScenarioDetailPage = lazy(() => + import('./pages/scenario/ScenarioDetailPage').then((m) => ({ default: m.ScenarioDetailPage })), +); +const CreateScenarioPage = lazy(() => + import('./pages/scenario/CreateScenarioPage').then((m) => ({ default: m.CreateScenarioPage })), +); +const EditScenarioPage = lazy(() => + import('./pages/scenario/EditScenarioPage').then((m) => ({ default: m.EditScenarioPage })), +); +const CreateStepPage = lazy(() => + import('./pages/scenario/CreateStepPage').then((m) => ({ default: m.CreateStepPage })), +); +const EditStepPage = lazy(() => + import('./pages/scenario/EditStepPage').then((m) => ({ default: m.EditStepPage })), +); + +const RunsPage = lazy(() => + import('./pages/run/RunsPage').then((m) => ({ default: m.RunsPage })), +); +const RunDetailPage = lazy(() => + import('./pages/run/RunDetailPage').then((m) => ({ default: m.RunDetailPage })), +); +const AllRunsPage = lazy(() => + import('./pages/run/AllRunsPage').then((m) => ({ default: m.AllRunsPage })), +); + +const CredentialsPage = lazy(() => + import('./pages/credential/CredentialsPage').then((m) => ({ default: m.CredentialsPage })), +); +const CreateCredentialPage = lazy(() => + import('./pages/credential/CreateCredentialPage').then((m) => ({ default: m.CreateCredentialPage })), +); +const EditCredentialPage = lazy(() => + import('./pages/credential/EditCredentialPage').then((m) => ({ default: m.EditCredentialPage })), +); +const CredentialDetailPage = lazy(() => + import('./pages/credential/CredentialDetailPage').then((m) => ({ default: m.CredentialDetailPage })), +); + +const SnippetsPage = lazy(() => + import('./pages/snippet/SnippetsPage').then((m) => ({ default: m.SnippetsPage })), +); +const CreateSnippetPage = lazy(() => + import('./pages/snippet/CreateSnippetPage').then((m) => ({ default: m.CreateSnippetPage })), +); +const EditSnippetPage = lazy(() => + import('./pages/snippet/EditSnippetPage').then((m) => ({ default: m.EditSnippetPage })), +); +const SnippetDetailPage = lazy(() => + import('./pages/snippet/SnippetDetailPage').then((m) => ({ default: m.SnippetDetailPage })), +); const NAV: { path: string; labelKey: string; Icon: LucideIcon }[] = [ { path: '/environments', labelKey: 'nav.environments', Icon: Globe }, @@ -75,33 +128,35 @@ export default function App() {
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + Loading…

}> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
); diff --git a/client/src/pages/environment/CreateEnvironmentPage.tsx b/client/src/pages/environment/CreateEnvironmentPage.tsx index 9ea9e2f..62f34b6 100644 --- a/client/src/pages/environment/CreateEnvironmentPage.tsx +++ b/client/src/pages/environment/CreateEnvironmentPage.tsx @@ -66,7 +66,7 @@ export function CreateEnvironmentPage() { {error &&

{error}

}
- +
- +
(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const dataEntries = Object.entries(env?.data ?? {}).filter(([, v]) => v); useEffect(() => { if (!id) return; @@ -107,14 +108,12 @@ export function EnvironmentDetailPage() {

{t('environments.section_data')}

- {Object.entries(env.data).filter(([, v]) => v).length === 0 ? ( + {dataEntries.length === 0 ? (

{t('environments.no_data')}

) : ( v) - .map(([key, value]) => ({ + items={dataEntries.map(([key, value]) => ({ term: key, detail: ( diff --git a/client/src/pages/environment/EnvironmentsPage.tsx b/client/src/pages/environment/EnvironmentsPage.tsx index 4bb5c26..bd28bf9 100644 --- a/client/src/pages/environment/EnvironmentsPage.tsx +++ b/client/src/pages/environment/EnvironmentsPage.tsx @@ -20,7 +20,7 @@ import styles from '../Page.module.css'; function EnvironmentCard({ env, onDelete }: { env: Environment; onDelete: (id: string) => void }) { const { t } = useTranslation(); const navigate = useNavigate(); - const dataEntries = Object.entries(env.data).filter(([, v]) => v); + const dataEntries = Object.entries(env.data ?? {}).filter(([, v]) => v); const handleExport = async () => { const data = await environments.exportEnvironment(env.id); diff --git a/client/src/ui/SidePanel/SidePanel.module.css b/client/src/ui/SidePanel/SidePanel.module.css index 8dc1b6a..51824b6 100644 --- a/client/src/ui/SidePanel/SidePanel.module.css +++ b/client/src/ui/SidePanel/SidePanel.module.css @@ -79,10 +79,11 @@ /* ── Chevron rotation ────────────────────────────────────── */ .chevron { transition: transform 220ms ease; - /* Points right (→) by default = collapse direction */ + transform: rotate(180deg); + /* Points left (←) by default = collapse direction */ } .collapsed .chevron { - transform: rotate(180deg); - /* Points left (←) = expand direction */ + transform: rotate(0deg); + /* Points right (→) = expand direction */ } diff --git a/client/tsconfig.tsbuildinfo b/client/tsconfig.tsbuildinfo index 75bec30..76114a2 100644 --- a/client/tsconfig.tsbuildinfo +++ b/client/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/main.tsx"],"version":"5.8.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/declarations.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/index.ts","./src/api/types.ts","./src/hooks/useTheme.ts","./src/i18n/index.ts","./src/lib/hljs.ts","./src/pages/credential/CreateCredentialPage.tsx","./src/pages/credential/CredentialDetailPage.tsx","./src/pages/credential/CredentialsPage.tsx","./src/pages/credential/EditCredentialPage.tsx","./src/pages/environment/CreateEnvironmentPage.tsx","./src/pages/environment/EditEnvironmentPage.tsx","./src/pages/environment/EnvironmentDetailPage.tsx","./src/pages/environment/EnvironmentsPage.tsx","./src/pages/run/AllRunsPage.tsx","./src/pages/run/RunDetailPage.tsx","./src/pages/run/RunsPage.tsx","./src/pages/scenario/CreateScenarioPage.tsx","./src/pages/scenario/CreateStepPage.tsx","./src/pages/scenario/EditScenarioPage.tsx","./src/pages/scenario/EditStepPage.tsx","./src/pages/scenario/ScenarioDetailPage.tsx","./src/pages/scenario/ScenariosPage.tsx","./src/pages/session/SessionDetailPage.tsx","./src/pages/session/SessionsPage.tsx","./src/pages/snippet/CreateSnippetPage.tsx","./src/pages/snippet/EditSnippetPage.tsx","./src/pages/snippet/SnippetDetailPage.tsx","./src/pages/snippet/SnippetsPage.tsx","./src/ui/index.ts","./src/ui/AutoRefreshIndicator/AutoRefreshIndicator.tsx","./src/ui/Badge/Badge.tsx","./src/ui/Breadcrumbs/Breadcrumbs.tsx","./src/ui/Button/Button.tsx","./src/ui/Card/Card.tsx","./src/ui/CodeBlock/CodeBlock.tsx","./src/ui/CodeEditor/CodeEditor.tsx","./src/ui/ContextMenu/ContextMenu.tsx","./src/ui/DescriptionList/DescriptionList.tsx","./src/ui/Input/Input.tsx","./src/ui/Notification/Notification.tsx","./src/ui/Pagination/Pagination.tsx","./src/ui/Search/Search.tsx","./src/ui/Select/Select.tsx","./src/ui/SidePanel/SidePanel.tsx","./src/ui/Table/Table.tsx","./src/ui/Textarea/Textarea.tsx","./src/ui/ThemeSwitcher/ThemeSwitcher.tsx","./src/ui/Timestamp/Timestamp.tsx","./src/ui/UuidBadge/UuidBadge.tsx"],"version":"5.8.3"} \ No newline at end of file diff --git a/client/vite.config.ts b/client/vite.config.ts index 08ad0fe..4da88c7 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -25,6 +25,49 @@ export default defineConfig({ __APP_VERSION__: JSON.stringify(APP_VERSION), }, plugins: [react()], + build: { + rollupOptions: { + output: { + manualChunks(id) { + if (!id.includes('/client/src/') && !id.includes('/node_modules/')) { + return; + } + + if (id.includes('/node_modules/lucide-react')) { + return 'icons'; + } + if (id.includes('/node_modules/monaco-editor') || id.includes('/node_modules/@monaco-editor')) { + return 'editor'; + } + if (id.includes('/node_modules/')) { + return 'vendor'; + } + + if (id.includes('/src/pages/environment/')) { + return 'environments'; + } + if (id.includes('/src/pages/scenario/')) { + return 'scenarios'; + } + if (id.includes('/src/pages/run/')) { + return 'runs'; + } + if (id.includes('/src/pages/snippet/')) { + return 'snippets'; + } + if (id.includes('/src/pages/credential/')) { + return 'credentials'; + } + if (id.includes('/src/pages/session/')) { + return 'sessions'; + } + if (id.includes('/src/api/')) { + return 'api'; + } + }, + }, + }, + }, server: { proxy: { '/environments': 'http://localhost:13000',