Skip to content
2 changes: 1 addition & 1 deletion projects/frontend/apps/experiment-portal/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
min-height: 40px;
padding: 0 18px;
border: 1px solid var(--border-soft);
border-radius: var(--radius-sm);
border-radius: var(--radius-pill);
background: #fff;
color: var(--text);
font-size: 0.875rem;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@import '../../styles/colors';

.liveswitch {
display: inline-flex;
align-items: center;
gap: 8px;
height: 40px;
padding: 0 12px;
border-radius: var(--radius-pill);
border: 1px solid $input-border;
background: #fff;
user-select: none;
cursor: pointer;
transition: border-color 0.15s ease, background 0.15s ease;
flex: 0 0 auto;
white-space: nowrap;

&:hover { border-color: $outline-muted; }

&:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
border-color: $primary;
}
}

.liveswitch__track {
position: relative;
width: 32px;
height: 18px;
border-radius: 999px;
background: $input-border;
transition: background 0.2s ease;
flex: none;
}

.liveswitch__thumb {
position: absolute;
top: 2px;
left: 2px;
width: 14px;
height: 14px;
background: #fff;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.2);
transition: transform 0.2s ease;
}

.liveswitch--on .liveswitch__track { background: $primary; }
.liveswitch--on .liveswitch__thumb { transform: translateX(14px); }

.liveswitch__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: $input-border;
flex: none;
transition: background 0.2s ease, box-shadow 0.2s ease;
}

.liveswitch--on .liveswitch__dot {
background: #22c55e;
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
animation: live-pulse 1.6s ease-in-out infinite;
}

@keyframes live-pulse {
0%, 100% { box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18); }
50% { box-shadow: 0 0 0 6px rgba(34, 197, 94, 0.06); }
}

.liveswitch__label {
font-size: 0.82rem;
font-weight: 600;
color: $text;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import './LiveSwitch.scss'

interface LiveSwitchProps {
live: boolean
onChange: (live: boolean) => void
labelOn?: string
labelOff?: string
}

function LiveSwitch({ live, onChange, labelOn = 'Live', labelOff = 'History' }: LiveSwitchProps) {
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault()
onChange(!live)
}
}
return (
<div
className={`liveswitch${live ? ' liveswitch--on' : ''}`}
role="switch"
aria-checked={live}
tabIndex={0}
onClick={() => onChange(!live)}
onKeyDown={handleKeyDown}
>
<div className="liveswitch__track">
<div className="liveswitch__thumb" />
</div>
<span className="liveswitch__dot" />
<span className="liveswitch__label">{live ? labelOn : labelOff}</span>
</div>
)
}

export default LiveSwitch
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,110 @@
padding-bottom: 9px;
font-size: 0.88rem;
}

// ── Pill variant (B1 capsule style) ──
.md-select--pill .md-select__label {
display: none;
}

.md-select--pill .md-select__control {
min-height: 40px;
border-radius: var(--radius-pill);
border-color: $input-border;
background: #fff;
}

.md-select--pill .md-select__control:hover {
border-color: $outline-muted;
background: #f8fafc;
}

.md-select--pill .md-select__control--open {
border-color: $primary;
box-shadow: var(--focus-ring);
background: #fff;
}

.md-select--pill .md-select__control--disabled {
opacity: 0.62;
background: $surface-variant;
}

.md-select--pill .md-select__trigger {
position: relative;
min-height: 40px;
padding: 0 28px 0 12px;
font-size: 0.85rem;
border-radius: var(--radius-pill);
gap: 6px;
}

.md-select--pill.md-select--has-icon .md-select__trigger {
padding-left: 10px;
}

.md-select__pill-icon {
color: $text-secondary;
flex: none;
display: flex;
align-items: center;
transition: color 0.15s ease;
}

.md-select--pill .md-select__control--open .md-select__pill-icon {
color: $primary;
}

.md-select--pill .md-select__icon {
right: 10px;
width: 14px;
height: 14px;
color: $text-secondary;
transition: transform 0.2s ease, color 0.15s ease;
}

.md-select--pill .md-select__control--open .md-select__icon {
color: $primary;
}

.md-select__float-label {
position: absolute;
left: 36px;
top: 50%;
transform: translateY(-50%);
font-size: 0.85rem;
color: $text-secondary;
pointer-events: none;
background: #fff;
padding: 0 3px;
transition: top 0.15s ease, font-size 0.15s ease, color 0.15s ease, left 0.15s ease, transform 0.15s ease;
white-space: nowrap;
}

.md-select--pill:not(.md-select--has-icon) .md-select__float-label {
left: 12px;
}

.md-select__float-label.is-floating {
top: 0;
transform: translateY(-50%);
font-size: 0.63rem;
font-weight: 600;
letter-spacing: 0.04em;
color: $primary;
left: 10px;
}

.md-select--pill .md-select__control--open .md-select__float-label {
top: 0;
transform: translateY(-50%);
font-size: 0.63rem;
font-weight: 600;
letter-spacing: 0.04em;
color: $primary;
left: 10px;
}

.md-select--pill .md-select__value {
font-size: 0.85rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type MaterialSelectProps = {
multiple?: boolean
size?: number
placeholder?: string
icon?: ReactNode
variant?: 'default' | 'pill'
}

function MaterialSelect({
Expand All @@ -43,6 +45,8 @@ function MaterialSelect({
multiple = false,
size,
placeholder = '',
icon,
variant = 'default',
}: MaterialSelectProps) {
const [isOpen, setIsOpen] = useState(false)
const menuRef = useRef<HTMLDivElement | null>(null)
Expand Down Expand Up @@ -202,9 +206,19 @@ function MaterialSelect({
setIsOpen(false)
}
}
const isPill = variant === 'pill'
const rootClass = [
'md-select',
isPill ? 'md-select--pill' : '',
isPill && icon ? 'md-select--has-icon' : '',
className,
]
.filter(Boolean)
.join(' ')

return (
<div className={`md-select ${className}`.trim()}>
{label && (
<div className={rootClass}>
{label && !isPill && (
<label className="md-select__label" htmlFor={id}>
{label}
</label>
Expand Down Expand Up @@ -233,17 +247,30 @@ function MaterialSelect({
id={id}
ref={triggerRef}
type="button"
className="md-select__trigger"
className={`md-select__trigger${isPill ? ' md-select__trigger--pill' : ''}`}
onClick={toggleMenu}
onKeyDown={handleTriggerKeyDown}
disabled={isDisabled}
aria-haspopup="listbox"
aria-expanded={isOpen}
aria-controls={listboxId}
aria-label={isPill && label ? label : undefined}
>
{isPill && icon && (
<span className="md-select__pill-icon">{icon}</span>
)}
<span className={`md-select__value${selectedValue ? '' : ' is-placeholder'}`}>
{selectedLabel || placeholder}
{isPill ? (selectedValue ? selectedLabel : '') : (selectedLabel || placeholder)}
</span>
{isPill && label && (
<span
className={`md-select__float-label${
selectedValue || isOpen ? ' is-floating' : ''
}`}
>
{label}
</span>
)}
<svg
className="md-select__icon"
viewBox="0 0 24 24"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as LiveSwitch } from './LiveSwitch'
export { default as StatusBadge } from './StatusBadge'
export { default as ConnectionStatusIndicator } from './ConnectionStatusIndicator'
export { default as Loading } from './Loading'
Expand Down
69 changes: 28 additions & 41 deletions projects/frontend/apps/experiment-portal/src/pages/AuditLog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,40 @@
}
}

.audit-log-filters {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
.audit-filter-capsule {
flex-wrap: wrap;

&__fields {
&__date-range {
display: flex;
flex-wrap: wrap;
gap: 12px;

.form-group {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 160px;
flex: 1 1 160px;

label {
font-size: 0.75rem;
font-weight: 500;
color: var(--color-text-secondary, #888);
text-transform: uppercase;
letter-spacing: 0.05em;
}

input {
padding: 6px 10px;
border: 1px solid var(--outline);
border-radius: var(--radius-sm);
background: #fff;
color: var(--text);
font-size: 0.875rem;

&:focus {
outline: none;
border-color: var(--primary);
box-shadow: var(--focus-ring);
}
align-items: center;
gap: 6px;
height: 40px;
padding: 0 12px;
border: 1px solid #cbd5e1;
border-radius: var(--radius-pill);
background: #fff;
flex: 0 0 auto;

input[type='date'] {
border: none;
outline: none;
background: transparent;
font-size: 0.82rem;
color: var(--text);
width: 120px;
cursor: pointer;

&::-webkit-calendar-picker-indicator {
opacity: 0.5;
cursor: pointer;
}
}
}

&__actions {
display: flex;
gap: 8px;
align-items: center;
&__date-sep {
font-size: 0.82rem;
color: var(--text-secondary);
flex: none;
}
}

Expand Down
Loading
Loading