Добавлены новые зависимости, обновлены стили и улучшена структура проекта. Обновлен README с описанием функционала и технологий. Реализована анимация и адаптивный дизайн. Настроена авторизация с использованием Apollo Client.

This commit is contained in:
Bivekich
2025-07-16 18:00:41 +03:00
parent d260749bc9
commit 823ef9a28c
69 changed files with 15539 additions and 210 deletions

View File

@ -0,0 +1,108 @@
"use client"
import * as React from "react"
import { IMaskInput } from "react-imask"
import { cn } from "@/lib/utils"
export interface PhoneInputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> {
onChange?: (value: string) => void
value?: string
}
const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
({ className, onChange, value, ...props }, ref) => {
const handleAccept = (value: string) => {
onChange?.(value)
}
// Фильтруем пропсы, которые могут конфликтовать с IMaskInput
const { min, max, step, ...filteredProps } = props
return (
<IMaskInput
mask="+7 (000) 000-00-00"
value={value}
onAccept={handleAccept}
inputRef={ref}
{...filteredProps}
className={cn(
"flex h-12 w-full rounded-lg border border-input bg-background px-4 py-3 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"transition-all duration-200 hover:border-primary/50 focus:border-primary",
"cursor-pointer", // Добавляем cursor pointer в соответствии с предпочтениями пользователя
className
)}
/>
)
}
)
PhoneInput.displayName = "PhoneInput"
const GlassPhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
({ className, onChange, value, ...props }, ref) => {
const [isFocused, setIsFocused] = React.useState(false)
const handleAccept = (value: string) => {
onChange?.(value)
}
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
setIsFocused(true)
props.onFocus?.(e)
}
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
setIsFocused(false)
props.onBlur?.(e)
}
// Проверяем валидность номера
const isValid = value ? value.replace(/\D/g, '').length === 11 : false
const isEmpty = !value || value.replace(/\D/g, '').length === 0
// Фильтруем пропсы, которые могут конфликтовать с IMaskInput
const { min, max, step, onFocus, onBlur, ...filteredProps } = props
return (
<div className="relative">
<IMaskInput
mask="+7 (000) 000-00-00"
value={value}
onAccept={handleAccept}
onFocus={handleFocus}
onBlur={handleBlur}
inputRef={ref}
{...filteredProps}
className={cn(
"glass-input text-white placeholder:text-white/50 selection:bg-purple-500/30 flex h-12 w-full rounded-lg px-4 py-3 text-base font-medium outline-none cursor-pointer transition-all duration-300",
isFocused && "ring-2 ring-purple-400/50 border-purple-400/30",
isValid && !isFocused && "border-green-400/30 bg-green-500/5",
!isEmpty && !isValid && !isFocused && "border-yellow-400/30 bg-yellow-500/5",
className
)}
/>
{/* Индикатор валидности */}
<div className="absolute right-3 top-1/2 transform -translate-y-1/2 pointer-events-none">
{isValid && (
<div className="w-5 h-5 rounded-full bg-green-500/20 border border-green-400/30 flex items-center justify-center">
<svg className="w-3 h-3 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
)}
{!isEmpty && !isValid && (
<div className="w-5 h-5 rounded-full bg-yellow-500/20 border border-yellow-400/30 flex items-center justify-center">
<svg className="w-3 h-3 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.34 16.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
</div>
)}
</div>
</div>
)
}
)
GlassPhoneInput.displayName = "GlassPhoneInput"
export { PhoneInput, GlassPhoneInput }