Livrare Lot 3 (Frontend): aplicație web, aplicație mobilă Android, extensie browser
- Surse complete web (React/Vite) + mobil (React Native/Expo) + extensie (MV3) - Documentație de livrare: ghid utilizare, matrice trasabilitate cerințe, raport testare furnizor - Artefacte binare: imagine Docker didi-frontend:lot3-1.0, APK, extensie v3.2.6 + SHA256SUMS - Configurare adresă platformă externalizată (build args / .env / config.js) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
commit
cec967f953
321 changed files with 80506 additions and 0 deletions
91
web/src/components/ui/Input.tsx
Normal file
91
web/src/components/ui/Input.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { colors, typography, spacing, borderRadius, animation } from '../../theme';
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string;
|
||||
error?: string;
|
||||
helperText?: string;
|
||||
}
|
||||
|
||||
export const Input: React.FC<InputProps> = ({
|
||||
label,
|
||||
error,
|
||||
helperText,
|
||||
id,
|
||||
...props
|
||||
}) => {
|
||||
const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{label && <Label htmlFor={inputId}>{label}</Label>}
|
||||
<StyledInput id={inputId} hasError={!!error} {...props} />
|
||||
{error && <ErrorText>{error}</ErrorText>}
|
||||
{helperText && !error && <HelperText>{helperText}</HelperText>}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Label = styled.label`
|
||||
display: block;
|
||||
font-family: ${typography.fontFamily.primary};
|
||||
font-size: ${typography.fontSize.sm};
|
||||
font-weight: ${typography.fontWeight.medium};
|
||||
color: var(--fg-secondary);
|
||||
margin-bottom: ${spacing.sm}px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
`;
|
||||
|
||||
const StyledInput = styled.input<{ hasError: boolean }>`
|
||||
width: 100%;
|
||||
padding: ${spacing.md}px ${spacing.lg}px;
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid ${props => props.hasError ? colors.cautionRed : 'var(--border-default)'};
|
||||
border-radius: 10px;
|
||||
color: var(--fg-primary);
|
||||
font-family: ${typography.fontFamily.primary};
|
||||
font-size: ${typography.fontSize.base};
|
||||
transition: all ${animation.duration.normal} ${animation.easing.default};
|
||||
|
||||
&::placeholder {
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: ${props => props.hasError ? colors.cautionRed : 'var(--accent)'};
|
||||
background: ${props => props.hasError ? 'rgba(230, 57, 70, 0.05)' : 'var(--bg-surface)'};
|
||||
box-shadow: 0 0 0 3px ${props => props.hasError ? 'rgba(230, 57, 70, 0.1)' : 'rgba(0, 145, 152, 0.12)'};
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
`;
|
||||
|
||||
const ErrorText = styled.span`
|
||||
display: block;
|
||||
font-family: ${typography.fontFamily.primary};
|
||||
font-size: ${typography.fontSize.sm};
|
||||
color: ${colors.cautionRed};
|
||||
margin-top: ${spacing.xs}px;
|
||||
`;
|
||||
|
||||
const HelperText = styled.span`
|
||||
display: block;
|
||||
font-family: ${typography.fontFamily.primary};
|
||||
font-size: ${typography.fontSize.sm};
|
||||
color: var(--fg-muted);
|
||||
margin-top: ${spacing.xs}px;
|
||||
font-style: italic;
|
||||
`;
|
||||
Loading…
Add table
Add a link
Reference in a new issue