вывела список курсов из бека
This commit is contained in:
parent
bbffbd1fdb
commit
4b38fa67b0
18
SSL/localhost.crt
Normal file
18
SSL/localhost.crt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC1zCCAb+gAwIBAgIUPHdL8t6goPcrwjyu1bR0S+Ts7O8wDQYJKoZIhvcNAQEL
|
||||||
|
BQAwFDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTI1MDgyMTA4NTUzMFoXDTI2MDgy
|
||||||
|
MTA4NTUzMFowFDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEF
|
||||||
|
AAOCAQ8AMIIBCgKCAQEAmsutLy1yUE1TCvs475LV0UwGrSvuW818ccxh5LEKUiad
|
||||||
|
iWuv0NL5/OxaG+dWJvVzOz9uBafcxbBYwJ65XqOTXM44FKCKuDtYG/7E2TDc5KPH
|
||||||
|
/7f/GCFla+2k/aep2VagtTK+AFRiTFcmm9C34mFu34O/2fiAiNROiRBE+SxajPdQ
|
||||||
|
DEd71yyCypnJWMdvxODToaRSwBNPMCOd/QceHgWUxFuC3813ewASuqKMP+M9ZNEU
|
||||||
|
QfFPypkgqGvDMUun6olXHs34qYAlu8w1Fk9fu6+9JAEKONtvXaGQcBkF0LBwTicZ
|
||||||
|
LgDheKc2ZG0wHyrFSs8rMee4zd/gvcgaw7G1bxgcbQIDAQABoyEwHzAdBgNVHQ4E
|
||||||
|
FgQU9PnSNORZygTvdFeXfjbeYsxbbs8wDQYJKoZIhvcNAQELBQADggEBABo5fok7
|
||||||
|
NeeX/0UL01HEy/BwGNxZnscl3tAfTsLTCwhGC4ADd4ppGROTfv+R68eMSM8ZFtR7
|
||||||
|
wLO6zK/VE0bidyw9jXN1Tp9etv25IjfkQB4LqNcs6b7LgoeXWklVW9Ap4K2h6A6q
|
||||||
|
C1lBs8nVW6YtTHE2BPF2dNFMuuqnYiquw3bEikjrZ3waEYnxYP+l9eAFgcAEqyQB
|
||||||
|
5RKgqq6fv4q7PxyXmvKy18OFxalVogoUFNiyZOoNpgj8ZbqIKru20oU/Xi821yAU
|
||||||
|
xhGrQn/aY5IshTpNsjzxcP9LYQjOFUId/ISxA9tPucHctkMm/dnbcJmoPZlStIaK
|
||||||
|
j3xnOy8TLQLoNG4=
|
||||||
|
-----END CERTIFICATE-----
|
28
SSL/localhost.key
Normal file
28
SSL/localhost.key
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCay60vLXJQTVMK
|
||||||
|
+zjvktXRTAatK+5bzXxxzGHksQpSJp2Ja6/Q0vn87Fob51Ym9XM7P24Fp9zFsFjA
|
||||||
|
nrleo5NczjgUoIq4O1gb/sTZMNzko8f/t/8YIWVr7aT9p6nZVqC1Mr4AVGJMVyab
|
||||||
|
0LfiYW7fg7/Z+ICI1E6JEET5LFqM91AMR3vXLILKmclYx2/E4NOhpFLAE08wI539
|
||||||
|
Bx4eBZTEW4LfzXd7ABK6oow/4z1k0RRB8U/KmSCoa8MxS6fqiVcezfipgCW7zDUW
|
||||||
|
T1+7r70kAQo4229doZBwGQXQsHBOJxkuAOF4pzZkbTAfKsVKzysx57jN3+C9yBrD
|
||||||
|
sbVvGBxtAgMBAAECggEAHe4dW5aiOIgm8yPdn6cIzDnyvYeVxC7gbcF6uSUJLZa1
|
||||||
|
VbWablpox0Bs8OHDTO+fGvjZ/WJ2Y8wD8SSPi469xs9B1S0wfUxOLkWNyr/xqgzH
|
||||||
|
9jgdrQ0CBwMRaJFDXFhtZPO9fKNUSVX9i1mpQG6d6T5YeV0c/zfrwoFAkNxEWuaG
|
||||||
|
hmp+F7OW3QcvZhLm3jJo2PnWpVfhbcMyw2MDVfSVzcZrZS1qFp0AvalcOI3z1QWd
|
||||||
|
1CsJYVASc7adyfev0qJ64EmgdI6sIBbI1cxb7gDLDX48f2rSwznEvgQ1gKUdZ5a5
|
||||||
|
FBI/hSV8XkDDYNzaniFeQavHZ8BjrU66UxCd0x3JoQKBgQDaW54nommnUQYzOmYB
|
||||||
|
0tdAHBDrxjSQt5aUf+XngWMykpIYEYA0rtS5Q1i4CUVK+F4bKfCiURf4PSyp9vOs
|
||||||
|
4DLOC+e3O+ZGbXbIgmi9SuU6//T+wJY/NHppo1rIZldTMNNCdltW4nroO0ROSebW
|
||||||
|
Ot8I0JDLexwS6wedfGsnirggIQKBgQC1evu21yor0pZe6laPzsnnArX7NSdPiA8L
|
||||||
|
TidODrGXO9PzB2CifaQENBFe4SHKTVk5FtqUd5I0EJ32yUBDHbnqfVIXV7/juZxA
|
||||||
|
Ez98NyD70wT4FmiHVAG2SrN96NfaRASBaWlGk8oAA1P4pOQao6IVpE4ICQua1qXZ
|
||||||
|
jVa6gSIizQKBgHzKS1J/YT478f0o7M8x/c4nvAvi1Hu4UBhCF9P8gAKQ/Wk/5zM0
|
||||||
|
j2HpeTSEJ9O2qaGAkINTMQ2veG0SMySjZKWY7C4tz2aEfJQO42j6+pKDYTKOQc+R
|
||||||
|
YDwrHWDWDCYBC1s6ZOz8th0ucdUEKyZXsstI1tdTXjH5m/qG9n48qwnBAoGBAIqC
|
||||||
|
UclL0QjfUhshGW3qsthYjlIrUFR4EJtdS8xclJhLdkmfUNbTs7Htl3BlvWdsv3M+
|
||||||
|
CReVZwXLxPRMx3oyHupkOZpNQ8xnpmSVDDgez1LDzKAYYjEfIatiujmX097nWNT4
|
||||||
|
gfv24g4+fAFiwCHAz/1aiJ/DX5MOzkgLgq0Xz9jlAoGBAL521PPgAWdNngZh/3IZ
|
||||||
|
bcC88v3bgreZfw3FL6FEMiPD/lO4h021LqkwvicL9TlgsFxsdpZfEX01jMF4J23o
|
||||||
|
Ll0JQ4r6zPl0kO0dmeEN3vt/YWToekUfE9fOXjuXIPSgMU+8+xREO8PBtgE9q8WR
|
||||||
|
Nk49oeSsHE4dDm/hMnWUgRcp
|
||||||
|
-----END PRIVATE KEY-----
|
105
SSL/ssl
Normal file
105
SSL/ssl
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
самоподписанный сертификат. проблема в том, что не полуается его в доверенные добавть
|
||||||
|
|
||||||
|
1. Запустить эмулятор, потом добавить сертификат в sdcard/Download
|
||||||
|
adb push "/home/tatyana/Рабочий стол/ssl_test/localhost.crt" /sdcard/Download/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tatyana@fedora:~/Рабочий стол/ssl_test$ adb push "/home/tatyana/Рабочий стол/ssl_test/localhost.crt"
|
||||||
|
/sdcard/Download/
|
||||||
|
/home/tatyana/Рабочий стол/...pped. 12.2 MB/s (1046 bytes in 0.000s)
|
||||||
|
tatyana@fedora:~/Рабочий стол/ssl_test$
|
||||||
|
|
||||||
|
|
||||||
|
2. Сверзу нажать груг = хоум, Зайти в настройки устройства в самом эмуляторе
|
||||||
|
|
||||||
|
3. Найти "Encryption and Credentials /«Шифрование и учетные данные
|
||||||
|
|
||||||
|
4. Выбираем установка (второй пункт vpn ....)
|
||||||
|
|
||||||
|
5. сертификат будет в загрузках (пункт 1) на карте
|
||||||
|
|
||||||
|
6. Объединить ключ и сертификат
|
||||||
|
openssl pkcs12 -export -inkey localhost.key -in localhost.crt -out localhost.p12
|
||||||
|
|
||||||
|
7. перенести на устройство
|
||||||
|
adb push /home/tatyana/Рабочий\ стол/ssl_test/localhost.p12 /sdcard/Download/
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
2 способ поместить в доверенные в ручную
|
||||||
|
|
||||||
|
app/
|
||||||
|
src/
|
||||||
|
main/
|
||||||
|
res/
|
||||||
|
raw/
|
||||||
|
your_cert.der
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Конечно! Ниже я подробно объясню, как доверять самоподписанному сертификату в эмуляторе Android для разработки, используя network_security_config.xml. Это наиболее безопасный и удобный способ для тестирования в приложениях без необходимости рутировать устройство или эмулятор.
|
||||||
|
Подробная инструкция: доверие к самоподписанному сертификату в Android через network_security_config.xml
|
||||||
|
Шаг 1. Подготовьте ваш сертификат
|
||||||
|
1.1. Экспортируйте сертификат в нужном формате
|
||||||
|
|
||||||
|
Если у вас есть .p12 или .crt, убедитесь, что он в формате .der или .pem.
|
||||||
|
|
||||||
|
Для этого используйте команду OpenSSL:
|
||||||
|
|
||||||
|
bash
|
||||||
|
|
||||||
|
# Извлечь сертификат из p12 в PEM
|
||||||
|
openssl pkcs12 -in localhost.p12 -clcerts -nokeys -out localhost.pem
|
||||||
|
|
||||||
|
или
|
||||||
|
bash
|
||||||
|
|
||||||
|
# Конвертировать PEM в DER (бинарный формат)
|
||||||
|
openssl x509 -in localhost.pem -outform DER -out localhost.der
|
||||||
|
|
||||||
|
Важно: Назовите файл, например, your_cert.der.
|
||||||
|
1.2. Поместите сертификат в проект
|
||||||
|
|
||||||
|
В вашем проекте Android создайте папку res/raw/ (если ее еще нет).
|
||||||
|
Поместите туда файл your_cert.der.
|
||||||
|
|
||||||
|
Шаг 2. Создайте файл конфигурации network_security_config.xml
|
||||||
|
|
||||||
|
Этот файл укажет вашему приложению доверять вашему сертификату.
|
||||||
|
2.1. Создайте папку и файл
|
||||||
|
|
||||||
|
В вашем проекте перейдите по пути: app/src/main/res/xml/
|
||||||
|
Если папки xml нет, создайте ее.
|
||||||
|
Внутри создайте файл network_security_config.xml.
|
||||||
|
|
||||||
|
2.2. Напишите содержимое файла
|
||||||
|
xml
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config>
|
||||||
|
<domain>your.server.domain</domain> <!-- замените на ваш домен или IP -->
|
||||||
|
<trust-anchors>
|
||||||
|
<certificates src="@raw/your_cert" />
|
||||||
|
</trust-anchors>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
||||||
|
|
||||||
|
Обратите внимание:
|
||||||
|
|
||||||
|
Замените your.server.domain на ваш реальный сервер или IP, к которому вы подключаетесь.
|
||||||
|
Если вы хотите доверять всему трафику (не рекомендуется), можно оставить <domain>, а не указывать конкретный.
|
||||||
|
|
||||||
|
Шаг 3. Обновите AndroidManifest.xml
|
||||||
|
|
||||||
|
Добавьте ссылку на конфигурацию безопасности в ваш манифест:
|
||||||
|
xml
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
... >
|
||||||
|
...
|
||||||
|
</application>
|
||||||
|
|
||||||
|
|
@ -4,9 +4,9 @@ import Welcome from "./pages/Welcome";
|
|||||||
import Login from "./pages/Login";
|
import Login from "./pages/Login";
|
||||||
import Home from "./pages/Home";
|
import Home from "./pages/Home";
|
||||||
import ForgotPasword from "./pages/ForgotPassword";
|
import ForgotPasword from "./pages/ForgotPassword";
|
||||||
import Courses from "./pages/Courses";
|
import { Courses } from "./pages/Courses";
|
||||||
import CourseExercises from "./pages/CourseExercises";
|
import CourseExercises from "./pages/CourseExercises";
|
||||||
import Exercise from "./pages/Exercise";
|
import { Exercise } from "./pages/Exercise";
|
||||||
import Settings from "./pages/Settings";
|
import Settings from "./pages/Settings";
|
||||||
import CourseComplete from "./pages/CourseComplete";
|
import CourseComplete from "./pages/CourseComplete";
|
||||||
|
|
||||||
|
133
src/pages/Courses old.tsx
Normal file
133
src/pages/Courses old.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import BottomNavigation from "../components/BottomNavigation"
|
||||||
|
import { useHistory } from "react-router-dom"
|
||||||
|
import HeaderNav from "../components/HeaderNav"
|
||||||
|
import { getRouteCourseExercises } from "../shared/consts/router"
|
||||||
|
|
||||||
|
// Типизация Exercise
|
||||||
|
interface Exercise {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
url_file_img: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProgressLine = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="h-full bg-gradient-to-r from-orange-300 to-orange-500 transition-all duration-500 absolute left-0 top-0 rounded-l-xl"
|
||||||
|
style={{ width: '85%' }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnalitcsCards = () => {
|
||||||
|
return (
|
||||||
|
<div className="px-6 mb-8" >
|
||||||
|
<div className="text-center relative ">
|
||||||
|
<div className="w-full h-10 bg-gray-500 rounded-xl relative flex items-center justify-center mb-4 shadow-xl">
|
||||||
|
{/* Прогрессная часть */}
|
||||||
|
<ProgressLine />
|
||||||
|
|
||||||
|
{/* Текст поверх линии */}
|
||||||
|
<div className="absolute text-white font-semibold text-base">Вы прошли реабилитацию на {85}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Courses = () => {
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
// Внутренний компонент для загрузки упражнений
|
||||||
|
const Exercises = () => {
|
||||||
|
const [exercises, setExercises] = useState<Exercise[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/pacient/exercises')
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) throw new Error('Ошибка при загрузке упражнений');
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((data: Exercise[]) => {
|
||||||
|
console.log('test');
|
||||||
|
setExercises(data);
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
setError(err.message);
|
||||||
|
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Можно добавить заголовок или обертку */}
|
||||||
|
<ul>
|
||||||
|
{exercises.map(ex => (
|
||||||
|
<li key={ex.id} className="mb-4 p-4 bg-white/20 rounded-lg shadow-md hover:bg-white/30 transition">
|
||||||
|
<h3 className="font-semibold text-[#5F5C5C] mb-2">{ex.title}</h3>
|
||||||
|
<p className="text-sm text-[#5F5C5C]/70 mb-2">{ex.desc}</p>
|
||||||
|
{/* Вставка изображения */}
|
||||||
|
<img src={ex.url_file_img} alt={ex.title} className="w-full rounded" />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
||||||
|
<HeaderNav item='Курсы' text='все назначенные' />
|
||||||
|
<AnalitcsCards />
|
||||||
|
|
||||||
|
{/* Внедрение компонента упражнений */}
|
||||||
|
<Exercises />
|
||||||
|
|
||||||
|
{/* Можно оставить закомментированный блок или убрать его */}
|
||||||
|
{/*
|
||||||
|
<div className="px-6 mb-8">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h2 className="text-xl font-black text-[#5F5C5C]">Мои курсы</h2>
|
||||||
|
<button onClick={() => history.push("/courses")} className="text-[#2BACBE] text-sm font-bold hover:underline">
|
||||||
|
Все курсы
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{courses.map((course) => (
|
||||||
|
<div
|
||||||
|
key={course.id}
|
||||||
|
onClick={() => history.push(getRouteCourseExercises(':id'))}
|
||||||
|
className="bg-white/30 backdrop-blur-2xl rounded-3xl p-6 border border-white/20 shadow-xl cursor-pointer hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02]"
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-5">
|
||||||
|
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="font-semibold text-[#5F5C5C] text-lg mb-2">{course.name}</h3>
|
||||||
|
<div className="bg-white/50 rounded-full h-3 mb-2 overflow-hidden">
|
||||||
|
<div
|
||||||
|
className={`bg-gradient-to-r ${course.color} h-3 rounded-full transition-all duration-700 shadow-sm`}
|
||||||
|
style={{ width: `${course.progress}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-[#5F5C5C]/70 font-semibold">{course.progress}% завершено</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-[#2BACBE] transform transition-transform duration-300 hover:translate-x-1">
|
||||||
|
{/* Иконка стрелки */}
|
||||||
|
{/*<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>*/}
|
||||||
|
{/*</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
<BottomNavigation />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,93 +1,100 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import { useState, useEffect } from 'react';
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
import BottomNavigation from "../components/BottomNavigation"
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom" // для v5
|
||||||
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
import HeaderNav from "../components/HeaderNav"
|
||||||
import { getRouteCourseExercises } from "../shared/consts/router"
|
import { connect } from '../confconnect';
|
||||||
|
|
||||||
const ProgressLine = () => {
|
interface Course {
|
||||||
return (<div
|
ID: number;
|
||||||
className="h-full bg-gradient-to-r from-orange-300 to-orange-500 transition-all duration-500 absolute left-0 top-0 rounded-l-xl"
|
title: string;
|
||||||
style={{ width: '85%' }}
|
desc: string;
|
||||||
/>
|
url_file_img: string;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnalitcsCards = () => {
|
export const Courses = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
{/* Analytics Cards */ }
|
const [courses, setCourses] = useState<Course[]>([]);
|
||||||
return (
|
const [error, setError] = useState<string>('');
|
||||||
<div className="px-6 mb-8" >
|
const token = localStorage.getItem('authToken');
|
||||||
<div className="text-center relative ">
|
|
||||||
<div className="w-full h-10 bg-gray-500 rounded-xl relative flex items-center justify-center mb-4 shadow-xl">
|
|
||||||
{/* Прогрессная часть */}
|
|
||||||
<ProgressLine/>
|
|
||||||
|
|
||||||
{/* Текст поверх линии */}
|
useEffect(() => {
|
||||||
<div className="absolute text-white font-semibold text-base">Вы прошли реабилитацию на {85}%</div>
|
if (!token) {
|
||||||
</div>
|
setError('Токен не найден');
|
||||||
</div>
|
return;
|
||||||
</div>
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
connect.get('/pacient/courses', {
|
||||||
const history = useHistory()
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
console.log('Response status:', response.status);
|
||||||
|
|
||||||
const courses = [
|
// Предполагаемая структура:
|
||||||
{ id: 1, name: "Восстановление колена", progress: 75, color: "from-[#2BACBE] to-[#2BACBE]/80" },
|
// response.data.courses — массив пользователей
|
||||||
{ id: 2, name: "Укрепление спины", progress: 45, color: "from-[#5F5C5C] to-[#5F5C5C]/80" },
|
const users = response.data.courses || [];
|
||||||
{ id: 3, name: "Реабилитация плеча", progress: 90, color: "from-[#2BACBE]/80 to-[#5F5C5C]" },
|
|
||||||
];
|
// Собираем все курсы из всех пользователей
|
||||||
|
const allCourses: Course[] = [];
|
||||||
|
|
||||||
|
users.forEach(user => {
|
||||||
|
if (user.Courses && Array.isArray(user.Courses)) {
|
||||||
|
user.Courses.forEach(course => {
|
||||||
|
// Можно добавить проверку или преобразование
|
||||||
|
allCourses.push({
|
||||||
|
ID: course.id, // или course.ID, зависит от структуры
|
||||||
|
title: course.title,
|
||||||
|
desc: course.desc,
|
||||||
|
url_file_img: course.url_file_img,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setCourses(allCourses);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (error.response) {
|
||||||
|
console.error('Ошибка ответа сервера:', error.response.status, error.response.data);
|
||||||
|
setError(`Ошибка сервера: ${error.response.status}`);
|
||||||
|
} else if (error.request) {
|
||||||
|
console.error('Нет ответа от сервера:', error.request);
|
||||||
|
setError('Нет ответа от сервера');
|
||||||
|
} else {
|
||||||
|
console.error('Ошибка при настройке запроса:', error.message);
|
||||||
|
setError(`Ошибка: ${error.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
||||||
<HeaderNav item='Курсы' text='все назначенные' />
|
<HeaderNav item='Курсы' text='все назначенные' />
|
||||||
<AnalitcsCards />
|
|
||||||
|
|
||||||
|
{error && <p className="text-red-500">{error}</p>}
|
||||||
|
|
||||||
{/* Courses */}
|
{/* Выводим список курсов */}
|
||||||
<div className="px-6 mb-8">
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
{courses.length > 0 ? (
|
||||||
<h2 className="text-xl font-black text-[#5F5C5C]">Мои курсы</h2>
|
courses.map(course => (
|
||||||
{/* <button onClick={() => history.push("/courses")} className="text-[#2BACBE] text-sm font-bold hover:underline">
|
<div key={course.ID} className="border p-4 mb-4 rounded shadow">
|
||||||
Все курсы
|
<h2 className="text-xl font-bold">{course.title}</h2>
|
||||||
</button> */}
|
<p>{course.desc}</p>
|
||||||
</div>
|
{course.url_file_img && (
|
||||||
<div className="space-y-4">
|
<img src={course.url_file_img} alt={course.title} className="mt-2 w-full h-auto" />
|
||||||
{courses.map((course) => (
|
)}
|
||||||
<div
|
|
||||||
key={course.id}
|
|
||||||
onClick={() => history.push(getRouteCourseExercises(':id'))}
|
|
||||||
className="bg-white/30 backdrop-blur-2xl rounded-3xl p-6 border border-white/20 shadow-xl cursor-pointer hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02]"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-5">
|
|
||||||
|
|
||||||
<div className="flex-1">
|
|
||||||
<h3 className="font-semibold text-[#5F5C5C] text-lg mb-2">{course.name}</h3>
|
|
||||||
<div className="bg-white/50 rounded-full h-3 mb-2 overflow-hidden">
|
|
||||||
<div
|
|
||||||
className={`bg-gradient-to-r ${course.color} h-3 rounded-full transition-all duration-700 shadow-sm`}
|
|
||||||
style={{ width: `${course.progress}%` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-[#5F5C5C]/70 font-semibold">{course.progress}% завершено</p>
|
|
||||||
</div>
|
|
||||||
<div className="text-[#2BACBE] transform transition-transform duration-300 hover:translate-x-1">
|
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
!error && <p>Нет назначенных курсов</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BottomNavigation />
|
<BottomNavigation />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Home
|
|
@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import type React from "react"
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom"
|
||||||
|
|
||||||
@ -9,7 +9,19 @@ import HeaderNav from "../components/HeaderNav"
|
|||||||
import video from "../assets/img/video.mov"
|
import video from "../assets/img/video.mov"
|
||||||
|
|
||||||
|
|
||||||
const Exercise: React.FC = () => {
|
export interface Exercise {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
url_file: string;
|
||||||
|
url_file_img: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const Exercise = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
// const { id } = useParams<{ id: string }>()
|
// const { id } = useParams<{ id: string }>()
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
@ -104,7 +116,7 @@ const Exercise: React.FC = () => {
|
|||||||
|
|
||||||
<HeaderNav item='Надо разобраться?' text='упражнение' />
|
<HeaderNav item='Надо разобраться?' text='упражнение' />
|
||||||
|
|
||||||
{/* Video/Image Section */}
|
|
||||||
<div className="px-4 sm:px-6 mt-10 mb-6">
|
<div className="px-4 sm:px-6 mt-10 mb-6">
|
||||||
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/20 backdrop-blur-2xl">
|
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/20 backdrop-blur-2xl">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -129,7 +141,7 @@ const Exercise: React.FC = () => {
|
|||||||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* Live indicators */}
|
|
||||||
{isPlaying && (
|
{isPlaying && (
|
||||||
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
||||||
<div className="w-3 h-3 bg-orange-500 rounded-full animate-pulse "></div>
|
<div className="w-3 h-3 bg-orange-500 rounded-full animate-pulse "></div>
|
||||||
@ -138,14 +150,14 @@ const Exercise: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Timer Display */}
|
|
||||||
<div className="absolute top-4 right-4 bg-cyan-50 backdrop-blur-sm px-3 py-1 rounded-xl">
|
<div className="absolute top-4 right-4 bg-cyan-50 backdrop-blur-sm px-3 py-1 rounded-xl">
|
||||||
<span className="text-gray-800 text-sm font-bold">{formatTime(currentTime)}</span>
|
<span className="text-gray-800 text-sm font-bold">{formatTime(currentTime)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Exercise Steps - Vertical Scroll */}
|
|
||||||
<div className="px-4 sm:px-6 space-y-4 mb-6">
|
<div className="px-4 sm:px-6 space-y-4 mb-6">
|
||||||
{exerciseSteps.map((step, index) => (
|
{exerciseSteps.map((step, index) => (
|
||||||
<div
|
<div
|
||||||
@ -225,4 +237,4 @@ const Exercise: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Exercise
|
|
||||||
|
70
ответы сервера
Normal file
70
ответы сервера
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
curl -i -X OPTIONS http://localhost:8093/auth/api/login -H "Origin: http://localhost" -H "Access-Control-Request-Method: POST"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tatyana@fedora:~/Рабочий стол/RehabMobileApp/RehabMobileApp/android$ curl -i -X OPTIONS http://localhost:8093/auth/api/login -H "Origin: http://localhost" -H "Access-Control-Request-Method: POST"
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Access-Control-Allow-Credentials: true
|
||||||
|
Access-Control-Allow-Headers: Accept-Language,Access-Control-Allow-Origin,Authorization,Content-Language,X-Csrf-Token,X-Xsrf-Token,Origin,X-Requested-With,Content-Type,Accept
|
||||||
|
Access-Control-Allow-Methods: PUT,GET,POST,PATCH,OPTIONS,DELETE
|
||||||
|
Access-Control-Allow-Origin: http://localhost
|
||||||
|
Vary: Origin
|
||||||
|
Vary: Access-Control-Request-Method
|
||||||
|
Vary: Access-Control-Request-Headers
|
||||||
|
Date: Fri, 22 Aug 2025 08:09:40 GMT
|
||||||
|
|
||||||
|
|
||||||
|
tatyana@fedora:~/Рабочий стол/RehabMobileApp/RehabMobileApp/android$ curl -i -X POST http://localhost:8093/auth/api/login \
|
||||||
|
-H "Origin: http://localhost" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"Email": "krasnova@mail.ru", "Password": "Tatyana1"}'
|
||||||
|
|
||||||
|
|
||||||
|
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8093/pacient/courses
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"courses": [
|
||||||
|
{
|
||||||
|
"CreatedAt": "2025-08-19T10:50:04.454157+03:00",
|
||||||
|
"UpdatedAt": "2025-08-19T10:50:04.454157+03:00",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"ID": 25,
|
||||||
|
"Name": "krasnova@mail.ru",
|
||||||
|
"Email": "krasnova@mail.ru",
|
||||||
|
"Password": "$2a$10$ryI/OSON56IlmDL0sAHhmOk9vhFkuTRieFlj32p/cKYCrPWGOJZKC",
|
||||||
|
"Role": "",
|
||||||
|
"UserDoctorID": 0,
|
||||||
|
"ExerciseCourses": null,
|
||||||
|
"Courses": [
|
||||||
|
{
|
||||||
|
"ID": 0,
|
||||||
|
"CreatedAt": "2025-08-15T11:46:58.879953+03:00",
|
||||||
|
"UpdatedAt": "2025-08-15T11:46:58.879953+03:00",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"id": 1,
|
||||||
|
"title": "Восстановление",
|
||||||
|
"desc": "Восстановиться",
|
||||||
|
"time_course": "",
|
||||||
|
"day_course": 0,
|
||||||
|
"url_file_img": "https://vivasport.ru/assets/images/news/12555/spine-training-1.webp",
|
||||||
|
"users": null,
|
||||||
|
"exercises": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CreatedAt": "2025-08-20T14:19:19.754239+03:00",
|
||||||
|
"UpdatedAt": "2025-08-20T14:19:19.754239+03:00",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"ID": 26,
|
||||||
|
"Name": "krasnova5@mail.ru",
|
||||||
|
"Email": "krasnova5@mail.ru",
|
||||||
|
"Password": "$2a$10$sdcCn51708LV5y6wsmOho.dLtpDGf43muXobIcvGYQ1.TtB1WxFju",
|
||||||
|
"Role": "",
|
||||||
|
"UserDoctorID": 0,
|
||||||
|
"ExerciseCourses": null,
|
||||||
|
"Courses": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sessionname": "krasnova@mail.ru"
|
Loading…
x
Reference in New Issue
Block a user