1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2025-11-23 22:05:15 +02:00
This commit is contained in:
Anton Titovets
2025-11-22 16:05:24 +03:00
parent 23951963f7
commit 3b18da2b3e
2 changed files with 0 additions and 835 deletions

View File

@@ -1,303 +0,0 @@
import React, { useState, useMemo } from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import Heading from '@theme/Heading';
import styles from './courses.module.css';
import CustomFooter from '@site/src/components/CustomFooter';
import coursesData from '../../data/courses.json';
const CoursesPage = () => {
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState('all');
const [showSubscriptionModal, setShowSubscriptionModal] = useState(false);
const [showPurchaseModal, setShowPurchaseModal] = useState(false);
const [selectedCourse, setSelectedCourse] = useState(null);
const { courses, categories, subscriptionInfo } = coursesData;
const filteredCourses = useMemo(() => {
return courses.filter(course => {
const matchesSearch = course.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
course.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
course.tags.some(tag => tag.toLowerCase().includes(searchQuery.toLowerCase()));
const matchesCategory = selectedCategory === 'all' || course.category === selectedCategory;
return matchesSearch && matchesCategory;
});
}, [searchQuery, selectedCategory]);
const handlePurchaseClick = (course) => {
setSelectedCourse(course);
setShowPurchaseModal(true);
};
const SubscriptionModal = () => (
<div className={`${styles.modalOverlay} ${showSubscriptionModal ? styles.modalOverlayVisible : ''}`}
onClick={() => setShowSubscriptionModal(false)}>
<div className={styles.modalFeaturesContent} onClick={(e) => e.stopPropagation()}>
<div className={styles.modalHeader}>
<h1>{subscriptionInfo.title}</h1>
</div>
<div className={styles.modalBody}>
<p className={styles.subscriptionDescription}>{subscriptionInfo.description}</p>
<div className={styles.featuresGrid}>
{subscriptionInfo.features.map((feature, index) => (
<div key={index} className={styles.featureCard}>
<svg className={styles.featureIcon} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={0.8} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d={feature.icon} />
</svg>
<h3 className={styles.featureTitle}>{feature.title}</h3>
<p className={styles.featureDescription}>{feature.description}</p>
</div>
))}
</div>
</div>
<div className={styles.modalFooter}>
<button
className={styles.secondaryButton}
onClick={() => setShowSubscriptionModal(false)}
>
Закрыть
</button>
<Link
href={subscriptionInfo.boostyUrl}
className={styles.primaryButton}
target="_blank"
>
Перейти на Boosty
</Link>
</div>
</div>
</div>
);
const PurchaseModal = () => {
if (!selectedCourse) return null;
return (
<div className={`${styles.modalOverlay} ${showPurchaseModal ? styles.modalOverlayVisible : ''}`}
onClick={() => setShowPurchaseModal(false)}>
<div className={styles.modalContent} onClick={(e) => e.stopPropagation()}>
<div className={styles.courseBannerModal}>
<img
src={selectedCourse.banner}
alt={selectedCourse.title}
onError={(e) => {
e.target.src = '/img/Courses/default.png';
}}
/>
</div>
<div className={styles.modalHeader}>
<h2>{selectedCourse.title}</h2>
</div>
<div className={styles.modalBody}>
<div className={styles.tags}>
{selectedCourse.tags.map(tag => (
<span key={tag} className={styles.tag}>#{tag}</span>
))}
</div>
<div className={styles.courseDescriptionModal}>
<h3>О материале</h3>
<p>{selectedCourse.descriptionExtended}</p>
<div className={styles.courseDetails}>
<div className={styles.detailItem}>
<strong>Формат:</strong> {selectedCourse.format}
</div>
<div className={styles.detailItem}>
<strong>Доступ:</strong> Бессрочный
</div>
</div>
</div>
<div className={styles.courseProgram}>
<h3>Что вы получите:</h3>
<ul className={styles.programList}>
<li> Полный доступ к текстовому материалу</li>
<li> Обновления контента бесплатно</li>
</ul>
</div>
<div className={styles.accessInfo}>
<h4>Как работает доступ:</h4>
<p>
Доступ приобретается путем покупки поста на сервисе Boosty. Внутри этого поста находится ссылка для перехода к купленным материалам. Подобные ссылки обновляются в начале каждого месяца. Если ссылка была обновлена (перестала работать) или вы утратили свою старую ссылку еще до обновления, то всегда можете получить ее повторно в том же посте, что приобрели ранее.
</p>
<br/>
<p>Если вы столкнулись с проблемой, то можете связаться со мной одним из следующих способов:</p>
<ul>
<li>
<span>Email:</span> <a href={"mailto:support@openintegrations.dev"}>support@openintegrations.dev</a>
</li>
<li>
<span>Telegram:</span> <a href={"https://t.me/bayselonarrend"}>@bayselonarrend</a>
</li>
</ul>
</div>
</div>
<div className={styles.modalFooter}>
<button
className={styles.secondaryButton}
onClick={() => setShowPurchaseModal(false)}
>
Закрыть
</button>
<Link
href={selectedCourse.buyUrl}
className={styles.primaryButton}
target="_blank"
onClick={() => setShowPurchaseModal(false)}
>
Купить за {selectedCourse.price} ₽
</Link>
</div>
</div>
</div>
);
};
const CourseCard = ({ course }) => (
<div className={styles.courseCard}>
<div className={styles.courseBanner}>
<img
src={course.banner}
alt={course.title}
onError={(e) => {
e.target.src = '/img/Courses/default.png';
}}
/>
</div>
<div className={styles.courseContent}>
<div className={styles.courseHeader}>
<h3 className={styles.courseTitle}>{course.title}</h3>
<div className={styles.tags}>
{course.tags.map(tag => (
<span key={tag} className={styles.tag}>#{tag}</span>
))}
</div>
</div>
<p className={styles.courseDescription}>{course.description}</p>
<div className={styles.courseActions}>
{course.type === 'free' ? (
<Link
href={course.externalUrl}
className={styles.freeButton}
target="_blank"
>
<svg className={styles.buttonIcon} viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
Бесплатно
</Link>
) : (
<>
<button
className={styles.buyButton}
onClick={() => handlePurchaseClick(course)}
>
{course.price} ₽
</button>
<button
className={styles.subscriptionButton}
onClick={() => setShowSubscriptionModal(true)}
>
<svg className={styles.buttonIcon} viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
По подписке
</button>
</>
)}
</div>
</div>
</div>
);
return (
<Layout
title="Курсы"
description="Курсы и материалы"
wrapperClassName={styles.layoutWrapper}
>
<div className={styles.pageContainer}>
<main className={`container margin-vert--lg ${styles.mainContent}`}>
<div className={styles.coursesHeader}>
<svg className={styles.coursesIcon} viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
<div>
<Heading as="h1" className={styles.coursesTitle}>
Обучающие курсы и материалы
</Heading>
<p className={styles.coursesSubtitle}>
Углубленное изучение аспектов и практик разработки с использованием Открытого пакета интеграций
</p>
</div>
</div>
<hr />
<div className={styles.filtersSection}>
<div className={styles.searchBox}>
<svg className={styles.searchIcon} viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<input
type="text"
placeholder="Поиск по материалам..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={styles.searchInput}
/>
</div>
<div className={styles.categories}>
{categories.map(category => (
<button
key={category.id}
className={`${styles.categoryButton} ${
selectedCategory === category.id ? styles.categoryButtonActive : ''
}`}
onClick={() => setSelectedCategory(category.id)}
>
{category.name}
</button>
))}
</div>
</div>
<div className={styles.coursesGrid}>
{filteredCourses.map(course => (
<CourseCard key={course.id} course={course} />
))}
</div>
{filteredCourses.length === 0 && (
<div className={styles.noResults}>
<p>Материалы по вашему запросу не найдены</p>
</div>
)}
<SubscriptionModal />
<PurchaseModal />
</main>
<CustomFooter />
</div>
</Layout>
);
};
export default CoursesPage;

View File

@@ -1,532 +0,0 @@
.layoutWrapper {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.pageContainer {
flex: 1;
display: flex;
flex-direction: column;
}
.mainContent {
flex: 1;
padding-bottom: 2rem; /* Отступ перед футером */
}
/* Остальные ваши существующие стили остаются без изменений */
.coursesHeader {
display: flex;
align-items: flex-start;
gap: 1rem;
margin-bottom: 0;
}
.coursesIcon {
width: 4rem;
height: 4rem;
flex-shrink: 0;
color: #004943;
margin-top: 0.31rem;
margin-right: 10px;
}
.coursesTitle {
margin: 0;
font-weight: 350;
color: inherit;
}
.coursesSubtitle {
margin: 0.1rem 0 0;
font-weight: lighter;
color: #555;
}
/* Фильтры */
.filtersSection {
margin: 2rem 0;
}
.searchBox {
position: relative;
max-width: 400px;
margin-bottom: 1.5rem;
}
.searchIcon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
color: #666;
}
.searchInput {
width: 100%;
padding: 12px 12px 12px 40px;
border: 1px solid #ddd;
border-radius: 12px;
font-size: 1rem;
transition: border-color 0.2s;
}
.searchInput:focus {
outline: none;
border-color: #004943;
}
.categories {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.categoryButton {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 20px;
background: white;
color: #555;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s;
}
.categoryButton:hover {
border-color: #004943;
color: #004943;
}
.categoryButtonActive {
background: #004943;
border-color: #004943;
color: white;
}
.categoryButtonActive:hover {
color: white;
background: #025a53;
}
/* Сетка курсов */
.coursesGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.courseCard {
background: white;
border: 1px solid #ddd;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
}
.courseCard:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.courseBanner {
width: 100%;
height: 160px;
overflow: hidden;
}
.courseBanner img {
width: 100%;
height: 100%;
object-fit: cover;
}
.courseContent {
padding: 1.5rem;
flex: 1;
display: flex;
flex-direction: column;
}
.courseHeader {
margin-bottom: 1rem;
}
.courseTitle {
margin: 0 0 0.5rem 0;
font-size: 1.25rem;
font-weight: 500;
line-height: 1.3;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
}
.tag {
font-size: 0.75rem;
color: #666;
background: #f5f5f5;
padding: 2px 8px;
border-radius: 10px;
}
.courseDescription {
flex: 1;
margin: 0 0 1.5rem 0;
color: #555;
line-height: 1.5;
font-size: 0.95rem;
}
.courseActions {
display: flex;
gap: 0.8rem;
}
/* Кнопки */
.freeButton,
.buyButton,
.subscriptionButton {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 10px 16px;
border: none;
border-radius: 12px;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.freeButton {
background: #e8f5e8;
color: #2e7d32;
border: 1px solid #c8e6c9;
}
.freeButton:hover {
background: #d4edda;
color: #1b5e20;
}
.buyButton {
background: #004943;
color: white; /* Вместо lightgray */
font-weight: 500; /* Вместо 100 */
font-size: 0.95rem; /* Вместо 1em */
font-style: normal;
font-family: inherit; /* Добавьте это */
text-rendering: optimizeLegibility; /* Улучшает четкость */
-webkit-font-smoothing: antialiased; /* Для Mac */
-moz-osx-font-smoothing: grayscale; /* Для Firefox */
}
.buyButton:hover {
background: #00302a;
text-decoration: none;
color: white;
}
.subscriptionButton {
background: #e3f2fd;
color: #1565c0;
border: 1px solid #bbdefb;
}
.subscriptionButton:hover {
background: #bbdefb;
color: #0d47a1;
}
.buttonIcon {
width: 18px;
height: 18px;
}
.noResults {
text-align: center;
padding: 3rem;
color: #666;
}
/* Адаптивность */
@media (max-width: 768px) {
.coursesGrid {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.courseActions {
flex-direction: column;
}
.categories {
justify-content: center;
}
}
.modalOverlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.modalOverlayVisible {
opacity: 1;
pointer-events: auto;
}
.modalContent {
background: white;
border-radius: 16px;
padding: 0;
max-width: 670px;
width: 90%;
max-height: 90vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modalFeaturesContent {
background: white;
border-radius: 16px;
padding: 0;
max-width: 840px;
width: 90%;
max-height: 90vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modalHeader {
display: flex;
justify-content: between;
align-items: center;
padding: 1.5rem 1.5rem 0;
border-bottom: 1px solid #eee;
}
.modalHeader h2 {
margin: 0;
flex: 1;
}
.closeButton {
background: none;
border: none;
font-size: 2rem;
cursor: pointer;
color: #666;
line-height: 1;
}
.modalBody {
padding: 1.5rem;
flex: 1;
overflow-y: auto;
}
.subscriptionDescription {
text-align: center;
margin-bottom: 2rem;
color: #555;
line-height: 1.5;
}
.featuresGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.featureCard {
text-align: center;
background: white;
border: 1px solid #ddd;
border-radius: 12px;
padding: 1.2rem;
box-shadow:
inset 0 1px 3px rgba(255, 255, 255, 0.7),
0 4px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.featureCard:hover {
box-shadow:
inset 0 1px 3px rgba(255, 255, 255, 0.7),
0 6px 12px rgba(0, 0, 0, 0.12);
}
.featureIcon {
width: 4rem;
height: 4rem;
flex-shrink: 0;
margin-bottom: 1rem;
color: #004943;
}
.featureTitle {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
font-weight: 600;
}
.featureDescription {
margin: 0;
color: #666;
font-size: 0.9rem;
line-height: 1.4;
}
.modalFooter {
display: flex;
gap: 1rem;
padding: 1rem 1.5rem;
border-top: 1px solid #eee;
background: #fafafa;
}
.secondaryButton {
flex: 1;
padding: 12px 24px;
border: 1px solid #ddd;
border-radius: 8px;
background: white;
color: #666;
cursor: pointer;
font-size: 1rem;
}
.primaryButton {
flex: 2;
padding: 12px 24px;
border: none;
border-radius: 8px;
background: #004943;
color: white;
text-decoration: none;
text-align: center;
font-size: 1rem;
cursor: pointer;
}
.primaryButton:hover {
background: #00302a;
color: white;
text-decoration: none;
}
/* Адаптивность */
@media (max-width: 768px) {
.featuresGrid {
grid-template-columns: 1fr;
}
.modalFooter {
flex-direction: column;
}
.modalContent {
width: 95%;
margin: 1rem;
}
}
/* Стили для модалки покупки */
.courseBannerModal {
width: 100%;
height: 300px;
overflow: hidden;
}
.courseBannerModal img {
width: 100%;
height: auto;
}
.courseDescriptionModal h3 {
margin: 1rem 0 0.5rem 0;
color: #333;
font-size: 1.2rem;
}
.courseDetails {
margin: 1rem 0;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
}
.detailItem {
margin: 0.5rem 0;
color: #555;
}
.courseProgram {
margin: 1.5rem 0;
}
.courseProgram h3 {
margin-bottom: 0.5rem;
color: #333;
}
.programList {
list-style: none;
padding: 0;
margin: 0;
}
.programList li {
padding: 0.3rem 0;
color: #555;
}
.accessInfo {
margin: 1.5rem 0 0 0;
padding: 1rem;
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
}
.accessInfo h4 {
margin: 0 0 0.5rem 0;
color: #856404;
font-size: 1rem;
}
.accessInfo p {
margin: 0;
color: #856404;
font-size: 0.9rem;
line-height: 1.4;
}