Skip to content

Commit bd2fde1

Browse files
authored
chore(portal): translate aurora compute nav and loading (#396)
* chore(portal): some translations for compute * chore(portal): compiled trans * chore(portal): correct test for i18n * chore(portal): made suspense vertical * chore(portal): corrected margin spinner * chore(portal): changed lang to en
1 parent 0e6c31b commit bd2fde1

15 files changed

Lines changed: 361 additions & 101 deletions

File tree

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/ComputeNavBar.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ export const ComputeSideNavBar = () => {
1515
const computeRootPath = `/accounts/${domain}/projects/${projectId}/compute`
1616

1717
const links = [
18-
{ path: computeRootPath, label: "Overview" },
19-
{ path: `${computeRootPath}/instances`, label: "Instances" },
20-
{ path: `${computeRootPath}/images`, label: "Images" },
21-
{ path: `${computeRootPath}/keypairs`, label: "Key Pairs" },
22-
{ path: `${computeRootPath}/servergroups`, label: "Server Groups" },
23-
{ path: `${computeRootPath}/flavors`, label: "Flavors" },
18+
{ path: computeRootPath, label: <Trans>Overview</Trans> },
19+
{ path: `${computeRootPath}/instances`, label: <Trans>Instances</Trans> },
20+
{ path: `${computeRootPath}/images`, label: <Trans>Images</Trans> },
21+
{ path: `${computeRootPath}/keypairs`, label: <Trans>Key Pairs</Trans> },
22+
{ path: `${computeRootPath}/servergroups`, label: <Trans>Server Groups</Trans> },
23+
{ path: `${computeRootPath}/flavors`, label: <Trans>Flavors</Trans> },
2424
]
2525

2626
return (

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/Flavors/-components/FlavorListContainer.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
Stack,
1313
} from "@cloudoperators/juno-ui-components"
1414
import { Trans } from "@lingui/react/macro"
15+
import { useLingui } from "@lingui/react/macro"
1516
import { DeleteFlavorModal } from "./DeleteFlavorModal"
1617
import { useState } from "react"
1718
import { TrpcClient } from "@/client/trpcClient"
@@ -34,6 +35,7 @@ export const FlavorListContainer = ({
3435
onFlavorDeleted,
3536
canDeleteFlavor,
3637
}: FlavorListContainerProps) => {
38+
const { t } = useLingui()
3739
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
3840
const [specModalOpen, setSpecModalOpen] = useState(false)
3941
const [selectedFlavor, setSelectedFlavor] = useState<Flavor | null>(null)
@@ -138,9 +140,13 @@ export const FlavorListContainer = ({
138140
<DataGridCell>
139141
<PopupMenu>
140142
<PopupMenuOptions>
141-
<PopupMenuItem label="Extra Specs" icon="info" onClick={() => openSpecModal(flavor)} />
143+
<PopupMenuItem label={t`Extra Specs`} icon="info" onClick={() => openSpecModal(flavor)} />
142144
{canDeleteFlavor && (
143-
<PopupMenuItem icon="deleteForever" label="Delete Flavor" onClick={() => openDeleteModal(flavor)} />
145+
<PopupMenuItem
146+
icon="deleteForever"
147+
label={t`Delete Flavor`}
148+
onClick={() => openDeleteModal(flavor)}
149+
/>
144150
)}
145151
</PopupMenuOptions>
146152
</PopupMenu>

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/Flavors/List.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { use, Suspense, useState, startTransition } from "react"
2-
import { useLingui } from "@lingui/react/macro"
2+
import { Trans, useLingui } from "@lingui/react/macro"
33
import { TrpcClient } from "@/client/trpcClient"
44
import { Flavor } from "@/server/Compute/types/flavor"
5-
import { Message, Button } from "@cloudoperators/juno-ui-components"
5+
import { Message, Button, Stack, Spinner } from "@cloudoperators/juno-ui-components"
66
import FilterToolbar from "./-components/FilterToolbar"
77
import { FlavorListContainer } from "./-components/FlavorListContainer"
88
import { CreateFlavorModal } from "./-components/CreateFlavorModal"
@@ -177,7 +177,14 @@ export const Flavors = ({ client, project }: FlavorsProps) => {
177177
/>
178178
)}
179179

180-
<Suspense fallback={<div>Loading flavors...</div>}>
180+
<Suspense
181+
fallback={
182+
<Stack className="fixed inset-0" distribution="center" alignment="center" direction="vertical">
183+
<Spinner variant="primary" size="large" className="mb-2" />
184+
<Trans>Loading Flavors...</Trans>
185+
</Stack>
186+
}
187+
>
181188
<ErrorBoundary fallback={<ErrorFallback onRetry={refetchFlavors} />}>
182189
<FlavorsContent
183190
flavorsPromise={flavorsPromise}

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/Images/List.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Suspense, use } from "react"
22
import { GlanceImage } from "@/server/Compute/types/image"
33
import { TrpcClient } from "@/client/trpcClient"
44
import { ImageListView } from "./-components/ImageListView"
5+
import { Trans } from "@lingui/react/macro"
6+
import { Spinner, Stack } from "@cloudoperators/juno-ui-components/index"
57

68
const ImageListContainer = ({ getImagesPromise }: { getImagesPromise: Promise<GlanceImage[] | undefined> }) => {
79
const images = use(getImagesPromise)
@@ -20,7 +22,14 @@ export const Images = ({ client }: ImagesProps) => {
2022
const getImagesPromise = client.compute.listImages.query({})
2123

2224
return (
23-
<Suspense fallback={<div className="p-4 text-center ">Loading images...</div>}>
25+
<Suspense
26+
fallback={
27+
<Stack className="fixed inset-0" distribution="center" alignment="center" direction="vertical">
28+
<Spinner variant="primary" size="large" className="mb-2" />
29+
<Trans>Loading Images...</Trans>
30+
</Stack>
31+
}
32+
>
2433
<ImageListContainer getImagesPromise={getImagesPromise} />
2534
</Suspense>
2635
)

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/Instances/-components/ServerListView.test.tsx

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
import { describe, it, expect } from "vitest"
2-
import { render, screen } from "@testing-library/react"
1+
import { describe, it, expect, beforeAll } from "vitest"
2+
import { render, screen, act } from "@testing-library/react"
33
import { ServerListView } from "./ServerListView"
44
import type { Server } from "@/server/Compute/types/server"
5+
import { I18nProvider } from "@lingui/react"
6+
import { ReactNode } from "react"
7+
import { i18n } from "@lingui/core"
8+
9+
const TestingProvider = ({ children }: { children: ReactNode }) => <I18nProvider i18n={i18n}>{children}</I18nProvider>
510

611
const mockServers: Server[] = [
712
{
@@ -37,15 +42,29 @@ const mockServers: Server[] = [
3742
]
3843

3944
describe("ServerListView", () => {
40-
it("renders a table with server names", () => {
41-
render(<ServerListView servers={mockServers} />)
45+
beforeAll(async () => {
46+
await act(async () => {
47+
i18n.activate("en")
48+
})
49+
})
50+
51+
it("renders a table with server names", async () => {
52+
await act(async () => {
53+
render(<ServerListView servers={mockServers} />, {
54+
wrapper: TestingProvider,
55+
})
56+
})
4257

4358
expect(screen.getByText("Cache Server")).toBeInTheDocument()
4459
expect(screen.getByText("Development Server")).toBeInTheDocument()
4560
})
4661

47-
it("displays the correct server status with icons", () => {
48-
render(<ServerListView servers={mockServers} />)
62+
it("displays the correct server status with icons", async () => {
63+
await act(async () => {
64+
render(<ServerListView servers={mockServers} />, {
65+
wrapper: TestingProvider,
66+
})
67+
})
4968

5069
// "ACTIVE" should have a success icon
5170
expect(screen.getByTestId("icon-success")).toBeInTheDocument()
@@ -57,8 +76,12 @@ describe("ServerListView", () => {
5776
expect(screen.getByText("SHUTOFF")).toBeInTheDocument()
5877
})
5978

60-
it("displays correct IPv4 and IPv6 addresses", () => {
61-
render(<ServerListView servers={mockServers} />)
79+
it("displays correct IPv4 and IPv6 addresses", async () => {
80+
await act(async () => {
81+
render(<ServerListView servers={mockServers} />, {
82+
wrapper: TestingProvider,
83+
})
84+
})
6285

6386
expect(screen.getByText("192.168.1.90")).toBeInTheDocument()
6487
expect(screen.getByText("fe80::9")).toBeInTheDocument()
@@ -67,8 +90,12 @@ describe("ServerListView", () => {
6790
expect(screen.getByText("fe80::A")).toBeInTheDocument()
6891
})
6992

70-
it("displays correct CPU, RAM, and disk details", () => {
71-
render(<ServerListView servers={mockServers} />)
93+
it("displays correct CPU, RAM, and disk details", async () => {
94+
await act(async () => {
95+
render(<ServerListView servers={mockServers} />, {
96+
wrapper: TestingProvider,
97+
})
98+
})
7299

73100
expect(screen.getByText("4")).toBeInTheDocument()
74101
expect(screen.getByText("8192 MB")).toBeInTheDocument()
@@ -79,8 +106,12 @@ describe("ServerListView", () => {
79106
expect(screen.getByText("60 GB")).toBeInTheDocument()
80107
})
81108

82-
it("renders action buttons for each server", () => {
83-
render(<ServerListView servers={mockServers} />)
109+
it("renders action buttons for each server", async () => {
110+
await act(async () => {
111+
render(<ServerListView servers={mockServers} />, {
112+
wrapper: TestingProvider,
113+
})
114+
})
84115

85116
const viewButtons = screen.getAllByRole("button", { name: "View" })
86117
const restartButtons = screen.getAllByRole("button", { name: "Restart" })
@@ -89,8 +120,12 @@ describe("ServerListView", () => {
89120
expect(restartButtons.length).toBe(2)
90121
})
91122

92-
it("shows 'No servers available' when the list is empty", () => {
93-
render(<ServerListView servers={[]} />)
123+
it("shows 'No servers available' when the list is empty", async () => {
124+
await act(async () => {
125+
render(<ServerListView servers={[]} />, {
126+
wrapper: TestingProvider,
127+
})
128+
})
94129

95130
expect(screen.getByText("No servers available.")).toBeInTheDocument()
96131
})

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/Instances/-components/ServerListView.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import { ToastProps, auroraToast, sonnerToast } from "@/client/components/Notifi
22
import type { Server } from "@/server/Compute/types/server"
33
import { Button } from "@cloudoperators/juno-ui-components"
44
import { Icon } from "@cloudoperators/juno-ui-components"
5+
import { Trans, useLingui } from "@lingui/react/macro"
56
type ServerListViewProps = {
67
servers: Server[] | undefined
78
}
89

910
export function ServerListView({ servers }: ServerListViewProps) {
11+
const { t } = useLingui()
1012
return (
1113
<div>
1214
{servers && servers.length > 0 ? (
@@ -15,14 +17,14 @@ export function ServerListView({ servers }: ServerListViewProps) {
1517
{/* Table Header */}
1618
<thead>
1719
<tr>
18-
<th className="p-3">Server Name</th>
19-
<th className="p-3">Status</th>
20-
<th className="p-3">IPv4</th>
21-
<th className="p-3">IPv6</th>
22-
<th className="p-3">CPU</th>
23-
<th className="p-3">RAM</th>
24-
<th className="p-3">Disk</th>
25-
<th className="p-3 flex justify-center">Actions</th>
20+
<th className="p-3">{t`Server Name`}</th>
21+
<th className="p-3">{t`Status`}</th>
22+
<th className="p-3">{t`IPv4`}</th>
23+
<th className="p-3">{t`IPv6`}</th>
24+
<th className="p-3">{t`CPU`}</th>
25+
<th className="p-3">{t`RAM`}</th>
26+
<th className="p-3">{t`Disk`}</th>
27+
<th className="p-3 flex justify-center">{t`Actions`}</th>
2628
</tr>
2729
</thead>
2830

@@ -106,7 +108,7 @@ export function ServerListView({ servers }: ServerListViewProps) {
106108
auroraToast(toastProps)
107109
}}
108110
>
109-
Restart
111+
<Trans>Restart</Trans>
110112
</Button>
111113
</div>
112114
</td>
@@ -116,7 +118,9 @@ export function ServerListView({ servers }: ServerListViewProps) {
116118
</table>
117119
</div>
118120
) : (
119-
<p>No servers available.</p>
121+
<p>
122+
<Trans>No servers available.</Trans>
123+
</p>
120124
)}
121125
</div>
122126
)

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/Instances/List.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import ServerListView from "./-components/ServerListView"
33
import { Suspense, use } from "react"
44
import { Server } from "@/server/Compute/types/server"
55
import { TrpcClient } from "@/client/trpcClient"
6-
import { Button } from "@cloudoperators/juno-ui-components/index"
6+
import { Button, Spinner, Stack } from "@cloudoperators/juno-ui-components/index"
7+
import { Trans } from "@lingui/react/macro"
78

89
interface InstanceContainerProps {
910
getServersPromise: Promise<Server[] | undefined>
@@ -30,7 +31,14 @@ export const Instances = ({
3031
const getServersPromise = client.compute.getServersByProjectId.query({ projectId: project })
3132

3233
return (
33-
<Suspense fallback={<div className="p-4 text-center">Loading instances...</div>}>
34+
<Suspense
35+
fallback={
36+
<Stack className="fixed inset-0" distribution="center" alignment="center" direction="vertical">
37+
<Spinner variant="primary" size="large" className="mb-2" />
38+
<Trans>Loading Instances...</Trans>
39+
</Stack>
40+
}
41+
>
3442
<Button
3543
onClick={async () => {
3644
const canList = await client.compute.canUser.query("servers:list")

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/KeyPairs/List.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TrpcClient } from "@/client/trpcClient"
55

66
import { Suspense, use } from "react"
77
import { Trans } from "@lingui/react/macro"
8+
import { Spinner, Stack } from "@cloudoperators/juno-ui-components/index"
89

910
interface KeyPairsContainerProps {
1011
getKeyPairsPromise: Promise<Keypair[] | undefined>
@@ -33,9 +34,10 @@ export function KeyPairs({ client, project }: KeyPairsProps) {
3334
return (
3435
<Suspense
3536
fallback={
36-
<div className="p-4 text-center">
37-
<Trans>Loading...</Trans>
38-
</div>
37+
<Stack className="fixed inset-0" distribution="center" alignment="center" direction="vertical">
38+
<Spinner variant="primary" size="large" className="mb-2" />
39+
<Trans>Loading Key Pairs...</Trans>
40+
</Stack>
3941
}
4042
>
4143
<KeyPairsContainer getKeyPairsPromise={getKeyPairsPromise} />

apps/aurora-portal/src/client/routes/_auth/accounts/$accountId/projects/$projectId/compute/-components/ServerGroups/List.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// ServerGroups.tsx - Main component for server groups
22
import { TrpcClient } from "@/client/trpcClient"
3-
43
import { ServerGroupListView } from "./components/ServerGroupListView"
54
import type { ServerGroup } from "@/server/Compute/types/serverGroup"
65
import { Suspense, use } from "react"
6+
import { Trans } from "@lingui/react/macro"
7+
import { Spinner, Stack } from "@cloudoperators/juno-ui-components/index"
78

89
interface ServerGroupsContainerProps {
910
getServerGroupsPromise: Promise<ServerGroup[] | undefined>
@@ -12,7 +13,7 @@ interface ServerGroupsContainerProps {
1213
const ServerGroupsContainer = ({ getServerGroupsPromise }: ServerGroupsContainerProps) => {
1314
const serverGroups = use(getServerGroupsPromise)
1415
if (!serverGroups || serverGroups.length === 0) {
15-
return <p>No server groups available.</p>
16+
return <Trans>No server groups available.</Trans>
1617
}
1718

1819
return <ServerGroupListView serverGroups={serverGroups} />
@@ -27,7 +28,14 @@ export function ServerGroups({ client, project }: ServerGroupsProps) {
2728
const getServerGroupsPromise = client.compute.getServerGroupsByProjectId.query({ projectId: project })
2829

2930
return (
30-
<Suspense fallback={<div className="p-4 text-center ">Loading server groups...</div>}>
31+
<Suspense
32+
fallback={
33+
<Stack className="fixed inset-0" distribution="center" alignment="center" direction="vertical">
34+
<Spinner variant="primary" size="large" className="mb-2" />
35+
<Trans>Loading Server Groups...</Trans>
36+
</Stack>
37+
}
38+
>
3139
<ServerGroupsContainer getServerGroupsPromise={getServerGroupsPromise} />
3240
</Suspense>
3341
)

0 commit comments

Comments
 (0)