Compare commits

...

3 Commits

8 changed files with 362 additions and 77 deletions

144
package-lock.json generated
View File

@ -11,11 +11,15 @@
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"leaflet-defaulticon-compatibility": "^0.1.2", "leaflet-defaulticon-compatibility": "^0.1.2",
"qrcode.react": "^4.0.1", "qrcode.react": "^4.0.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-image-crop": "^11.0.7",
"react-leaflet": "^4.2.1", "react-leaflet": "^4.2.1",
"react-router-dom": "^6.26.2", "react-router-dom": "^6.26.2",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@ -5862,6 +5866,31 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/axobject-query": { "node_modules/axobject-query": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -6384,6 +6413,12 @@
"node-int64": "^0.4.0" "node-int64": "^0.4.0"
} }
}, },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -7792,6 +7827,15 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -13417,6 +13461,28 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/jsonwebtoken": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
"license": "MIT",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jsx-ast-utils": { "node_modules/jsx-ast-utils": {
"version": "3.3.5", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@ -13432,6 +13498,27 @@
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"license": "MIT",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -13592,6 +13679,42 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"license": "MIT"
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
"node_modules/lodash.memoize": { "node_modules/lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -13604,6 +13727,12 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
"node_modules/lodash.sortby": { "node_modules/lodash.sortby": {
"version": "4.7.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@ -16015,6 +16144,12 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/psl": { "node_modules/psl": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@ -16395,6 +16530,15 @@
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/react-image-crop": {
"version": "11.0.7",
"resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-11.0.7.tgz",
"integrity": "sha512-ZciKWHDYzmm366JDL18CbrVyjnjH0ojufGDmScfS4ZUqLHg4nm6ATY+K62C75W4ZRNt4Ii+tX0bSjNk9LQ2xzQ==",
"license": "ISC",
"peerDependencies": {
"react": ">=16.13.1"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",

View File

@ -6,11 +6,15 @@
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"leaflet-defaulticon-compatibility": "^0.1.2", "leaflet-defaulticon-compatibility": "^0.1.2",
"qrcode.react": "^4.0.1", "qrcode.react": "^4.0.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-image-crop": "^11.0.7",
"react-leaflet": "^4.2.1", "react-leaflet": "^4.2.1",
"react-router-dom": "^6.26.2", "react-router-dom": "^6.26.2",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",

View File

@ -28,7 +28,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>YANDEX ZAKLADKI</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,87 +1,140 @@
.user-account { .user-account {
padding: 20px; padding: 40px;
background-color: #f4f4f4; background-color: #ffffff;
border-radius: 10px; border-radius: 15px;
max-width: 400px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
margin: 0 auto; max-width: 600px;
margin: 60px auto;
h1 { font-family: 'Arial', sans-serif;
text-align: center;
h1 {
text-align: center;
font-size: 28px;
color: #333;
margin-bottom: 30px;
font-weight: 500;
}
.user-info, .edit-form {
display: flex;
flex-direction: column;
gap: 20px;
.avatar-container {
display: flex;
justify-content: flex-start;
margin-bottom: 20px; margin-bottom: 20px;
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.avatar-placeholder {
width: 100px;
height: 100px;
background-color: #3498db;
color: white;
font-size: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
} }
.user-info, .edit-form { p, input {
font-size: 18px;
color: #333;
margin-bottom: 15px;
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #f9f9f9;
outline: none;
transition: border-color 0.3s ease;
&:focus {
border-color: #3498db;
}
}
label {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; font-size: 16px;
color: #666;
label { margin-bottom: 15px;
display: flex;
justify-content: space-between; input[type="text"], input[type="email"], input[type="tel"], input[type="file"] {
padding: 10px;
font-size: 16px; font-size: 16px;
border-radius: 8px;
background-color: #f9f9f9;
border: 1px solid #ddd;
margin-top: 10px;
transition: border-color 0.3s ease;
&:focus {
border-color: #3498db;
}
} }
}
input {
padding: 8px; .buttons {
font-size: 14px; display: flex;
border: 1px solid #ccc; justify-content: space-between;
border-radius: 5px;
} button {
padding: 14px 30px;
p { font-size: 16px;
font-size: 18px; cursor: pointer;
margin: 10px 0; border: none;
} border-radius: 8px;
background-color: #3498db;
.buttons { color: white;
display: flex; transition: background-color 0.3s ease, transform 0.2s ease;
justify-content: space-between;
&:hover {
button { background-color: #2980b9;
padding: 10px 20px; transform: scale(1.03);
font-size: 14px; }
cursor: pointer;
border: none; &:last-child {
border-radius: 5px; background-color: #e74c3c;
background-color: #3498db;
color: white;
transition: background-color 0.3s ease;
&:hover { &:hover {
background-color: #2980b9; background-color: #c0392b;
}
&:last-child {
background-color: #e74c3c;
&:hover {
background-color: #c0392b;
}
} }
} }
} }
} }
}
.edit-button {
margin-top: 20px; .edit-button {
padding: 10px 25px; margin-top: 30px;
font-size: 16px; padding: 14px 35px;
font-weight: bold; font-size: 18px;
cursor: pointer; font-weight: 600;
background-color: #2ecc71; /* Зелёный цвет */ cursor: pointer;
color: white; background-color: #2ecc71;
border: none; color: white;
border-radius: 5px; border: none;
transition: background-color 0.3s ease, transform 0.2s ease; border-radius: 8px;
transition: background-color 0.3s ease, transform 0.3s ease;
&:hover {
background-color: #27ae60; &:hover {
transform: scale(1.05); /* Небольшой зум при наведении */ background-color: #27ae60;
} transform: scale(1.05);
&:active {
background-color: #1e8449;
transform: scale(0.98); /* Легкий уменьшенный эффект при нажатии */
}
} }
}
&:active {
background-color: #1e8449;
transform: scale(0.98);
}
}
}

View File

@ -1,5 +1,6 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { QRCodeCanvas } from "qrcode.react"; import { QRCodeCanvas } from "qrcode.react";
import axios from 'axios';
import '../Devices.css'; import '../Devices.css';
const Devices = () => { const Devices = () => {
@ -7,10 +8,26 @@ const Devices = () => {
const [selectedDevice, setSelectedDevice] = useState(null); const [selectedDevice, setSelectedDevice] = useState(null);
const [deviceName, setDeviceName] = useState(''); const [deviceName, setDeviceName] = useState('');
const [deviceStatus, setDeviceStatus] = useState(''); const [deviceStatus, setDeviceStatus] = useState('');
const [isRegenerating, setIsRegenerating] = useState(false); // отображение генерации токена
const [showFullToken, setShowFullToken] = useState({}); // управление отображением токена
useEffect(() => {
// получение устройств
const fetchDevices = async () => {
try {
const response = await axios.get('/api/devices'); // потом нужно будет заменить на нашу апишку
setDevices(response.data.devices);
} catch (error) {
console.error('Ошибка при загрузке устройств:', error);
}
};
fetchDevices();
}, []);
const addDevice = () => { const addDevice = () => {
if (deviceName) { if (deviceName) {
const newDevice = { name: deviceName, status: deviceStatus || 'Неактивен', id: Date.now() }; const newDevice = { name: deviceName, status: deviceStatus || 'Неактивен', id: Date.now(), access_key: '' };
setDevices([...devices, newDevice]); setDevices([...devices, newDevice]);
setDeviceName(''); setDeviceName('');
setDeviceStatus(''); setDeviceStatus('');
@ -38,6 +55,25 @@ const Devices = () => {
} }
}; };
// генерация токена
const handleShowFullToken = (deviceId) => {
setShowFullToken(prevState => ({ ...prevState, [deviceId]: !prevState[deviceId] }));
};
// пересоздание токена
const handleRegenerateToken = async (deviceId) => {
setIsRegenerating(true);
try {
const response = await axios.post(`/api/devices/${deviceId}/regenerate-token`); // тоже заменить на апишку
setDevices(devices.map(device =>
device.id === deviceId ? { ...device, access_key: response.data.access_key } : device
));
} catch (error) {
console.error('Ошибка при пересоздании токена:', error);
}
setIsRegenerating(false);
};
const generateQR = (device) => { const generateQR = (device) => {
return ( return (
<QRCodeCanvas value={JSON.stringify(device)} size={128} /> <QRCodeCanvas value={JSON.stringify(device)} size={128} />
@ -65,6 +101,7 @@ const Devices = () => {
<th>Название</th> <th>Название</th>
<th>Статус</th> <th>Статус</th>
<th>QR Код</th> <th>QR Код</th>
<th>Токен устройства</th>
<th>Действия</th> <th>Действия</th>
</tr> </tr>
</thead> </thead>
@ -74,8 +111,24 @@ const Devices = () => {
<td>{device.name}</td> <td>{device.name}</td>
<td>{device.status}</td> <td>{device.status}</td>
<td>{generateQR(device)}</td> <td>{generateQR(device)}</td>
<td>
{showFullToken[device.id] ? (
device.access_key
) : (
<span>{device.access_key.slice(0, 6)}...</span>
)}
<button onClick={() => handleShowFullToken(device.id)}>
{showFullToken[device.id] ? 'Скрыть' : 'Показать'}
</button>
</td>
<td> <td>
<button onClick={editDevice}>Редактировать</button> <button onClick={editDevice}>Редактировать</button>
<button
onClick={() => handleRegenerateToken(device.id)}
disabled={isRegenerating}
>
{isRegenerating ? 'Пересоздание...' : 'Пересоздать токен'}
</button>
</td> </td>
</tr> </tr>
))} ))}

View File

@ -6,6 +6,7 @@ const UserAccount = () => {
username: 'Мамут Рахал', username: 'Мамут Рахал',
email: 'yatupoidayn@mail.ru', email: 'yatupoidayn@mail.ru',
phone: '+666', phone: '+666',
avatar: '', // Новое поле для аватара
}); });
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
@ -19,6 +20,21 @@ const UserAccount = () => {
})); }));
}; };
// Функция для загрузки аватара
const handleAvatarChange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setFormData((prevData) => ({
...prevData,
avatar: reader.result,
}));
};
reader.readAsDataURL(file);
}
};
const handleSave = () => { const handleSave = () => {
setUserData(formData); setUserData(formData);
setIsEditing(false); setIsEditing(false);
@ -62,6 +78,14 @@ const UserAccount = () => {
onChange={handleChange} onChange={handleChange}
/> />
</label> </label>
<label>
Загрузить аватар:
<input
type="file"
accept="image/*"
onChange={handleAvatarChange}
/>
</label>
<div className="buttons"> <div className="buttons">
<button onClick={handleSave}>Сохранить</button> <button onClick={handleSave}>Сохранить</button>
<button onClick={handleCancel}>Отмена</button> <button onClick={handleCancel}>Отмена</button>
@ -69,6 +93,13 @@ const UserAccount = () => {
</div> </div>
) : ( ) : (
<div className="user-info"> <div className="user-info">
<div className="avatar-container">
{userData.avatar ? (
<img src={userData.avatar} alt="User Avatar" className="avatar" />
) : (
<div className="avatar-placeholder">A</div>
)}
</div>
<p><strong>Имя:</strong> {userData.username}</p> <p><strong>Имя:</strong> {userData.username}</p>
<p><strong>Email:</strong> {userData.email}</p> <p><strong>Email:</strong> {userData.email}</p>
<p><strong>Телефон:</strong> {userData.phone}</p> <p><strong>Телефон:</strong> {userData.phone}</p>