Page.tsx 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import type { ReactNode } from 'react';
  2. import { Activity } from 'lucide-react';
  3. export function PageHeader({
  4. title,
  5. description,
  6. actions,
  7. }: {
  8. title: string;
  9. description?: string;
  10. actions?: ReactNode;
  11. }) {
  12. return (
  13. <div className="page-toolbar">
  14. <div className="page-heading">
  15. <div className="page-kicker">
  16. <Activity size={13} aria-hidden="true" />
  17. <span>OPERATIONS / LIVE INTERFACE</span>
  18. </div>
  19. <h1 className="page-title">{title}</h1>
  20. {description ? <p className="page-description">{description}</p> : null}
  21. </div>
  22. {actions ? <div className="page-actions">{actions}</div> : null}
  23. </div>
  24. );
  25. }
  26. export function Surface({
  27. title,
  28. actions,
  29. children,
  30. className = '',
  31. }: {
  32. title?: string;
  33. actions?: ReactNode;
  34. children: ReactNode;
  35. className?: string;
  36. }) {
  37. return (
  38. <section className={`surface ${className}`.trim()}>
  39. {title || actions ? (
  40. <header className="surface-header">
  41. {title ? (
  42. <h2 className="surface-title">
  43. <span className="surface-title-mark" aria-hidden="true" />
  44. {title}
  45. </h2>
  46. ) : <span />}
  47. {actions}
  48. </header>
  49. ) : null}
  50. <div className="surface-body">{children}</div>
  51. </section>
  52. );
  53. }
  54. export function Metric({
  55. label,
  56. value,
  57. icon,
  58. hint,
  59. tone = 'default',
  60. }: {
  61. label: string;
  62. value: number | string;
  63. icon?: ReactNode;
  64. hint?: string;
  65. tone?: 'default' | 'info' | 'success' | 'warning' | 'danger';
  66. }) {
  67. return (
  68. <div className={`metric metric--${tone}`}>
  69. <span className="metric-signal" aria-hidden="true" />
  70. <div className="metric-copy">
  71. <div className="metric-label">{label}</div>
  72. <div className="metric-value">{value}</div>
  73. {hint ? <div className="metric-hint">{hint}</div> : null}
  74. </div>
  75. {icon ? <div className="metric-icon" aria-hidden="true">{icon}</div> : null}
  76. </div>
  77. );
  78. }