ADD new frontend

This commit is contained in:
Urtzi Alfaro
2025-08-28 10:41:04 +02:00
parent 9c247a5f99
commit 0fd273cfce
492 changed files with 114979 additions and 1632 deletions

View File

@@ -0,0 +1,171 @@
import { forwardRef } from 'react';
import { clsx } from 'clsx';
export interface StatusIndicatorProps {
status: 'success' | 'warning' | 'danger' | 'error' | 'info' | 'neutral';
size?: 'xs' | 'sm' | 'md' | 'lg';
variant?: 'dot' | 'badge' | 'pill';
label?: string;
showLabel?: boolean;
pulse?: boolean;
className?: string;
}
const StatusIndicator = forwardRef<HTMLDivElement, StatusIndicatorProps>(({
status,
size = 'md',
variant = 'dot',
label,
showLabel = false,
pulse = false,
className,
...props
}, ref) => {
const statusClasses = {
success: {
bg: 'bg-[var(--color-success)]',
text: 'text-[var(--color-success)]',
border: 'border-[var(--color-success)]',
},
warning: {
bg: 'bg-[var(--color-warning)]',
text: 'text-[var(--color-warning)]',
border: 'border-[var(--color-warning)]',
},
danger: {
bg: 'bg-[var(--color-error)]',
text: 'text-[var(--color-error)]',
border: 'border-[var(--color-error)]',
},
error: {
bg: 'bg-[var(--color-error)]',
text: 'text-[var(--color-error)]',
border: 'border-[var(--color-error)]',
},
info: {
bg: 'bg-[var(--color-info)]',
text: 'text-[var(--color-info)]',
border: 'border-[var(--color-info)]',
},
neutral: {
bg: 'bg-[var(--text-tertiary)]',
text: 'text-[var(--text-tertiary)]',
border: 'border-[var(--text-tertiary)]',
},
};
const sizeClasses = {
xs: {
dot: 'w-2 h-2',
badge: 'w-4 h-4',
text: 'text-xs',
},
sm: {
dot: 'w-3 h-3',
badge: 'w-5 h-5',
text: 'text-sm',
},
md: {
dot: 'w-4 h-4',
badge: 'w-6 h-6',
text: 'text-base',
},
lg: {
dot: 'w-5 h-5',
badge: 'w-8 h-8',
text: 'text-lg',
},
};
const renderDot = () => (
<div
className={clsx(
'rounded-full',
sizeClasses[size].dot,
statusClasses[status].bg,
{
'animate-pulse': pulse,
}
)}
/>
);
const renderBadge = () => (
<div
className={clsx(
'rounded-full border-2 flex items-center justify-center',
sizeClasses[size].badge,
statusClasses[status].border,
'bg-white',
{
'animate-pulse': pulse,
}
)}
>
<div
className={clsx(
'rounded-full',
`w-${parseInt(sizeClasses[size].badge.split(' ')[0].substring(2)) - 2}`,
`h-${parseInt(sizeClasses[size].badge.split(' ')[1].substring(2)) - 2}`,
statusClasses[status].bg
)}
/>
</div>
);
const renderPill = () => (
<div
className={clsx(
'rounded-full px-2 py-1 text-white text-xs font-medium',
statusClasses[status].bg,
{
'animate-pulse': pulse,
}
)}
>
{label || status.charAt(0).toUpperCase() + status.slice(1)}
</div>
);
const renderIndicator = () => {
switch (variant) {
case 'badge':
return renderBadge();
case 'pill':
return renderPill();
default:
return renderDot();
}
};
if (showLabel && variant !== 'pill') {
return (
<div
ref={ref}
className={clsx('flex items-center gap-2', className)}
{...props}
>
{renderIndicator()}
<span
className={clsx(
'font-medium',
sizeClasses[size].text,
statusClasses[status].text
)}
>
{label || status.charAt(0).toUpperCase() + status.slice(1)}
</span>
</div>
);
}
return (
<div ref={ref} className={clsx(className)} {...props}>
{renderIndicator()}
</div>
);
});
StatusIndicator.displayName = 'StatusIndicator';
export default StatusIndicator;

View File

@@ -0,0 +1,2 @@
export { default as StatusIndicator } from './StatusIndicator';
export type { StatusIndicatorProps } from './StatusIndicator';