Добавлены bookmarks. Улучшения интерфейса, логотип. Есть дублирующмй код Header в отдельной папке, разбитый на отдельные компоненты. Сейчас используется монолит.
This commit is contained in:
parent
7fb5daf210
commit
e4d5029e72
30
.nginx/nginx.conf
Normal file
30
.nginx/nginx.conf
Normal file
@ -0,0 +1,30 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
root /var/www/dist;
|
||||
index index.html;
|
||||
|
||||
server_name russcult.anibilag.ru;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://192.168.1.67:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
error_page 404 /index.html;
|
||||
}
|
BIN
public/images/Logo-1.webp
Normal file
BIN
public/images/Logo-1.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
BIN
public/images/Logo-2.webp
Normal file
BIN
public/images/Logo-2.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
BIN
server.zip
BIN
server.zip
Binary file not shown.
@ -13,6 +13,7 @@ import { Footer } from './components/Footer';
|
||||
import { AuthGuard } from './components/AuthGuard';
|
||||
import { ImportArticlesPage } from "./pages/ImportArticlesPage";
|
||||
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5000';
|
||||
|
||||
function App() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Clock, ThumbsUp, MapPin } from 'lucide-react';
|
||||
import { Clock, ThumbsUp, MapPin, ThumbsDown } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Article, CategoryTitles, CityTitles } from '../types';
|
||||
import MinutesWord from './MinutesWord';
|
||||
@ -9,30 +9,28 @@ interface ArticleCardProps {
|
||||
}
|
||||
|
||||
export function ArticleCard({ article, featured = false }: ArticleCardProps) {
|
||||
const categoryName = CategoryTitles[article.categoryId];
|
||||
|
||||
return (
|
||||
<article className={`overflow-hidden rounded-lg shadow-lg transition-transform hover:scale-[1.02] ${
|
||||
featured ? 'col-span-2 row-span-2' : ''
|
||||
}`}>
|
||||
} flex flex-col`}>
|
||||
<div className="relative pt-7">
|
||||
<img
|
||||
src={article.coverImage}
|
||||
alt={article.title}
|
||||
className={`w-full object-cover ${featured ? 'h-96' : 'h-64'}`}
|
||||
/>
|
||||
<div className="absolute top-1 left-0 flex gap-2 z-10">
|
||||
<span className="bg-gray-100 shadow px-3 py-1 rounded-full text-sm font-medium">
|
||||
{categoryName}
|
||||
<div className="absolute bottom-0 left-0 w-full flex justify-between items-center z-10 px-4 py-2 bg-gradient-to-t from-black/70 to-transparent">
|
||||
<span className="text-white text-sm md:text-base font-medium">
|
||||
{CategoryTitles[article.categoryId]}
|
||||
</span>
|
||||
<span className="bg-gray-100 shadow px-3 py-1 rounded-full text-sm font-medium flex items-center">
|
||||
<MapPin size={14} className="mr-1" />
|
||||
<span className="text-white text-sm md:text-base font-medium flex items-center">
|
||||
<MapPin size={14} className="mr-1 text-white" />
|
||||
{CityTitles[article.cityId]}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="p-6 flex flex-col flex-grow">
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<img
|
||||
src={article.author.avatarUrl}
|
||||
@ -58,7 +56,7 @@ export function ArticleCard({ article, featured = false }: ArticleCardProps) {
|
||||
</h2>
|
||||
<p className="text-gray-600 line-clamp-2">{article.excerpt}</p>
|
||||
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<div className="p-4 mt-auto flex items-center justify-between">
|
||||
<Link
|
||||
to={`/article/${article.id}`}
|
||||
className="text-blue-600 font-medium hover:text-blue-800"
|
||||
@ -68,6 +66,10 @@ export function ArticleCard({ article, featured = false }: ArticleCardProps) {
|
||||
<div className="flex items-center text-gray-500">
|
||||
<ThumbsUp size={16} className="mr-1" />
|
||||
<span>{article.likes}</span>
|
||||
<span className="ml-2">
|
||||
<ThumbsDown size={16} className="mr-1" />
|
||||
</span>
|
||||
<span>{article.dislikes}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@ export function AuthGuard({ children, requiredPermissions }: AuthGuardProps) {
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">Access Denied</h2>
|
||||
<p className="text-gray-600">
|
||||
У вас нет прав на {action} статьи в {categoryId} категории.
|
||||
У вас нет прав на {action} статьи в разделе {categoryId}.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,14 +27,14 @@ export function AuthorsSection() {
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl font-bold text-gray-900">Наши авторы</h2>
|
||||
<p className="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
Познакомьтесь с талантливыми писателями и экспертами, работающими для вас
|
||||
Познакомьтесь с талантливыми писателями и экспертами, работающими для Вас
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{authors.map((author) => (
|
||||
<div key={author.id} className="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-[1.02]">
|
||||
<div className="p-6">
|
||||
<div key={author.id} className="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-[1.02] h-full">
|
||||
<div className="p-6 flex flex-col flex-grow min-h-[200px] h-full">
|
||||
<div className="flex items-center mb-4">
|
||||
<img
|
||||
src={author.avatarUrl}
|
||||
@ -58,7 +58,7 @@ export function AuthorsSection() {
|
||||
</div>
|
||||
<p className="text-gray-600">{author.bio}</p>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-gray-100">
|
||||
<div className="mt-auto pt-6 border-t border-gray-100">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-500">
|
||||
{author.articlesCount} статей
|
||||
|
@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import { Bookmark } from 'lucide-react';
|
||||
import { useBookmarkStore } from '../stores/bookmarkStore';
|
||||
import { Article } from '../types';
|
||||
|
@ -68,7 +68,7 @@ export function FeaturedSection() {
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900">
|
||||
{city ? `${CityTitles[Number(city)]} ` : ''}
|
||||
{category ? `${CategoryTitles[Number(category)]} Статьи` : 'Тематические статьи'}
|
||||
{category ? `${CategoryTitles[Number(category)]} Статьи` : 'Читайте сегодня'}
|
||||
</h2>
|
||||
<p className="font-bold text-gray-600">
|
||||
Статьи {Math.min((currentPage - 1) * ARTICLES_PER_PAGE + 1, totalArticles)}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Menu, Search, X, ChevronDown } from 'lucide-react';
|
||||
import { Logo } from "./Header/Logo";
|
||||
import { Menu, Search, X, ChevronDown, Bookmark } from 'lucide-react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { CategoryIds, CategoryTitles, CityIds, CityTitles } from '../types';
|
||||
import { useBookmarkStore } from "../stores/bookmarkStore";
|
||||
|
||||
|
||||
export function Header() {
|
||||
@ -12,6 +14,7 @@ export function Header() {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const currentCategory = searchParams.get('category');
|
||||
const currentCity = searchParams.get('city');
|
||||
const { bookmarks } = useBookmarkStore();
|
||||
|
||||
const handleCityChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const city = event.target.value;
|
||||
@ -50,9 +53,9 @@ export function Header() {
|
||||
>
|
||||
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
<Link to="/" className="ml-2 flex items-center space-x-2">
|
||||
<h1 className="text-2xl font-bold text-gray-900">Культура</h1>
|
||||
</Link>
|
||||
<div className="ml-2">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="hidden lg:flex items-center space-x-8">
|
||||
@ -80,26 +83,16 @@ export function Header() {
|
||||
|
||||
<div className="h-6 w-px bg-gray-200" />
|
||||
|
||||
{/* Menu */}
|
||||
<div className="flex items-center space-x-4 overflow-x-auto">
|
||||
<span className="text-sm font-medium text-gray-500"></span>
|
||||
<Link
|
||||
to={currentCity ? `/?city=${currentCity}` : '/'}
|
||||
className={`px-3 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
||||
!currentCategory
|
||||
? 'text-blue-600 hover:text-blue-800'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
Все категории
|
||||
</Link>
|
||||
{CategoryIds.map((category) => (
|
||||
<Link
|
||||
key={category}
|
||||
to={`/?category=${category}${currentCity ? `&city=${currentCity}` : ''}`}
|
||||
className={`px-3 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
||||
className={`px-3 py-2 font-medium transition-colors whitespace-nowrap ${
|
||||
Number(currentCategory) === category
|
||||
? 'text-blue-600 hover:text-blue-800'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
? 'text-base md:text-lg lg:text-xl font-semibold text-blue-600 hover:text-blue-800 underline'
|
||||
: 'text-sm md:text-base lg:text-lg font-medium text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{CategoryTitles[category]}
|
||||
@ -112,7 +105,7 @@ export function Header() {
|
||||
<form onSubmit={handleSearch} className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
placeholder="Поиск..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={handleSearchKeyPress}
|
||||
@ -125,6 +118,17 @@ export function Header() {
|
||||
<Search size={18} />
|
||||
</button>
|
||||
</form>
|
||||
<Link
|
||||
to="/bookmarks"
|
||||
className="relative p-2 rounded-full hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<Bookmark size={20} className="text-gray-500" />
|
||||
{bookmarks.length > 0 && (
|
||||
<span className="absolute -top-1 -right-1 bg-blue-600 text-white text-xs w-4 h-4 flex items-center justify-center rounded-full">
|
||||
{bookmarks.length}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -137,7 +141,7 @@ export function Header() {
|
||||
>
|
||||
<div className="px-2 pt-2 pb-3 space-y-1">
|
||||
<div className="px-3 py-2">
|
||||
<h3 className="text-sm font-medium text-gray-500 mb-2">City</h3>
|
||||
<h3 className="text-sm font-medium text-gray-500 mb-2">Столицы</h3>
|
||||
<select
|
||||
value={currentCity || ''}
|
||||
onChange={handleCityChange}
|
||||
@ -153,18 +157,8 @@ export function Header() {
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2">
|
||||
<h3 className="text-sm font-medium text-gray-500 mb-2">Categories</h3>
|
||||
<div className="space-y-1">
|
||||
<Link
|
||||
to={currentCity ? `/?city=${currentCity}` : '/'}
|
||||
className={`block px-3 py-2 rounded-md text-base font-medium ${
|
||||
!currentCategory
|
||||
? 'bg-blue-50 text-blue-600'
|
||||
: 'text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
Все категории
|
||||
</Link>
|
||||
<h3 className="text-sm font-medium text-gray-500 mb-2">Разделы</h3>
|
||||
<div className="space-y-0">
|
||||
{CategoryIds.map((category) => (
|
||||
<Link
|
||||
key={category}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { City } from '../../types';
|
||||
import { CityTitles } from '../../types';
|
||||
|
||||
interface CitySelectorProps {
|
||||
cities: City[];
|
||||
cities: number[];
|
||||
currentCity: string | null;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ export function CitySelector({ cities, currentCity }: CitySelectorProps) {
|
||||
<option value="">All Cities</option>
|
||||
{cities.map((city) => (
|
||||
<option key={city} value={city}>
|
||||
{city}
|
||||
{CityTitles[city]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Palette } from 'lucide-react';
|
||||
|
||||
export function Logo() {
|
||||
return (
|
||||
<Link to="/" className="flex items-center space-x-2 text-gray-900">
|
||||
<Palette size={32} className="text-blue-600" />
|
||||
<span className="text-2xl font-bold">CultureScope</span>
|
||||
<Link to="/" className="flex items-center space-x-4 text-gray-900 min-w-0">
|
||||
<img
|
||||
src="/images/Logo-2.webp"
|
||||
alt="Культура двух столиц"
|
||||
className="h-10 w-10 md:h-12 md:w-12 flex-shrink-0 transition-transform duration-300 hover:scale-150"
|
||||
/>
|
||||
<span className="invisible">_____</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Category, City } from '../../types';
|
||||
import { CityTitles, CategoryTitles } from '../../types';
|
||||
|
||||
interface MobileMenuProps {
|
||||
isOpen: boolean;
|
||||
categories: Category[];
|
||||
cities: City[];
|
||||
categories: number[];
|
||||
cities: number[];
|
||||
currentCategory: string | null;
|
||||
currentCity: string | null;
|
||||
onCityChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
|
||||
@ -34,7 +34,7 @@ export function MobileMenu({
|
||||
<option value="">All Cities</option>
|
||||
{cities.map((city) => (
|
||||
<option key={city} value={city}>
|
||||
{city}
|
||||
{CityTitles[city]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@ -58,12 +58,12 @@ export function MobileMenu({
|
||||
key={category}
|
||||
to={`/?category=${category}${currentCity ? `&city=${currentCity}` : ''}`}
|
||||
className={`block px-3 py-2 rounded-md text-base font-medium ${
|
||||
currentCategory === category
|
||||
Number(currentCategory) === category
|
||||
? 'bg-blue-50 text-blue-600'
|
||||
: 'text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
{CategoryTitles[category]}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Category } from '../../types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { CategoryTitles } from '../../types';
|
||||
|
||||
interface NavigationProps {
|
||||
categories: Category[];
|
||||
categories: number[];
|
||||
currentCategory: string | null;
|
||||
currentCity: string | null;
|
||||
}
|
||||
@ -26,12 +26,12 @@ export function Navigation({ categories, currentCategory, currentCity }: Navigat
|
||||
key={category}
|
||||
to={`/?category=${category}${currentCity ? `&city=${currentCity}` : ''}`}
|
||||
className={`px-3 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
||||
currentCategory === category
|
||||
Number(currentCategory) === category
|
||||
? 'text-blue-600 hover:text-blue-800'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
{CategoryTitles[category]}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
@ -1,16 +1,14 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Menu, X, Bookmark } from 'lucide-react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Category, City } from '../../types';
|
||||
import { Logo } from './Logo';
|
||||
import { SearchBar } from './SearchBar';
|
||||
import { Navigation } from './Navigation';
|
||||
import { CitySelector } from './CitySelector';
|
||||
import { MobileMenu } from './MobileMenu';
|
||||
import { CategoryIds, CityIds } from '../../types';
|
||||
import { useBookmarkStore } from '../../stores/bookmarkStore';
|
||||
|
||||
const categories: Category[] = ['Film', 'Theater', 'Music', 'Sports', 'Art', 'Legends', 'Anniversaries', 'Memory'];
|
||||
const cities: City[] = ['New York', 'London'];
|
||||
|
||||
export function Header() {
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
@ -52,13 +50,13 @@ export function Header() {
|
||||
<nav className="hidden lg:flex items-center space-x-8">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm font-medium text-gray-500"></span>
|
||||
<CitySelector cities={cities} currentCity={currentCity} />
|
||||
<CitySelector cities={CityIds} currentCity={currentCity} />
|
||||
</div>
|
||||
|
||||
<div className="h-6 w-px bg-gray-200" />
|
||||
|
||||
<Navigation
|
||||
categories={categories}
|
||||
categories={CategoryIds}
|
||||
currentCategory={currentCategory}
|
||||
currentCity={currentCity}
|
||||
/>
|
||||
@ -83,8 +81,8 @@ export function Header() {
|
||||
|
||||
<MobileMenu
|
||||
isOpen={isMobileMenuOpen}
|
||||
categories={categories}
|
||||
cities={cities}
|
||||
categories={CategoryIds}
|
||||
cities={CityIds}
|
||||
currentCategory={currentCategory}
|
||||
currentCity={currentCity}
|
||||
onCityChange={handleCityChange}
|
||||
|
46
src/components/ShareButton.tsx
Normal file
46
src/components/ShareButton.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { useState } from 'react';
|
||||
import { Share2 } from 'lucide-react';
|
||||
|
||||
interface ShareButtonProps {
|
||||
title: string;
|
||||
url: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ShareButton({ title, url, className = '' }: ShareButtonProps) {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
const handleShare = async () => {
|
||||
try {
|
||||
if (navigator.share) {
|
||||
await navigator.share({
|
||||
title,
|
||||
url
|
||||
});
|
||||
} else {
|
||||
await navigator.clipboard.writeText(url);
|
||||
setShowTooltip(true);
|
||||
setTimeout(() => setShowTooltip(false), 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sharing:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={handleShare}
|
||||
className={`p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100 ${className}`}
|
||||
aria-label="Share article"
|
||||
>
|
||||
<Share2 size={20} />
|
||||
</button>
|
||||
{showTooltip && (
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded whitespace-nowrap">
|
||||
Ссылка скопированна!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { ArrowLeft, Clock, Share2, Bookmark } from 'lucide-react';
|
||||
import { ArrowLeft, Clock } from 'lucide-react';
|
||||
import { Header } from '../components/Header';
|
||||
import { ReactionButtons } from '../components/ReactionButtons';
|
||||
import { PhotoGallery } from '../components/PhotoGallery';
|
||||
import { Article, CategoryTitles } from '../types';
|
||||
import { ArticleContent } from '../components/ArticleContent';
|
||||
import { ShareButton } from '../components/ShareButton';
|
||||
import { BookmarkButton } from '../components/BookmarkButton';
|
||||
import MinutesWord from '../components/MinutesWord';
|
||||
import axios from "axios";
|
||||
import api from "../utils/api";
|
||||
@ -122,12 +124,8 @@ export function ArticlePage() {
|
||||
{CategoryTitles[articleData.categoryId]}
|
||||
</span>
|
||||
<div className="flex-1" />
|
||||
<button className="p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100">
|
||||
<Share2 size={20} />
|
||||
</button>
|
||||
<button className="p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100">
|
||||
<Bookmark size={20} />
|
||||
</button>
|
||||
<ShareButton title={articleData.title} url={window.location.href} />
|
||||
<BookmarkButton article={articleData} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -12,7 +12,7 @@ export function BookmarksPage() {
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="flex items-center space-x-4 mb-8">
|
||||
<Bookmark size={24} className="text-blue-600" />
|
||||
<h1 className="text-3xl font-bold text-gray-900">Your Bookmarks</h1>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Мои закладки</h1>
|
||||
</div>
|
||||
|
||||
{bookmarks.length > 0 ? (
|
||||
|
@ -30,13 +30,13 @@ export function HomePage() {
|
||||
const { main, sub, description } = getHeroTitle();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="min-h-screen bg-cover bg-center bg-fixed bg-white/95" style={{ backgroundImage: `url('/images/main-bg.jpg')` }}>
|
||||
<Header />
|
||||
<main>
|
||||
<div className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||
<div className="px-4 py-6 sm:px-0">
|
||||
<div className="relative">
|
||||
|
||||
<div className="absolute inset-0">
|
||||
<div
|
||||
className="w-full h-full bg-cover bg-center transition-all duration-500 ease-in-out"
|
||||
@ -47,6 +47,7 @@ export function HomePage() {
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-gray-900/90 to-gray-900/60 backdrop-blur-sm" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="text-center max-w-4xl mx-auto py-24 px-4 sm:py-32 sm:px-6 lg:px-8">
|
||||
<h1 className="text-4xl font-extrabold tracking-tight text-white sm:text-5xl md:text-6xl">
|
||||
@ -60,6 +61,7 @@ export function HomePage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="relative bg-white/95">
|
||||
@ -73,6 +75,5 @@ export function HomePage() {
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -6,6 +6,9 @@ export default defineConfig(({ mode }) => {
|
||||
|
||||
return {
|
||||
plugins: [react()],
|
||||
esbuild: {
|
||||
minifyIdentifiers: false, // Отключаем переименование переменных
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['@prisma/client', 'lucide-react'],
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user