refactor: change routing to astro
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ lerna-debug.log*
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
.astro
|
||||
*.local
|
||||
|
||||
# Editor
|
||||
|
||||
11
astro.config.mjs
Normal file
11
astro.config.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from 'astro/config'
|
||||
import react from '@astrojs/react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [react()],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
})
|
||||
17
index.html
17
index.html
@@ -1,17 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="SOFTCLICK - российская IT-компания, специализирующаяся на импортозамещении и внедрении отечественных решений"
|
||||
/>
|
||||
<title>SOFTCLICK - Российские IT-решения с 2021 года</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
5030
package-lock.json
generated
5030
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -8,11 +8,12 @@
|
||||
"npm": ">=9.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/react": "^4.4.2",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.2",
|
||||
@@ -39,6 +40,7 @@
|
||||
"@radix-ui/react-toggle": "^1.1.2",
|
||||
"@radix-ui/react-toggle-group": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"astro": "^5.16.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
@@ -62,10 +64,8 @@
|
||||
"@types/node": "^20.11.19",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"prettier": "^3.8.1",
|
||||
"tailwindcss": "^4.1.12",
|
||||
"typescript": "~5.6.3",
|
||||
"vite": "^6.3.5"
|
||||
"typescript": "~5.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
6
plan.md
Normal file
6
plan.md
Normal file
@@ -0,0 +1,6 @@
|
||||
- [x] Починить подскролл, убрать ворнинг про useLayoutEffect из консоли
|
||||
- [ ] Вынести services и vendors куда-то в статику, а не код
|
||||
- [ ] Сделать фейковое апи чтобы страницы ходили за нужным им контентом, который я вынесу выше
|
||||
- [ ] Собрать все views в одну страницу, которая ходила бы в апи и получало нужный контент оттуда
|
||||
|
||||
- [ ] Сделать динамические блоки (декомпозировать)
|
||||
130
src/App.tsx
130
src/App.tsx
@@ -1,130 +0,0 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Header } from './components/Header'
|
||||
import { Footer } from './components/Footer'
|
||||
import { ContactModal } from './components/ContactModal'
|
||||
import { HomePage } from './pages/HomePage'
|
||||
import { CloudPage } from './pages/CloudPage'
|
||||
import { RussianSoftwarePage } from './pages/RussianSoftwarePage'
|
||||
import { CybersecurityPage } from './pages/CybersecurityPage'
|
||||
import { AISolutionsPage } from './pages/AISolutionsPage'
|
||||
import { RussianHardwarePage } from './pages/RussianHardwarePage'
|
||||
import { HardwareSolutionsPage } from './pages/HardwareSolutionsPage'
|
||||
import { CADandGISPage } from './pages/CADandGISPage'
|
||||
import { IndustrialSolutionsPage } from './pages/IndustrialSolutionsPage'
|
||||
import { GovernmentPage } from './pages/GovernmentPage'
|
||||
import { BusinessPage } from './pages/BusinessPage'
|
||||
import { ConsultingPage } from './pages/ConsultingPage'
|
||||
import { ImplementationPage } from './pages/ImplementationPage'
|
||||
import { ManagedServicesPage } from './pages/ManagedServicesPage'
|
||||
import { IntegrationPage } from './pages/IntegrationPage'
|
||||
import { SpecializedServicesPage } from './pages/SpecializedServicesPage'
|
||||
import { SupportPage } from './pages/SupportPage'
|
||||
import { SubscriptionPage } from './pages/SubscriptionPage'
|
||||
|
||||
export default function App() {
|
||||
const [currentPage, setCurrentPage] = useState('home')
|
||||
const [pendingScroll, setPendingScroll] = useState<string | null>(null)
|
||||
const [isContactModalOpen, setIsContactModalOpen] = useState(false)
|
||||
|
||||
const scrollToSection = (sectionId: string) => {
|
||||
// Даем странице время отрендериться
|
||||
setTimeout(() => {
|
||||
const section = document.getElementById(sectionId)
|
||||
console.log('Trying to scroll to:', sectionId, 'Found element:', section)
|
||||
if (section) {
|
||||
const headerHeight = 85 // Header высота + минимальный отступ
|
||||
const targetPosition = section.offsetTop - headerHeight
|
||||
console.log('Scrolling to position:', targetPosition)
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
} else {
|
||||
console.error('Section not found:', sectionId)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const handleNavigate = (page: string, section?: string) => {
|
||||
if (section) {
|
||||
// Клик на Кейсы/О компании/Контакты
|
||||
if (currentPage !== 'home') {
|
||||
// Если не на главной - переходим туда
|
||||
setCurrentPage('home')
|
||||
setPendingScroll(section)
|
||||
} else {
|
||||
// Уже на главной - скроллим сразу
|
||||
scrollToSection(section)
|
||||
}
|
||||
} else {
|
||||
// Обычная навигация на другие страницы
|
||||
setCurrentPage(page)
|
||||
setPendingScroll(null)
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Когда вернулись на главную с отложенным скроллом
|
||||
if (currentPage === 'home' && pendingScroll) {
|
||||
scrollToSection(pendingScroll)
|
||||
setPendingScroll(null)
|
||||
}
|
||||
}, [currentPage, pendingScroll])
|
||||
|
||||
const handleBackToHome = () => {
|
||||
setCurrentPage('home')
|
||||
setPendingScroll(null)
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
const renderPage = () => {
|
||||
switch (currentPage) {
|
||||
case 'cloud':
|
||||
return <CloudPage onBack={handleBackToHome} />
|
||||
case 'russian-software':
|
||||
return <RussianSoftwarePage onBack={handleBackToHome} />
|
||||
case 'cybersecurity':
|
||||
return <CybersecurityPage onBack={handleBackToHome} />
|
||||
case 'ai-solutions':
|
||||
return <AISolutionsPage onBack={handleBackToHome} />
|
||||
case 'russian-hardware':
|
||||
return <RussianHardwarePage onBack={handleBackToHome} />
|
||||
case 'hardware-solutions':
|
||||
return <HardwareSolutionsPage onBack={handleBackToHome} />
|
||||
case 'cad-gis':
|
||||
return <CADandGISPage onBack={handleBackToHome} />
|
||||
case 'industrial-solutions':
|
||||
return <IndustrialSolutionsPage onBack={handleBackToHome} />
|
||||
case 'government':
|
||||
return <GovernmentPage onBack={handleBackToHome} />
|
||||
case 'business':
|
||||
return <BusinessPage onBack={handleBackToHome} />
|
||||
case 'consulting':
|
||||
return <ConsultingPage onBack={handleBackToHome} />
|
||||
case 'implementation':
|
||||
return <ImplementationPage onBack={handleBackToHome} />
|
||||
case 'managed-services':
|
||||
return <ManagedServicesPage onBack={handleBackToHome} />
|
||||
case 'integration':
|
||||
return <IntegrationPage onBack={handleBackToHome} />
|
||||
case 'specialized-services':
|
||||
return <SpecializedServicesPage onBack={handleBackToHome} />
|
||||
case 'support':
|
||||
return <SupportPage onBack={handleBackToHome} />
|
||||
case 'subscription':
|
||||
return <SubscriptionPage onBack={handleBackToHome} />
|
||||
default:
|
||||
return <HomePage onOpenContactModal={() => setIsContactModalOpen(true)} />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white w-full overflow-x-hidden">
|
||||
<Header onNavigate={handleNavigate} />
|
||||
{renderPage()}
|
||||
<Footer />
|
||||
<ContactModal isOpen={isContactModalOpen} onClose={() => setIsContactModalOpen(false)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,22 +11,21 @@ interface CategoryPageProps {
|
||||
title: string
|
||||
description: string
|
||||
vendors: Vendor[]
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function CategoryPage({ title, description, vendors, onBack }: CategoryPageProps) {
|
||||
export function CategoryPage({ title, description, vendors }: CategoryPageProps) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Hero секция */}
|
||||
<div className="bg-white border-b border-gray-100 pt-32 pb-16">
|
||||
<div className="max-w-[1600px] mx-auto px-6 xl:px-12 2xl:px-16">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="flex items-center gap-2 text-gray-600 hover:text-blue-600 mb-8 transition-colors group"
|
||||
<a
|
||||
href="/"
|
||||
className="inline-flex items-center gap-2 text-gray-600 hover:text-blue-600 mb-8 transition-colors group"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
|
||||
Назад к главной
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<div className="flex items-start gap-8">
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { Menu, X, ChevronDown } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { ContactModal } from './ContactModal'
|
||||
import { navigateHomeSectionAnchor } from '../lib/scrollToHash'
|
||||
|
||||
interface HeaderProps {
|
||||
onNavigate?: (page: string, section?: string) => void
|
||||
}
|
||||
|
||||
export function Header({ onNavigate }: HeaderProps) {
|
||||
export function Header() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
const [isSolutionsOpen, setIsSolutionsOpen] = useState(false)
|
||||
const [isServicesOpen, setIsServicesOpen] = useState(false)
|
||||
@@ -35,26 +32,12 @@ export function Header({ onNavigate }: HeaderProps) {
|
||||
{ name: 'Услуги по подписке', path: 'subscription' },
|
||||
]
|
||||
|
||||
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, sectionId: string) => {
|
||||
e.preventDefault()
|
||||
console.log('Header: handleNavClick called with sectionId:', sectionId)
|
||||
onNavigate?.('home', sectionId)
|
||||
setIsMenuOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-100">
|
||||
<nav className="max-w-[1600px] mx-auto px-6 xl:px-12 2xl:px-16 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-12">
|
||||
<a
|
||||
href="#"
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
onNavigate?.('home')
|
||||
}}
|
||||
className="flex items-center gap-3 group"
|
||||
>
|
||||
<a href="/" className="flex items-center gap-3 group">
|
||||
<span className="text-2xl text-gray-900 tracking-wide font-bold">SOFTCLICK</span>
|
||||
</a>
|
||||
|
||||
@@ -73,16 +56,13 @@ export function Header({ onNavigate }: HeaderProps) {
|
||||
<div className="absolute top-full left-0 pt-2">
|
||||
<div className="w-80 bg-white rounded-xl shadow-xl border border-gray-100 py-3 animate-in fade-in slide-in-from-top-2 duration-200">
|
||||
{solutions.map(solution => (
|
||||
<button
|
||||
<a
|
||||
key={solution.path}
|
||||
onClick={() => {
|
||||
onNavigate?.(solution.path)
|
||||
setIsSolutionsOpen(false)
|
||||
}}
|
||||
className="w-full text-left px-5 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors"
|
||||
href={`/${solution.path}`}
|
||||
className="block w-full text-left px-5 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors"
|
||||
>
|
||||
{solution.name}
|
||||
</button>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,39 +82,36 @@ export function Header({ onNavigate }: HeaderProps) {
|
||||
<div className="absolute top-full left-0 pt-2">
|
||||
<div className="w-80 bg-white rounded-xl shadow-xl border border-gray-100 py-3 animate-in fade-in slide-in-from-top-2 duration-200">
|
||||
{services.map(service => (
|
||||
<button
|
||||
<a
|
||||
key={service.path}
|
||||
onClick={() => {
|
||||
onNavigate?.(service.path)
|
||||
setIsServicesOpen(false)
|
||||
}}
|
||||
className="w-full text-left px-5 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors"
|
||||
href={`/${service.path}`}
|
||||
className="block w-full text-left px-5 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors"
|
||||
>
|
||||
{service.name}
|
||||
</button>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<a
|
||||
href="#cases"
|
||||
onClick={e => handleNavClick(e, 'cases')}
|
||||
href="/#cases"
|
||||
className="text-gray-700 hover:text-blue-600 transition-colors whitespace-nowrap"
|
||||
onClick={e => navigateHomeSectionAnchor(e, 'cases')}
|
||||
>
|
||||
Кейсы
|
||||
</a>
|
||||
<a
|
||||
href="#about"
|
||||
onClick={e => handleNavClick(e, 'about')}
|
||||
href="/#about"
|
||||
className="text-gray-700 hover:text-blue-600 transition-colors whitespace-nowrap"
|
||||
onClick={e => navigateHomeSectionAnchor(e, 'about')}
|
||||
>
|
||||
О компании
|
||||
</a>
|
||||
<a
|
||||
href="#contact"
|
||||
onClick={e => handleNavClick(e, 'contact')}
|
||||
href="/#contact"
|
||||
className="text-gray-700 hover:text-blue-600 transition-colors whitespace-nowrap"
|
||||
onClick={e => navigateHomeSectionAnchor(e, 'contact')}
|
||||
>
|
||||
Контакты
|
||||
</a>
|
||||
@@ -150,7 +127,7 @@ export function Header({ onNavigate }: HeaderProps) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button onClick={() => setIsMenuOpen(!isMenuOpen)} className="lg:hidden p-2">
|
||||
<button type="button" onClick={() => setIsMenuOpen(!isMenuOpen)} className="lg:hidden p-2">
|
||||
{isMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||
</button>
|
||||
</div>
|
||||
@@ -160,6 +137,7 @@ export function Header({ onNavigate }: HeaderProps) {
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsSolutionsOpen(!isSolutionsOpen)}
|
||||
className="text-gray-700 hover:text-blue-600 w-full text-left flex items-center justify-between"
|
||||
>
|
||||
@@ -169,57 +147,74 @@ export function Header({ onNavigate }: HeaderProps) {
|
||||
{isSolutionsOpen && (
|
||||
<div className="pl-4 mt-2 space-y-2">
|
||||
{solutions.map(solution => (
|
||||
<button
|
||||
<a
|
||||
key={solution.path}
|
||||
href={`/${solution.path}`}
|
||||
onClick={() => {
|
||||
onNavigate?.(solution.path)
|
||||
setIsMenuOpen(false)
|
||||
setIsSolutionsOpen(false)
|
||||
}}
|
||||
className="block w-full text-left text-sm text-gray-600 hover:text-blue-600 py-2"
|
||||
>
|
||||
{solution.name}
|
||||
</button>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsServicesOpen(!isServicesOpen)}
|
||||
className="text-gray-700 hover:text-blue-600 w-full text-left flex items-center justify-between"
|
||||
>
|
||||
Услуги
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${isSolutionsOpen ? 'rotate-180' : ''}`} />
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{isServicesOpen && (
|
||||
<div className="pl-4 mt-2 space-y-2">
|
||||
{services.map(service => (
|
||||
<button
|
||||
<a
|
||||
key={service.path}
|
||||
href={`/${service.path}`}
|
||||
onClick={() => {
|
||||
onNavigate?.(service.path)
|
||||
setIsMenuOpen(false)
|
||||
setIsSolutionsOpen(false)
|
||||
setIsServicesOpen(false)
|
||||
}}
|
||||
className="block w-full text-left text-sm text-gray-600 hover:text-blue-600 py-2"
|
||||
>
|
||||
{service.name}
|
||||
</button>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<a href="#cases" onClick={e => handleNavClick(e, 'cases')} className="text-gray-700 hover:text-blue-600">
|
||||
<a
|
||||
href="/#cases"
|
||||
onClick={e => {
|
||||
navigateHomeSectionAnchor(e, 'cases')
|
||||
setIsMenuOpen(false)
|
||||
}}
|
||||
className="text-gray-700 hover:text-blue-600"
|
||||
>
|
||||
Кейсы
|
||||
</a>
|
||||
<a href="#about" onClick={e => handleNavClick(e, 'about')} className="text-gray-700 hover:text-blue-600">
|
||||
<a
|
||||
href="/#about"
|
||||
onClick={e => {
|
||||
navigateHomeSectionAnchor(e, 'about')
|
||||
setIsMenuOpen(false)
|
||||
}}
|
||||
className="text-gray-700 hover:text-blue-600"
|
||||
>
|
||||
О компании
|
||||
</a>
|
||||
<a
|
||||
href="#contact"
|
||||
onClick={e => handleNavClick(e, 'contact')}
|
||||
href="/#contact"
|
||||
onClick={e => {
|
||||
navigateHomeSectionAnchor(e, 'contact')
|
||||
setIsMenuOpen(false)
|
||||
}}
|
||||
className="text-gray-700 hover:text-blue-600"
|
||||
>
|
||||
Контакты
|
||||
|
||||
1
src/env.d.ts
vendored
Normal file
1
src/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="astro/client" />
|
||||
27
src/layouts/Layout.astro
Normal file
27
src/layouts/Layout.astro
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
import ClientRouter from 'astro/components/ClientRouter.astro'
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
const {
|
||||
title = 'SOFTCLICK - Российские IT-решения с 2021 года',
|
||||
description = 'SOFTCLICK - российская IT-компания, специализирующаяся на импортозамещении и внедрении отечественных решений',
|
||||
} = Astro.props
|
||||
---
|
||||
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>{title}</title>
|
||||
<ClientRouter />
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
51
src/lib/scrollToHash.ts
Normal file
51
src/lib/scrollToHash.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { MouseEvent } from 'react'
|
||||
|
||||
const HEADER_OFFSET_PX = 85
|
||||
|
||||
function prefersReducedMotion() {
|
||||
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
}
|
||||
|
||||
/** Smooth scroll to an element id (fixed header offset). Safe to call from click handlers on `/`. */
|
||||
export function scrollToHashTarget(id: string) {
|
||||
if (!id) return
|
||||
let attempts = 0
|
||||
const maxAttempts = 24
|
||||
|
||||
const tryScroll = () => {
|
||||
const el = document.getElementById(id)
|
||||
if (el) {
|
||||
const top = el.getBoundingClientRect().top + window.scrollY - HEADER_OFFSET_PX
|
||||
const y = Math.max(0, top)
|
||||
const behavior = prefersReducedMotion() ? ('auto' as const) : ('smooth' as const)
|
||||
// Double rAF: smooth scroll often fails to animate when run in the same frame as layout/hydration
|
||||
window.requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
window.scrollTo({ left: 0, top: y, behavior })
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
if (attempts < maxAttempts) {
|
||||
attempts += 1
|
||||
requestAnimationFrame(tryScroll)
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(tryScroll)
|
||||
}
|
||||
|
||||
/** On the home page only: prevent the browser’s instant # jump, update the URL, then smooth-scroll. */
|
||||
export function navigateHomeSectionAnchor(e: MouseEvent<HTMLAnchorElement>, id: string) {
|
||||
if (typeof window === 'undefined') return
|
||||
const path = window.location.pathname
|
||||
const isHome = path === '/' || path === ''
|
||||
if (!isHome) return
|
||||
|
||||
e.preventDefault()
|
||||
const nextHash = `#${id}`
|
||||
if (window.location.hash !== nextHash) {
|
||||
window.history.replaceState(null, '', nextHash)
|
||||
}
|
||||
scrollToHashTarget(id)
|
||||
}
|
||||
11
src/main.tsx
11
src/main.tsx
@@ -1,11 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './styles/globals.css'
|
||||
import './styles/animations.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Hero } from '../components/Hero'
|
||||
import { Services } from '../components/Services'
|
||||
import { Solutions } from '../components/Solutions'
|
||||
import { Cases } from '../components/Cases'
|
||||
import { About } from '../components/About'
|
||||
import { Contact } from '../components/Contact'
|
||||
|
||||
interface HomePageProps {
|
||||
onOpenContactModal?: () => void
|
||||
}
|
||||
|
||||
export function HomePage({ onOpenContactModal }: HomePageProps) {
|
||||
return (
|
||||
<>
|
||||
<Hero onOpenContactModal={onOpenContactModal} />
|
||||
<Services />
|
||||
<Solutions />
|
||||
<div id="cases">
|
||||
<Cases />
|
||||
</div>
|
||||
<div id="about">
|
||||
<About />
|
||||
</div>
|
||||
<div id="contact">
|
||||
<Contact />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
39
src/pages/[slug].astro
Normal file
39
src/pages/[slug].astro
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import '../styles/globals.css'
|
||||
import { Header } from '../components/Header'
|
||||
import { Footer } from '../components/Footer'
|
||||
import { CategoryRoute } from '../views/CategoryRoute'
|
||||
|
||||
export function getStaticPaths() {
|
||||
return [
|
||||
{ params: { slug: 'cloud' } },
|
||||
{ params: { slug: 'russian-software' } },
|
||||
{ params: { slug: 'cybersecurity' } },
|
||||
{ params: { slug: 'ai-solutions' } },
|
||||
{ params: { slug: 'russian-hardware' } },
|
||||
{ params: { slug: 'hardware-solutions' } },
|
||||
{ params: { slug: 'cad-gis' } },
|
||||
{ params: { slug: 'industrial-solutions' } },
|
||||
{ params: { slug: 'government' } },
|
||||
{ params: { slug: 'business' } },
|
||||
{ params: { slug: 'consulting' } },
|
||||
{ params: { slug: 'implementation' } },
|
||||
{ params: { slug: 'managed-services' } },
|
||||
{ params: { slug: 'integration' } },
|
||||
{ params: { slug: 'specialized-services' } },
|
||||
{ params: { slug: 'support' } },
|
||||
{ params: { slug: 'subscription' } },
|
||||
]
|
||||
}
|
||||
|
||||
const slug = Astro.params.slug!
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="min-h-screen bg-white w-full overflow-x-hidden">
|
||||
<Header client:load />
|
||||
<CategoryRoute client:load slug={slug} />
|
||||
<Footer client:load />
|
||||
</div>
|
||||
</Layout>
|
||||
15
src/pages/index.astro
Normal file
15
src/pages/index.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import '../styles/globals.css'
|
||||
import { Header } from '../components/Header'
|
||||
import { Footer } from '../components/Footer'
|
||||
import { HomePage } from '../views/HomePage'
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="min-h-screen bg-white w-full overflow-x-hidden">
|
||||
<Header client:load />
|
||||
<HomePage client:load />
|
||||
<Footer client:load />
|
||||
</div>
|
||||
</Layout>
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface AISolutionsPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function AISolutionsPage({ onBack }: AISolutionsPageProps) {
|
||||
export function AISolutionsPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'Яндекс AI',
|
||||
@@ -58,7 +55,6 @@ export function AISolutionsPage({ onBack }: AISolutionsPageProps) {
|
||||
title="Решения на базе ИИ"
|
||||
description="Передовые технологии искусственного интеллекта и машинного обучения для автоматизации бизнес-процессов"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface BusinessPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function BusinessPage({ onBack }: BusinessPageProps) {
|
||||
export function BusinessPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'SAP',
|
||||
@@ -58,7 +55,6 @@ export function BusinessPage({ onBack }: BusinessPageProps) {
|
||||
title="Решения для бизнеса"
|
||||
description="Комплексные IT-решения для автоматизации бизнес-процессов, CRM, ERP и управления предприятием"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface CADandGISPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function CADandGISPage({ onBack }: CADandGISPageProps) {
|
||||
export function CADandGISPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'АСКОН',
|
||||
@@ -58,7 +55,6 @@ export function CADandGISPage({ onBack }: CADandGISPageProps) {
|
||||
title="САПР и ГИС"
|
||||
description="Системы автоматизированного проектирования и геоинформационные системы для инженеров, архитекторов и проектировщиков"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
58
src/views/CategoryRoute.tsx
Normal file
58
src/views/CategoryRoute.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { AISolutionsPage } from './AISolutionsPage'
|
||||
import { BusinessPage } from './BusinessPage'
|
||||
import { CADandGISPage } from './CADandGISPage'
|
||||
import { CloudPage } from './CloudPage'
|
||||
import { ConsultingPage } from './ConsultingPage'
|
||||
import { CybersecurityPage } from './CybersecurityPage'
|
||||
import { GovernmentPage } from './GovernmentPage'
|
||||
import { HardwareSolutionsPage } from './HardwareSolutionsPage'
|
||||
import { ImplementationPage } from './ImplementationPage'
|
||||
import { IndustrialSolutionsPage } from './IndustrialSolutionsPage'
|
||||
import { IntegrationPage } from './IntegrationPage'
|
||||
import { ManagedServicesPage } from './ManagedServicesPage'
|
||||
import { RussianHardwarePage } from './RussianHardwarePage'
|
||||
import { RussianSoftwarePage } from './RussianSoftwarePage'
|
||||
import { SpecializedServicesPage } from './SpecializedServicesPage'
|
||||
import { SubscriptionPage } from './SubscriptionPage'
|
||||
import { SupportPage } from './SupportPage'
|
||||
|
||||
export function CategoryRoute({ slug }: { slug: string }) {
|
||||
switch (slug) {
|
||||
case 'cloud':
|
||||
return <CloudPage />
|
||||
case 'russian-software':
|
||||
return <RussianSoftwarePage />
|
||||
case 'cybersecurity':
|
||||
return <CybersecurityPage />
|
||||
case 'ai-solutions':
|
||||
return <AISolutionsPage />
|
||||
case 'russian-hardware':
|
||||
return <RussianHardwarePage />
|
||||
case 'hardware-solutions':
|
||||
return <HardwareSolutionsPage />
|
||||
case 'cad-gis':
|
||||
return <CADandGISPage />
|
||||
case 'industrial-solutions':
|
||||
return <IndustrialSolutionsPage />
|
||||
case 'government':
|
||||
return <GovernmentPage />
|
||||
case 'business':
|
||||
return <BusinessPage />
|
||||
case 'consulting':
|
||||
return <ConsultingPage />
|
||||
case 'implementation':
|
||||
return <ImplementationPage />
|
||||
case 'managed-services':
|
||||
return <ManagedServicesPage />
|
||||
case 'integration':
|
||||
return <IntegrationPage />
|
||||
case 'specialized-services':
|
||||
return <SpecializedServicesPage />
|
||||
case 'support':
|
||||
return <SupportPage />
|
||||
case 'subscription':
|
||||
return <SubscriptionPage />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface CloudPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function CloudPage({ onBack }: CloudPageProps) {
|
||||
export function CloudPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'Яндекс.Облако',
|
||||
@@ -43,7 +40,6 @@ export function CloudPage({ onBack }: CloudPageProps) {
|
||||
title="Облачные решения"
|
||||
description="Российские облачные платформы для размещения инфраструктуры, приложений и данных с соблюдением требований законодательства РФ"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface ConsultingPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function ConsultingPage({ onBack }: ConsultingPageProps) {
|
||||
export function ConsultingPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'IT-стратегия и архитектура',
|
||||
@@ -47,7 +44,6 @@ export function ConsultingPage({ onBack }: ConsultingPageProps) {
|
||||
title="Консалтинг и проектирование"
|
||||
description="Экспертный консалтинг по построению и трансформации IT-инфраструктуры. Помогаем разработать стратегию цифровизации, выбрать оптимальные технологии и спланировать миграцию на российские решения."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface CybersecurityPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function CybersecurityPage({ onBack }: CybersecurityPageProps) {
|
||||
export function CybersecurityPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'Лаборатория Касперского',
|
||||
@@ -58,7 +55,6 @@ export function CybersecurityPage({ onBack }: CybersecurityPageProps) {
|
||||
title="Кибербезопасность"
|
||||
description="Комплексные решения для защиты корпоративной инфраструктуры, данных и приложений от современных киберугроз"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface GovernmentPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function GovernmentPage({ onBack }: GovernmentPageProps) {
|
||||
export function GovernmentPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'Astra Linux',
|
||||
@@ -58,7 +55,6 @@ export function GovernmentPage({ onBack }: GovernmentPageProps) {
|
||||
title="Для госсектора"
|
||||
description="Специализированные решения для государственных органов с соблюдением требований ФСТЭК и ФСБ"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface HardwareSolutionsPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function HardwareSolutionsPage({ onBack }: HardwareSolutionsPageProps) {
|
||||
export function HardwareSolutionsPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'YADRO',
|
||||
@@ -58,7 +55,6 @@ export function HardwareSolutionsPage({ onBack }: HardwareSolutionsPageProps) {
|
||||
title="Аппаратные решения"
|
||||
description="Комплексные аппаратные решения для построения IT-инфраструктуры: СХД, сетевое оборудование, компоненты"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
41
src/views/HomePage.tsx
Normal file
41
src/views/HomePage.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Hero } from '../components/Hero'
|
||||
import { Services } from '../components/Services'
|
||||
import { Solutions } from '../components/Solutions'
|
||||
import { Cases } from '../components/Cases'
|
||||
import { About } from '../components/About'
|
||||
import { Contact } from '../components/Contact'
|
||||
import { ContactModal } from '../components/ContactModal'
|
||||
import { scrollToHashTarget } from '../lib/scrollToHash'
|
||||
|
||||
const HEADER_OFFSET_PX = 85
|
||||
|
||||
export function HomePage() {
|
||||
const [contactOpen, setContactOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const onHashChange = () => scrollToHashTarget(window.location.hash.slice(1))
|
||||
|
||||
onHashChange()
|
||||
window.addEventListener('hashchange', onHashChange)
|
||||
return () => window.removeEventListener('hashchange', onHashChange)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero onOpenContactModal={() => setContactOpen(true)} />
|
||||
<Services />
|
||||
<Solutions />
|
||||
<div id="cases" style={{ scrollMarginTop: HEADER_OFFSET_PX }}>
|
||||
<Cases />
|
||||
</div>
|
||||
<div id="about" style={{ scrollMarginTop: HEADER_OFFSET_PX }}>
|
||||
<About />
|
||||
</div>
|
||||
<div id="contact" style={{ scrollMarginTop: HEADER_OFFSET_PX }}>
|
||||
<Contact />
|
||||
</div>
|
||||
<ContactModal isOpen={contactOpen} onClose={() => setContactOpen(false)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface ImplementationPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function ImplementationPage({ onBack }: ImplementationPageProps) {
|
||||
export function ImplementationPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'Внедрение облачных платформ',
|
||||
@@ -44,7 +41,6 @@ export function ImplementationPage({ onBack }: ImplementationPageProps) {
|
||||
title="Поставка и внедрение решений"
|
||||
description="Полный цикл поставки и внедрения IT-решений: от подбора оборудования и ПО до установки, настройки и ввода в эксплуатацию. Работаем только с сертифицированными российскими решениями."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface IndustrialSolutionsPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function IndustrialSolutionsPage({ onBack }: IndustrialSolutionsPageProps) {
|
||||
export function IndustrialSolutionsPage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'ОВЕН',
|
||||
@@ -58,7 +55,6 @@ export function IndustrialSolutionsPage({ onBack }: IndustrialSolutionsPageProps
|
||||
title="Индустриальные решения"
|
||||
description="Системы промышленной автоматизации, SCADA, MES и решения для управления технологическими процессами"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface IntegrationPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function IntegrationPage({ onBack }: IntegrationPageProps) {
|
||||
export function IntegrationPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'Интеграция корпоративных систем',
|
||||
@@ -45,7 +42,6 @@ export function IntegrationPage({ onBack }: IntegrationPageProps) {
|
||||
title="Интеграция и разработка"
|
||||
description="Разрабатываем и интегрируем ПО любой сложности. От интеграции корпоративных систем до создания уникальных решений с нуля на современных технологических стеках."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface ManagedServicesPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function ManagedServicesPage({ onBack }: ManagedServicesPageProps) {
|
||||
export function ManagedServicesPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'Управление инфраструктурой',
|
||||
@@ -44,7 +41,6 @@ export function ManagedServicesPage({ onBack }: ManagedServicesPageProps) {
|
||||
title="Управляемые сервисы"
|
||||
description="Передайте управление IT-инфраструктурой профессионалам. Обеспечиваем круглосуточный мониторинг, поддержку и развитие ваших IT-систем с гарантией SLA."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface RussianHardwarePageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function RussianHardwarePage({ onBack }: RussianHardwarePageProps) {
|
||||
export function RussianHardwarePage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'Kraftway',
|
||||
@@ -58,7 +55,6 @@ export function RussianHardwarePage({ onBack }: RussianHardwarePageProps) {
|
||||
title="Российские ноутбуки, ПК и серверы"
|
||||
description="Отечественное компьютерное и серверное оборудование для корпоративного сектора и государственных организаций"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface RussianSoftwarePageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function RussianSoftwarePage({ onBack }: RussianSoftwarePageProps) {
|
||||
export function RussianSoftwarePage() {
|
||||
const vendors = [
|
||||
{
|
||||
name: 'МойОфис',
|
||||
@@ -58,7 +55,6 @@ export function RussianSoftwarePage({ onBack }: RussianSoftwarePageProps) {
|
||||
title="Российское ПО"
|
||||
description="Комплексные программные решения от российских разработчиков для бизнеса, образования и государственного сектора"
|
||||
vendors={vendors}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface SpecializedServicesPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function SpecializedServicesPage({ onBack }: SpecializedServicesPageProps) {
|
||||
export function SpecializedServicesPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'Решения для финансового сектора',
|
||||
@@ -44,7 +41,6 @@ export function SpecializedServicesPage({ onBack }: SpecializedServicesPageProps
|
||||
title="Специализированные услуги по отраслям"
|
||||
description="Отраслевая экспертиза в финансах, промышленности, здравоохранении, образовании, ритейле и телекоме. Понимаем специфику бизнеса и требования регуляторов."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface SubscriptionPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function SubscriptionPage({ onBack }: SubscriptionPageProps) {
|
||||
export function SubscriptionPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'Облачная инфраструктура по подписке',
|
||||
@@ -43,7 +40,6 @@ export function SubscriptionPage({ onBack }: SubscriptionPageProps) {
|
||||
title="Услуги по подписке"
|
||||
description="Современная модель потребления IT-услуг. Платите только за использованные ресурсы, получайте актуальные обновления и профессиональную поддержку без капитальных затрат."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CategoryPage } from '../components/CategoryPage'
|
||||
|
||||
interface SupportPageProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function SupportPage({ onBack }: SupportPageProps) {
|
||||
export function SupportPage() {
|
||||
const services = [
|
||||
{
|
||||
name: 'Техническая поддержка 24/7',
|
||||
@@ -43,7 +40,6 @@ export function SupportPage({ onBack }: SupportPageProps) {
|
||||
title="Обслуживание и сопровождение"
|
||||
description="Надежная техническая поддержка и обслуживание IT-инфраструктуры. Гарантируем стабильную работу всех систем с минимальным временем простоя."
|
||||
vendors={services}
|
||||
onBack={onBack}
|
||||
/>
|
||||
)
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
},
|
||||
"include": ["src", ".astro/types.d.ts"]
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
});
|
||||
Reference in New Issue
Block a user