165 lines
7.1 KiB
TypeScript
165 lines
7.1 KiB
TypeScript
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: 'var(--color-surface)',
|
|
border: '1px solid var(--color-border)',
|
|
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: 'var(--color-surface)',
|
|
border: '1px solid var(--color-border)',
|
|
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 = 'var(--color-yellow)';
|
|
(e.currentTarget as HTMLAnchorElement).style.background = 'rgba(37,99,235,0.05)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
(e.currentTarget as HTMLAnchorElement).style.borderColor = 'var(--color-border)';
|
|
(e.currentTarget as HTMLAnchorElement).style.background = 'var(--color-surface)';
|
|
}}
|
|
>
|
|
<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: 'var(--color-surface)', border: '1px solid var(--color-border)' }}>
|
|
{MOCK_STAFF_POSTS.slice(0, 4).map((post, idx) => (
|
|
<div
|
|
key={post.id}
|
|
style={{
|
|
padding: '0.85rem 1.25rem',
|
|
borderBottom: idx < 3 ? '1px solid var(--color-border)' : '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>
|
|
);
|
|
}
|