-
-
Notifications
You must be signed in to change notification settings - Fork 84
Update container metadata card UI and overview tab #466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,85 @@ | ||||||||||||||
| import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; | ||||||||||||||
| import { Badge } from '@/components/ui/badge'; | ||||||||||||||
| import { formatDistanceToNow } from 'date-fns'; | ||||||||||||||
| import { Container } from '@/redux/services/container/containerApi'; | ||||||||||||||
| import { Info } from 'lucide-react'; | ||||||||||||||
| import React from 'react'; | ||||||||||||||
|
|
||||||||||||||
| interface ContainerMetadataCardProps { | ||||||||||||||
| container: Container; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| export function ContainerMetadataCard({ container }: ContainerMetadataCardProps) { | ||||||||||||||
| 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" /> Label | ||||||||||||||
| </CardTitle> | ||||||||||||||
| </CardHeader> | ||||||||||||||
| <CardContent className="p-4"> | ||||||||||||||
| <div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> | ||||||||||||||
| {/* 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> | ||||||||||||||
|
|
||||||||||||||
| {/* Container ID */} | ||||||||||||||
| <div className="flex justify-between items-center"> | ||||||||||||||
| <span className="text-sm text-muted-foreground font-medium">Container 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">Image</span> | ||||||||||||||
| <span className="text-sm font-mono truncate">{container.image}</span> | ||||||||||||||
| </div> | ||||||||||||||
|
|
||||||||||||||
| {/* Command */} | ||||||||||||||
| <div className="flex justify-between items-center col-span-1 sm:col-span-2"> | ||||||||||||||
| <span className="text-sm text-muted-foreground font-medium">Command</span> | ||||||||||||||
| <span className="text-sm font-mono truncate">{container.command}</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> | ||||||||||||||
| )} | ||||||||||||||
|
|
||||||||||||||
| {/* Mounts */} | ||||||||||||||
| {container?.mounts?.length > 0 && ( | ||||||||||||||
| <div className="col-span-1 sm:col-span-2"> | ||||||||||||||
| <span className="text-sm text-muted-foreground font-medium">Mounts</span> | ||||||||||||||
| <ul className="list-disc ml-4 mt-1 text-sm font-mono"> | ||||||||||||||
| {container.mounts.map((mount: { source: string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined; destination: string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined; }, idx: React.Key | null | undefined) => ( | ||||||||||||||
| <li key={idx}>{mount.source} → {mount.destination}</li> | ||||||||||||||
| ))} | ||||||||||||||
|
Comment on lines
+75
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace verbose inline type with proper interface. The inline type annotation for mounts is extremely verbose and unreadable. This should use the properly typed After fixing the types in - {container.mounts.map((mount: { source: string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined; destination: string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined; }, idx: React.Key | null | undefined) => (
+ {container.mounts.map((mount, idx) => (
<li key={idx}>{mount.source} → {mount.destination}</li>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| </ul> | ||||||||||||||
| </div> | ||||||||||||||
| )} | ||||||||||||||
| </div> | ||||||||||||||
| </CardContent> | ||||||||||||||
| </Card> | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -25,7 +25,7 @@ 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 { DetailsTab } from './components/DetailsTab'; | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove commented import. Commented-out code should be deleted rather than kept in the codebase. The Details tab has been removed per PR objectives, so this import is no longer needed. Apply this diff: import { LogsTab } from './components/LogsTab';
-// import { DetailsTab } from './components/DetailsTab';
import { Terminal as TerminalComponent } from './components/Terminal';📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||
| import { Terminal as TerminalComponent } from './components/Terminal'; | ||||||||
| import ContainerDetailsLoading from './components/ContainerDetailsLoading'; | ||||||||
| import { DeleteDialog } from '@/components/ui/delete-dialog'; | ||||||||
|
|
@@ -170,7 +170,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')} | ||||||||
|
|
@@ -187,20 +187,20 @@ export default function ContainerDetailsPage() { | |||||||
| <Terminal className="mr-2 h-4 w-4" /> | ||||||||
| {t('containers.logs')} | ||||||||
| </TabsTrigger> | ||||||||
| <TabsTrigger value="details"> | ||||||||
| {/* <TabsTrigger value="details"> | ||||||||
| <HardDrive className="mr-2 h-4 w-4" /> | ||||||||
| {t('containers.details')} | ||||||||
| </TabsTrigger> | ||||||||
| </TabsTrigger> */} | ||||||||
|
Comment on lines
+190
to
+193
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove commented tab trigger and content. Dead code should be removed, not commented out. Version control preserves the history if needed. Apply this diff: <TabsTrigger value="logs">
<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>
- {/* <TabsContent value="details" className="mt-4"> */}
- {/* <DetailsTab container={container} /> */}
- {/* </TabsContent> */}
<TabsContent value="terminal" className="mt-4">Also applies to: 201-203 🤖 Prompt for AI Agents |
||||||||
| </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="details" className="mt-4"> */} | ||||||||
| {/* <DetailsTab container={container} /> */} | ||||||||
| {/* </TabsContent> */} | ||||||||
| <TabsContent value="terminal" className="mt-4"> | ||||||||
| {container.status === 'running' ? ( | ||||||||
| <TerminalComponent containerId={containerId} /> | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,8 @@ import { baseQueryWithReauth } from '@/redux/base-query'; | |||||||||||||||||||||||||||||||
| import { CONTAINERURLS } from '@/redux/api-conf'; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export interface Container { | ||||||||||||||||||||||||||||||||
| labels: any; | ||||||||||||||||||||||||||||||||
| mounts: any; | ||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+7
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace Using Apply this diff to add proper type definitions: +export interface ContainerMount {
+ source: string;
+ destination: string;
+ mode?: string;
+ rw?: boolean;
+ propagation?: string;
+}
+
export interface Container {
- labels: any;
- mounts: any;
+ labels: Record<string, string>;
+ mounts: ContainerMount[];
id: string;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| id: string; | ||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||
| image: string; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misleading card title.
The card title "Label" doesn't accurately describe the content, which shows comprehensive container metadata including status, creation time, IDs, ports, and mounts. Consider using "Metadata" or "Container Details" instead.
Apply this diff:
<CardHeader> <CardTitle className="flex items-center gap-2 text-lg"> - <Info className="h-5 w-5" /> Label + <Info className="h-5 w-5" /> Container Metadata </CardTitle> </CardHeader>📝 Committable suggestion
🤖 Prompt for AI Agents