improved a lot of things
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
interface ProjectCardProps {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
framework?: string | null;
|
||||
appUrl?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
|
||||
const FRAMEWORK_ICON: Record<string, string> = {
|
||||
next: '▲',
|
||||
react: '⚛',
|
||||
vue: 'V',
|
||||
svelte: 'S',
|
||||
angular: 'A',
|
||||
nuxt: 'N',
|
||||
};
|
||||
|
||||
export function ProjectCard({ workspaceId, projectId, name, framework, appUrl, description }: ProjectCardProps) {
|
||||
const icon = framework ? (FRAMEWORK_ICON[framework.toLowerCase()] ?? '◻') : '◻';
|
||||
|
||||
return (
|
||||
<Link href={`/workspace/${workspaceId}/project/${projectId}`} style={{ textDecoration: 'none' }}>
|
||||
<div
|
||||
style={{
|
||||
background: '#FFFFFF',
|
||||
border: '1px solid rgba(0,0,0,0.07)',
|
||||
borderRadius: 14, padding: '20px 22px', cursor: 'pointer',
|
||||
transition: 'box-shadow 0.15s, border-color 0.15s',
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.08)';
|
||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.12)';
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.07)';
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
|
||||
<div style={{
|
||||
width: 38, height: 38, borderRadius: 9,
|
||||
background: '#0A0A0A',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontSize: '0.875rem', color: '#FFFFFF', fontWeight: 700, flexShrink: 0,
|
||||
}}>
|
||||
{icon}
|
||||
</div>
|
||||
<span style={{ fontSize: '0.9375rem', fontWeight: 600, color: '#0A0A0A', letterSpacing: '-0.01em' }}>
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{appUrl && (
|
||||
<p style={{
|
||||
margin: '0 0 6px',
|
||||
fontSize: '0.75rem', fontFamily: 'monospace',
|
||||
color: '#71717A', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
||||
}}>
|
||||
{appUrl}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{description && (
|
||||
<p style={{ margin: '0 0 12px', fontSize: '0.8125rem', color: '#71717A', lineHeight: 1.5 }}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: '0.8125rem', color: '#0066FF', fontWeight: 500 }}>
|
||||
Open canvas →
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
interface WorkspaceCardProps {
|
||||
id: string;
|
||||
name: string;
|
||||
plan: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
const PLAN_BADGE: Record<string, { label: string; bg: string; color: string }> = {
|
||||
FREE: { label: 'Free', bg: 'rgba(0,0,0,0.05)', color: '#52525B' },
|
||||
TEAM: { label: 'Team', bg: 'rgba(0,102,255,0.08)', color: '#0066FF' },
|
||||
ENTERPRISE: { label: 'Enterprise', bg: 'rgba(124,58,237,0.08)', color: '#7C3AED' },
|
||||
};
|
||||
|
||||
export function WorkspaceCard({ id, name, plan, createdAt }: WorkspaceCardProps) {
|
||||
const badge = PLAN_BADGE[plan] ?? PLAN_BADGE['FREE']!;
|
||||
|
||||
return (
|
||||
<Link href={`/workspace/${id}`} style={{ textDecoration: 'none' }}>
|
||||
<div
|
||||
style={{
|
||||
background: '#FFFFFF',
|
||||
border: '1px solid rgba(0,0,0,0.07)',
|
||||
borderRadius: 14,
|
||||
padding: '20px 22px',
|
||||
cursor: 'pointer',
|
||||
transition: 'box-shadow 0.15s, border-color 0.15s',
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.08)';
|
||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.12)';
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(0,0,0,0.07)';
|
||||
}}
|
||||
>
|
||||
{/* Workspace icon */}
|
||||
<div style={{
|
||||
width: 40, height: 40, borderRadius: 10,
|
||||
background: 'rgba(0,102,255,0.07)', border: '1px solid rgba(0,102,255,0.15)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
marginBottom: 14,
|
||||
}}>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||
<rect x="1.5" y="1.5" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.7"/>
|
||||
<rect x="10" y="1.5" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.4"/>
|
||||
<rect x="1.5" y="10" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.4"/>
|
||||
<rect x="10" y="10" width="6.5" height="6.5" rx="1.5" fill="#0066FF" opacity="0.2"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}>
|
||||
<span style={{ fontSize: '0.9375rem', fontWeight: 600, color: '#0A0A0A', letterSpacing: '-0.01em', lineHeight: 1.3 }}>
|
||||
{name}
|
||||
</span>
|
||||
<span style={{
|
||||
fontSize: '0.6875rem', fontWeight: 600, letterSpacing: '0.04em',
|
||||
textTransform: 'uppercase', padding: '2px 8px', borderRadius: 99,
|
||||
background: badge.bg, color: badge.color, flexShrink: 0,
|
||||
}}>
|
||||
{badge.label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p style={{ margin: '6px 0 0', fontSize: '0.8125rem', color: '#A1A1AA' }}>
|
||||
Created {new Date(createdAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||
</p>
|
||||
|
||||
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: '0.8125rem', color: '#0066FF', fontWeight: 500 }}>
|
||||
Open workspace →
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user