10 Commits

Author SHA1 Message Date
f49c7f205b updated md 2025-05-12 14:54:33 +04:00
9d8bff8a6a LabWork_6 is absolutly ready 2025-05-12 12:44:17 +03:00
20957928fe LabWork_6 is ready 2025-05-12 12:31:19 +03:00
14a176ca05 LabWork_5 is ready 2025-05-08 10:57:39 +03:00
29cea978cd Удаляем 2025-05-08 10:40:02 +03:00
7e21009004 LabWork_4 is ready 2025-05-08 10:37:11 +03:00
0164031d12 Done 2025-05-08 09:44:59 +03:00
fb2700a5dc LabWork_3 is ready 2025-04-07 09:08:15 +03:00
417aacc7a5 LabWork_2 is ready 2025-04-07 09:04:37 +03:00
ec4191f9c3 LabWork_1 is ready 2025-04-07 09:02:20 +03:00
36 changed files with 19967 additions and 1 deletions

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1,2 +1,70 @@
# PIBD_21_Basalov_A_D_Internet_Programming_EPTU
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

56
db.json Normal file

File diff suppressed because one or more lines are too long

18920
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

53
package.json Normal file
View File

@@ -0,0 +1,53 @@
{
"name": "university-react-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.8.4",
"bootstrap": "^5.3.5",
"bootstrap-icons": "^1.11.3",
"json-server": "^1.0.0-beta.3",
"react": "^19.1.0",
"react-bootstrap": "^2.10.9",
"react-dom": "^19.1.0",
"react-icons": "^4.12.0",
"react-router-dom": "^7.5.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"server": "json-server --watch db.json --port 3001",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"sass": "^1.87.0"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
public/index.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
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`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

27
src/App.js Normal file
View File

@@ -0,0 +1,27 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Layout from './pages/Layout';
import HomePage from './pages/HomePage';
import NewsPage from './pages/NewsPage';
import FacultiesPage from './pages/FacultiesPage';
import ContactsPage from './pages/ContactsPage';
import AdmissionsPage from './pages/AdmissionsPage';
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} />
<Route path="news" element={<NewsPage />} />
<Route path="faculties" element={<FacultiesPage />} />
<Route path="contacts" element={<ContactsPage />} />
<Route path="admissions" element={<AdmissionsPage />} />
<Route path="faculties/stormtrooper" element={<div>Страница штурмового факультета</div>} />
<Route path="faculties/worker" element={<div>Страница рабочего факультета</div>} />
</Route>
</Routes>
</Router>
);
};
export default App;

63
src/api/news.js Normal file
View File

@@ -0,0 +1,63 @@
import { useState, useEffect } from 'react';
import axios from 'axios';
const API_URL = 'http://localhost:3001/news';
export const useNews = () => {
const [news, setNews] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchNews = async () => {
try {
setLoading(true);
const response = await axios.get(API_URL);
setNews(response.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const createNews = async (data) => {
try {
const response = await axios.post(API_URL, data);
setNews(prev => [...prev, response.data]);
return true;
} catch (err) {
setError(err.message);
return false;
}
};
const updateNews = async (id, data) => {
try {
await axios.put(`${API_URL}/${id}`, data);
setNews(prev => prev.map(item =>
item.id === id ? {...item, ...data} : item
));
return true;
} catch (err) {
setError(err.message);
return false;
}
};
const deleteNews = async (id) => {
try {
await axios.delete(`${API_URL}/${id}`);
setNews(prev => prev.filter(item => item.id !== id));
return true;
} catch (err) {
setError(err.message);
return false;
}
};
useEffect(() => {
fetchNews();
}, []);
return { news, loading, error, createNews, updateNews, deleteNews };
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
src/assets/VKlogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/assets/WorkerPoster.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

View File

@@ -0,0 +1,17 @@
import React from 'react';
import vkLogo from '../../assets/VKlogo.png'; // Убедитесь, что файл существует
const Footer = () => {
return (
<footer className="footer bg-dark text-white py-4 mt-4">
<div className="container d-flex justify-content-between align-items-center">
<p className="mb-0">© 2023 Эркутский Государственный Педагогический Университет. Все права защищены.</p>
<a href="https://vk.com" target="_blank" rel="noopener noreferrer" className="corner-image">
<img src={vkLogo} alt="VK" style={{ width: '40px' }} />
</a>
</div>
</footer>
);
};
export default Footer;

View File

@@ -0,0 +1,23 @@
import React from 'react';
import logo from '../../assets/logo.png'; // Убедитесь, что файл существует
const Header = () => {
return (
<div className="header bg-light py-3">
<div className="container d-flex align-items-center">
<img
src={logo}
alt="Логотип университета"
className="logo"
style={{ width: '80px' }}
/>
<div className="header-text ms-4">
<h1 className="h4 mb-0">Эркутский Государственный Педагогический Университет</h1>
<p className="text-muted small mb-0">Здесь могла быть ваша реклама</p>
</div>
</div>
</div>
);
};
export default Header;

View File

@@ -0,0 +1,78 @@
import React from 'react';
import { NavLink } from 'react-router-dom';
import { Dropdown } from 'react-bootstrap';
import { FaHome, FaUsers, FaNewspaper, FaPhone, FaUserPlus } from 'react-icons/fa';
const Navbar = () => {
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<div className="container">
<button
className="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#mainNav"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="mainNav">
<ul className="navbar-nav me-auto">
{/* Главная */}
<li className="nav-item">
<NavLink className="nav-link" to="/">
<FaHome className="me-2" />
Главная
</NavLink>
</li>
{/* Факультеты (выпадающее меню) */}
<li className="nav-item dropdown">
<Dropdown>
<Dropdown.Toggle as={NavLink} className="nav-link dropdown-toggle">
<FaUsers className="me-2" />
Факультеты
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item as={NavLink} to="/faculties/stormtrooper">
Штурмовой факультет
</Dropdown.Item>
<Dropdown.Item as={NavLink} to="/faculties/worker">
Рабочий факультет
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</li>
{/* Новости */}
<li className="nav-item">
<NavLink className="nav-link" to="/news">
<FaNewspaper className="me-2" />
Новости
</NavLink>
</li>
{/* Приемная комиссия */}
<li className="nav-item">
<NavLink className="nav-link" to="/admissions">
<FaUserPlus className="me-2" />
Приемная комиссия
</NavLink>
</li>
{/* Контакты */}
<li className="nav-item">
<NavLink className="nav-link" to="/contacts">
<FaPhone className="me-2" />
Контакты
</NavLink>
</li>
</ul>
</div>
</div>
</nav>
);
};
export default Navbar;

View File

@@ -0,0 +1,31 @@
import React from 'react';
const NewsCard = ({ news, onDelete, onEdit }) => {
return (
<div className="col">
<div className="card h-100">
<img src={news.imageUrl} className="card-img-top" alt={news.title} />
<div className="card-body">
<h5 className="card-title">{news.title}</h5>
<p className="card-text">{news.description}</p>
<div className="d-flex justify-content-end gap-2">
<button
className="btn btn-sm btn-warning edit-btn"
onClick={() => onEdit(news)}
>
Редактировать
</button>
<button
className="btn btn-sm btn-danger delete-btn"
onClick={() => onDelete(news.id)}
>
Удалить
</button>
</div>
</div>
</div>
</div>
);
};
export default NewsCard;

View File

@@ -0,0 +1,77 @@
import { useState, useEffect } from 'react';
import { Modal, Form, Button } from 'react-bootstrap';
const NewsForm = ({ news, onClose, onSubmit }) => {
const [formData, setFormData] = useState({
title: '',
description: '',
imageUrl: ''
});
useEffect(() => {
if (news) {
setFormData({
title: news.title,
description: news.description,
imageUrl: news.imageUrl
});
}
}, [news]);
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(news?.id || null, formData);
onClose();
};
return (
<Modal show={true} onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title>
{news ? 'Редактирование' : 'Новая новость'}
</Modal.Title>
</Modal.Header>
<Form onSubmit={handleSubmit}>
<Modal.Body>
<Form.Group className="mb-3">
<Form.Label>Заголовок</Form.Label>
<Form.Control
required
value={formData.title}
onChange={(e) => setFormData({...formData, title: e.target.value})}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Описание</Form.Label>
<Form.Control
as="textarea"
rows={3}
required
value={formData.description}
onChange={(e) => setFormData({...formData, description: e.target.value})}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Ссылка на изображение</Form.Label>
<Form.Control
type="url"
required
value={formData.imageUrl}
onChange={(e) => setFormData({...formData, imageUrl: e.target.value})}
/>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onClose}>Отмена</Button>
<Button variant="primary" type="submit">
{news ? 'Сохранить' : 'Создать'}
</Button>
</Modal.Footer>
</Form>
</Modal>
);
};
export default NewsForm;

View File

@@ -0,0 +1,52 @@
import { useState } from 'react';
import NewsCard from './NewsCard';
import NewsForm from './NewsForm';
import { Button } from 'react-bootstrap';
const NewsList = ({ news, onDelete, onUpdate, createNews }) => {
const [showForm, setShowForm] = useState(false);
const [selectedNews, setSelectedNews] = useState(null);
const handleFormClose = () => {
setShowForm(false);
setSelectedNews(null);
};
return (
<div className="container mt-4">
<div className="d-flex justify-content-between mb-4">
<h2>Новости университета</h2>
<Button
variant="primary"
onClick={() => setShowForm(true)}
>
Добавить новость
</Button>
</div>
<div className="row row-cols-1 row-cols-md-2 g-4">
{news.map(item => (
<NewsCard
key={item.id}
news={item}
onDelete={onDelete}
onEdit={() => {
setSelectedNews(item);
setShowForm(true);
}}
/>
))}
</div>
{showForm && (
<NewsForm
news={selectedNews}
onClose={handleFormClose}
onSubmit={selectedNews ? onUpdate : createNews}
/>
)}
</div>
);
};
export default NewsList;

71
src/hooks/useNews.js Normal file
View File

@@ -0,0 +1,71 @@
import { useState, useEffect } from 'react';
import axios from 'axios';
import { API_URL } from '../services/api';
const useNews = () => {
const [news, setNews] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchNews = async () => {
try {
setLoading(true);
const response = await axios.get(`${API_URL}/news`);
setNews(response.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const createNews = async (data) => {
try {
const response = await axios.post(`${API_URL}/news`, data);
setNews(prev => [...prev, response.data]);
return true;
} catch (err) {
setError(err.message);
return false;
}
};
const updateNews = async (id, data) => {
try {
await axios.put(`${API_URL}/news/${id}`, data);
setNews(prev => prev.map(item =>
item.id === id ? {...item, ...data} : item
));
return true;
} catch (err) {
setError(err.message);
return false;
}
};
const deleteNews = async (id) => {
try {
await axios.delete(`${API_URL}/news/${id}`);
setNews(prev => prev.filter(item => item.id !== id));
return true;
} catch (err) {
setError(err.message);
return false;
}
};
useEffect(() => {
fetchNews();
}, []);
return {
news,
loading,
error,
createNews,
updateNews,
deleteNews
};
};
export default useNews;

14
src/index.js Normal file
View File

@@ -0,0 +1,14 @@
import React from 'react';
// В index.js
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle.min';
import { createRoot } from 'react-dom/client';
import App from './App';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

View File

@@ -0,0 +1,12 @@
import React from 'react';
const AdmissionsPage = () => {
return (
<div className="container mt-4">
<h2>Приемная комиссия</h2>
<p>Здесь будет информация о правилах приема, сроках подачи документов и вступительных испытаниях.</p>
</div>
);
};
export default AdmissionsPage;

14
src/pages/ContactsPage.js Normal file
View File

@@ -0,0 +1,14 @@
import React from 'react';
const ContactsPage = () => {
return (
<div className="container mt-4">
<h2>Контакты</h2>
<p>Адрес: г. Эркутск, ул. Университетская, 1</p>
<p>Телефон: +7 (3952) 123-456</p>
<p>Email: info@egpu.ru</p>
</div>
);
};
export default ContactsPage;

View File

@@ -0,0 +1,20 @@
import React from 'react';
import { Link } from 'react-router-dom';
const FacultiesPage = () => {
return (
<div className="container mt-4">
<h2>Факультеты</h2>
<div className="list-group">
<a href="/faculties/stormtrooper" className="list-group-item list-group-item-action">
Штурмовой факультет
</a>
<a href="/faculties/worker" className="list-group-item list-group-item-action">
Рабочий факультет
</a>
</div>
</div>
);
};
export default FacultiesPage;

14
src/pages/HomePage.js Normal file
View File

@@ -0,0 +1,14 @@
import React from 'react';
const HomePage = () => {
return (
<main className="content">
<h2>Добро пожаловать в ЭГПУ</h2>
<p>Самый престижный университет Эркутска - ЭГПУ приглашает присоединиться к команде неучей и их студентов!</p>
<p>Мы предлагаем обучение только на платной основе, ведь дорого - значит престижно!</p>
<p>Подробности о поступлении вы можете узнать тут: <a href="/admissions">Приемная комиссия</a></p>
</main>
);
};
export default HomePage;

23
src/pages/Layout.js Normal file
View File

@@ -0,0 +1,23 @@
import { Outlet } from 'react-router-dom';
import Header from '../components/common/Header';
import Navbar from '../components/common/Navbar';
import Footer from '../components/common/Footer';
const Layout = () => {
return (
<div className="d-flex flex-column" style={{ minHeight: '100vh' }}>
<Header />
<Navbar />
<main className="flex-grow-1 py-3">
<div className="container">
<Outlet />
</div>
</main>
<Footer />
</div>
);
};
export default Layout;

55
src/pages/NewsPage.js Normal file
View File

@@ -0,0 +1,55 @@
import { useState } from 'react';
import NewsList from '../components/news/NewsList';
import NewsForm from '../components/news/NewsForm';
import useNews from '../hooks/useNews';
const NewsPage = () => {
const {
news,
loading,
error,
createNews,
updateNews,
deleteNews
} = useNews();
const [selectedNews, setSelectedNews] = useState(null);
const handleFormSubmit = async (data) => {
if (selectedNews) {
await updateNews(selectedNews.id, data);
} else {
await createNews(data);
}
setSelectedNews(null);
};
return (
<div className="container mt-4">
{error && <div className="alert alert-danger">{error}</div>}
<NewsList
news={news}
onEdit={setSelectedNews}
onDelete={deleteNews}
/>
<button
className="btn btn-primary mt-3"
onClick={() => setSelectedNews({})}
>
Добавить новость
</button>
{selectedNews && (
<NewsForm
news={selectedNews}
onSubmit={handleFormSubmit}
onClose={() => setSelectedNews(null)}
/>
)}
</div>
);
};
export default NewsPage;

11
src/services/api.js Normal file
View File

@@ -0,0 +1,11 @@
import axios from 'axios';
export const API_URL = 'http://localhost:3001';
export const api = axios.create({
baseURL: API_URL,
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
});

137
src/styles/App.css Normal file
View File

@@ -0,0 +1,137 @@
/* Основные стили */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background-color: #f8f9fa;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Шапка */
.header {
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 1rem 0;
}
.header-top {
display: flex;
align-items: center;
padding: 0 20px;
}
.logo {
width: 80px;
height: auto;
margin-right: 20px;
}
.header-text h1 {
margin: 0;
font-size: 1.8rem;
color: #2c3e50;
}
.header-text p {
margin: 5px 0 0;
color: #7f8c8d;
font-size: 0.95rem;
}
/* Навигация */
.navbar {
background: #2c3e50 !important;
padding: 0.5rem 1rem;
}
.nav-link {
color: #ecf0f1 !important;
transition: all 0.3s ease;
}
.nav-link:hover {
color: #3498db !important;
}
/* Основной контент */
.main-content {
min-height: calc(100vh - 200px);
padding: 40px 0;
}
/* Карточки новостей */
.news-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 25px;
padding: 20px 0;
}
.news-card {
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
}
.news-card:hover {
transform: translateY(-5px);
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 10px 10px 0 0;
}
.card-body {
padding: 1.2rem;
}
.card-title {
font-size: 1.3rem;
margin-bottom: 0.8rem;
color: #2c3e50;
}
.card-text {
color: #7f8c8d;
line-height: 1.6;
}
/* Футер */
.footer {
background: #2c3e50;
color: #fff;
padding: 2rem 0;
margin-top: auto;
}
.corner-image img {
width: 60px;
height: 60px;
border-radius: 50%;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* Адаптивность */
@media (max-width: 768px) {
.header-top {
flex-direction: column;
text-align: center;
}
.logo {
margin-bottom: 1rem;
}
.news-grid {
grid-template-columns: 1fr;
}
}

23
src/styles/custom.scss Normal file
View File

@@ -0,0 +1,23 @@
// Переопределение Bootstrap переменных
$primary: #2c3e50;
$secondary: #34495e;
// Импорт Bootstrap
@import "~bootstrap/scss/bootstrap";
// Кастомные стили
body {
background-color: #f8f9fa;
}
.container.main-content {
min-height: calc(100vh - 200px);
padding: 20px 0;
}
.footer {
background: $dark;
color: white;
padding: 20px 0;
margin-top: auto;
}

13
src/styles/index.css Normal file
View File

@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

Binary file not shown.