Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# [0.1.0-alpha.31](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.30...v0.1.0-alpha.31) (2025-10-08)


### Bug Fixes

* **ci:** discord notification on webhooks ([a8261f9](https://github.com/raghavyuva/nixopus/commit/a8261f914aea1481021bd60656036439b88fc8a2))
* **i18n:** update terms phrasing for clarity in English locale ([#460](https://github.com/raghavyuva/nixopus/issues/460)) ([0b96b29](https://github.com/raghavyuva/nixopus/commit/0b96b29a459b8db0414d01c3e720e525e536d6c4))
* **terminal:** custom key event handler for Ctrl + J ([#459](https://github.com/raghavyuva/nixopus/issues/459)) ([291bec7](https://github.com/raghavyuva/nixopus/commit/291bec7e44a577a539286fefa3df8c73fdc997c9))


### Features

* automated discord notifications for new releases ([#439](https://github.com/raghavyuva/nixopus/issues/439)) ([180f299](https://github.com/raghavyuva/nixopus/commit/180f299fe935d04242bf39ba2843fb925cc91910))
* **i18n:** add support to malayalam ([#420](https://github.com/raghavyuva/nixopus/issues/420)) ([0a919b2](https://github.com/raghavyuva/nixopus/commit/0a919b2d4e312f838822891ce0ab88f8a1d817e0))



# [0.1.0-alpha.30](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.29...v0.1.0-alpha.30) (2025-09-19)


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nixopus",
"version": "0.1.0-alpha.30",
"version": "0.1.0-alpha.31",
"description": "A modern container management platform",
"private": false,
"cli-version": "0.1.5",
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.1.0-alpha.30
v0.1.0-alpha.31
31 changes: 2 additions & 29 deletions view/app/containers/[id]/components/ContainerDetailsLoading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function ContainerDetailsLoading() {
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="logs">Logs</TabsTrigger>
<TabsTrigger value="details">Details</TabsTrigger>

</TabsList>
<TabsContent value="overview">
<Card>
Expand Down Expand Up @@ -62,34 +62,7 @@ function ContainerDetailsLoading() {
</CardContent>
</Card>
</TabsContent>
<TabsContent value="details">
<Card>
<CardContent className="pt-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-4">
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-6 w-48" />
</div>
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-6 w-48" />
</div>
</div>
<div className="space-y-4">
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-6 w-48" />
</div>
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-6 w-48" />
</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>

</Tabs>
</div>
);
Expand Down
75 changes: 75 additions & 0 deletions view/app/containers/[id]/components/ContainerMetadataCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Info } from 'lucide-react';
import { Container } from '@/redux/services/container/containerApi';
import { useTranslation } from '@/hooks/use-translation';

interface ContainerMetadataCardProps {
container: Container;
}

export function ContainerMetadataCard({ container }: ContainerMetadataCardProps) {
const { t } = useTranslation();

return (
<Card className="w-full rounded-lg shadow-md border border-gray-200">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Info className="h-5 w-5" />
{t('containers.metadata.title')}
</CardTitle>
</CardHeader>

<CardContent className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{/* Container ID */}
<div className="flex justify-between items-center">
<span className="text-sm text-muted-foreground font-medium">
{t('containers.metadata.id')}
</span>
<span className="text-sm font-mono truncate">{container.id}</span>
</div>

{/* Image */}
<div className="flex justify-between items-center">
<span className="text-sm text-muted-foreground font-medium">
{t('containers.metadata.image')}
</span>
<span className="text-sm font-mono truncate">{container.image}</span>
</div>

{/* Mounts */}
{container?.mounts?.length > 0 && (
<div className="col-span-1 sm:col-span-2">
<span className="text-sm text-muted-foreground font-medium">
{t('containers.metadata.mounts')}
</span>
<ul className="list-disc ml-4 mt-1 text-sm font-mono">
{container.mounts.map((mount) => (
<li key={`${mount.source}-${mount.destination}`}>
{mount.source} → {mount.destination}
</li>
))}
</ul>
</div>
)}

{/* Labels */}
{Object.keys(container.labels ?? {}).length > 0 && (
<div className="col-span-1 sm:col-span-2">
<span className="text-sm text-muted-foreground font-medium">
{t('containers.metadata.labels')}
</span>
<div className="flex flex-wrap gap-2 mt-1">
{Object.entries(container.labels).map(([key, value]) => (
<Badge key={key} variant="outline">
{key}: {value}
</Badge>
))}
</div>
</div>
)}
</CardContent>
</Card>
);
}
16 changes: 0 additions & 16 deletions view/app/containers/[id]/components/DetailsTab.tsx

This file was deleted.

6 changes: 6 additions & 0 deletions view/app/containers/[id]/components/OverviewTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { formatDistanceToNow } from 'date-fns';
import { Container } from '@/redux/services/container/containerApi';
import { useTranslation } from '@/hooks/use-translation';
import { ContainerMetadataCard } from './ContainerMetadataCard';

interface OverviewTabProps {
container: Container;
Expand Down Expand Up @@ -97,6 +98,11 @@ export function OverviewTab({ container }: OverviewTabProps) {
</div>
</CardContent>
</Card>
{/* New Metadata Card */}
<div className="col-span-1 md:col-span-2">
<ContainerMetadataCard container={container} />
</div>
Comment on lines +101 to +104
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid duplicating Overview content.

ContainerMetadataCard repeats Status, Created, Command, and Ports that are already rendered by the four existing cards above, so the Overview now shows the same facts twice. Please either retire the overlapping cards or trim the metadata card down to only surface the additional fields (e.g. ID, image, mounts, labels) before shipping.

@@
-          {/* Status */}
-          <div className="flex justify-between items-center">
-            <span className="text-sm text-muted-foreground font-medium">Status</span>
-            <Badge variant={container.status === 'running' ? 'default' : 'secondary'}>
-              {container.status}
-            </Badge>
-          </div>
-
-          {/* Created */}
-          <div className="flex justify-between items-center">
-            <span className="text-sm text-muted-foreground font-medium">Created</span>
-            <span className="text-sm">
-              {formatDistanceToNow(new Date(container.created), { addSuffix: true })}
-            </span>
-          </div>
-
@@
-          {/* Ports */}
-          {container?.ports?.length > 0 && (
-            <div className="col-span-1 sm:col-span-2">
-              <span className="text-sm text-muted-foreground font-medium">Ports</span>
-              <div className="flex flex-wrap gap-2 mt-1">
-                {container.ports.map((port, index) => (
-                  <Badge key={`${port.private_port}-${port.public_port}-${index}`} variant="outline">
-                    {port.public_port} → {port.private_port} ({port.type})
-                  </Badge>
-                ))}
-              </div>
-            </div>
-          )}
+          {/* Labels */}
+          {Object.keys(container.labels ?? {}).length > 0 && (
+            <div className="col-span-1 sm:col-span-2">
+              <span className="text-sm text-muted-foreground font-medium">
+                {t('containers.labels')}
+              </span>
+              <div className="flex flex-wrap gap-2 mt-1">
+                {Object.entries(container.labels).map(([key, value]) => (
+                  <Badge key={key} variant="outline">
+                    {key}: {value}
+                  </Badge>
+                ))}
+              </div>
+            </div>
+          )}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In view/app/containers/[id]/components/OverviewTab.tsx around lines 101 to 104,
the new ContainerMetadataCard duplicates information shown by the four existing
cards (Status, Created, Command, Ports); remove that duplication by trimming
ContainerMetadataCard to only render additional metadata (e.g., ID, Image,
Mounts, Labels) or by removing the card entirely and moving those extra fields
into a single non-duplicative component; update ContainerMetadataCard to drop
Status/Created/Command/Ports rendering (or add props to conditionally hide them)
and ensure the Overview layout still displays the four existing cards unchanged.


</div>
);
}
12 changes: 3 additions & 9 deletions view/app/containers/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { useRouter, useParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { OverviewTab } from './components/OverviewTab';
import { LogsTab } from './components/LogsTab';
import { DetailsTab } from './components/DetailsTab';
import { Terminal as TerminalComponent } from './components/Terminal';
import ContainerDetailsLoading from './components/ContainerDetailsLoading';
import { DeleteDialog } from '@/components/ui/delete-dialog';
Expand Down Expand Up @@ -170,7 +169,7 @@ export default function ContainerDetailsPage() {

<div className="space-y-4">
<Tabs defaultValue="overview" className="w-full">
<TabsList className="grid w-full grid-cols-5">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="overview">
<Info className="mr-2 h-4 w-4" />
{t('containers.overview')}
Expand All @@ -187,20 +186,15 @@ export default function ContainerDetailsPage() {
<Terminal className="mr-2 h-4 w-4" />
{t('containers.logs')}
</TabsTrigger>
<TabsTrigger value="details">
<HardDrive className="mr-2 h-4 w-4" />
{t('containers.details')}
</TabsTrigger>

</TabsList>
<TabsContent value="overview" className="mt-4">
<OverviewTab container={container} />
</TabsContent>
<TabsContent value="logs" className="mt-4">
<LogsTab container={container} logs={allLogs} onLoadMore={handleLoadMoreLogs} />
</TabsContent>
<TabsContent value="details" className="mt-4">
<DetailsTab container={container} />
</TabsContent>

<TabsContent value="terminal" className="mt-4">
{container.status === 'running' ? (
<TerminalComponent containerId={containerId} />
Expand Down
9 changes: 8 additions & 1 deletion view/lib/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,14 @@
"size": "Size",
"containers": "Containers",
"none": "<none>"
}
},
"metadata": {
"title": "Container Metadata",
"id": "Container ID",
"image": "Image",
"mounts": "Mounts",
"labels": "Labels"
}
},
"auth": {
"login": {
Expand Down
11 changes: 11 additions & 0 deletions view/redux/services/container/containerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ import { createApi } from '@reduxjs/toolkit/query/react';
import { baseQueryWithReauth } from '@/redux/base-query';
import { CONTAINERURLS } from '@/redux/api-conf';



export interface ContainerMount {
source: string;
destination: string;
mode?: string;
rw?: boolean;
propagation?: string;
}
export interface Container {
labels: Record<string, string>;
mounts: ContainerMount[];
id: string;
name: string;
image: string;
Expand Down