Files
cameleer-server/ui/src/components/shared/shared.module.css
hsiegeln 3eb83f97d3
Some checks failed
CI / build (push) Failing after 1m53s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
  system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
  expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
  JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
  FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
  hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
  NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
  resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
  deploy step for UI, UI credential secrets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00

202 lines
4.7 KiB
CSS

/* ─── Status Pill ─── */
.statusPill {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 10px;
border-radius: 99px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.3px;
}
.statusDot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.pillCompleted { background: var(--green-glow); color: var(--green); }
.pillFailed { background: var(--rose-glow); color: var(--rose); }
.pillRunning { background: rgba(59, 130, 246, 0.12); color: var(--blue); }
.pillRunning .statusDot { animation: livePulse 1.5s ease-in-out infinite; }
/* ─── Duration Bar ─── */
.durationBar {
display: flex;
align-items: center;
gap: 8px;
}
.bar {
width: 60px;
height: 4px;
background: var(--bg-base);
border-radius: 2px;
overflow: hidden;
}
.barFill {
height: 100%;
border-radius: 2px;
transition: width 0.3s;
}
.barFast { background: var(--green); }
.barMedium { background: var(--amber); }
.barSlow { background: var(--rose); }
/* ─── Stat Card ─── */
.statCard {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 16px 20px;
position: relative;
overflow: hidden;
transition: border-color 0.2s;
}
.statCard:hover { border-color: var(--border); }
.statCard::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
}
.amber::before { background: linear-gradient(90deg, var(--amber), transparent); }
.cyan::before { background: linear-gradient(90deg, var(--cyan), transparent); }
.rose::before { background: linear-gradient(90deg, var(--rose), transparent); }
.green::before { background: linear-gradient(90deg, var(--green), transparent); }
.blue::before { background: linear-gradient(90deg, var(--blue), transparent); }
.statLabel {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
color: var(--text-muted);
margin-bottom: 8px;
}
.statValue {
font-family: var(--font-mono);
font-size: 26px;
font-weight: 600;
letter-spacing: -1px;
}
.amber .statValue { color: var(--amber); }
.cyan .statValue { color: var(--cyan); }
.rose .statValue { color: var(--rose); }
.green .statValue { color: var(--green); }
.blue .statValue { color: var(--blue); }
.statChange {
font-size: 11px;
font-family: var(--font-mono);
margin-top: 4px;
}
.up { color: var(--rose); }
.down { color: var(--green); }
.neutral { color: var(--text-muted); }
/* ─── App Badge ─── */
.appBadge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 2px 8px;
background: var(--bg-raised);
border: 1px solid var(--border);
border-radius: 4px;
font-size: 11px;
font-family: var(--font-mono);
color: var(--text-secondary);
}
.appDot {
width: 6px;
height: 6px;
border-radius: 50%;
}
/* ─── Filter Chip ─── */
.chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 5px 12px;
background: var(--bg-raised);
border: 1px solid var(--border);
border-radius: 99px;
font-size: 12px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.15s;
white-space: nowrap;
user-select: none;
}
.chip:hover { border-color: var(--text-muted); color: var(--text-primary); }
.chipActive { background: var(--amber-glow); border-color: var(--amber-dim); color: var(--amber); }
.chipActive.chipGreen { background: var(--green-glow); border-color: rgba(16, 185, 129, 0.3); color: var(--green); }
.chipActive.chipRose { background: var(--rose-glow); border-color: rgba(244, 63, 94, 0.3); color: var(--rose); }
.chipActive.chipBlue { background: rgba(59, 130, 246, 0.12); border-color: rgba(59, 130, 246, 0.3); color: var(--blue); }
.chipDot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
display: inline-block;
}
.chipCount {
font-family: var(--font-mono);
font-size: 10px;
opacity: 0.7;
}
/* ─── Pagination ─── */
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
margin-top: 20px;
}
.pageBtn {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
color: var(--text-secondary);
font-family: var(--font-mono);
font-size: 13px;
cursor: pointer;
transition: all 0.15s;
}
.pageBtn:hover:not(:disabled) { border-color: var(--border); background: var(--bg-raised); }
.pageBtnActive { background: var(--amber-glow); border-color: var(--amber-dim); color: var(--amber); }
.pageBtnDisabled { opacity: 0.3; cursor: default; }
.pageEllipsis {
color: var(--text-muted);
padding: 0 4px;
font-family: var(--font-mono);
}