All Prompts
All Prompts

AssetDetailPage
AssetDetailPage: детальнее о каждом активе. Отображает полную информацию и характеристики объекта для удобного анализа и принятия решений.
by BourneLive Preview
Prompt
# AssetDetailPage
You are given a task to integrate an existing React component in the codebase
~~~/README.md
# Asset Detail Page
A comprehensive, production-grade asset detail view component designed for enterprise resource planning (ERP) or asset management systems.
## Features
- **Rich Header**: Breadcrumbs, status indicators, and primary action buttons.
- **Responsive Grid Layout**: Adaptive layout that works well on desktop and tablet.
- **Detailed Visuals**: Prominent asset image with overlay actions.
- **Categorized Information**:
- **Summary**: Key stats and location info.
- **Specs**: Technical specifications in a clean grid.
- **Ownership**: Clear owner attribution and contact info.
- **Location**: Detailed physical location tracking.
- **Timeline/History**: Vertical activity log showing lifecycle events.
- **Key Dates**: High-contrast card for critical dates (warranty, maintenance).
- **Documents**: File attachment list.
## Usage
```tsx
import { AssetDetailPage, Asset } from '@/sd-components/debb9ff1-feda-40e6-a8ec-cc9b7dddb5a0';
const myAsset: Asset = {
id: '123',
name: 'Generator X',
status: 'active',
// ... other fields
};
function MyPage() {
return (
<AssetDetailPage
asset={myAsset}
onBack={() => history.back()}
onEdit={(asset) => console.log('Edit', asset)}
/>
);
}
```
## Props
| Prop | Type | Description |
|------|------|-------------|
| `asset` | `Asset` | The full asset data object to display |
| `onBack` | `() => void` | Callback for the back button |
| `onEdit` | `(asset: Asset) => void` | Callback for the edit action |
| `onDelete` | `(asset: Asset) => void` | Callback for the delete action |
| `onExport` | `(asset: Asset) => void` | Callback for the export action |
## Types
### Asset
```typescript
interface Asset {
id: string;
name: string;
type: string;
serialNumber: string;
status: 'active' | 'maintenance' | 'retired' | 'draft' | 'warning';
imageUrl?: string;
tags: string[];
location: {
site: string;
building: string;
room: string;
coordinates?: string;
};
owner: {
name: string;
department: string;
email: string;
avatar?: string;
};
specs: Record<string, string>;
dates: {
created: string;
updated: string;
lastMaintenance: string;
nextMaintenance: string;
warrantyExpiration: string;
};
history: AssetHistoryEvent[];
}
```
~~~
~~~/src/App.tsx
import React, { useState } from 'react';
import AssetDetailPage, { Asset } from './Component';
/**
* Demo application for Asset Detail Page
* Shows the component with a rich mock data object
*/
export default function App() {
// Mock data representing a complex industrial asset
const mockAsset: Asset = {
id: 'AST-2024-8842',
name: 'Hydraulic Press System X500',
type: 'Heavy Machinery',
serialNumber: 'HPX-500-V2-9982',
status: 'active',
imageUrl: 'https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?auto=format&fit=crop&q=80&w=2670&ixlib=rb-4.0.3',
tags: ['High Priority', 'Production Line A', 'Requires Certification', 'Heavy Load'],
location: {
site: 'Manufacturing Plant Beta',
building: 'Sector 7',
room: 'Assembly Hall Main',
coordinates: '34.0522° N, 118.2437° W'
},
owner: {
name: 'Robert Chen',
department: 'Operations & Safety',
email: 'r.chen@enterprise.com',
avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
},
specs: {
'Manufacturer': 'Industrial Dynamics Corp',
'Model Year': '2022',
'Power Rating': '480V 3-Phase',
'Max Pressure': '5000 PSI',
'Oil Capacity': '200 Gallons',
'Weight': '4,500 lbs',
'Dimensions': '12ft x 8ft x 10ft',
'Operating Temp': '-10°C to 45°C'
},
dates: {
created: 'Jan 15, 2022',
updated: 'Oct 24, 2023',
lastMaintenance: 'Oct 01, 2023',
nextMaintenance: 'Jan 01, 2024',
warrantyExpiration: 'Jan 15, 2027'
},
history: [
{
id: 'evt-1',
type: 'alert',
title: 'Pressure Warning Sensor B',
description: 'Automated alert: Hydraulic pressure fluctuation detected above threshold.',
user: 'System Monitor',
date: 'Oct 23, 2023 • 14:30'
},
{
id: 'evt-2',
type: 'maintenance',
title: 'Quarterly Servicing Completed',
description: 'Fluids replaced, seals checked, calibration verified. All nominal.',
user: 'Sarah Miller (Tech)',
date: 'Oct 01, 2023 • 09:15'
},
{
id: 'evt-3',
type: 'assignment',
title: 'Transfer to Sector 7',
description: 'Physical relocation and re-commissioning in new assembly wing.',
user: 'Robert Chen',
date: 'Sep 15, 2023 • 11:00'
},
{
id: 'evt-4',
type: 'update',
title: 'Firmware Update v2.1',
description: 'Control panel software updated to latest security patch.',
user: 'IT Admin',
date: 'Aug 20, 2023 • 16:45'
}
]
};
const handleEdit = (asset: Asset) => {
console.log('Edit clicked for', asset.id);
alert(`Edit mode for ${asset.name}`);
};
const handleBack = () => {
console.log('Back clicked');
};
return (
<div className="bg-slate-100 min-h-screen">
<AssetDetailPage
asset={mockAsset}
onBack={handleBack}
onEdit={handleEdit}
onExport={(a) => console.log('Exporting', a.name)}
/>
</div>
);
}
~~~
~~~/package.json
{
"name": "asset-detail-page",
"version": "1.0.0",
"description": "A production-grade asset detail page component",
"main": "src/Component.tsx",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"lucide-react": "^0.344.0",
"date-fns": "^3.3.1",
"clsx": "^2.1.0",
"tailwind-merge": "^2.2.1"
}
}
~~~
~~~/src/Component.tsx
import React from 'react';
import {
ArrowLeft,
Printer,
Share2,
Pencil,
Trash2,
Box,
MapPin,
User,
Calendar,
Tag,
Activity,
FileText,
Clock,
ShieldCheck,
AlertTriangle,
History,
MoreHorizontal,
ChevronRight,
Download
} from 'lucide-react';
export type AssetStatus = 'active' | 'maintenance' | 'retired' | 'draft' | 'warning';
export interface AssetHistoryEvent {
id: string;
type: 'update' | 'maintenance' | 'assignment' | 'alert';
title: string;
description: string;
user: string;
date: string;
}
export interface Asset {
id: string;
name: string;
type: string;
serialNumber: string;
status: AssetStatus;
imageUrl?: string;
tags: string[];
location: {
site: string;
building: string;
room: string;
coordinates?: string;
};
owner: {
name: string;
department: string;
email: string;
avatar?: string;
};
specs: Record<string, string>;
dates: {
created: string;
updated: string;
lastMaintenance: string;
nextMaintenance: string;
warrantyExpiration: string;
};
history: AssetHistoryEvent[];
}
interface AssetDetailPageProps {
asset: Asset;
onBack?: () => void;
onEdit?: (asset: Asset) => void;
onDelete?: (asset: Asset) => void;
onExport?: (asset: Asset) => void;
}
/**
* Helper to render status badge with appropriate styling
*/
const StatusBadge = ({ status }: { status: AssetStatus }) => {
const styles = {
active: 'bg-emerald-50 text-emerald-700 border-emerald-200 ring-emerald-500/20',
maintenance: 'bg-amber-50 text-amber-700 border-amber-200 ring-amber-500/20',
retired: 'bg-slate-50 text-slate-700 border-slate-200 ring-slate-500/20',
draft: 'bg-blue-50 text-blue-700 border-blue-200 ring-blue-500/20',
warning: 'bg-red-50 text-red-700 border-red-200 ring-red-500/20',
};
const labels = {
active: 'Operational',
maintenance: 'In Maintenance',
retired: 'Decommissioned',
draft: 'Draft',
warning: 'Attention Required',
};
const icons = {
active: ShieldCheck,
maintenance: Clock,
retired: Box,
draft: FileText,
warning: AlertTriangle,
};
const Icon = icons[status];
return (
<span className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium border ring-1 ring-inset ${styles[status]}`}>
<Icon className="w-3.5 h-3.5" />
{labels[status]}
</span>
);
};
/**
* Asset Detail Page Component
* Displays comprehensive information about a single asset.
*/
export function AssetDetailPage({ asset, onBack, onEdit, onDelete, onExport }: AssetDetailPageProps) {
return (
<div
className="min-h-screen bg-background text-foreground font-sans pb-12"
style={{
"--primary": "209 100% 54.7%",
"--primary-foreground": "0 0% 100%",
} as React.CSSProperties}
>
{/* Top Navigation / Breadcrumbs */}
<div className="sticky top-0 z-30 bg-background/80 backdrop-blur-md border-b border-border px-6 py-4">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-4">
<button
onClick={onBack}
className="p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted/50 rounded-lg transition-colors"
>
<ArrowLeft className="w-5 h-5" />
</button>
<nav className="hidden sm:flex items-center text-sm text-muted-foreground">
<span className="hover:text-foreground cursor-pointer transition-colors">Assets</span>
<ChevronRight className="w-4 h-4 mx-2 text-muted-foreground/50" />
<span className="hover:text-foreground cursor-pointer transition-colors">{asset.type}s</span>
<ChevronRight className="w-4 h-4 mx-2 text-muted-foreground/50" />
<span className="text-foreground font-medium truncate max-w-[200px]">{asset.name}</span>
</nav>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => onExport?.(asset)}
className="hidden sm:flex items-center gap-2 px-3 py-2 text-sm font-medium text-muted-foreground bg-card border border-border rounded-lg hover:bg-muted/50 hover:text-foreground transition-all shadow-sm"
>
<Download className="w-4 h-4" />
Export
</button>
<div className="h-6 w-px bg-border mx-1 hidden sm:block" />
<button
onClick={() => onEdit?.(asset)}
className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-primary-foreground bg-primary border border-transparent rounded-lg hover:bg-primary/90 transition-all shadow-sm"
>
<Pencil className="w-4 h-4" />
<span className="hidden sm:inline">Edit Asset</span>
</button>
<button className="sm:hidden p-2 text-muted-foreground hover:bg-muted/50 rounded-lg">
<MoreHorizontal className="w-5 h-5" />
</button>
</div>
</div>
</div>
<main className="max-w-7xl mx-auto px-4 sm:px-6 py-8">
{/* Header Section */}
<div className="mb-8">
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-6">
<div className="flex-1">
<div className="flex flex-wrap items-center gap-3 mb-3">
<StatusBadge status={asset.status} />
<span className="text-xs font-mono text-muted-foreground px-2 py-0.5 bg-muted rounded border border-border">
ID: {asset.id}
</span>
<span className="text-xs font-mono text-muted-foreground px-2 py-0.5 bg-muted rounded border border-border">
SN: {asset.serialNumber}
</span>
</div>
<h1 className="text-3xl font-bold text-foreground tracking-tight mb-2">{asset.name}</h1>
<div className="flex flex-wrap items-center gap-6 text-sm text-muted-foreground">
<div className="flex items-center gap-2">
<Box className="w-4 h-4 text-muted-foreground/70" />
<span>{asset.type}</span>
</div>
<div className="flex items-center gap-2">
<MapPin className="w-4 h-4 text-muted-foreground/70" />
<span>{asset.location.site} • {asset.location.room}</span>
</div>
<div className="flex items-center gap-2">
<Clock className="w-4 h-4 text-muted-foreground/70" />
<span>Updated {asset.dates.updated}</span>
</div>
</div>
</div>
{/* Quick Actions / Stats Card - Mobile only shows actions above, desktop shows high-level stats here */}
<div className="hidden md:flex gap-4">
<div className="px-5 py-3 bg-card rounded-xl border border-border shadow-sm">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Total Uptime</div>
<div className="text-xl font-bold text-emerald-600">99.8%</div>
</div>
<div className="px-5 py-3 bg-card rounded-xl border border-border shadow-sm">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Next Service</div>
<div className="text-xl font-bold text-foreground">{asset.dates.nextMaintenance}</div>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Main Content Column */}
<div className="lg:col-span-2 space-y-8">
{/* Image & Key Info */}
<div className="bg-card rounded-2xl border border-border overflow-hidden shadow-sm">
<div className="aspect-video w-full bg-muted relative group overflow-hidden">
{asset.imageUrl ? (
<img
src={asset.imageUrl}
alt={asset.name}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-muted-foreground/30">
<Box className="w-20 h-20" />
</div>
)}
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity flex items-end p-6">
<button className="text-white text-sm font-medium flex items-center gap-2 hover:underline">
<Share2 className="w-4 h-4" /> Share Asset Preview
</button>
</div>
</div>
<div className="p-6">
<h3 className="text-lg font-semibold text-foreground mb-4 flex items-center gap-2">
<FileText className="w-5 h-5 text-primary" />
Description & Notes
</h3>
<p className="text-muted-foreground leading-relaxed mb-6">
High-performance {asset.name.toLowerCase()} utilized primarily for {asset.type.toLowerCase()} operations.
Currently assigned to the {asset.owner.department} department.
This asset requires quarterly maintenance checks and is covered under the enterprise warranty program until {asset.dates.warrantyExpiration}.
</p>
<div className="flex flex-wrap gap-2">
{asset.tags.map(tag => (
<span key={tag} className="inline-flex items-center gap-1 px-2.5 py-1 rounded-md bg-muted text-muted-foreground text-xs font-medium border border-border">
<Tag className="w-3 h-3 text-muted-foreground/70" />
{tag}
</span>
))}
<button className="px-2.5 py-1 rounded-md border border-dashed border-border text-muted-foreground text-xs hover:bg-muted/50 transition-colors">
+ Add Tag
</button>
</div>
</div>
</div>
{/* Technical Specifications Grid */}
<div className="bg-card rounded-2xl border border-border shadow-sm overflow-hidden">
<div className="border-b border-border px-6 py-4 bg-muted/30 flex justify-between items-center">
<h3 className="text-lg font-semibold text-foreground">Technical Specifications</h3>
<button className="text-sm text-primary font-medium hover:underline">View Manual</button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 divide-y sm:divide-y-0 sm:divide-x divide-border">
{Object.entries(asset.specs).map(([key, value], idx) => (
<div key={key} className={`p-5 hover:bg-muted/30 transition-colors`}>
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">{key}</dt>
<dd className="text-sm font-medium text-foreground">{value}</dd>
</div>
))}
</div>
</div>
{/* Location & Assignment */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-card rounded-2xl border border-border shadow-sm p-6">
<h3 className="text-base font-semibold text-foreground mb-4 flex items-center gap-2">
<User className="w-4 h-4 text-purple-500" />
Responsibility
</h3>
<div className="flex items-start gap-4">
{asset.owner.avatar ? (
<img src={asset.owner.avatar} alt={asset.owner.name} className="w-12 h-12 rounded-full border-2 border-card shadow-sm" />
) : (
<div className="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center text-purple-600 font-bold text-lg">
{asset.owner.name.charAt(0)}
</div>
)}
<div>
<div className="font-medium text-foreground">{asset.owner.name}</div>
<div className="text-sm text-muted-foreground mb-2">{asset.owner.department}</div>
<a href={`mailto:${asset.owner.email}`} className="text-sm text-primary hover:underline">
{asset.owner.email}
</a>
</div>
</div>
</div>
<div className="bg-card rounded-2xl border border-border shadow-sm p-6">
<h3 className="text-base font-semibold text-foreground mb-4 flex items-center gap-2">
<MapPin className="w-4 h-4 text-red-500" />
Storage Location
</h3>
<div className="space-y-3">
<div className="flex justify-between items-center text-sm">
<span className="text-muted-foreground">Site</span>
<span className="font-medium text-foreground">{asset.location.site}</span>
</div>
<div className="flex justify-between items-center text-sm">
<span className="text-muted-foreground">Building</span>
<span className="font-medium text-foreground">{asset.location.building}</span>
</div>
<div className="flex justify-between items-center text-sm">
<span className="text-muted-foreground">Room/Area</span>
<span className="font-medium text-foreground">{asset.location.room}</span>
</div>
{asset.location.coordinates && (
<div className="pt-2 border-t border-border mt-2">
<div className="text-xs font-mono text-muted-foreground truncate bg-muted p-1.5 rounded text-center">
{asset.location.coordinates}
</div>
</div>
)}
</div>
</div>
</div>
</div>
{/* Sidebar Column */}
<div className="space-y-6">
{/* Timeline / Activity */}
<div className="bg-card rounded-2xl border border-border shadow-sm flex flex-col h-full max-h-[600px]">
<div className="p-5 border-b border-border flex justify-between items-center">
<h3 className="font-semibold text-foreground flex items-center gap-2">
<Activity className="w-4 h-4 text-muted-foreground" />
Activity Log
</h3>
<button className="text-xs font-medium text-muted-foreground hover:text-foreground">View All</button>
</div>
<div className="p-5 overflow-y-auto custom-scrollbar flex-1">
<div className="space-y-6 relative before:absolute before:inset-y-0 before:left-[17px] before:w-px before:bg-border">
{asset.history.map((event) => (
<div key={event.id} className="relative pl-10">
<div className={`absolute left-0 top-1 w-9 h-9 rounded-full border-4 border-card shadow-sm flex items-center justify-center text-white
${event.type === 'maintenance' ? 'bg-amber-500' :
event.type === 'alert' ? 'bg-red-500' :
event.type === 'assignment' ? 'bg-purple-500' : 'bg-primary'
}`}
>
{event.type === 'maintenance' ? <Clock className="w-4 h-4" /> :
event.type === 'alert' ? <AlertTriangle className="w-4 h-4" /> :
event.type === 'assignment' ? <User className="w-4 h-4" /> : <FileText className="w-4 h-4" />
}
</div>
<div className="flex flex-col">
<span className="text-xs text-muted-foreground font-medium mb-0.5">{event.date}</span>
<span className="text-sm font-medium text-foreground">{event.title}</span>
<span className="text-xs text-muted-foreground mt-0.5">{event.description}</span>
<div className="mt-1.5 flex items-center gap-1.5">
<div className="w-4 h-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold text-muted-foreground">
{event.user.charAt(0)}
</div>
<span className="text-xs text-muted-foreground">{event.user}</span>
</div>
</div>
</div>
))}
</div>
</div>
<div className="p-4 border-t border-border bg-muted/30 rounded-b-2xl">
<button className="w-full py-2 text-sm font-medium text-muted-foreground bg-card border border-border rounded-lg hover:bg-muted/50 hover:text-foreground transition-colors shadow-sm">
Add Note or Log Event
</button>
</div>
</div>
{/* Quick Stats/Metadata */}
<div className="bg-primary rounded-2xl shadow-lg p-6 text-primary-foreground overflow-hidden relative">
<div className="absolute top-0 right-0 -mr-4 -mt-4 w-24 h-24 bg-white/10 rounded-full blur-2xl"></div>
<h3 className="font-semibold text-primary-foreground/90 mb-4 flex items-center gap-2">
<Calendar className="w-4 h-4 text-primary-foreground/70" />
Key Dates
</h3>
<div className="space-y-4 relative z-10">
<div>
<div className="text-xs text-primary-foreground/60 uppercase tracking-wider mb-1">Commissioned</div>
<div className="font-medium">{asset.dates.created}</div>
</div>
<div>
<div className="text-xs text-primary-foreground/60 uppercase tracking-wider mb-1">Warranty Expires</div>
<div className="font-medium text-amber-300">{asset.dates.warrantyExpiration}</div>
</div>
<div>
<div className="text-xs text-primary-foreground/60 uppercase tracking-wider mb-1">Last Audit</div>
<div className="font-medium">{asset.dates.updated}</div>
</div>
</div>
</div>
{/* Associated Documents */}
<div className="bg-card rounded-2xl border border-border shadow-sm p-6">
<h3 className="font-semibold text-foreground mb-4 flex items-center gap-2">
<FileText className="w-4 h-4 text-muted-foreground" />
Documents
</h3>
<ul className="space-y-3">
{[
{ name: 'User Manual v2.4.pdf', size: '2.4 MB' },
{ name: 'Warranty_Certificate.pdf', size: '856 KB' },
{ name: 'Maintenance_Log_2023.xlsx', size: '1.2 MB' }
].map((doc, i) => (
<li key={i} className="flex items-center justify-between group cursor-pointer">
<div className="flex items-center gap-3">
<div className="p-2 bg-primary/10 text-primary rounded-lg group-hover:bg-primary/20 transition-colors">
<FileText className="w-4 h-4" />
</div>
<div>
<div className="text-sm font-medium text-foreground group-hover:text-primary transition-colors">{doc.name}</div>
<div className="text-xs text-muted-foreground">{doc.size}</div>
</div>
</div>
<Download className="w-4 h-4 text-muted-foreground/50 group-hover:text-muted-foreground" />
</li>
))}
</ul>
</div>
</div>
</div>
</main>
</div>
);
}
export default AssetDetailPage;
~~~
Implementation Guidelines
1. Analyze the component structure, styling, animation implementations
2. Review the component's arguments and state
3. Think through what is the best place to adopt this component/style into the design we are doing
4. Then adopt the component/design to our current system
Help me integrate this into my design