diff --git a/frontend/src/app/providers/layout/navigation/navigation.tsx b/frontend/src/app/providers/layout/navigation/navigation.tsx
index 7cd745e..cdfd348 100644
--- a/frontend/src/app/providers/layout/navigation/navigation.tsx
+++ b/frontend/src/app/providers/layout/navigation/navigation.tsx
@@ -1,5 +1,14 @@
import { useNavigate, useLocation } from "react-router-dom";
-import { FaHome, FaServer, FaPalette, FaUser, FaCode } from "react-icons/fa";
+import { FaCode } from "react-icons/fa";
+import {
+ FaHome,
+ FaServer,
+ FaPalette,
+ FaUser,
+ FaUsers,
+ FaRocket,
+ FaKey,
+} from "react-icons/fa";
import { useAuthStore } from "@/modules/auth/store/useAuthStore";
export const Navigation = () => {
@@ -11,6 +20,9 @@ export const Navigation = () => {
{ path: "/", label: "Главная", icon: FaHome },
{ path: "/add-agents", label: "Агенты", icon: FaServer },
{ path: "/templates", label: "Шаблоны", icon: FaCode },
+ { path: "/add-agents", label: "Деплой", icon: FaRocket },
+ { path: "/registration", label: "Регистрация", icon: FaKey },
+ { path: "/admin", label: "Админка", icon: FaUsers, adminOnly: true },
{ path: "/themes", label: "Темы", icon: FaPalette },
];
@@ -41,39 +53,44 @@ export const Navigation = () => {
{/* Навигация */}
- {navItems.map((item) => {
- const Icon = item.icon;
- const active = isActive(item.path);
- return (
-
- );
- })}
+ {navItems
+ .filter((item) => {
+ if (item.adminOnly && !user?.permission_admin) return false;
+ return true;
+ })
+ .map((item) => {
+ const Icon = item.icon;
+ const active = isActive(item.path);
+ return (
+
+ );
+ })}
{/* Профиль пользователя */}
diff --git a/frontend/src/app/providers/routing/routing.tsx b/frontend/src/app/providers/routing/routing.tsx
index 05b6b27..b454166 100644
--- a/frontend/src/app/providers/routing/routing.tsx
+++ b/frontend/src/app/providers/routing/routing.tsx
@@ -10,6 +10,8 @@ import { DefaultLayout } from "@/shared/layouts/DefaultLayout";
import { AddAgentsPage } from "@/pages/add-agents.page";
import { IDEPage } from "@/pages/ide.page";
import { TemplatesPage } from "@/pages/templates.page";
+import { AdminPage } from "@/pages/admin.page";
+import { RegistrationTokenPage } from "@/pages/registration.page";
export const mockGraphData: GraphData = {
nodes: [
@@ -120,6 +122,8 @@ export const Routing = () => {
} />
} />
} />
+ } />
+ } />
} />
} />
diff --git a/frontend/src/modules/agent/api/agent.api.service.ts b/frontend/src/modules/agent/api/agent.api.service.ts
index e5f629f..db258e8 100644
--- a/frontend/src/modules/agent/api/agent.api.service.ts
+++ b/frontend/src/modules/agent/api/agent.api.service.ts
@@ -7,6 +7,12 @@ import type {
LogFilters,
InsertLogRequest,
InsertLogsRequest,
+ TokenUpdate,
+ TokenUpdatePermissions,
+ TokenPasswordReset,
+ RegistrationRequest,
+ DeployAgentsRequest,
+ DeployResponse,
} from "../types/agent.types";
class AgentApiService {
@@ -20,7 +26,9 @@ class AgentApiService {
}
async getUsers(): Promise {
- const response = await apiClient.get(`${this.authBasePath}/tokens`);
+ const response = await apiClient.get(
+ `${this.authBasePath}/tokens`,
+ );
return response.data;
}
@@ -60,17 +68,85 @@ class AgentApiService {
}
async getDistinctAgents(): Promise {
- const response = await apiClient.get(`${this.logsBasePath}/agents`);
+ const response = await apiClient.get(
+ `${this.logsBasePath}/agents`,
+ );
return response.data;
}
async getDistinctLevels(): Promise {
- const response = await apiClient.get(`${this.logsBasePath}/levels`);
+ const response = await apiClient.get(
+ `${this.logsBasePath}/levels`,
+ );
return response.data;
}
async getDistinctServices(): Promise {
- const response = await apiClient.get(`${this.logsBasePath}/services`);
+ const response = await apiClient.get(
+ `${this.logsBasePath}/services`,
+ );
+ return response.data;
+ }
+
+ // User management methods
+ async getUserByLogin(login: string): Promise {
+ const response = await apiClient.get(
+ `${this.authBasePath}/users/${login}`,
+ );
+ return response.data;
+ }
+
+ async getInactiveUsers(): Promise {
+ const response = await apiClient.get(
+ `${this.authBasePath}/users/inactive`,
+ );
+ return response.data;
+ }
+
+ async updateUser(login: string, data: TokenUpdate): Promise {
+ await apiClient.put(`${this.authBasePath}/users/${login}`, data);
+ }
+
+ async updateUserPermissions(
+ login: string,
+ data: TokenUpdatePermissions,
+ ): Promise {
+ await apiClient.put(
+ `${this.authBasePath}/users/${login}/permissions`,
+ data,
+ );
+ }
+
+ async resetUserPassword(
+ login: string,
+ data: TokenPasswordReset,
+ ): Promise {
+ await apiClient.put(`${this.authBasePath}/users/${login}/password`, data);
+ }
+
+ async activateUser(login: string): Promise {
+ await apiClient.post(`${this.authBasePath}/users/${login}/activate`);
+ }
+
+ async deactivateUser(login: string): Promise {
+ await apiClient.post(`${this.authBasePath}/users/${login}/deactivate`);
+ }
+
+ async createRegistrationToken(
+ data: RegistrationRequest,
+ ): Promise> {
+ const response = await apiClient.post>(
+ `${this.basePath}/register-token`,
+ data,
+ );
+ return response.data;
+ }
+
+ async deployAgents(data: DeployAgentsRequest): Promise {
+ const response = await apiClient.post(
+ `${this.basePath}/deploy`,
+ data,
+ );
return response.data;
}
}
diff --git a/frontend/src/modules/agent/index.ts b/frontend/src/modules/agent/index.ts
index 0dab406..3b4cdad 100644
--- a/frontend/src/modules/agent/index.ts
+++ b/frontend/src/modules/agent/index.ts
@@ -15,4 +15,12 @@ export type {
InsertLogRequest,
InsertLogsRequest,
LogFilters,
+ TokenUpdate,
+ TokenUpdatePermissions,
+ TokenPasswordReset,
+ RegistrationRequest,
+ DeployResult,
+ DeployAgentsRequest,
+ AgentDeployConfig,
+ DeployResponse,
} from "./types/agent.types";
diff --git a/frontend/src/modules/agent/types/agent.types.ts b/frontend/src/modules/agent/types/agent.types.ts
index 7d0e342..6b7b168 100644
--- a/frontend/src/modules/agent/types/agent.types.ts
+++ b/frontend/src/modules/agent/types/agent.types.ts
@@ -74,3 +74,51 @@ export interface LogFilters {
limit?: number;
offset?: number;
}
+
+export interface TokenUpdate {
+ name?: string;
+ last_name?: string;
+}
+
+export interface TokenUpdatePermissions {
+ is_active?: boolean;
+ permission_admin?: boolean;
+ permission_manage_agent?: boolean;
+ permission_view?: boolean;
+}
+
+export interface TokenPasswordReset {
+ new_password: string;
+}
+
+export interface RegistrationRequest {
+ label: string;
+}
+
+export interface DeployResult {
+ agent_label: string;
+ error?: string;
+ ip: string;
+ success: boolean;
+ token?: string;
+}
+
+export interface DeployAgentsRequest {
+ servers: AgentDeployConfig[];
+}
+
+export interface AgentDeployConfig {
+ agentLabel: string;
+ authMethod: "key" | "password";
+ deployType: "docker" | "binary";
+ ip: string;
+ password?: string;
+ port?: number;
+ sshKey?: string;
+ user: string;
+}
+
+export interface DeployResponse {
+ message?: string;
+ results: DeployResult[];
+}
diff --git a/frontend/src/modules/agent/ui/SSHAgentForm.tsx b/frontend/src/modules/agent/ui/SSHAgentForm.tsx
index d53b187..032dce4 100644
--- a/frontend/src/modules/agent/ui/SSHAgentForm.tsx
+++ b/frontend/src/modules/agent/ui/SSHAgentForm.tsx
@@ -7,6 +7,7 @@ import {
FiPlus,
FiTrash2,
FiSettings,
+ FiLink,
} from "react-icons/fi";
import { SiDocker } from "react-icons/si";
import { FiPackage, FiUploadCloud } from "react-icons/fi";
@@ -20,8 +21,10 @@ interface ExtraField {
}
export interface SSHAgentConfig {
+ agentLabel: string;
user: string;
ip: string;
+ port: number;
authMethod: AuthMethod;
sshKey?: string;
password?: string;
@@ -189,11 +192,31 @@ export const SSHAgentForm: React.FC = ({
- {/* User и IP */}
+ {/* Agent Label */}
+
+
+ handleChange("agentLabel", e.target.value)}
+ required
+ style={inputBaseStyle}
+ onFocus={handleFocus}
+ onBlur={handleBlur}
+ placeholder="production-server-1"
+ />
+
+
+ {/* User, IP и Port */}
@@ -238,6 +261,31 @@ export const SSHAgentForm: React.FC = ({
placeholder="192.168.1.1"
/>
+
+
+
+
+ handleChange("port", parseInt(e.target.value) || 22)
+ }
+ required
+ min={1}
+ max={65535}
+ style={inputBaseStyle}
+ onFocus={handleFocus}
+ onBlur={handleBlur}
+ placeholder="22"
+ />
+
{/* Метод аутентификации */}
@@ -457,7 +505,7 @@ export const SSHAgentForm: React.FC = ({
diff --git a/frontend/src/modules/auth/store/useAuthStore.ts b/frontend/src/modules/auth/store/useAuthStore.ts
index 523cea3..081f837 100644
--- a/frontend/src/modules/auth/store/useAuthStore.ts
+++ b/frontend/src/modules/auth/store/useAuthStore.ts
@@ -17,12 +17,18 @@ const login = async (credentials: LoginCredentials): Promise
=> {
return response.data;
};
-const register = async (data: RegisterData): Promise => {
- const response = await apiClient.post("/auth/register", {
+const register = async (
+ data: RegisterData,
+): Promise> => {
+ const response = await apiClient.post>("/auth/token", {
login: data.login,
password: data.password,
name: data.firstName,
last_name: data.lastName,
+ is_active: data.is_active,
+ permission_admin: data.permission_admin,
+ permission_manage_agent: data.permission_manage_agent,
+ permission_view: data.permission_view,
});
return response.data;
};
@@ -62,9 +68,10 @@ export const useAuthStore = create()(
register: async (data: RegisterData) => {
set({ isLoading: true, error: null });
try {
- const response = await register(data);
- const user = mapResponseToUser(response);
- set({ user, token: response.token, isLoading: false });
+ await register(data);
+ // После регистрации пользователь не авторизуется автоматически
+ // Нужно войти через /auth/login
+ set({ isLoading: false });
} catch (error) {
set({
error:
diff --git a/frontend/src/modules/auth/types/auth.types.ts b/frontend/src/modules/auth/types/auth.types.ts
index ca4e6d8..ff289a8 100644
--- a/frontend/src/modules/auth/types/auth.types.ts
+++ b/frontend/src/modules/auth/types/auth.types.ts
@@ -8,6 +8,10 @@ export interface RegisterData {
password: string;
firstName: string;
lastName: string;
+ is_active?: boolean;
+ permission_admin?: boolean;
+ permission_manage_agent?: boolean;
+ permission_view?: boolean;
}
export interface LoginResponse {
diff --git a/frontend/src/pages/add-agents.page.tsx b/frontend/src/pages/add-agents.page.tsx
index eb01f41..0eee91c 100644
--- a/frontend/src/pages/add-agents.page.tsx
+++ b/frontend/src/pages/add-agents.page.tsx
@@ -1,21 +1,24 @@
import React, { useState } from "react";
import { SSHAgentForm } from "../modules/agent/ui/SSHAgentForm";
import { agentApiService } from "../modules/agent/api/agent.api.service";
-import { FiPlusCircle, FiSend } from "react-icons/fi";
-
-interface SSHAgentConfig {
- user: string;
- ip: string;
- authMethod: string;
- sshKey?: string;
- password?: string;
- extraFields: { key: string; value: string }[];
- deployType: string;
-}
+import type { SSHAgentConfig } from "../modules/agent/ui/SSHAgentForm";
+import type {
+ DeployAgentsRequest,
+ DeployResult,
+} from "../modules/agent/types/agent.types";
+import {
+ FiPlusCircle,
+ FiSend,
+ FiCheck,
+ FiX,
+ FiAlertCircle,
+} from "react-icons/fi";
const createEmptyAgentConfig = (): SSHAgentConfig => ({
+ agentLabel: "",
user: "",
ip: "",
+ port: 22,
authMethod: "key",
sshKey: "",
password: "",
@@ -51,7 +54,9 @@ export const AddAgentsPage: React.FC = () => {
// Валидация
const isValid = agents.every((agent) => {
- if (!agent.user || !agent.ip) return false;
+ if (!agent.agentLabel || !agent.user || !agent.ip || !agent.port)
+ return false;
+ if (agent.port < 1 || agent.port > 65535) return false;
if (agent.authMethod === "key" && !agent.sshKey) return false;
if (agent.authMethod === "password" && !agent.password) return false;
return true;
@@ -67,21 +72,52 @@ export const AddAgentsPage: React.FC = () => {
setSubmitError(null);
try {
- // Получаем текущих агентов для проверки подключения
- const currentAgents = await agentApiService.getAgents();
- console.log("Current agents:", currentAgents);
+ // Преобразуем данные из формы в формат API
+ const deployData: DeployAgentsRequest = {
+ servers: agents.map((agent) => ({
+ agentLabel: agent.agentLabel,
+ ip: agent.ip,
+ user: agent.user,
+ port: agent.port,
+ authMethod: agent.authMethod as "key" | "password",
+ deployType: (agent.deployType === "deploy"
+ ? "docker"
+ : agent.deployType) as "docker" | "binary",
+ ...(agent.authMethod === "key"
+ ? { sshKey: agent.sshKey }
+ : { password: agent.password }),
+ })),
+ };
- // TODO: Реальный API вызов для развертывания агентов
- // Пока выводим список подключенных агентов
- setSubmitMessage(
- `Успешно подключено ${currentAgents.length} агент(ов). Серверы: ${agents.length}`,
- );
- setAgents([createEmptyAgentConfig()]);
+ // Вызываем API для развертывания агентов
+ const response = await agentApiService.deployAgents(deployData);
+
+ // Формируем сообщение о результатах
+ const successCount = response.results.filter(
+ (r: DeployResult) => r.success,
+ ).length;
+ const failCount = response.results.length - successCount;
+
+ if (failCount === 0) {
+ setSubmitMessage(
+ `Успешно развернуто ${successCount} агент(ов) на ${agents.length} сервер(ах)`,
+ );
+ setAgents([createEmptyAgentConfig()]);
+ } else {
+ const errorMsg = response.results
+ .filter((r: DeployResult) => !r.success)
+ .map(
+ (r: DeployResult) => `${r.ip}: ${r.error || "Неизвестная ошибка"}`,
+ )
+ .join("\n");
+ setSubmitMessage(`Успешно: ${successCount}, Ошибки: ${failCount}`);
+ setSubmitError(`Ошибки при развертывании:\n${errorMsg}`);
+ }
} catch (error) {
setSubmitError(
error instanceof Error
? error.message
- : "Ошибка при подключении к серверам",
+ : "Ошибка при развертывании агентов",
);
} finally {
setIsSubmitting(false);
@@ -167,20 +203,26 @@ export const AddAgentsPage: React.FC = () => {
color: "var(--success-text)",
}}
>
- {submitMessage}
+
+
+ {submitMessage}
+
)}
{submitError && (
- {submitError}
+
+
+ {submitError}
+
)}
diff --git a/frontend/src/pages/admin.page.tsx b/frontend/src/pages/admin.page.tsx
new file mode 100644
index 0000000..1db30d9
--- /dev/null
+++ b/frontend/src/pages/admin.page.tsx
@@ -0,0 +1,730 @@
+import React, { useState, useEffect, useCallback } from "react";
+import { agentApiService } from "@/modules/agent";
+import type { TokenUser, TokenCreate, TokenUpdatePermissions, TokenPasswordReset } from "@/modules/agent";
+import { FiUsers, FiUserPlus, FiEdit2, FiTrash2, FiUnlock, FiLock, FiKey, FiX, FiCheck, FiSearch } from "react-icons/fi";
+
+export const AdminPage: React.FC = () => {
+ const [users, setUsers] = useState([]);
+ const [inactiveUsers, setInactiveUsers] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [successMessage, setSuccessMessage] = useState(null);
+ const [activeTab, setActiveTab] = useState<"active" | "inactive">("active");
+ const [searchQuery, setSearchQuery] = useState("");
+
+ // Modal states
+ const [showCreateModal, setShowCreateModal] = useState(false);
+ const [showEditModal, setShowEditModal] = useState(false);
+ const [showPasswordModal, setShowPasswordModal] = useState(false);
+ const [selectedUser, setSelectedUser] = useState(null);
+
+ // Form states
+ const [createData, setCreateData] = useState({
+ login: "",
+ name: "",
+ last_name: "",
+ password: "",
+ permission_admin: false,
+ permission_manage_agent: false,
+ permission_view: false,
+ is_active: false,
+ });
+
+ const [editData, setEditData] = useState({
+ is_active: false,
+ permission_admin: false,
+ permission_manage_agent: false,
+ permission_view: false,
+ });
+
+ const [passwordData, setPasswordData] = useState({
+ new_password: "",
+ });
+
+ const fetchUsers = useCallback(async () => {
+ setIsLoading(true);
+ setError(null);
+ try {
+ const [active, inactive] = await Promise.all([
+ agentApiService.getUsers(),
+ agentApiService.getInactiveUsers(),
+ ]);
+ setUsers(active);
+ setInactiveUsers(inactive);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при загрузке пользователей");
+ } finally {
+ setIsLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ fetchUsers();
+ }, [fetchUsers]);
+
+ const handleCreateUser = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setError(null);
+ try {
+ await agentApiService.createUser(createData);
+ setSuccessMessage("Пользователь успешно создан");
+ setShowCreateModal(false);
+ setCreateData({
+ login: "",
+ name: "",
+ last_name: "",
+ password: "",
+ permission_admin: false,
+ permission_manage_agent: false,
+ permission_view: false,
+ is_active: false,
+ });
+ await fetchUsers();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при создании пользователя");
+ }
+ };
+
+ const handleUpdatePermissions = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!selectedUser) return;
+
+ setError(null);
+ try {
+ await agentApiService.updateUserPermissions(selectedUser.login, editData);
+ setSuccessMessage("Права пользователя обновлены");
+ setShowEditModal(false);
+ await fetchUsers();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при обновлении прав");
+ }
+ };
+
+ const handleResetPassword = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!selectedUser) return;
+
+ setError(null);
+ try {
+ await agentApiService.resetUserPassword(selectedUser.login, passwordData);
+ setSuccessMessage("Пароль изменен");
+ setShowPasswordModal(false);
+ setPasswordData({ new_password: "" });
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при сбросе пароля");
+ }
+ };
+
+ const handleActivateUser = async (login: string) => {
+ setError(null);
+ try {
+ await agentApiService.activateUser(login);
+ setSuccessMessage("Пользователь активирован");
+ await fetchUsers();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при активации пользователя");
+ }
+ };
+
+ const handleDeactivateUser = async (login: string) => {
+ setError(null);
+ try {
+ await agentApiService.deactivateUser(login);
+ setSuccessMessage("Пользователь деактивирован");
+ await fetchUsers();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при деактивации пользователя");
+ }
+ };
+
+ const handleDeleteUser = async (login: string) => {
+ if (!confirm(`Вы уверены, что хотите удалить пользователя ${login}?`)) return;
+
+ setError(null);
+ try {
+ await agentApiService.deleteUser(login);
+ setSuccessMessage("Пользователь удален");
+ await fetchUsers();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Ошибка при удалении пользователя");
+ }
+ };
+
+ const openEditModal = (user: TokenUser) => {
+ setSelectedUser(user);
+ setEditData({
+ is_active: user.is_active,
+ permission_admin: user.permission_admin,
+ permission_manage_agent: user.permission_manage_agent,
+ permission_view: user.permission_view,
+ });
+ setShowEditModal(true);
+ };
+
+ const openPasswordModal = (user: TokenUser) => {
+ setSelectedUser(user);
+ setPasswordData({ new_password: "" });
+ setShowPasswordModal(true);
+ };
+
+ const filteredUsers = (activeTab === "active" ? users : inactiveUsers).filter(
+ (user) =>
+ user.login.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ user.last_name.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ const inputStyle: React.CSSProperties = {
+ width: "100%",
+ padding: "10px 12px",
+ border: "1px solid var(--border)",
+ borderRadius: "8px",
+ backgroundColor: "var(--input-bg)",
+ color: "var(--text-primary)",
+ fontSize: "14px",
+ };
+
+ const labelStyle: React.CSSProperties = {
+ display: "block",
+ marginBottom: "8px",
+ color: "var(--text-secondary)",
+ fontSize: "14px",
+ fontWeight: 500,
+ };
+
+ const buttonBaseStyle: React.CSSProperties = {
+ padding: "8px 16px",
+ borderRadius: "8px",
+ border: "none",
+ fontSize: "14px",
+ fontWeight: 500,
+ cursor: "pointer",
+ display: "inline-flex",
+ alignItems: "center",
+ gap: "6px",
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+ Управление пользователями
+
+
+ Администрирование учетных записей
+
+
+
+
+
+ {/* Messages */}
+ {successMessage && (
+
+
+ {successMessage}
+
+
+
+ )}
+
+ {error && (
+
+
+ {error}
+
+
+
+ )}
+
+ {/* Tabs and Actions */}
+
+
+ {/* Tabs */}
+
+
+
+
+
+ {/* Create Button */}
+
+
+
+ {/* Search */}
+
+
+ setSearchQuery(e.target.value)}
+ placeholder="Поиск по логину, имени или фамилии..."
+ className="w-full pl-10 pr-4 py-2.5 rounded-lg border"
+ style={{
+ backgroundColor: "var(--input-bg)",
+ borderColor: "var(--border)",
+ color: "var(--text-primary)",
+ }}
+ />
+
+
+
+ {/* Users Table */}
+ {isLoading ? (
+
+ Загрузка...
+
+ ) : filteredUsers.length === 0 ? (
+
+ Пользователи не найдены
+
+ ) : (
+
+
+
+
+ | Логин |
+ Имя |
+ Фамилия |
+ Админ |
+ Управление |
+ Просмотр |
+ Действия |
+
+
+
+ {filteredUsers.map((user, index) => (
+
+ | {user.login} |
+ {user.name} |
+ {user.last_name} |
+
+ {user.permission_admin ? (
+
+ ) : (
+
+ )}
+ |
+
+ {user.permission_manage_agent ? (
+
+ ) : (
+
+ )}
+ |
+
+ {user.permission_view ? (
+
+ ) : (
+
+ )}
+ |
+
+
+
+
+ {activeTab === "active" ? (
+
+ ) : (
+
+ )}
+
+
+ |
+
+ ))}
+
+
+
+ )}
+
+
+ {/* Create User Modal */}
+ {showCreateModal && (
+
+
+
+
Создать пользователя
+
+
+
+
+
+ )}
+
+ {/* Edit Permissions Modal */}
+ {showEditModal && selectedUser && (
+
+
+
+
+ Редактировать: {selectedUser.login}
+
+
+
+
+
+
+ )}
+
+ {/* Reset Password Modal */}
+ {showPasswordModal && selectedUser && (
+
+
+
+
+ Сброс пароля: {selectedUser.login}
+
+
+
+
+
+
+ )}
+
+ );
+};
diff --git a/frontend/src/pages/register.page.tsx b/frontend/src/pages/register.page.tsx
index 98d8843..7406899 100644
--- a/frontend/src/pages/register.page.tsx
+++ b/frontend/src/pages/register.page.tsx
@@ -5,7 +5,7 @@ import { useAuthStore } from "@/modules/auth/store/useAuthStore";
export const RegisterPage: React.FC = () => {
const navigate = useNavigate();
- const { register, isLoading, error, clearError, token } = useAuthStore();
+ const { register, isLoading, error, clearError } = useAuthStore();
const [formData, setFormData] = useState({
login: "",
password: "",
@@ -14,12 +14,7 @@ export const RegisterPage: React.FC = () => {
lastName: "",
});
const [passwordError, setPasswordError] = useState(null);
-
- useEffect(() => {
- if (token) {
- navigate("/");
- }
- }, [token, navigate]);
+ const [successMessage, setSuccessMessage] = useState(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -38,7 +33,17 @@ export const RegisterPage: React.FC = () => {
firstName: formData.firstName,
lastName: formData.lastName,
});
- navigate("/");
+ setSuccessMessage("Аккаунт успешно создан! Теперь вы можете войти.");
+ setFormData({
+ login: "",
+ password: "",
+ confirmPassword: "",
+ firstName: "",
+ lastName: "",
+ });
+ setTimeout(() => {
+ navigate("/auth");
+ }, 2000);
} catch (err) {
// Error is handled by store
}
@@ -82,7 +87,10 @@ export const RegisterPage: React.FC = () => {
className="w-16 h-16 mx-auto mb-4 rounded-full flex items-center justify-center"
style={{ backgroundColor: "var(--bg-secondary)" }}
>
-
+
{
)}
+ {/* Success Message */}
+ {successMessage && (
+
+ {successMessage}
+
+ )}
+
{/* Form */}