Skip to content

Commit acfa24a

Browse files
authored
Merge pull request #195 from spicecodecli/spicecloud-better-ui
Spicecloud better UI
2 parents a84717b + 3bc82a7 commit acfa24a

24 files changed

+866
-241
lines changed

spicecloud/pages/_app.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import '../styles/globals.css'
22
import type { AppProps } from 'next/app'
3+
import Head from 'next/head'
4+
import React from 'react'
35

46
export default function App({ Component, pageProps }: AppProps) {
5-
return <Component {...pageProps} />
7+
return (
8+
<>
9+
<Head>
10+
<title>SpiceCloud | Powered by SpiceCodeCLI</title>
11+
</Head>
12+
13+
<Component {...pageProps} />
14+
</>
15+
)
16+
617
}

spicecloud/pages/_document.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { Html, Head, Main, NextScript } from "next/document";
33
export default function Document() {
44
return (
55
<Html lang="en">
6-
<Head />
6+
<Head>
7+
<link rel="icon" href="/spicecode-logo.png" />
8+
</Head>
79
<body className="antialiased">
810
<Main />
911
<NextScript />

spicecloud/pages/api/files.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// pages/api/files.ts
2+
import { promises as fs } from 'fs';
3+
import path from 'path';
4+
5+
export default async function handler(req: any, res: { status: (arg0: number) => { (): any; new(): any; json: { (arg0: { error: string; }): void; new(): any; }; }; }) {
6+
try {
7+
const filePath = path.join(process.cwd(), 'data', 'metrics.json');
8+
const fileContent = await fs.readFile(filePath, 'utf-8');
9+
const jsonData = JSON.parse(fileContent);
10+
res.status(200).json(jsonData);
11+
} catch (err) {
12+
console.error('Erro ao ler metrics.json', err);
13+
res.status(500).json({ error: 'Failed to load metrics.json' });
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import { styles } from '../utils/styles';
3+
4+
export const EmptyState: React.FC = () => {
5+
return (
6+
<div style={styles.emptyState}>
7+
<div style={styles.emptyStateContent}>
8+
<div style={styles.emptyStateIcon}>👆</div>
9+
<p style={styles.emptyStateText}>Select a file from the sidebar to view its metrics</p>
10+
</div>
11+
</div>
12+
);
13+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { MetricData } from '../utils/types';
3+
import { getFileIconSrc } from '../utils/utils';
4+
import { styles } from '../utils/styles';
5+
6+
interface FileHeaderProps {
7+
selectedFile: MetricData;
8+
}
9+
10+
export const FileHeader: React.FC<FileHeaderProps> = ({ selectedFile }) => {
11+
return (
12+
<div style={styles.fileHeader}>
13+
<div style={styles.fileHeaderContent}>
14+
<div style={styles.fileHeaderIcon}>
15+
<img
16+
src={getFileIconSrc(selectedFile.metrics.file_extension)}
17+
alt={selectedFile.metrics.file_extension}
18+
style={styles.fileHeaderImg}
19+
/>
20+
</div>
21+
22+
<div style={{ flex: 1 }}>
23+
<h2 style={styles.fileHeaderTitle}>{selectedFile.file_name}</h2>
24+
<p style={styles.fileHeaderPath} title={selectedFile.file_path}>
25+
📁 {selectedFile.file_path}
26+
</p>
27+
<div style={styles.fileHeaderMeta}>
28+
<span>🕒 {selectedFile.readable_timestamp}</span>
29+
<span>💾 {selectedFile.metrics.file_size ? `${(selectedFile.metrics.file_size / 1024).toFixed(1)} KB` : 'N/A'}</span>
30+
<span>📂 {selectedFile.metrics.file_extension || 'N/A'}</span>
31+
</div>
32+
</div>
33+
</div>
34+
</div>
35+
);
36+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React from 'react';
2+
import { MetricData } from '../utils/types';
3+
import { getFileIconSrc, formatAge } from '../utils/utils';
4+
import { styles } from '../utils/styles';
5+
6+
interface FileListProps {
7+
data: MetricData[];
8+
selectedFile: MetricData | null;
9+
onFileSelect: (file: MetricData) => void;
10+
}
11+
12+
export const FileList: React.FC<FileListProps> = ({ data, selectedFile, onFileSelect }) => {
13+
return (
14+
<div style={styles.sidebar}>
15+
<h2 style={styles.sidebarTitle}>
16+
<span>📁</span>
17+
Analyzed Files
18+
</h2>
19+
<div style={styles.fileList}>
20+
{data.map((item) => (
21+
<div
22+
key={item.id}
23+
onClick={() => onFileSelect(item)}
24+
style={{
25+
...styles.fileItem,
26+
...(selectedFile?.id === item.id
27+
? styles.fileItemSelected
28+
: styles.fileItemDefault)
29+
}}
30+
className="file-item"
31+
onMouseEnter={(e) => {
32+
if (selectedFile?.id !== item.id) {
33+
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.1)';
34+
}
35+
}}
36+
onMouseLeave={(e) => {
37+
if (selectedFile?.id !== item.id) {
38+
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)';
39+
}
40+
}}
41+
>
42+
<div style={styles.fileInfo}>
43+
<div style={styles.fileIcon}>
44+
<img
45+
src={getFileIconSrc(item.metrics.file_extension)}
46+
alt={item.metrics.file_extension}
47+
style={styles.fileIconImg}
48+
/>
49+
</div>
50+
51+
<div style={{ flex: 1, minWidth: 0 }}>
52+
<div style={styles.fileName} title={item.file_name}>
53+
{item.file_name}
54+
</div>
55+
<div style={styles.filePath} title={item.file_path}>
56+
{item.file_path.replace(item.file_name, '')}
57+
</div>
58+
<div style={styles.fileAge}>
59+
<span>🕒</span>
60+
{formatAge(item.age)}
61+
</div>
62+
</div>
63+
</div>
64+
</div>
65+
))}
66+
</div>
67+
</div>
68+
);
69+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { styles } from '../utils/styles';
3+
4+
interface HeaderProps {
5+
dataLength: number;
6+
loading: boolean;
7+
onRefresh: () => void;
8+
}
9+
10+
export const Header: React.FC<HeaderProps> = ({ dataLength, loading, onRefresh }) => {
11+
return (
12+
<div style={styles.header}>
13+
<div style={styles.headerContent}>
14+
<div>
15+
<h1 style={styles.title}>
16+
<img
17+
src="/spicecode-logo.png"
18+
alt="SpiceCode Logo"
19+
style={{ width: '3rem', height: '3rem', verticalAlign: 'middle', marginRight: '0.5rem' }}
20+
/>
21+
SpiceCloud | Powered by SpiceCodeCLI
22+
</h1>
23+
<p style={styles.subtitle}>{dataLength} files analyzed</p>
24+
</div>
25+
<button
26+
onClick={onRefresh}
27+
disabled={loading}
28+
style={{
29+
...styles.refreshButton,
30+
background: loading ? '#bfa865' : '#d97304'
31+
}}
32+
>
33+
{loading ? '🔄 Updating...' : '🔄 Refresh'}
34+
</button>
35+
</div>
36+
</div>
37+
);
38+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
import { styles } from '../utils/styles';
3+
4+
interface IndentationCardProps {
5+
indentationType?: string;
6+
indentationSize?: number;
7+
}
8+
9+
export const IndentationCard: React.FC<IndentationCardProps> = ({
10+
indentationType,
11+
indentationSize
12+
}) => {
13+
return (
14+
<div style={{ ...styles.metricCard, ...styles.indentationCard }} className="metric-card">
15+
<div style={styles.metricHeader}>
16+
<span style={styles.metricIcon} className="metric-icon">📐</span>
17+
<h3 style={styles.metricTitle}>Indentation</h3>
18+
</div>
19+
<div style={styles.indentationList}>
20+
<div style={styles.indentationItem}>
21+
<span style={styles.indentationLabel}>Type:</span>
22+
<span style={styles.indentationValue}>
23+
{indentationType || 'N/A'}
24+
</span>
25+
</div>
26+
<div style={styles.indentationItem}>
27+
<span style={styles.indentationLabel}>Size:</span>
28+
<span style={styles.indentationValue}>
29+
{indentationSize || 'N/A'}
30+
</span>
31+
</div>
32+
</div>
33+
</div>
34+
);
35+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import { styles } from '../utils/styles';
3+
4+
export const LoadingSpinner: React.FC = () => {
5+
return (
6+
<div style={styles.loading}>
7+
<div style={{ textAlign: 'center' }}>
8+
<div style={styles.loadingSpinner}></div>
9+
<p style={{ marginTop: '1.5rem', fontSize: '1.125rem' }}>Loading your code metrics...</p>
10+
</div>
11+
</div>
12+
);
13+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import { styles } from '../utils/styles';
3+
4+
interface MethodTypeCardProps {
5+
value: {
6+
public: number;
7+
private: number;
8+
};
9+
}
10+
11+
export const MethodTypeCard: React.FC<MethodTypeCardProps> = ({ value }) => {
12+
return (
13+
<div
14+
style={{ ...styles.metricCard, ...styles.methodTypeCard }}
15+
className="metric-card"
16+
>
17+
<div style={styles.metricHeader}>
18+
<span style={styles.metricIcon} className="metric-icon">🔧</span>
19+
<h3 style={styles.metricTitle}>Method Types</h3>
20+
</div>
21+
<div style={styles.methodTypeList}>
22+
<div style={styles.methodTypeItem}>
23+
<div style={styles.methodTypeLabel}>
24+
<span style={{ ...styles.methodTypeDot, background: '#10b981' }}></span>
25+
<span style={styles.methodTypeText}>Public</span>
26+
</div>
27+
<span style={styles.methodTypeValue}>{value.public}</span>
28+
</div>
29+
<div style={styles.methodTypeItem}>
30+
<div style={styles.methodTypeLabel}>
31+
<span style={{ ...styles.methodTypeDot, background: '#3b82f6' }}></span>
32+
<span style={styles.methodTypeText}>Private</span>
33+
</div>
34+
<span style={styles.methodTypeValue}>{value.private}</span>
35+
</div>
36+
</div>
37+
</div>
38+
);
39+
};

0 commit comments

Comments
 (0)