вход/рег под наш макет
This commit is contained in:
parent
28d20b23a0
commit
95ebf4d4f5
@ -1,7 +1,6 @@
|
||||
import { Route } from "react-router-dom"
|
||||
import Home from "./pages/Home"
|
||||
import Login from "./pages/Login"
|
||||
import Register from "./pages/Register"
|
||||
import Welcome from "./pages/Welcome"
|
||||
import ForgotPassword from "./pages/ForgotPassword"
|
||||
import Courses from "./pages/Courses"
|
||||
@ -14,7 +13,6 @@ const AppRoutes = () => (
|
||||
<>
|
||||
<Route exact path="/" component={Welcome} />
|
||||
<Route path="/home" component={Home} exact />
|
||||
<Route path="/register" component={Register} />
|
||||
<Route path="/login" component={Login} />
|
||||
<Route path="/forgot-password" component={ForgotPassword} />
|
||||
<Route path="/welcome" component={Welcome} />
|
||||
|
@ -7,74 +7,80 @@ const BottomNavigation: React.FC = () => {
|
||||
const history = useHistory()
|
||||
const location = useLocation()
|
||||
|
||||
// Define SVG icons directly within the component
|
||||
const HomeIcon = ({ active }: { active: boolean }) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className={active ? "text-white" : "text-[#5F5C5C]"}>
|
||||
<path
|
||||
d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z"
|
||||
stroke="currentColor"
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill={active ? "currentColor" : "none"}
|
||||
stroke={active ? "none" : "currentColor"}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path d="M9 22V12H15V22" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
>
|
||||
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const CoursesIcon = ({ active }: { active: boolean }) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className={active ? "text-white" : "text-[#5F5C5C]"}>
|
||||
<path
|
||||
d="M2 3H8C9.06087 3 10.0783 3.42143 10.8284 4.17157C11.5786 4.92172 12 5.93913 12 7V21C12 20.2044 11.6839 19.4413 11.1213 18.8787C10.5587 18.3161 9.79565 18 9 18H2V3Z"
|
||||
stroke="currentColor"
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill={active ? "currentColor" : "none"}
|
||||
stroke={active ? "none" : "currentColor"}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M22 3H16C14.9391 3 13.9217 3.42143 13.1716 4.17157C12.4214 4.92172 12 5.93913 12 7V21C12 20.2044 12.3161 19.4413 12.8787 18.8787C13.4413 18.3161 14.2044 18 15 18H22V3Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
>
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h6z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const ExerciseIcon = ({ active }: { active: boolean }) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className={active ? "text-white" : "text-[#5F5C5C]"}>
|
||||
<path
|
||||
d="M6.2 5H12.8C13.9201 5 14.9798 5.44772 15.7549 6.22183C16.5301 6.99594 16.9778 8.05556 16.9778 9.17647V19.8235C16.9778 18.7026 16.5301 17.643 15.7549 16.8689C14.9798 16.0948 13.9201 15.6471 12.8 15.6471H6.2V5Z"
|
||||
stroke="currentColor"
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill={active ? "currentColor" : "none"}
|
||||
stroke={active ? "none" : "currentColor"}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M6.2 5C5.07989 5 4.02015 5.44772 3.24505 6.22183C2.46995 6.99594 2.02222 8.05556 2.02222 9.17647V19.8235C2.02222 18.7026 2.46995 17.643 3.24505 16.8689C4.02015 16.0948 5.07989 15.6471 6.2 15.6471"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
>
|
||||
<rect x="3" y="9" width="4" height="6" rx="1" ry="1" />
|
||||
<rect x="17" y="9" width="4" height="6" rx="1" ry="1" />
|
||||
<line x1="7" y1="12" x2="17" y2="12" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const SettingsIcon = ({ active }: { active: boolean }) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className={active ? "text-white" : "text-[#5F5C5C]"}>
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="2" />
|
||||
<path
|
||||
d="M19.4 15C19.2669 15.3016 19.2272 15.6362 19.286 15.9606C19.3448 16.285 19.4995 16.5843 19.73 16.82L19.79 16.88C19.976 17.0657 20.1235 17.2863 20.2241 17.5291C20.3248 17.7719 20.3766 18.0322 20.3766 18.295C20.3766 18.5578 20.3248 18.8181 20.2241 19.0609C20.1235 19.3037 19.976 19.5243 19.79 19.71C19.6043 19.896 19.3837 20.0435 19.1409 20.1441C18.8981 20.2448 18.6378 20.2966 18.375 20.2966C18.1122 20.2966 17.8519 20.2448 17.6091 20.1441C17.3663 20.0435 17.1457 19.896 16.96 19.71L16.9 19.65C16.6643 19.4195 16.365 19.2648 16.0406 19.206C15.7162 19.1472 15.3816 19.1869 15.08 19.32C14.7842 19.4468 14.532 19.6572 14.3543 19.9255C14.1766 20.1938 14.0813 20.5082 14.08 20.83V21C14.08 21.5304 13.8693 22.0391 13.4942 22.4142C13.1191 22.7893 12.6104 23 12.08 23C11.5496 23 11.0409 22.7893 10.6658 22.4142C10.2907 22.0391 10.08 21.5304 10.08 21V20.91C10.0723 20.579 9.96512 20.2569 9.77251 19.9859C9.5799 19.7148 9.31074 19.5063 9 19.38C8.69838 19.2469 8.36381 19.2072 8.03941 19.266C7.71502 19.3248 7.41568 19.4795 7.18 19.71L7.12 19.77C6.93425 19.956 6.71368 20.1035 6.47088 20.2041C6.22808 20.3048 5.96783 20.3566 5.705 20.3566C5.44217 20.3566 5.18192 20.3048 4.93912 20.2041C4.69632 20.1035 4.47575 19.956 4.29 19.77C4.10405 19.5843 3.95653 19.3637 3.85588 19.1209C3.75523 18.8781 3.70343 18.6178 3.70343 18.355C3.70343 18.0922 3.75523 17.8319 3.85588 17.5891C3.95653 17.3463 4.10405 17.1257 4.29 16.94L4.35 16.88C4.58054 16.6443 4.73519 16.345 4.794 16.0206C4.85282 15.6962 4.81312 15.3616 4.68 15.06C4.55324 14.7642 4.34276 14.512 4.07447 14.3343C3.80618 14.1566 3.49179 14.0613 3.17 14.06H3C2.46957 14.06 1.96086 13.8493 1.58579 13.4742C1.21071 13.0991 1 12.5904 1 12.06C1 11.5296 1.21071 11.0209 1.58579 10.6458C1.96086 10.2707 2.46957 10.06 3 10.06H3.09C3.42099 10.0523 3.742 9.94512 4.01309 9.75251C4.28417 9.5599 4.49268 9.29074 4.62 8.98C4.75312 8.67838 4.79282 8.34381 4.734 8.01941C4.67519 7.69502 4.52054 7.39568 4.29 7.16L4.23 7.1C4.04405 6.91425 3.89653 6.69368 3.79588 6.45088C3.69523 6.20808 3.64343 5.94783 3.64343 5.685C3.64343 5.42217 3.69523 5.16192 3.79588 4.91912C3.89653 4.67632 4.04405 4.45575 4.23 4.27C4.41575 4.08405 4.63632 3.93653 4.87912 3.83588C5.12192 3.73523 5.38217 3.68343 5.645 3.68343C5.90783 3.68343 6.16808 3.73523 6.41088 3.83588C6.65368 3.93653 6.87425 4.08405 7.06 4.27L7.12 4.33C7.35568 4.56054 7.65502 4.71519 7.97941 4.774C8.30381 4.83282 8.63838 4.79312 8.94 4.66H9C9.29577 4.53324 9.54802 4.32276 9.72569 4.05447C9.90337 3.78618 9.99872 3.47179 10 3.15V3C10 2.46957 10.2107 1.96086 10.5858 1.58579C10.9609 1.21071 11.4696 1 12 1C12.5304 1 13.0391 1.21071 13.4142 1.58579C13.7893 1.96086 14 2.46957 14 3V3.09C14.0013 3.41179 14.0966 3.72618 14.2743 3.99447C14.452 4.26276 14.7042 4.47324 15 4.6C15.3016 4.73312 15.6362 4.77282 15.9606 4.714C16.285 4.65519 16.5843 4.50054 16.82 4.27L16.88 4.21C17.0657 4.02405 17.2863 3.87653 17.5291 3.77588C17.7719 3.67523 18.0322 3.62343 18.295 3.62343C18.5578 3.62343 18.8181 3.67523 19.0609 3.77588C19.3037 3.87653 19.5243 4.02405 19.71 4.21C19.896 4.39575 20.0435 4.61632 20.1441 4.85912C20.2448 5.10192 20.2966 5.36217 20.2966 5.625C20.2966 5.88783 20.2448 6.14808 20.1441 6.39088C20.0435 6.63368 19.896 6.85425 19.71 7.04L19.65 7.1C19.4195 7.33568 19.2648 7.63502 19.206 7.95941C19.1472 8.28381 19.1869 8.61838 19.32 8.92V9C19.4468 9.29577 19.6572 9.54802 19.9255 9.72569C20.1938 9.90337 20.5082 9.99872 20.83 10H21C21.5304 10 22.0391 10.2107 22.4142 10.5858C22.7893 10.9609 23 11.4696 23 12C23 12.5304 22.7893 13.0391 22.4142 13.4142C22.0391 13.7893 21.5304 14 21 14H20.91C20.5882 14.0013 20.2738 14.0966 20.0055 14.2743C19.7372 14.452 19.5268 14.7042 19.4 15Z"
|
||||
stroke="currentColor"
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill={active ? "currentColor" : "none"}
|
||||
stroke={active ? "none" : "currentColor"}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
>
|
||||
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.78 1.28a2 2 0 0 0 .73 2.73l.04.02a2 2 0 0 1 .97 1.94V12a2 2 0 0 1-.97 1.94l-.04.02a2 2 0 0 0-.73 2.73l.78 1.28a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.78-1.28a2 2 0 0 0-.73-2.73l-.04-.02a2 2 0 0 1-.97-1.94V12a2 2 0 0 1 .97-1.94l.04-.02a2 2 0 0 0 .73-2.73l-.78-1.28a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const navItems = [
|
||||
{ path: "/home", icon: HomeIcon, label: "Домой" },
|
||||
{ path: "/courses", icon: CoursesIcon, label: "Курсы" },
|
||||
{ path: "/exercise/1", icon: ExerciseIcon, label: "Заниматься" },
|
||||
{ path: "/exercise/1", icon: ExerciseIcon, label: "Тренировка" },
|
||||
{ path: "/settings", icon: SettingsIcon, label: "Меню" },
|
||||
]
|
||||
|
||||
@ -84,7 +90,7 @@ const BottomNavigation: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-white/20 backdrop-blur-xl border-t border-white/10 px-4 py-3 z-50 shadow-2xl">
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] p-4 z-50 shadow-lg">
|
||||
<div className="flex justify-around items-center max-w-md mx-auto">
|
||||
{navItems.map((item) => {
|
||||
const active = isActive(item.path)
|
||||
@ -93,16 +99,33 @@ const BottomNavigation: React.FC = () => {
|
||||
<button
|
||||
key={item.path}
|
||||
onClick={() => history.push(item.path)}
|
||||
className={`flex flex-col items-center py-2 px-4 rounded-2xl transition-all duration-300 transform ${
|
||||
active
|
||||
? "text-white bg-[#2BACBE]/80 backdrop-blur-lg scale-110 shadow-lg"
|
||||
: "text-[#5F5C5C] hover:text-[#2BACBE] hover:bg-white/10 hover:scale-105"
|
||||
}`}
|
||||
className="relative flex flex-col items-center justify-center w-24 h-24 overflow-hidden"
|
||||
>
|
||||
<div className="mb-1 filter drop-shadow-sm">
|
||||
{/* Active state background (glassmorphism rectangle) */}
|
||||
<div
|
||||
className={`absolute glass-morphism rounded-2xl shadow-md
|
||||
transition-all duration-300 ease-out
|
||||
${active ? "opacity-100 scale-100 w-20 h-20 -top-4" : "opacity-0 scale-0 w-0 h-0 top-0"}`}
|
||||
/>
|
||||
|
||||
{/* Icon and Label container */}
|
||||
<div
|
||||
className={`relative z-10 flex flex-col items-center justify-center
|
||||
transition-all duration-300 ease-out
|
||||
${active ? "text-[#2BACBE] translate-y-[-24px]" : "text-white/70 translate-y-0 group-hover:text-white"}`}
|
||||
>
|
||||
<div className="mb-1">
|
||||
<IconComponent active={active} />
|
||||
</div>
|
||||
<span className="text-xs font-semibold tracking-wide">{item.label}</span>
|
||||
<span className="text-xs font-medium">{item.label}</span>
|
||||
</div>
|
||||
|
||||
{/* Bottom circle with glow */}
|
||||
<div
|
||||
className={`absolute w-2 h-2 rounded-full border-2 border-white shadow-lg shadow-[#2BACBE]/50 animate-pulse
|
||||
transition-all duration-300 ease-out
|
||||
${active ? "opacity-100 scale-100 translate-y-[36px]" : "opacity-0 scale-0 translate-y-[0px]"}`}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
|
77
src/components/CircularProgressDisplay.tsx
Normal file
77
src/components/CircularProgressDisplay.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import type React from "react"
|
||||
|
||||
interface CircularProgressDisplayProps {
|
||||
overallProgress: number
|
||||
totalCourses: number
|
||||
totalExercises: number
|
||||
}
|
||||
|
||||
const CircularProgressDisplay: React.FC<CircularProgressDisplayProps> = ({
|
||||
overallProgress,
|
||||
totalCourses,
|
||||
totalExercises,
|
||||
}) => {
|
||||
const radius = 40
|
||||
const circumference = 2 * Math.PI * radius
|
||||
|
||||
// For the "Courses" ring (blue)
|
||||
const coursesProgress = (totalCourses / 5) * 100 // Assuming max 5 courses for visual representation
|
||||
const coursesStrokeDashoffset = circumference - (coursesProgress / 100) * circumference
|
||||
|
||||
// For the "Exercises" ring (green)
|
||||
const exercisesProgress = (totalExercises / 50) * 100 // Assuming max 50 exercises for visual representation
|
||||
const exercisesStrokeDashoffset = circumference - (exercisesProgress / 100) * circumference
|
||||
|
||||
return (
|
||||
<div className="relative w-32 h-32 mx-auto flex items-center justify-center">
|
||||
<svg className="w-full h-full" viewBox="0 0 100 100">
|
||||
{/* Overall Progress Background Circle */}
|
||||
<circle
|
||||
className="text-gray-200"
|
||||
strokeWidth="8"
|
||||
stroke="currentColor"
|
||||
fill="transparent"
|
||||
r={radius}
|
||||
cx="50"
|
||||
cy="50"
|
||||
/>
|
||||
|
||||
{/* Courses Ring (Blue) */}
|
||||
<circle
|
||||
className="text-blue-500"
|
||||
strokeWidth="8"
|
||||
strokeDasharray={circumference}
|
||||
strokeDashoffset={coursesStrokeDashoffset}
|
||||
strokeLinecap="round"
|
||||
stroke="currentColor"
|
||||
fill="transparent"
|
||||
r={radius}
|
||||
cx="50"
|
||||
cy="50"
|
||||
transform="rotate(-90 50 50)"
|
||||
/>
|
||||
|
||||
{/* Exercises Ring (Green) */}
|
||||
<circle
|
||||
className="text-green-500"
|
||||
strokeWidth="8"
|
||||
strokeDasharray={circumference}
|
||||
strokeDashoffset={exercisesStrokeDashoffset}
|
||||
strokeLinecap="round"
|
||||
stroke="currentColor"
|
||||
fill="transparent"
|
||||
r={radius - 10} // Slightly smaller radius for the inner ring
|
||||
cx="50"
|
||||
cy="50"
|
||||
transform="rotate(-90 50 50)"
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute text-center">
|
||||
<div className="text-3xl font-black text-gray-800">{overallProgress}%</div>
|
||||
<div className="text-xs text-gray-600 font-semibold">Общий прогресс</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CircularProgressDisplay
|
247
src/index.css
247
src/index.css
@ -10,7 +10,7 @@
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary: oklch(0.63 0.08 220.5); /* #2BACBE */
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
@ -29,7 +29,7 @@
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--radius: 0.625rem;
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
@ -75,6 +75,8 @@
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
/* optional: --font-sans, --font-serif, --font-mono if they are applied in the layout.tsx */
|
||||
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
@ -122,240 +124,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Custom glass-morphism effect */
|
||||
.glass-morphism {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
||||
}
|
||||
|
||||
.glass-morphism-dark {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
|
||||
}
|
||||
|
||||
.scrollable-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(43, 172, 190, 0.3) transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.animate-float {
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slideUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.8s ease-out;
|
||||
}
|
||||
|
||||
.animate-scale-in {
|
||||
animation: scaleIn 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.animate-bounce-gentle {
|
||||
animation: bounceGentle 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-glow {
|
||||
animation: glow 2s ease-in-out infinite alternate;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-10px) rotate(1deg);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(-5px) rotate(-1deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceGentle {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from {
|
||||
box-shadow: 0 0 20px rgba(43, 172, 190, 0.3);
|
||||
}
|
||||
to {
|
||||
box-shadow: 0 0 30px rgba(43, 172, 190, 0.6);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Enhanced scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(45deg, #2bacbe, #5f5c5c);
|
||||
border-radius: 10px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(45deg, #2bacbe, #2bacbe);
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
/* Smooth transitions for all interactive elements */
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
div {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* Focus styles for accessibility */
|
||||
button:focus,
|
||||
input:focus,
|
||||
select:focus {
|
||||
outline: 2px solid #2bacbe;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Loading animation */
|
||||
.loading-spinner {
|
||||
border: 3px solid rgba(43, 172, 190, 0.1);
|
||||
border-top: 3px solid #2bacbe;
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Backdrop blur support */
|
||||
@supports (backdrop-filter: blur(20px)) {
|
||||
.backdrop-blur-2xl {
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
}
|
||||
|
||||
@supports not (backdrop-filter: blur(20px)) {
|
||||
.backdrop-blur-2xl {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
/* Modern gradient backgrounds */
|
||||
.gradient-mesh {
|
||||
background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced card hover effects */
|
||||
.card-hover {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.card-hover:hover {
|
||||
transform: translateY(-4px) scale(1.02);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Improved button styles */
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
padding: 12px 24px;
|
||||
border-radius: 16px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: translateY(0);
|
||||
background-color: rgba(255, 255, 255, 0.1); /* White with transparency */
|
||||
backdrop-filter: blur(10px); /* Blur effect */
|
||||
-webkit-backdrop-filter: blur(10px); /* For Safari */
|
||||
border: 1px solid rgba(255, 255, 255, 0.2); /* Subtle white border */
|
||||
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); /* Shadow */
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { useHistory, useParams } from "react-router-dom"
|
||||
import BottomNavigation from "../components/BottomNavigation"
|
||||
// Удален импорт BottomNavigation
|
||||
|
||||
const Exercise: React.FC = () => {
|
||||
const history = useHistory()
|
||||
@ -15,7 +14,7 @@ const Exercise: React.FC = () => {
|
||||
const [totalSets] = useState(3)
|
||||
|
||||
useEffect(() => {
|
||||
let interval: NodeJS.Timeout
|
||||
let interval: NodeJS.Timeout | undefined
|
||||
if (isPlaying) {
|
||||
interval = setInterval(() => {
|
||||
setCurrentTime((prev) => {
|
||||
@ -29,7 +28,9 @@ const Exercise: React.FC = () => {
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
return () => clearInterval(interval)
|
||||
return () => {
|
||||
if (interval) clearInterval(interval)
|
||||
}
|
||||
}, [isPlaying, totalTime, history])
|
||||
|
||||
const formatTime = (seconds: number) => {
|
||||
@ -40,62 +41,14 @@ const Exercise: React.FC = () => {
|
||||
|
||||
const progress = (currentTime / totalTime) * 100
|
||||
|
||||
const exerciseSteps = [
|
||||
{
|
||||
title: "Исходное положение",
|
||||
description:
|
||||
"Лягте на спину на коврик. Руки вдоль тела, ладони прижаты к полу. Ноги выпрямлены, носки направлены вверх. Поясница плотно прижата к полу.",
|
||||
icon: "1️⃣",
|
||||
color: "from-blue-400 to-cyan-500",
|
||||
},
|
||||
{
|
||||
title: "Задание",
|
||||
description:
|
||||
"Медленно поднимите прямые ноги до угла 90 градусов. Задержитесь на 2 секунды, затем медленно опустите ноги, не касаясь пола. Повторите движение плавно и контролируемо.",
|
||||
icon: "2️⃣",
|
||||
color: "from-emerald-400 to-green-500",
|
||||
},
|
||||
{
|
||||
title: "Подходы",
|
||||
description: "Выполните 3 подхода по 12 повторений с отдыхом 60 секунд между подходами.",
|
||||
icon: "3️⃣",
|
||||
color: "from-purple-400 to-pink-500",
|
||||
},
|
||||
{
|
||||
title: "Перерыв",
|
||||
description: "Отдыхайте 60 секунд между подходами. Дышите спокойно и расслабьте мышцы.",
|
||||
icon: "4️⃣",
|
||||
color: "from-orange-400 to-red-500",
|
||||
},
|
||||
{
|
||||
title: "Динамика и статика",
|
||||
description:
|
||||
"Динамическая фаза: подъем и опускание ног выполняется плавно, 2 секунды вверх, 2 секунды вниз. Статическая фаза: удержание ног в верхней точке на 2 секунды.",
|
||||
icon: "5️⃣",
|
||||
color: "from-indigo-400 to-purple-500",
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 overflow-y-auto scrollable-content">
|
||||
{/* Header */}
|
||||
<div className="sticky top-0 z-40 bg-gradient-to-br from-[#2BACBE] via-blue-500 to-indigo-600 backdrop-blur-xl">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/10 to-transparent"></div>
|
||||
<div className="relative px-4 sm:px-6 pt-12 pb-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<button
|
||||
onClick={() => history.goBack()}
|
||||
className="w-12 h-12 bg-white/20 backdrop-blur-xl rounded-2xl flex items-center justify-center border border-white/30 hover:bg-white/30 transition-all duration-300 shadow-lg"
|
||||
>
|
||||
// SVG Icons
|
||||
const BackIcon = () => (
|
||||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<h1 className="text-lg sm:text-xl font-black text-white">Подъемы ног лежа</h1>
|
||||
<p className="text-blue-100 text-sm font-medium">Упражнение 1 из 12</p>
|
||||
</div>
|
||||
<div className="w-12 h-12 bg-white/20 backdrop-blur-xl rounded-2xl flex items-center justify-center border border-white/30 shadow-lg">
|
||||
)
|
||||
|
||||
const HeartIcon = () => (
|
||||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
@ -104,18 +57,96 @@ const Exercise: React.FC = () => {
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const PlayIcon = () => (
|
||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const PauseIcon = () => (
|
||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const RefreshIcon = () => (
|
||||
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11.356-1.913a9.75 9.75 0 00-1.036-1.388C18.29 4.71 16.05 3 12 3c-4.05 0-6.29 1.71-7.778 3.702M4 12a8.001 8.001 0 0015.356 2m0 0H15m-11.356 2A9.75 9.75 0 004.582 15m0 0H9m11.356-1.913a9.75 9.75 0 00-1.036-1.388C18.29 4.71 16.05 3 12 3c-4.05 0-6.29 1.71-7.778 3.702"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const CheckIcon = () => (
|
||||
<svg className="w-6 h-6 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>
|
||||
)
|
||||
|
||||
const exerciseSteps = [
|
||||
{
|
||||
title: "Исходное положение",
|
||||
description:
|
||||
"Лягте на спирку на коврик. Руки вдоль тела, ладони прижаты к полу. Ноги выпрямлены, носки направлены вверх. Поясница плотно прижата к полу.",
|
||||
icon: "1",
|
||||
},
|
||||
{
|
||||
title: "Задание",
|
||||
description:
|
||||
"Медленно поднимите прямые ноги до угла 90 градусов. Задержитесь на 2 секунды, затем медленно опустите ноги, не касаясь пола. Повторите движение плавно и контролируемо.",
|
||||
icon: "2",
|
||||
},
|
||||
{
|
||||
title: "Подходы",
|
||||
description: "Выполните 3 подхода по 12 повторений с отдыхом 60 секунд между подходами.",
|
||||
icon: "3",
|
||||
},
|
||||
{
|
||||
title: "Перерыв",
|
||||
description: "Отдыхайте 60 секунд между подходами. Дышите спокойно и расслабьте мышцы.",
|
||||
icon: "4",
|
||||
},
|
||||
{
|
||||
title: "Динамика и статика",
|
||||
description:
|
||||
"Динамическая фаза: подъем и опускание ног выполняется плавно, 2 секунды вверх, 2 секунды вниз. Статическая фаза: удержание ног в верхней точке на 2 секунды.",
|
||||
icon: "5",
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] overflow-y-auto pb-28">
|
||||
{/* Header */}
|
||||
<div className="sticky top-0 z-40 bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] backdrop-blur-xl pt-12 pb-4 px-4 sm:px-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<button
|
||||
onClick={() => history.goBack()}
|
||||
className="w-12 h-12 glass-morphism rounded-2xl flex items-center justify-center border border-white/30 hover:bg-white/30 transition-all duration-300 shadow-lg"
|
||||
>
|
||||
<BackIcon />
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<h1 className="text-lg sm:text-xl font-black text-white">Подъемы ног лежа</h1>
|
||||
<p className="text-white/80 text-sm font-medium">Упражнение 1 из 12</p>
|
||||
</div>
|
||||
<div className="w-12 h-12 glass-morphism rounded-2xl flex items-center justify-center border border-white/30 shadow-lg">
|
||||
<HeartIcon />
|
||||
</div>
|
||||
</div>
|
||||
{/* Set Counter */}
|
||||
<div className="flex justify-center space-x-2">
|
||||
<div className="flex justify-center space-x-2 mt-4">
|
||||
{Array.from({ length: totalSets }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm transition-all duration-300 ${
|
||||
index + 1 <= currentSet
|
||||
? "bg-white text-[#2BACBE] shadow-lg"
|
||||
: "bg-white/20 text-white border border-white/30"
|
||||
? "bg-white text-[#2BACBE] shadow-lg scale-110 ring-2 ring-white"
|
||||
: "bg-gray-100 text-gray-600 border border-gray-200"
|
||||
}`}
|
||||
>
|
||||
{index + 1}
|
||||
@ -123,11 +154,9 @@ const Exercise: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Video/Image Section */}
|
||||
<div className="px-4 sm:px-6 -mt-4 mb-6">
|
||||
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/50 backdrop-blur-2xl">
|
||||
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/20 backdrop-blur-2xl">
|
||||
<div className="relative">
|
||||
<img
|
||||
src="/placeholder.svg?height=250&width=400"
|
||||
@ -140,10 +169,9 @@ const Exercise: React.FC = () => {
|
||||
onClick={() => setIsPlaying(!isPlaying)}
|
||||
className="w-20 h-20 bg-white/90 backdrop-blur-xl rounded-full flex items-center justify-center shadow-2xl hover:bg-white transition-all duration-300 transform hover:scale-110 border border-white/50"
|
||||
>
|
||||
<span className="text-3xl text-gray-800 ml-1">{isPlaying ? "⏸️" : "▶️"}</span>
|
||||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Live indicators */}
|
||||
{isPlaying && (
|
||||
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
||||
@ -153,47 +181,44 @@ const Exercise: React.FC = () => {
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Timer Display */}
|
||||
<div className="absolute top-4 right-4 bg-black/30 backdrop-blur-sm px-3 py-1 rounded-full">
|
||||
<span className="text-white text-sm font-bold">{formatTime(currentTime)}</span>
|
||||
<div className="absolute top-4 right-4 bg-white/90 backdrop-blur-sm px-3 py-1 rounded-xl">
|
||||
<span className="text-gray-800 text-sm font-bold">{formatTime(currentTime)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Exercise Steps */}
|
||||
{/* Exercise Steps - Vertical Scroll */}
|
||||
<div className="px-4 sm:px-6 space-y-4 mb-6">
|
||||
{exerciseSteps.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="glass-morphism rounded-2xl p-5 border border-white/50 shadow-lg hover:shadow-xl transition-all duration-300 backdrop-blur-xl"
|
||||
className="bg-white rounded-2xl p-5 border border-gray-200 shadow-lg hover:shadow-xl transition-all duration-300"
|
||||
>
|
||||
<div className="flex items-start space-x-4">
|
||||
<div
|
||||
className={`w-12 h-12 bg-gradient-to-r ${step.color} rounded-xl flex items-center justify-center shadow-lg flex-shrink-0`}
|
||||
className={`w-12 h-12 bg-gray-100 rounded-xl flex items-center justify-center shadow-sm flex-shrink-0 text-[#2BACBE] font-black text-xl border border-gray-200`}
|
||||
>
|
||||
<span className="text-lg">{step.icon}</span>
|
||||
{step.icon}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-black text-gray-800 mb-2">{step.title}</h3>
|
||||
<p className="text-gray-700 leading-relaxed text-sm sm:text-base">{step.description}</p>
|
||||
<p className="text-gray-600 leading-relaxed text-sm sm:text-base">{step.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Important Notes */}
|
||||
<div className="px-4 sm:px-6 mb-32">
|
||||
<div className="bg-gradient-to-r from-amber-50 to-orange-50 backdrop-blur-xl rounded-2xl p-5 border border-amber-200/50 shadow-lg">
|
||||
<div className="bg-white rounded-2xl p-5 border border-gray-200 shadow-lg">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-8 h-8 bg-gradient-to-r from-amber-400 to-orange-500 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-white font-bold text-sm">!</span>
|
||||
<div className="w-8 h-8 bg-amber-400/20 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-amber-400 font-bold text-sm">!</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-black text-amber-800 mb-2">Важные замечания</h3>
|
||||
<ul className="space-y-1 text-amber-800 text-sm">
|
||||
<h3 className="text-lg font-black text-gray-800 mb-2">Важные замечания</h3>
|
||||
<ul className="space-y-1 text-gray-600 text-sm">
|
||||
<li>• Не отрывайте поясницу от пола</li>
|
||||
<li>• Дышите равномерно, не задерживайте дыхание</li>
|
||||
<li>• При болевых ощущениях немедленно прекратите</li>
|
||||
@ -203,60 +228,63 @@ const Exercise: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Fixed Timer at Bottom */}
|
||||
<div className="fixed bottom-20 left-0 right-0 bg-white/95 backdrop-blur-2xl border-t border-gray-200/50 px-4 sm:px-6 py-4 shadow-2xl z-30">
|
||||
<div className="fixed bottom-0 left-0 right-0 glass-morphism border-t border-white/20 px-4 sm:px-6 py-4 shadow-2xl z-30">
|
||||
<div className="max-w-md mx-auto">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span className="text-sm font-bold text-gray-600">
|
||||
<span className="text-sm font-bold text-gray-700">
|
||||
Подход {currentSet} из {totalSets}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm font-black text-gray-800">
|
||||
<span className="text-sm font-black text-gray-700">
|
||||
{formatTime(currentTime)} / {formatTime(totalTime)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-200 rounded-full h-3 mb-4 overflow-hidden">
|
||||
<div className="bg-white/30 rounded-full h-3 mb-4 overflow-hidden">
|
||||
<div
|
||||
className="bg-gradient-to-r from-[#2BACBE] via-blue-500 to-indigo-600 h-3 rounded-full transition-all duration-1000 shadow-sm"
|
||||
style={{ width: `${progress}%` }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-3">
|
||||
<button
|
||||
onClick={() => setIsPlaying(!isPlaying)}
|
||||
className={`flex-1 font-bold py-3 px-4 rounded-xl transition-all duration-300 transform hover:scale-105 ${
|
||||
className={`flex-1 font-bold py-3 px-4 rounded-xl transition-all duration-300 transform hover:scale-105 flex items-center justify-center space-x-2 ${
|
||||
isPlaying
|
||||
? "bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white shadow-lg"
|
||||
: "bg-gradient-to-r from-[#2BACBE] via-blue-500 to-indigo-600 hover:shadow-xl text-white"
|
||||
: "bg-[#2BACBE] hover:bg-[#2099A8] text-white shadow-lg"
|
||||
}`}
|
||||
>
|
||||
{isPlaying ? "⏸️ Пауза" : "▶️ Начать"}
|
||||
{isPlaying ? (
|
||||
<>
|
||||
<PauseIcon /> <span>Пауза</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlayIcon /> <span>Начать</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentTime(0)
|
||||
setIsPlaying(false)
|
||||
}}
|
||||
className="px-6 py-3 bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold rounded-xl transition-all duration-300 hover:shadow-lg"
|
||||
className="px-6 py-3 bg-white text-gray-800 font-bold rounded-xl transition-all duration-300 hover:shadow-lg border border-gray-200 flex items-center justify-center"
|
||||
>
|
||||
🔄
|
||||
<RefreshIcon />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentSet((prev) => Math.min(prev + 1, totalSets))}
|
||||
className="px-6 py-3 bg-gradient-to-r from-emerald-500 to-green-600 hover:from-emerald-600 hover:to-green-700 text-white font-bold rounded-xl transition-all duration-300 hover:shadow-lg"
|
||||
className="px-6 py-3 bg-white text-gray-800 font-bold rounded-xl transition-all duration-300 hover:shadow-lg border border-gray-200 flex items-center justify-center"
|
||||
>
|
||||
✅
|
||||
<CheckIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BottomNavigation />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useState } from "react"
|
||||
import { useHistory } from "react-router-dom"
|
||||
@ -20,18 +19,22 @@ const ForgotPassword: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="w-full max-w-md relative">
|
||||
{/* Back Button */}
|
||||
<button
|
||||
onClick={() => history.goBack()}
|
||||
className="absolute top-6 left-6 w-12 h-12 bg-white/10 backdrop-blur-lg rounded-xl flex items-center justify-center border border-white/20 text-white hover:bg-white/20 transition-all"
|
||||
className="absolute top-0 left-0 w-12 h-12 bg-white/10 backdrop-blur-lg rounded-xl flex items-center justify-center border border-white/20 text-white hover:bg-white/20 transition-all"
|
||||
aria-label="Go back"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Header */}
|
||||
<div className="text-center mb-8 mt-16">
|
||||
{" "}
|
||||
{/* Added mt-16 to push content down from back button */}
|
||||
<div className="w-20 h-20 bg-white/10 backdrop-blur-2xl rounded-2xl flex items-center justify-center mx-auto mb-4 border border-white/20 shadow-2xl">
|
||||
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
@ -54,18 +57,21 @@ const ForgotPassword: React.FC = () => {
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Email Input */}
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Электронная почта</label>
|
||||
<label htmlFor="email" className="block text-gray-700 text-sm font-semibold mb-2 sr-only">
|
||||
Электронная почта
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="example@mail.com"
|
||||
className="w-full bg-white rounded-xl px-4 py-3 text-gray-800 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#2BACBE] focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="Электронная почта"
|
||||
required
|
||||
/>
|
||||
<svg
|
||||
className="absolute right-3 top-3.5 w-5 h-5 text-white/60"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@ -79,11 +85,10 @@ const ForgotPassword: React.FC = () => {
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-white/20 backdrop-blur-lg hover:bg-white/30 text-white font-bold py-3 px-6 rounded-2xl border border-white/30 transition-all duration-300 transform hover:scale-105 shadow-lg"
|
||||
className="w-full bg-[#2BACBE] hover:bg-[#2099A8] text-white font-bold py-3 px-6 rounded-xl border border-[#2BACBE] transition-all duration-300 transform hover:scale-105 shadow-lg"
|
||||
>
|
||||
Отправить ссылку
|
||||
</button>
|
||||
@ -96,21 +101,16 @@ const ForgotPassword: React.FC = () => {
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white mb-2">Письмо отправлено!</h3>
|
||||
<p className="text-white/80 text-sm leading-relaxed">
|
||||
<h3 className="text-xl font-bold text-gray-800 mb-2">Письмо отправлено!</h3>
|
||||
<p className="text-gray-700/80 text-sm leading-relaxed">
|
||||
Мы отправили инструкции по восстановлению пароля на адрес{" "}
|
||||
<span className="font-semibold">{email}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<p className="text-white/60 text-xs mb-4">Автоматический переход через 3 секунды...</p>
|
||||
<button
|
||||
onClick={() => history.push("/login")}
|
||||
className="text-white/80 text-sm font-medium hover:text-white transition-colors"
|
||||
>
|
||||
<p className="text-gray-700/60 text-xs mb-4">Автоматический переход через 3 секунды...</p>
|
||||
<button onClick={() => history.push("/login")} className="text-gray-700 font-semibold hover:underline">
|
||||
Вернуться к входу
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,11 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useHistory } from "react-router-dom"
|
||||
import BottomNavigation from "../components/BottomNavigation"
|
||||
import CircularProgressDisplay from "../components/CircularProgressDisplay"
|
||||
|
||||
const Home: React.FC = () => {
|
||||
export default function Home() {
|
||||
const history = useHistory()
|
||||
const [currentDate, setCurrentDate] = useState("")
|
||||
|
||||
@ -21,10 +20,89 @@ const Home: React.FC = () => {
|
||||
}, [])
|
||||
|
||||
const stats = [
|
||||
{ value: "12", label: "Дней подряд", icon: "🔥", color: "from-orange-400 to-red-500" },
|
||||
{ value: "85%", label: "Прогресс", icon: "📈", color: "from-emerald-400 to-green-500" },
|
||||
{ value: "3", label: "Курса", icon: "📚", color: "from-blue-400 to-cyan-500" },
|
||||
{ value: "45", label: "Минут", icon: "⏱️", color: "from-purple-400 to-pink-500" },
|
||||
{
|
||||
value: "12",
|
||||
label: "Дней подряд",
|
||||
color: "from-orange-400 to-red-500",
|
||||
svg: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34" />
|
||||
<polygon points="18 2 22 6 12 16 8 12 18 2" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "85%",
|
||||
label: "Прогресс",
|
||||
color: "from-emerald-400 to-green-500",
|
||||
svg: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M3 3v18h18" />
|
||||
<path d="M18.7 8.3L12 15L7.1 10.1" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "Курса",
|
||||
color: "from-blue-400 to-cyan-500",
|
||||
svg: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "45",
|
||||
label: "Минут",
|
||||
color: "from-purple-400 to-pink-500",
|
||||
svg: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<polyline points="12 6 12 12 16 14" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const courses = [
|
||||
@ -57,120 +135,110 @@ const Home: React.FC = () => {
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 overflow-y-auto scrollable-content">
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] pt-12 pb-8 px-4 sm:px-6 relative overflow-hidden">
|
||||
{/* Background Pattern */}
|
||||
<div className="absolute inset-0 opacity-10">
|
||||
<div className="absolute top-10 left-10 w-20 h-20 bg-white rounded-full animate-pulse"></div>
|
||||
<div className="absolute bottom-10 right-10 w-16 h-16 bg-white rounded-full animate-bounce"></div>
|
||||
<div className="absolute top-1/2 right-1/4 w-12 h-12 bg-white rounded-full animate-ping"></div>
|
||||
<div className="absolute top-1/4 left-1/3 w-8 h-8 bg-white rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
// Calculate overall progress, total courses, total exercises
|
||||
const totalCourses = courses.length
|
||||
const totalExercises = courses.reduce((sum, course) => sum + course.exercises, 0)
|
||||
const overallProgress = Math.round(courses.reduce((sum, course) => sum + course.progress, 0) / totalCourses)
|
||||
|
||||
<div className="relative z-10">
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 overflow-y-auto pb-28">
|
||||
{/* Header */}
|
||||
<div className="bg-white rounded-b-3xl p-6 shadow-lg mb-6 mx-4 sm:mx-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 className="text-2xl sm:text-3xl font-black text-white mb-2">Привет, Александр! 👋</h1>
|
||||
<p className="text-white/80 font-medium text-sm sm:text-base">{currentDate}</p>
|
||||
<h1 className="text-2xl sm:text-3xl font-black text-gray-800 mb-2">Привет, Александр!</h1>
|
||||
</div>
|
||||
<div className="w-16 h-16 bg-white/20 backdrop-blur-xl rounded-2xl flex items-center justify-center border border-white/30 shadow-xl relative">
|
||||
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
<div className="text-right">
|
||||
<p className="text-gray-600 font-medium text-sm sm:text-base">{currentDate}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Circular Progress Display */}
|
||||
<div className="flex justify-center items-center gap-8 mt-8">
|
||||
<CircularProgressDisplay
|
||||
overallProgress={overallProgress}
|
||||
totalCourses={totalCourses}
|
||||
totalExercises={totalExercises}
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute -top-1 -right-1 w-5 h-5 bg-green-400 rounded-full border-2 border-white animate-pulse"></div>
|
||||
</div>
|
||||
<div className="flex justify-around mt-4">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-gray-800">{totalCourses}</div>
|
||||
<div className="text-xs text-gray-600">Курсов</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-2 gap-3 sm:gap-4">
|
||||
{stats.map((stat, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-white/15 backdrop-blur-xl rounded-2xl p-4 border border-white/20 shadow-lg hover:bg-white/20 transition-all duration-300 transform hover:scale-105"
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-r ${stat.color} rounded-xl flex items-center justify-center shadow-lg`}
|
||||
>
|
||||
<span className="text-lg sm:text-xl">{stat.icon}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xl sm:text-2xl font-black text-white">{stat.value}</div>
|
||||
<div className="text-white/70 text-xs sm:text-sm font-medium">{stat.label}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-gray-800">{totalExercises}</div>
|
||||
<div className="text-xs text-gray-600">Упражнений</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-4 sm:px-6 space-y-8 -mt-4 pb-28">
|
||||
<div className="px-4 sm:px-6 space-y-6">
|
||||
{/* Current Exercise */}
|
||||
<div className="glass-morphism rounded-3xl p-6 border border-white/50 shadow-2xl backdrop-blur-2xl">
|
||||
<div className="bg-[#2BACBE] rounded-3xl p-6 shadow-2xl text-white">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl sm:text-2xl font-black text-gray-800">Текущее упражнение</h2>
|
||||
<div className="px-3 py-1 bg-gradient-to-r from-green-400 to-emerald-500 rounded-full shadow-lg">
|
||||
<h2 className="text-xl sm:text-2xl font-black">Тренировка</h2> {/* Changed from "Текущее упражнение" */}
|
||||
<div className="px-3 py-1 bg-white/20 rounded-full shadow-lg">
|
||||
<span className="text-white text-sm font-bold">Активно</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="relative">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-[#2BACBE] via-blue-500 to-indigo-600 rounded-2xl flex items-center justify-center shadow-xl">
|
||||
<span className="text-3xl">🦵</span>
|
||||
<div className="w-20 h-20 bg-white/20 rounded-2xl flex items-center justify-center shadow-xl">
|
||||
{/* Clock icon SVG */}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="text-white"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<polyline points="12 6 12 12 16 14" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-gradient-to-r from-orange-400 to-red-500 rounded-full flex items-center justify-center shadow-lg animate-pulse">
|
||||
<svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-white rounded-full flex items-center justify-center shadow-lg animate-pulse">
|
||||
<svg className="w-3 h-3 text-[#2BACBE]" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-black text-gray-800 text-lg mb-1">Подъемы ног лежа</h3>
|
||||
<p className="text-gray-600 font-medium mb-3 text-sm">Восстановление колена • 3 подхода по 12</p>
|
||||
|
||||
<h3 className="font-black text-lg mb-1">Подъемы ног лежа</h3>
|
||||
<p className="text-white/80 font-medium mb-3 text-sm">Восстановление колена • 3 подхода по 12</p>
|
||||
<div className="relative">
|
||||
<div className="bg-gray-200 rounded-full h-3 overflow-hidden">
|
||||
<div className="bg-white/30 rounded-full h-3 overflow-hidden">
|
||||
<div
|
||||
className="bg-gradient-to-r from-[#2BACBE] via-blue-500 to-indigo-600 h-3 rounded-full shadow-sm transition-all duration-700"
|
||||
className="bg-white h-3 rounded-full shadow-sm transition-all duration-700"
|
||||
style={{ width: "60%" }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="absolute right-0 -top-6 text-sm font-bold text-gray-600">60%</div>
|
||||
<div className="absolute right-0 -top-6 text-sm font-bold text-white/80">60%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => history.push("/exercise/1")}
|
||||
className="bg-gradient-to-r from-[#2BACBE] via-blue-500 to-indigo-600 text-white px-4 sm:px-6 py-3 rounded-2xl font-bold hover:shadow-xl transition-all duration-300 transform hover:scale-105 backdrop-blur-sm text-sm sm:text-base"
|
||||
className="bg-white text-[#2BACBE] px-4 sm:px-6 py-3 rounded-2xl font-bold hover:shadow-xl transition-all duration-300 transform hover:scale-105 backdrop-blur-sm text-sm sm:text-base"
|
||||
>
|
||||
Продолжить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{/* Quick Stats (Total Exercises & Total Courses) */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="glass-morphism rounded-2xl p-4 text-center border border-white/50 shadow-lg backdrop-blur-xl">
|
||||
<div className="text-2xl font-black text-[#2BACBE] mb-1">24</div>
|
||||
<div className="text-xs text-gray-600 font-semibold">Упражнений</div>
|
||||
<div className="text-2xl font-black text-gray-800 mb-1">{totalExercises}</div>
|
||||
<div className="text-xs text-gray-600 font-semibold">Всего упражнений</div>
|
||||
</div>
|
||||
<div className="glass-morphism rounded-2xl p-4 text-center border border-white/50 shadow-lg backdrop-blur-xl">
|
||||
<div className="text-2xl font-black text-emerald-500 mb-1">180</div>
|
||||
<div className="text-xs text-gray-600 font-semibold">Минут</div>
|
||||
</div>
|
||||
<div className="glass-morphism rounded-2xl p-4 text-center border border-white/50 shadow-lg backdrop-blur-xl">
|
||||
<div className="text-2xl font-black text-purple-500 mb-1">7</div>
|
||||
<div className="text-xs text-gray-600 font-semibold">Дней</div>
|
||||
<div className="text-2xl font-black text-gray-800 mb-1">{totalCourses}</div>
|
||||
<div className="text-xs text-gray-600 font-semibold">Всего курсов</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -188,7 +256,6 @@ const Home: React.FC = () => {
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{courses.map((course) => (
|
||||
<div
|
||||
@ -227,10 +294,7 @@ const Home: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BottomNavigation />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
|
@ -3,143 +3,362 @@
|
||||
import type React from "react"
|
||||
import { useState } from "react"
|
||||
import { useHistory } from "react-router-dom"
|
||||
import manImage from "../assets/man.svg" // Reverted to original import
|
||||
|
||||
const Login: React.FC = () => {
|
||||
export default function LoginPage() {
|
||||
const history = useHistory()
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [rememberMe, setRememberMe] = useState(false)
|
||||
const [activeTab, setActiveTab] = useState<"login" | "register">("login")
|
||||
|
||||
// State and handlers for the registration form
|
||||
const [registerFormData, setRegisterFormData] = useState({
|
||||
email: "", // Removed firstName, lastName
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
})
|
||||
const [showRegisterPassword, setShowRegisterPassword] = useState(false)
|
||||
const [showConfirmRegisterPassword, setShowConfirmRegisterPassword] = useState(false)
|
||||
|
||||
const handleLogin = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
// Simulate login
|
||||
console.log("Logging in with:", { email, password, rememberMe })
|
||||
history.push("/home")
|
||||
}
|
||||
|
||||
const handleRegisterSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (registerFormData.password !== registerFormData.confirmPassword) {
|
||||
alert("Пароли не совпадают")
|
||||
return
|
||||
}
|
||||
// Simulate registration
|
||||
console.log("Registering with:", registerFormData)
|
||||
history.push("/home")
|
||||
}
|
||||
|
||||
const handleRegisterInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setRegisterFormData({
|
||||
...registerFormData,
|
||||
[e.target.name]: e.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="w-20 h-20 bg-white/10 backdrop-blur-2xl rounded-2xl flex items-center justify-center mx-auto mb-4 border border-white/20 shadow-2xl">
|
||||
<svg width="40" height="40" viewBox="0 0 100 100" fill="none" className="text-white">
|
||||
<circle cx="50" cy="20" r="8" fill="currentColor" />
|
||||
<path
|
||||
d="M50 28 L50 60 M50 45 L35 35 M50 45 L65 35 M50 60 L35 80 M50 60 L65 80"
|
||||
stroke="currentColor"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<div className="bg-white h-[55%] w-full z-0 absolute bottom-0"></div>
|
||||
<div className="relative z-10 w-full max-w-md">
|
||||
{/* Header Section */}
|
||||
<div className="flex items-center justify-center gap-4 mb-8">
|
||||
<img className="h-20 w-auto flex-shrink-0" src={manImage || "/placeholder.svg"} alt="man" />
|
||||
<div className="flex flex-col text-white">
|
||||
<h1 className="text-4xl font-bold mb-1">Реабилитация</h1>
|
||||
<div className="w-full bg-white h-0.5 mb-1"></div>
|
||||
<p className="font-medium text-lg">Восстановление после травмы</p>
|
||||
</div>
|
||||
<h1 className="text-3xl font-black text-white mb-2">Добро пожаловать!</h1>
|
||||
<p className="text-white/80 font-medium">Войдите в свой аккаунт</p>
|
||||
</div>
|
||||
|
||||
{/* Login Form */}
|
||||
{/* Login/Register Form Container */}
|
||||
<div className="glass-morphism rounded-3xl p-8 border border-white/20 shadow-2xl">
|
||||
{/* Tab Buttons Container */}
|
||||
<div className="flex justify-center gap-4 mb-8 bg-white/10 backdrop-blur-lg rounded-xl px-4 py-2 shadow-inner">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab("login")}
|
||||
className={`flex-1 px-8 py-3 rounded-xl text-lg font-semibold transition-all duration-300 ${
|
||||
activeTab === "login"
|
||||
? "bg-white text-gray-800 shadow-lg"
|
||||
: "text-white/80 hover:text-white hover:bg-white/10"
|
||||
}`}
|
||||
>
|
||||
Вход
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab("register")}
|
||||
className={`flex-1 px-8 py-3 rounded-xl text-lg font-semibold transition-all duration-300 ${
|
||||
activeTab === "register"
|
||||
? "bg-white text-gray-800 shadow-lg"
|
||||
: "text-white/80 hover:text-white hover:bg-white/10"
|
||||
}`}
|
||||
>
|
||||
Регистрация
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{activeTab === "login" && (
|
||||
<form onSubmit={handleLogin} className="space-y-6">
|
||||
<p className="text-gray-700 text-center text-lg font-medium mb-4">Введите логин и пароль</p>
|
||||
{/* Email Input */}
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Электронная почта</label>
|
||||
<label htmlFor="email" className="block text-gray-700 text-sm font-semibold mb-2 sr-only">
|
||||
Электронная почта
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="example@mail.com"
|
||||
className="w-full bg-white rounded-xl px-4 py-3 text-gray-800 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#2BACBE] focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="Электронная почта"
|
||||
required
|
||||
/>
|
||||
<svg
|
||||
className="absolute right-3 top-3.5 w-5 h-5 text-white/60"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Password Input */}
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Пароль</label>
|
||||
<label htmlFor="password" className="block text-gray-700/90 text-sm font-semibold mb-2 sr-only">
|
||||
Пароль
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="••••••••"
|
||||
className="w-full bg-white rounded-xl px-4 py-3 text-gray-800 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#2BACBE] focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="Пароль"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-3.5 text-white/60 hover:text-white transition-colors"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors"
|
||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword ? (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v3"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Forgot Password */}
|
||||
<div className="text-right">
|
||||
{/* Remember Me & Forgot Password */}
|
||||
<div className="flex justify-between items-center text-sm mt-4">
|
||||
<label htmlFor="remember-me" className="flex items-center text-gray-700/80 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="remember-me"
|
||||
checked={rememberMe}
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
className="mr-2 h-4 w-4 text-[#2BACBE] rounded border-gray-300 focus:ring-[#2BACBE] accent-[#2BACBE]"
|
||||
/>
|
||||
Запомнить
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => history.push("/forgot-password")}
|
||||
className="text-white/80 text-sm font-medium hover:text-white transition-colors"
|
||||
className="text-[#2BACBE] font-medium hover:text-[#2099A8] transition-colors"
|
||||
>
|
||||
Забыли пароль?
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Login Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-white/20 backdrop-blur-lg hover:bg-white/30 text-white font-bold py-3 px-6 rounded-2xl border border-white/30 transition-all duration-300 transform hover:scale-105 shadow-lg"
|
||||
className="w-full bg-[#2BACBE] hover:bg-[#2099A8] text-white font-bold py-3 px-6 rounded-xl border border-[#2BACBE] transition-all duration-300 transform hover:scale-105 shadow-lg mt-6"
|
||||
>
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
{/* Register Link */}
|
||||
<div className="text-center mt-6">
|
||||
<p className="text-white/80 text-sm">
|
||||
Нет аккаунта?{" "}
|
||||
<button onClick={() => history.push("/register")} className="text-white font-semibold hover:underline">
|
||||
{activeTab === "register" && (
|
||||
<form onSubmit={handleRegisterSubmit} className="space-y-4">
|
||||
<p className="text-gray-700 text-center text-lg font-medium mb-4">Создайте новый аккаунт</p>
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label htmlFor="registerEmail" className="block text-gray-700 text-sm font-semibold mb-2 sr-only">
|
||||
Электронная почта
|
||||
</label>
|
||||
<input
|
||||
id="registerEmail"
|
||||
type="email"
|
||||
name="email"
|
||||
value={registerFormData.email}
|
||||
onChange={handleRegisterInputChange}
|
||||
className="w-full bg-white rounded-xl px-4 py-3 text-gray-800 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#2BACBE] focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="Электронная почта"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{/* Password */}
|
||||
<div>
|
||||
<label htmlFor="registerPassword" className="block text-gray-700/90 text-sm font-semibold mb-2 sr-only">
|
||||
Пароль
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="registerPassword"
|
||||
type={showRegisterPassword ? "text" : "password"}
|
||||
name="password"
|
||||
value={registerFormData.password}
|
||||
onChange={handleRegisterInputChange}
|
||||
className="w-full bg-white rounded-xl px-4 py-3 text-gray-800 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#2BACBE] focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="Пароль"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowRegisterPassword(!showRegisterPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors"
|
||||
aria-label={showRegisterPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showRegisterPassword ? (
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v3"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Confirm Password */}
|
||||
<div>
|
||||
<label htmlFor="confirmPassword" className="block text-gray-700/90 text-sm font-semibold mb-2 sr-only">
|
||||
Подтвердите пароль
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="confirmPassword"
|
||||
type={showConfirmRegisterPassword ? "text" : "password"}
|
||||
name="confirmPassword"
|
||||
value={registerFormData.confirmPassword}
|
||||
onChange={handleRegisterInputChange}
|
||||
className="w-full bg-white rounded-xl px-4 py-3 text-gray-800 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#2BACBE] focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="Подтвердите пароль"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirmRegisterPassword(!showConfirmRegisterPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors"
|
||||
aria-label={showConfirmRegisterPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showConfirmRegisterPassword ? (
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v3"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Register Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-[#2BACBE] hover:bg-[#2099A8] text-white font-bold py-3 px-6 rounded-xl border border-[#2BACBE] transition-all duration-300 transform hover:scale-105 shadow-lg mt-6"
|
||||
>
|
||||
Зарегистрироваться
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
{/* Login/Register Link (always visible) */}
|
||||
<div className="text-center mt-6">
|
||||
<p className="text-gray-700/80 text-sm">
|
||||
{activeTab === "login" ? "Нет аккаунта?" : "Уже есть аккаунт?"}{" "}
|
||||
<button
|
||||
onClick={() => setActiveTab(activeTab === "login" ? "register" : "login")} // Toggle tab instead of pushing to new route
|
||||
className="text-gray-700 font-semibold hover:underline"
|
||||
>
|
||||
{activeTab === "login" ? "Зарегистрироваться" : "Войти"}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -147,5 +366,3 @@ const Login: React.FC = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
|
@ -1,228 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useState } from "react"
|
||||
import { useHistory } from "react-router-dom"
|
||||
|
||||
const Register: React.FC = () => {
|
||||
const history = useHistory()
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
})
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
alert("Пароли не совпадают")
|
||||
return
|
||||
}
|
||||
// Simulate registration
|
||||
history.push("/home")
|
||||
}
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-8">
|
||||
<button
|
||||
onClick={() => history.goBack()}
|
||||
className="absolute top-6 left-6 w-12 h-12 bg-white/10 backdrop-blur-lg rounded-xl flex items-center justify-center border border-white/20 text-white hover:bg-white/20 transition-all"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div className="w-20 h-20 bg-white/10 backdrop-blur-2xl rounded-2xl flex items-center justify-center mx-auto mb-4 border border-white/20 shadow-2xl">
|
||||
<svg width="40" height="40" viewBox="0 0 100 100" fill="none" className="text-white">
|
||||
<circle cx="50" cy="20" r="8" fill="currentColor" />
|
||||
<path
|
||||
d="M50 28 L50 60 M50 45 L35 35 M50 45 L65 35 M50 60 L35 80 M50 60 L65 80"
|
||||
stroke="currentColor"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 className="text-3xl font-black text-white mb-2">Регистрация</h1>
|
||||
<p className="text-white/80 font-medium">Создайте новый аккаунт</p>
|
||||
</div>
|
||||
|
||||
{/* Registration Form */}
|
||||
<div className="glass-morphism rounded-3xl p-8 border border-white/20 shadow-2xl">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{/* Name Fields */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Имя</label>
|
||||
<input
|
||||
type="text"
|
||||
name="firstName"
|
||||
value={formData.firstName}
|
||||
onChange={handleInputChange}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="Иван"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Фамилия</label>
|
||||
<input
|
||||
type="text"
|
||||
name="lastName"
|
||||
value={formData.lastName}
|
||||
onChange={handleInputChange}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="Иванов"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Электронная почта</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="example@mail.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Password */}
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Пароль</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
name="password"
|
||||
value={formData.password}
|
||||
onChange={handleInputChange}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="••••••••"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-3.5 text-white/60 hover:text-white transition-colors"
|
||||
>
|
||||
{showPassword ? (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Confirm Password */}
|
||||
<div>
|
||||
<label className="block text-white/90 text-sm font-semibold mb-2">Подтвердите пароль</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showConfirmPassword ? "text" : "password"}
|
||||
name="confirmPassword"
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleInputChange}
|
||||
className="w-full bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl px-4 py-3 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/30 focus:border-transparent transition-all"
|
||||
placeholder="••••••••"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
className="absolute right-3 top-3.5 text-white/60 hover:text-white transition-colors"
|
||||
>
|
||||
{showConfirmPassword ? (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Register Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-white/20 backdrop-blur-lg hover:bg-white/30 text-white font-bold py-3 px-6 rounded-2xl border border-white/30 transition-all duration-300 transform hover:scale-105 shadow-lg mt-6"
|
||||
>
|
||||
Зарегистрироваться
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Login Link */}
|
||||
<div className="text-center mt-6">
|
||||
<p className="text-white/80 text-sm">
|
||||
Уже есть аккаунт?{" "}
|
||||
<button onClick={() => history.push("/login")} className="text-white font-semibold hover:underline">
|
||||
Войти
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Register
|
@ -1,12 +1,9 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useHistory } from "react-router-dom"
|
||||
import manImage from '../assets/man.svg';
|
||||
import emblemImage from '../assets/emblem.png';
|
||||
import manImage from "../assets/man.svg" // Reverted to original import
|
||||
|
||||
const Welcome: React.FC = () => {
|
||||
export default function Welcome() {
|
||||
const history = useHistory()
|
||||
const [animationPhase, setAnimationPhase] = useState(0)
|
||||
|
||||
@ -14,7 +11,7 @@ const Welcome: React.FC = () => {
|
||||
const timer1 = setTimeout(() => setAnimationPhase(1), 500)
|
||||
const timer2 = setTimeout(() => setAnimationPhase(2), 1500)
|
||||
const timer3 = setTimeout(() => setAnimationPhase(3), 2500)
|
||||
// const timer4 = setTimeout(() => history.push("/login"), 4000)
|
||||
// const timer4 = setTimeout(() => history.push("/login"), 4000) // Uncomment if you want auto-redirect
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer1)
|
||||
@ -26,19 +23,17 @@ const Welcome: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen relative overflow-hidden">
|
||||
|
||||
<img
|
||||
className="h-[30rem] lg:h-[50rem] w-auto z-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-15"
|
||||
src={manImage}
|
||||
alt=""
|
||||
src={manImage || "/placeholder.svg"} // Reverted to original img tag and src
|
||||
alt="Man illustration"
|
||||
/>
|
||||
{/* Uncomment and update path for emblemImage if needed */}
|
||||
{/* <img
|
||||
className="h-[30rem] lg:h-[0rem] w-auto z-50 absolute right-0 bottom-0 transform opacity-10"
|
||||
src={emblemImage}
|
||||
alt=""
|
||||
src={emblemImage || "/placeholder.svg"} // Reverted to original img tag and src
|
||||
alt="Emblem"
|
||||
/> */}
|
||||
|
||||
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#3ABBC7] to-[#0D212C]">
|
||||
{/* Floating particles */}
|
||||
<div className="absolute inset-0">
|
||||
@ -47,29 +42,23 @@ const Welcome: React.FC = () => {
|
||||
<div className="absolute top-1/2 left-1/4 w-16 h-16 bg-white/5 rounded-full animate-ping blur-md"></div>
|
||||
<div className="absolute top-1/3 right-1/3 w-20 h-20 bg-white/5 rounded-full animate-pulse blur-lg"></div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
|
||||
|
||||
|
||||
<div className="text-center z-10 px-8">
|
||||
|
||||
{/* App Name */}
|
||||
<div
|
||||
className={`transition-all duration-1000 delay-300 ${animationPhase >= 1 ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
|
||||
className={`transition-all duration-1000 delay-300 ${
|
||||
animationPhase >= 1 ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
|
||||
}`}
|
||||
>
|
||||
|
||||
|
||||
|
||||
<h1 className="text-5xl font-black text-white mb-4 tracking-tight filter drop-shadow-lg">Реабилитация</h1>
|
||||
<p className="text-white/90 text-xl font-medium tracking-wide">Восстановление через движение</p>
|
||||
</div>
|
||||
{/* Loading indicator */}
|
||||
<div
|
||||
className={`mt-16 transition-all duration-500 delay-700 ${animationPhase >= 1 ? "opacity-100" : "opacity-0"}`}
|
||||
className={`mt-16 transition-all duration-500 delay-700 ${
|
||||
animationPhase >= 1 ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<div className="w-64 h-3 bg-white/10 rounded-full mx-auto overflow-hidden backdrop-blur-sm border border-white/20">
|
||||
<div
|
||||
@ -83,16 +72,8 @@ const Welcome: React.FC = () => {
|
||||
<p className="text-white/70 text-lg mt-6 font-medium">Загрузка...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default Welcome
|
||||
|
Loading…
x
Reference in New Issue
Block a user