feat: implement core UI components and build initial dashboard layout with navigation structure

This commit is contained in:
RaymondVerhoef
2026-05-10 10:52:12 +02:00
parent 2fb50a19c9
commit b988028cf8
13 changed files with 365 additions and 16 deletions

View File

@@ -0,0 +1,36 @@
import React from 'react';
const Button = ({
children,
variant = 'primary',
size = 'md',
className = '',
...props
}) => {
const baseStyles = "inline-flex items-center justify-center font-bold transition-all focus:outline-none focus:ring-2 focus:ring-offset-2";
const variants = {
primary: "bg-accent text-paper hover:bg-purple-700 focus:ring-accent",
secondary: "bg-teal text-paper hover:bg-teal-700 focus:ring-teal",
outline: "border-2 border-teal text-teal hover:bg-teal hover:text-paper focus:ring-teal",
ghost: "text-teal hover:bg-bg-warm focus:ring-bg-warm"
};
const sizes = {
sm: "px-3 py-1.5 text-sm rounded-[var(--r-sm)]",
md: "px-5 py-2.5 text-base rounded-[var(--r-md)]",
lg: "px-8 py-3.5 text-lg rounded-[var(--r-lg)]",
pill: "px-6 py-3 text-base rounded-[var(--r-pill)] shadow-pill"
};
return (
<button
className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
{...props}
>
{children}
</button>
);
};
export default Button;

View File

@@ -0,0 +1,18 @@
import React from 'react';
const Card = ({ children, className = '', hoverable = false, ...props }) => {
return (
<div
className={`
bg-paper rounded-[var(--r-md)] shadow-soft p-6
${hoverable ? 'transition-transform hover:-translate-y-1 hover:shadow-pill' : ''}
${className}
`}
{...props}
>
{children}
</div>
);
};
export default Card;

View File

@@ -0,0 +1,31 @@
import React from 'react';
const Input = React.forwardRef(({ label, error, className = '', ...props }, ref) => {
return (
<div className={`flex flex-col gap-1 ${className}`}>
{label && (
<label className="text-[var(--t-small)] font-medium text-fg-muted">
{label}
</label>
)}
<input
ref={ref}
className={`
w-full px-4 py-2.5 bg-paper border border-bg-warm rounded-[var(--r-sm)]
text-fg placeholder:text-fg-subtle
focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent
transition-all
${error ? 'border-red-500 focus:ring-red-500' : ''}
`}
{...props}
/>
{error && (
<span className="text-xs text-red-500 mt-1">{error}</span>
)}
</div>
);
});
Input.displayName = 'Input';
export default Input;

View File

@@ -0,0 +1,42 @@
import React from 'react';
const Select = React.forwardRef(({ label, error, options, className = '', ...props }, ref) => {
return (
<div className={`flex flex-col gap-1 ${className}`}>
{label && (
<label className="text-[var(--t-small)] font-medium text-fg-muted">
{label}
</label>
)}
<select
ref={ref}
className={`
w-full px-4 py-2.5 bg-paper border border-bg-warm rounded-[var(--r-sm)]
text-fg focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent
transition-all cursor-pointer appearance-none
${error ? 'border-red-500 focus:ring-red-500' : ''}
`}
style={{
backgroundImage: `url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e")`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 1rem center',
backgroundSize: '1em'
}}
{...props}
>
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
{error && (
<span className="text-xs text-red-500 mt-1">{error}</span>
)}
</div>
);
});
Select.displayName = 'Select';
export default Select;

26
src/components/ui/Tag.jsx Normal file
View File

@@ -0,0 +1,26 @@
import React from 'react';
const Tag = ({ children, variant = 'default', className = '', ...props }) => {
const variants = {
default: "bg-bg-warm text-fg",
accent: "bg-accent-soft text-purple-700",
success: "bg-success text-teal-900",
dark: "bg-bg-dark text-paper",
};
return (
<span
className={`
inline-flex items-center px-2 py-1 rounded-[var(--r-pill)]
label whitespace-nowrap
${variants[variant]}
${className}
`}
{...props}
>
{children}
</span>
);
};
export default Tag;