Init project
This commit is contained in:
164
nest-front/client/src/pages/intranet/IntranetDashboard.tsx
Normal file
164
nest-front/client/src/pages/intranet/IntranetDashboard.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { MOCK_BUGS, MOCK_STAFF_POSTS, MOCK_USERS, MOCK_THREADS } from '../../data/mockData';
|
||||
|
||||
interface StatCardProps {
|
||||
label: string;
|
||||
value: number | string;
|
||||
accent?: 'green' | 'amber' | 'red';
|
||||
}
|
||||
|
||||
function StatCard({ label, value, accent = 'green' }: StatCardProps) {
|
||||
const colors = {
|
||||
green: 'var(--color-green)',
|
||||
amber: 'var(--color-amber)',
|
||||
red: 'var(--color-red)',
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background: '#0a0d0a',
|
||||
border: '1px solid #1a2a1a',
|
||||
padding: '1.25rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.7rem', letterSpacing: '0.15em', textTransform: 'uppercase' }}>
|
||||
{label}
|
||||
</div>
|
||||
<div style={{ fontFamily: 'var(--font-heading)', color: colors[accent], fontSize: '2.5rem', lineHeight: 1 }}>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface NavTileProps {
|
||||
to: string;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
function NavTile({ to, label, description, icon }: NavTileProps) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
style={{
|
||||
background: '#0a0d0a',
|
||||
border: '1px solid #1a2a1a',
|
||||
padding: '1.5rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.6rem',
|
||||
textDecoration: 'none',
|
||||
transition: 'border-color 0.2s, background 0.2s',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
(e.currentTarget as HTMLAnchorElement).style.borderColor = 'rgba(255,176,0,0.4)';
|
||||
(e.currentTarget as HTMLAnchorElement).style.background = 'rgba(255,176,0,0.03)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
(e.currentTarget as HTMLAnchorElement).style.borderColor = '#1a2a1a';
|
||||
(e.currentTarget as HTMLAnchorElement).style.background = '#0a0d0a';
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-amber)', fontSize: '0.75rem', letterSpacing: '0.1em' }}>
|
||||
{icon}
|
||||
</div>
|
||||
<div style={{ fontFamily: 'var(--font-heading)', color: 'var(--color-text)', fontSize: '1.1rem' }}>
|
||||
{label}
|
||||
</div>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.78rem', lineHeight: 1.6 }}>
|
||||
{description}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default function IntranetDashboard() {
|
||||
const { user } = useAuth();
|
||||
|
||||
const openBugs = MOCK_BUGS.filter((b) => b.status === 'open').length;
|
||||
const criticalBugs = MOCK_BUGS.filter((b) => b.severity === 'critical').length;
|
||||
const assignedToMe = MOCK_BUGS.filter((b) => b.assignedToId === user?.id).length;
|
||||
const totalUsers = MOCK_USERS.filter((u) => !u.isAdmin).length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Header */}
|
||||
<div style={{ marginBottom: '2rem' }}>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.7rem', letterSpacing: '0.15em', marginBottom: '0.5rem' }}>
|
||||
INTRANET / DASHBOARD
|
||||
</div>
|
||||
<h1 style={{ fontFamily: 'var(--font-heading)', color: 'var(--color-text)', fontSize: '1.8rem', marginBottom: '0.25rem' }}>
|
||||
Welcome, {user?.username}
|
||||
</h1>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.8rem' }}>
|
||||
{new Date().toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div style={{ marginBottom: '2rem' }}>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.7rem', letterSpacing: '0.15em', marginBottom: '1rem' }}>
|
||||
QUICK STATS
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: '0.75rem' }}>
|
||||
<StatCard label="Open Bugs" value={openBugs} accent="green" />
|
||||
<StatCard label="Critical" value={criticalBugs} accent="red" />
|
||||
<StatCard label="Assigned to Me" value={assignedToMe} accent="amber" />
|
||||
<StatCard label="Total Users" value={totalUsers} accent="green" />
|
||||
<StatCard label="Forum Threads" value={MOCK_THREADS.length} accent="green" />
|
||||
<StatCard label="Staff Posts Today" value={MOCK_STAFF_POSTS.filter((p) => p.createdAt.startsWith('2026-02-18')).length} accent="amber" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation tiles */}
|
||||
<div style={{ marginBottom: '2rem' }}>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.7rem', letterSpacing: '0.15em', marginBottom: '1rem' }}>
|
||||
SECTIONS
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: '0.75rem' }}>
|
||||
<NavTile to="/intranet/bugs" label="Bug Reports" description="Review, assign, and update reported issues. Filter by severity and status." icon="[!]" />
|
||||
<NavTile to="/intranet/feed" label="Team Feed" description="Internal staff activity feed. Post updates visible only to the team." icon="[~]" />
|
||||
<NavTile to="/intranet/users" label="User Management" description="View all registered users. Promote or ban accounts." icon="[U]" />
|
||||
<NavTile to="/intranet/moderation" label="Forum Moderation" description="Pin, lock, and delete threads and replies from the public forum." icon="[M]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent staff posts */}
|
||||
<div>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.7rem', letterSpacing: '0.15em', marginBottom: '1rem' }}>
|
||||
RECENT TEAM ACTIVITY
|
||||
</div>
|
||||
<div style={{ background: '#0a0d0a', border: '1px solid #1a2a1a' }}>
|
||||
{MOCK_STAFF_POSTS.slice(0, 4).map((post, idx) => (
|
||||
<div
|
||||
key={post.id}
|
||||
style={{
|
||||
padding: '0.85rem 1.25rem',
|
||||
borderBottom: idx < 3 ? '1px solid #1a2a1a' : 'none',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem', marginBottom: '0.3rem', flexWrap: 'wrap' }}>
|
||||
<span style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-amber)', fontSize: '0.78rem' }}>
|
||||
{post.authorName}
|
||||
<span style={{ color: 'var(--color-text-muted)', marginLeft: '0.4rem', fontSize: '0.65rem' }}>[{post.authorRole}]</span>
|
||||
</span>
|
||||
<span style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.7rem' }}>
|
||||
{new Date(post.createdAt).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-dim)', fontSize: '0.82rem', lineHeight: 1.7 }}>
|
||||
{post.content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user