Добавлена система регистрации, небезопасная
This commit is contained in:
43
ospabhost/package-lock.json
generated
43
ospabhost/package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"framer-motion": "^12.23.12",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.9.1",
|
||||
@@ -8424,6 +8425,33 @@
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.12",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz",
|
||||
"integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.12",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
@@ -11524,6 +11552,21 @@
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.12",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz",
|
||||
"integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"framer-motion": "^12.23.12",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.9.1",
|
||||
|
||||
@@ -1,20 +1,37 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import Index from './pages/index';
|
||||
import Pricing from './pages/pricing';
|
||||
import Login from './pages/login';
|
||||
import Dashboard from './pages/dashboard/index';
|
||||
import './styles/tailwind.css';
|
||||
import React from "react";
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||
import { AuthProvider } from "./context/AuthContext";
|
||||
import Navbar from "./components/Navbar";
|
||||
import Footer from "./components/Footer";
|
||||
import Home from "./pages/index";
|
||||
import Login from "./pages/login";
|
||||
import Register from "./pages/register";
|
||||
import Dashboard from "./pages/dashboard";
|
||||
|
||||
export default function App() {
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Index />} />
|
||||
<Route path="/pricing" element={<Pricing />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
<AuthProvider>
|
||||
<Router>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
{/* Навигация сверху */}
|
||||
<Navbar />
|
||||
|
||||
{/* Основной контент */}
|
||||
<main className="flex-grow">
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
{/* Подвал */}
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="bg-gray-900 text-white p-4 text-center">
|
||||
© {new Date().getFullYear()} osapab.host. Все права защищены.
|
||||
<footer className="bg-gray-800 text-white py-6 mt-12">
|
||||
<div className="container mx-auto text-center">
|
||||
© 2025 ospab.host. Все права защищены.
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,30 +1,47 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
|
||||
interface NavbarProps {
|
||||
user: { email: string } | null;
|
||||
logout: () => void;
|
||||
}
|
||||
export default function Navbar() {
|
||||
const { user, logout } = useAuth();
|
||||
|
||||
const Navbar: React.FC<NavbarProps> = ({ user, logout }) => {
|
||||
return (
|
||||
<nav className="bg-gray-800 text-white p-4 flex justify-between items-center">
|
||||
<div className="text-xl font-bold">ospab.host</div>
|
||||
<div className="space-x-4">
|
||||
<Link to="/">Главная</Link>
|
||||
<Link to="/pricing">Цены</Link>
|
||||
{user ? (
|
||||
<>
|
||||
<Link to="/dashboard">ЛК</Link>
|
||||
<button onClick={logout} className="bg-red-600 px-2 py-1 rounded">
|
||||
Выйти
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<Link to="/login">Вход</Link>
|
||||
)}
|
||||
<nav className="bg-white shadow-md px-8 py-4 fixed w-full z-50 rounded-b-3xl">
|
||||
<div className="container mx-auto flex justify-between items-center">
|
||||
<Link to="/" className="text-2xl font-bold text-indigo-700 hover:text-pink-500 transition">
|
||||
ospab.host
|
||||
</Link>
|
||||
<div className="space-x-6">
|
||||
<Link to="/" className="hover:text-pink-500 transition">Главная</Link>
|
||||
<Link to="/pricing" className="hover:text-pink-500 transition">Цены</Link>
|
||||
{user ? (
|
||||
<>
|
||||
<Link to="/dashboard" className="hover:text-pink-500 transition">Личный кабинет</Link>
|
||||
<span className="text-gray-700 font-semibold">Привет, {user.name}</span>
|
||||
<button
|
||||
onClick={logout}
|
||||
className="bg-red-500 text-white px-4 py-2 rounded-full hover:bg-pink-500 transition-all"
|
||||
>
|
||||
Выйти
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
to="/login"
|
||||
className="bg-indigo-700 text-white px-4 py-2 rounded-full hover:bg-pink-500 transition-all"
|
||||
>
|
||||
Войти
|
||||
</Link>
|
||||
<Link
|
||||
to="/register"
|
||||
className="bg-white text-indigo-700 px-4 py-2 rounded-full border border-indigo-700 hover:bg-pink-500 hover:text-white transition-all"
|
||||
>
|
||||
Регистрация
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
}
|
||||
|
||||
48
ospabhost/src/context/AuthContext.tsx
Normal file
48
ospabhost/src/context/AuthContext.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from "react";
|
||||
|
||||
interface User {
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
login: (email: string, password: string) => boolean;
|
||||
register: (name: string, email: string, password: string, password2: string) => boolean;
|
||||
logout: () => void;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
|
||||
const login = (email: string, password: string) => {
|
||||
if (email && password) {
|
||||
setUser({ name: "Пользователь", email });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const register = (name: string, email: string, password: string, password2: string) => {
|
||||
if (!name || !email || !password || !password2) return false;
|
||||
if (password !== password2) return false;
|
||||
setUser({ name, email });
|
||||
return true;
|
||||
};
|
||||
|
||||
const logout = () => setUser(null);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, login, register, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAuth = (): AuthContextType => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) throw new Error("useAuth must be used within AuthProvider");
|
||||
return context;
|
||||
};
|
||||
@@ -9,18 +9,13 @@ import './styles/tailwind.css';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root')!);
|
||||
|
||||
// Заглушка для функции login
|
||||
const login = (email: string) => {
|
||||
// Здесь может быть логика авторизации
|
||||
console.log('Вход для:', email);
|
||||
};
|
||||
|
||||
root.render(
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Index />} />
|
||||
<Route path="/pricing" element={<Pricing />} />
|
||||
<Route path="/login" element={<Login login={login} />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import React from 'react';
|
||||
import Navbar from "../../components/Navbar";
|
||||
|
||||
export default function Billing() {
|
||||
return <div>Billing details</div>;
|
||||
}
|
||||
const Billing: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
<section className="pt-32 px-4 container mx-auto">
|
||||
<h1 className="text-3xl font-bold mb-6">Баланс и платежи</h1>
|
||||
<div className="bg-white rounded-3xl shadow p-6">
|
||||
<p>Информация о платежах появится здесь.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Billing;
|
||||
|
||||
@@ -1,23 +1,34 @@
|
||||
import { Link, Routes, Route } from "react-router-dom";
|
||||
import Servers from "./servers";
|
||||
import Billing from "./billing";
|
||||
import Support from "./support";
|
||||
import Navbar from "../../components/Navbar";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-4">Личный кабинет</h1>
|
||||
<div className="flex space-x-4 mb-4">
|
||||
<Link className="underline" to="servers">Сервера</Link>
|
||||
<Link className="underline" to="billing">Биллинг</Link>
|
||||
<Link className="underline" to="support">Поддержка</Link>
|
||||
</div>
|
||||
<Routes>
|
||||
<Route path="servers" element={<Servers />} />
|
||||
<Route path="billing" element={<Billing />} />
|
||||
<Route path="support" element={<Support />} />
|
||||
<Route path="/" element={<div>Выберите раздел</div>} />
|
||||
</Routes>
|
||||
<div className="text-gray-800 min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
|
||||
<section className="pt-32 px-4 container mx-auto">
|
||||
<h1 className="text-4xl font-bold mb-6">Личный кабинет</h1>
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<Link
|
||||
to="/dashboard/servers"
|
||||
className="bg-white rounded-3xl p-6 shadow hover:shadow-2xl transition-all text-center"
|
||||
>
|
||||
Мои серверы
|
||||
</Link>
|
||||
<Link
|
||||
to="/dashboard/billing"
|
||||
className="bg-white rounded-3xl p-6 shadow hover:shadow-2xl transition-all text-center"
|
||||
>
|
||||
Баланс и платежи
|
||||
</Link>
|
||||
<Link
|
||||
to="/dashboard/support"
|
||||
className="bg-white rounded-3xl p-6 shadow hover:shadow-2xl transition-all text-center"
|
||||
>
|
||||
Поддержка
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import Navbar from "../../components/Navbar";
|
||||
|
||||
const Servers: React.FC = () => {
|
||||
return <div>Здесь будут ваши VPS</div>;
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
<section className="pt-32 px-4 container mx-auto">
|
||||
<h1 className="text-3xl font-bold mb-6">Мои VPS</h1>
|
||||
<div className="bg-white rounded-3xl shadow p-6">
|
||||
<p>Список серверов пока пуст...</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Servers;
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import React from 'react';
|
||||
import Navbar from "../../components/Navbar";
|
||||
|
||||
export default function Support() {
|
||||
return <div>Support tickets</div>;
|
||||
}
|
||||
const Support: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
<section className="pt-32 px-4 container mx-auto">
|
||||
<h1 className="text-3xl font-bold mb-6">Поддержка</h1>
|
||||
<div className="bg-white rounded-3xl shadow p-6">
|
||||
<p>Здесь будет форма обращения в поддержку.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Support;
|
||||
|
||||
@@ -1,16 +1,110 @@
|
||||
import React from 'react';
|
||||
import Navbar from '../components/Navbar';
|
||||
import Footer from '../components/Footer';
|
||||
import Navbar from "../components/Navbar";
|
||||
import { Link } from "react-router-dom";
|
||||
import { motion } from "framer-motion"; // анимации
|
||||
|
||||
export default function Index() {
|
||||
const Home: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-white">
|
||||
<Navbar user={null} logout={() => {}} />
|
||||
<main className="flex-grow p-10">
|
||||
<h1 className="text-4xl font-bold">Добро пожаловать в ospab.host</h1>
|
||||
<p className="mt-4 text-lg">Ваша панель управления VPS-хостингом</p>
|
||||
</main>
|
||||
<Footer />
|
||||
<div className="text-gray-800">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero section */}
|
||||
<section className="bg-gradient-to-r from-blue-600 to-indigo-700 text-white py-36">
|
||||
<div className="container mx-auto text-center px-4">
|
||||
<motion.h1
|
||||
className="text-5xl font-bold mb-4 rounded-lg"
|
||||
initial={{ opacity: 0, y: -50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 1 }}
|
||||
>
|
||||
ospab.host — Надёжный VPS и хостинг
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-xl mb-8"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 0.5 }}
|
||||
>
|
||||
Создавайте серверы, управляйте VPS и следите за метриками прямо из панели.
|
||||
</motion.p>
|
||||
<motion.div
|
||||
initial={{ scale: 0.8, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.5, delay: 1 }}
|
||||
>
|
||||
<Link
|
||||
to="/login"
|
||||
className="bg-white text-blue-700 font-bold py-3 px-6 rounded-full shadow-lg hover:bg-pink-500 hover:text-white transition-all"
|
||||
>
|
||||
Начать бесплатно
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features section */}
|
||||
<section className="py-20 bg-gray-100">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold text-center mb-12">Почему выбирают ospab.host</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8 text-center">
|
||||
{[
|
||||
{
|
||||
title: "VPS на Proxmox",
|
||||
desc: "Мгновенное создание и управление VPS, автоматические snapshot'ы и бэкапы.",
|
||||
},
|
||||
{
|
||||
title: "Мониторинг и графики",
|
||||
desc: "Полная информация о нагрузке CPU, RAM и дисков в реальном времени через ЛК.",
|
||||
},
|
||||
{
|
||||
title: "Безопасность",
|
||||
desc: "SSL, защита API, role-based access и безопасное хранение данных.",
|
||||
},
|
||||
].map((feature, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
className="bg-white rounded-3xl shadow p-6 hover:shadow-2xl transition-all"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
|
||||
<p>{feature.desc}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Pricing teaser */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-6">Прозрачные тарифы</h2>
|
||||
<p className="mb-8">Выбирайте тариф под свои задачи, платите только за то, что используете.</p>
|
||||
<Link
|
||||
to="/pricing"
|
||||
className="bg-blue-600 text-white font-bold py-3 px-6 rounded-full shadow hover:bg-pink-500 transition-all"
|
||||
>
|
||||
Посмотреть тарифы
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
{/* Call-to-action */}
|
||||
<section className="py-8 bg-indigo-700 text-white text-center rounded-t-3xl">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-4">Готовы начать?</h2>
|
||||
<p className="mb-6">
|
||||
Создайте аккаунт и получите доступ к панели управления VPS уже сегодня.
|
||||
</p>
|
||||
<Link
|
||||
to="/login"
|
||||
className="bg-white text-indigo-700 font-bold py-3 px-6 rounded-full shadow hover:bg-pink-500 hover:text-white transition-all"
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Home;
|
||||
|
||||
@@ -1,45 +1,53 @@
|
||||
import Navbar from "../components/Navbar";
|
||||
import { useState } from "react";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface LoginProps {
|
||||
login: (email: string) => void;
|
||||
}
|
||||
|
||||
const Login: React.FC<LoginProps> = ({ login }) => {
|
||||
const Login: React.FC = () => {
|
||||
const { login } = useAuth();
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
login(email);
|
||||
navigate("/dashboard");
|
||||
if (login(email, password)) {
|
||||
navigate("/dashboard");
|
||||
} else {
|
||||
alert("Неверный логин или пароль");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto mt-20 p-6 bg-gray-800 text-white rounded">
|
||||
<h1 className="text-2xl font-bold mb-4">Вход</h1>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full p-2 rounded text-black"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Пароль"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full p-2 rounded text-black"
|
||||
required
|
||||
/>
|
||||
<button className="w-full bg-blue-600 py-2 rounded font-bold hover:bg-blue-700">
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
<section className="flex justify-center items-center h-screen">
|
||||
<div className="bg-white rounded-3xl shadow-lg p-10 w-full max-w-md">
|
||||
<h2 className="text-3xl font-bold mb-6 text-center">Вход в ЛК</h2>
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-lg border focus:ring-2 focus:ring-pink-500 outline-none"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Пароль"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-lg border focus:ring-2 focus:ring-pink-500 outline-none"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-indigo-700 text-white py-2 rounded-full hover:bg-pink-500 transition-all"
|
||||
>
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,36 +1,44 @@
|
||||
import React from 'react';
|
||||
import Navbar from '../components/Navbar';
|
||||
import Footer from '../components/Footer';
|
||||
import Button from '../components/Button';
|
||||
import Navbar from "../components/Navbar";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export default function Pricing() {
|
||||
const Pricing: React.FC = () => {
|
||||
const plans = [
|
||||
{ name: "Мини", price: "200 р/мес", features: ["1 vCPU", "1GB RAM", "25GB SSD"] },
|
||||
{ name: "Стандарт", price: "500 р/мес", features: ["2 vCPU", "2GB RAM", "50GB SSD"] },
|
||||
{ name: "Профессионал", price: "700 р/мес", features: ["4 vCPU", "8GB RAM", "100GB SSD"] },
|
||||
{ title: "Basic VPS", price: "$5/мес", features: ["1 CPU", "1 GB RAM", "20 GB SSD"] },
|
||||
{ title: "Pro VPS", price: "$15/мес", features: ["2 CPU", "4 GB RAM", "50 GB SSD"] },
|
||||
{ title: "Enterprise", price: "$30/мес", features: ["4 CPU", "8 GB RAM", "100 GB SSD"] },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-gray-50">
|
||||
<Navbar user={null} logout={() => {}} />
|
||||
<div className="text-gray-800">
|
||||
<Navbar />
|
||||
|
||||
<main className="flex-grow px-6 py-12 text-center">
|
||||
<h1 className="text-4xl font-bold mb-6">Выбери свой тариф</h1>
|
||||
<div className="grid md:grid-cols-3 gap-6 max-w-5xl mx-auto">
|
||||
{plans.map(plan => (
|
||||
<div key={plan.name} className="bg-white shadow-md rounded-xl p-6 hover:shadow-xl transition">
|
||||
<h2 className="text-2xl font-bold mb-4">{plan.name}</h2>
|
||||
<p className="text-3xl font-extrabold text-blue-600 mb-4">{plan.price}</p>
|
||||
<ul className="text-gray-600 mb-6 space-y-2">
|
||||
{plan.features.map(f => <li key={f}>✅ {f}</li>)}
|
||||
</ul>
|
||||
<Button>Заказать</Button>
|
||||
</div>
|
||||
))}
|
||||
<section className="py-24 bg-gray-100">
|
||||
<div className="container mx-auto text-center px-4">
|
||||
<h1 className="text-4xl font-bold mb-12">Наши тарифы</h1>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{plans.map((plan, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
className="bg-white rounded-3xl shadow p-6 hover:shadow-2xl transition-all"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<h2 className="text-2xl font-bold mb-4">{plan.title}</h2>
|
||||
<p className="text-xl mb-4">{plan.price}</p>
|
||||
<ul className="mb-6">
|
||||
{plan.features.map((f, i) => (
|
||||
<li key={i} className="mb-1">{f}</li>
|
||||
))}
|
||||
</ul>
|
||||
<button className="bg-indigo-700 text-white py-2 px-6 rounded-full hover:bg-pink-500 transition-all">
|
||||
Выбрать
|
||||
</button>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Pricing;
|
||||
|
||||
77
ospabhost/src/pages/register.tsx
Normal file
77
ospabhost/src/pages/register.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useState } from "react";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Navbar from "../components/Navbar";
|
||||
|
||||
export default function Register() {
|
||||
const { register } = useAuth();
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [password2, setPassword2] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name || !email || !password || !password2) {
|
||||
alert("Заполните все поля");
|
||||
return;
|
||||
}
|
||||
if (password !== password2) {
|
||||
alert("Пароли не совпадают");
|
||||
return;
|
||||
}
|
||||
if (register(name, email, password, password2)) {
|
||||
navigate("/dashboard");
|
||||
} else {
|
||||
alert("Ошибка регистрации");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
<section className="flex justify-center items-center h-screen">
|
||||
<div className="bg-white rounded-3xl shadow-lg p-10 w-full max-w-md">
|
||||
<h2 className="text-3xl font-bold mb-6 text-center">Регистрация</h2>
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Имя"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-lg border focus:ring-2 focus:ring-pink-500 outline-none"
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-lg border focus:ring-2 focus:ring-pink-500 outline-none"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Пароль"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-lg border focus:ring-2 focus:ring-pink-500 outline-none"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Повторите пароль"
|
||||
value={password2}
|
||||
onChange={(e) => setPassword2(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-lg border focus:ring-2 focus:ring-pink-500 outline-none"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-indigo-700 text-white py-2 rounded-full hover:bg-pink-500 transition-all"
|
||||
>
|
||||
Зарегистрироваться
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user