init commit

This commit is contained in:
apheyhys 2026-02-11 20:49:50 +03:00
commit 99faa78434
25 changed files with 895 additions and 0 deletions

39
.gitignore vendored Normal file
View file

@ -0,0 +1,39 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
.env
.venv
poetry.lock
alembic/versions/
unloading.db
# Backend
backend/backend_transfer_tdms_tcs/__pycache__/
backend/backend_transfer_tdms_tcs/*.db
backend/backend_transfer_tdms_tcs/alembic/versions/
# Frontend
frontend/node_modules/
frontend/.next/
frontend/out/
frontend/build
frontend/.env.local
frontend/.env.*.local
frontend/next-env.d.ts
frontend/package-lock.json
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db

View file

@ -0,0 +1,46 @@
[alembic]
script_location = alembic
prepend_sys_path = .
version_path_separator = os
sqlalchemy.url = sqlite:///./unloading.db
[post_write_hooks]
hooks = black
black.type = console_scripts
black.entrypoint = black
black.options = -l 88
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View file

@ -0,0 +1,83 @@
from sqladmin import Admin, ModelView
from sqladmin.authentication import AuthenticationBackend
from fastapi import Request
from wtforms import TextAreaField
from wtforms.widgets import TextArea
from .database import engine
from .models import Unloading, Product, UnloadCheck, TestLoad, TestCheck, ProdTransfer
class JSONTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
kwargs["class"] = "json-textarea"
return super().__call__(field, **kwargs)
class UnloadingAdmin(ModelView, model=Unloading):
column_list = [Unloading.guid, Unloading.name, Unloading.created_at, Unloading.computer_name]
column_searchable_list = [Unloading.name, Unloading.guid, Unloading.computer_name]
column_sortable_list = [Unloading.created_at, Unloading.unloading_time_seconds]
form_overrides = {"warnings": TextAreaField}
form_widget_args = {"warnings": {"widget": JSONTextAreaWidget(), "rows": 5}}
can_create = True
can_edit = True
can_delete = True
can_view_details = True
class ProductAdmin(ModelView, model=Product):
column_list = [Product.guid, Product.name]
column_searchable_list = [Product.name, Product.guid]
can_create = False # Продукты создаются через триггер
can_edit = True
can_delete = False
can_view_details = True
class UnloadCheckAdmin(ModelView, model=UnloadCheck):
column_list = [UnloadCheck.id, UnloadCheck.product_guid, UnloadCheck.fio, UnloadCheck.date, UnloadCheck.is_completed]
column_searchable_list = [UnloadCheck.fio, UnloadCheck.product_guid]
class TestLoadAdmin(ModelView, model=TestLoad):
column_list = [TestLoad.id, TestLoad.product_guid, TestLoad.fio, TestLoad.date, TestLoad.is_completed]
class TestCheckAdmin(ModelView, model=TestCheck):
column_list = [TestCheck.id, TestCheck.product_guid, TestCheck.fio, TestCheck.date, TestCheck.is_completed]
class ProdTransferAdmin(ModelView, model=ProdTransfer):
column_list = [ProdTransfer.id, ProdTransfer.product_guid, ProdTransfer.fio, ProdTransfer.date, ProdTransfer.is_completed]
# Простая аутентификация (можно заменить на более сложную)
class AdminAuth(AuthenticationBackend):
async def login(self, request: Request) -> bool:
form = await request.form()
username = form.get("username")
password = form.get("password")
# Простая проверка (в production используйте безопасную аутентификацию)
if username == "admin" and password == "admin":
request.session.update({"token": "admin-token"})
return True
return False
async def logout(self, request: Request) -> bool:
request.session.clear()
return True
async def authenticate(self, request: Request) -> bool:
token = request.session.get("token")
if token == "admin-token":
return True
return False
def setup_admin(app):
authentication_backend = AdminAuth(secret_key="secret-key")
admin = Admin(app=app, engine=engine, authentication_backend=authentication_backend)
admin.add_view(UnloadingAdmin)
admin.add_view(ProductAdmin)
admin.add_view(UnloadCheckAdmin)
admin.add_view(TestLoadAdmin)
admin.add_view(TestCheckAdmin)
admin.add_view(ProdTransferAdmin)
return admin

View file

@ -0,0 +1,75 @@
from sqlalchemy.orm import Session
from . import models, schemas
from typing import Optional, List
import uuid
def create_unloading(db: Session, unloading: schemas.UnloadingCreate):
db_unloading = models.Unloading(
guid=unloading.guid or str(uuid.uuid4()).upper(),
name=unloading.name,
unloading_time_seconds=unloading.unloading_time_seconds,
warnings=unloading.warnings,
row_count=unloading.row_count,
file_count=unloading.file_count,
computer_name=unloading.computer_name
)
db.add(db_unloading)
db.commit()
db.refresh(db_unloading)
return db_unloading
def get_unloadings(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Unloading).offset(skip).limit(limit).all()
def get_unloading(db: Session, guid: str):
return db.query(models.Unloading).filter(models.Unloading.guid == guid).first()
def get_products(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Product).offset(skip).limit(limit).all()
def get_product(db: Session, guid: str):
return db.query(models.Product).filter(models.Product.guid == guid).first()
def add_unload_check(db: Session, product_guid: str, stage: schemas.UnloadCheckCreate):
db_stage = models.UnloadCheck(
product_guid=product_guid,
fio=stage.fio,
is_completed=stage.is_completed
)
db.add(db_stage)
db.commit()
db.refresh(db_stage)
return db_stage
def add_test_load(db: Session, product_guid: str, stage: schemas.TestLoadCreate):
db_stage = models.TestLoad(
product_guid=product_guid,
fio=stage.fio,
is_completed=stage.is_completed
)
db.add(db_stage)
db.commit()
db.refresh(db_stage)
return db_stage
def add_test_check(db: Session, product_guid: str, stage: schemas.TestCheckCreate):
db_stage = models.TestCheck(
product_guid=product_guid,
fio=stage.fio,
is_completed=stage.is_completed
)
db.add(db_stage)
db.commit()
db.refresh(db_stage)
return db_stage
def add_prod_transfer(db: Session, product_guid: str, stage: schemas.ProdTransferCreate):
db_stage = models.ProdTransfer(
product_guid=product_guid,
fio=stage.fio,
is_completed=stage.is_completed
)
db.add(db_stage)
db.commit()
db.refresh(db_stage)
return db_stage

View file

@ -0,0 +1,23 @@
from sqlalchemy import create_engine, event, DDL
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
SQLALCHEMY_DATABASE_URL = "sqlite:///./unloading.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
connect_args={"check_same_thread": False},
poolclass=StaticPool
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

View file

@ -0,0 +1,72 @@
from contextlib import asynccontextmanager # ДОБАВЬТЕ ЭТОТ ИМПОРТ!
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from sqlalchemy import text
from .database import engine, Base, get_db
from .models import *
from .routers import unloadings, products
from .admin import setup_admin
@asynccontextmanager
async def lifespan(app: FastAPI):
# Создание таблиц при старте
Base.metadata.create_all(bind=engine)
# Создание триггера
# Создание триггера
from sqlalchemy import text
with engine.begin() as conn:
conn.execute(text("""
CREATE TRIGGER IF NOT EXISTS insert_product_from_unloading
AFTER INSERT ON unloading
FOR EACH ROW
WHEN NOT EXISTS (SELECT 1 FROM product WHERE guid = NEW.guid)
BEGIN
INSERT INTO product (guid, name)
VALUES (NEW.guid, NEW.name);
-- Обновляем product_guid после создания продукта
UPDATE unloading
SET product_guid = NEW.guid
WHERE guid = NEW.guid;
END;
"""))
yield
# Очистка при завершении
pass
app = FastAPI(
title="Unloading System API",
description="Система управления выгрузками и продуктами",
version="1.0.0",
lifespan=lifespan
)
# Настройка админки
admin = setup_admin(app)
# Подключение роутеров
app.include_router(unloadings.router)
app.include_router(products.router)
@app.get("/")
async def root():
return {
"message": "Unloading System API",
"docs": "/docs",
"admin": "/admin"
}
@app.get("/test-db")
def test_db(db: Session = Depends(get_db)):
from sqlalchemy import text
result = db.execute(text("SELECT name FROM sqlite_master WHERE type='table'"))
tables = [row[0] for row in result]
return {"tables": tables}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

View file

@ -0,0 +1,65 @@
from sqlalchemy import Column, String, Integer, DateTime, JSON, ForeignKey, Boolean
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship, declared_attr
import uuid
from .database import Base
class Unloading(Base):
__tablename__ = "unloading"
guid = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()).upper())
name = Column(String(500), nullable=False)
unloading_time_seconds = Column(Integer, nullable=False)
warnings = Column(JSON, default={})
row_count = Column(Integer, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
file_count = Column(Integer, nullable=False)
computer_name = Column(String(100), nullable=True)
# ДОБАВЬТЕ ЭТОТ ВНЕШНИЙ КЛЮЧ
product_guid = Column(String(36), ForeignKey("product.guid"), unique=True)
# Исправленная связь
product = relationship("Product", back_populates="unloading", uselist=False)
class Product(Base):
__tablename__ = "product"
guid = Column(String(36), primary_key=True, index=True)
name = Column(String(500), nullable=False)
# Связь остается без изменений
unloading = relationship("Unloading", back_populates="product", uselist=False)
unload_checks = relationship("UnloadCheck", back_populates="product", cascade="all, delete-orphan")
test_loads = relationship("TestLoad", back_populates="product", cascade="all, delete-orphan")
test_checks = relationship("TestCheck", back_populates="product", cascade="all, delete-orphan")
prod_transfers = relationship("ProdTransfer", back_populates="product", cascade="all, delete-orphan")
class BaseStage(Base):
__abstract__ = True
id = Column(Integer, primary_key=True, index=True)
fio = Column(String(200), nullable=False)
date = Column(DateTime(timezone=True), server_default=func.now())
is_completed = Column(Boolean, default=False)
@declared_attr
def product_guid(cls):
return Column(String(36), ForeignKey("product.guid"))
class UnloadCheck(BaseStage):
__tablename__ = "unload_check"
product = relationship("Product", back_populates="unload_checks")
class TestLoad(BaseStage):
__tablename__ = "test_load"
product = relationship("Product", back_populates="test_loads")
class TestCheck(BaseStage):
__tablename__ = "test_check"
product = relationship("Product", back_populates="test_checks")
class ProdTransfer(BaseStage):
__tablename__ = "prod_transfer"
product = relationship("Product", back_populates="prod_transfers")

View file

@ -0,0 +1,34 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from .. import crud, schemas
from ..database import get_db
router = APIRouter(prefix="/products", tags=["products"])
@router.get("/", response_model=List[schemas.Product])
def read_products(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
return crud.get_products(db, skip=skip, limit=limit)
@router.get("/{guid}", response_model=schemas.Product)
def read_product(guid: str, db: Session = Depends(get_db)):
db_product = crud.get_product(db, guid=guid)
if db_product is None:
raise HTTPException(status_code=404, detail="Product not found")
return db_product
@router.post("/{guid}/unload-check/", response_model=schemas.Stage)
def create_unload_check(guid: str, stage: schemas.UnloadCheckCreate, db: Session = Depends(get_db)):
return crud.add_unload_check(db, product_guid=guid, stage=stage)
@router.post("/{guid}/test-load/", response_model=schemas.Stage)
def create_test_load(guid: str, stage: schemas.TestLoadCreate, db: Session = Depends(get_db)):
return crud.add_test_load(db, product_guid=guid, stage=stage)
@router.post("/{guid}/test-check/", response_model=schemas.Stage)
def create_test_check(guid: str, stage: schemas.TestCheckCreate, db: Session = Depends(get_db)):
return crud.add_test_check(db, product_guid=guid, stage=stage)
@router.post("/{guid}/prod-transfer/", response_model=schemas.Stage)
def create_prod_transfer(guid: str, stage: schemas.ProdTransferCreate, db: Session = Depends(get_db)):
return crud.add_prod_transfer(db, product_guid=guid, stage=stage)

View file

@ -0,0 +1,23 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from .. import crud, schemas
from ..database import get_db
router = APIRouter(prefix="/unloadings", tags=["unloadings"])
@router.post("/", response_model=schemas.Unloading)
def create_unloading(unloading: schemas.UnloadingCreate, db: Session = Depends(get_db)):
return crud.create_unloading(db=db, unloading=unloading)
@router.get("/", response_model=List[schemas.Unloading])
def read_unloadings(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
unloadings = crud.get_unloadings(db, skip=skip, limit=limit)
return unloadings
@router.get("/{guid}", response_model=schemas.Unloading)
def read_unloading(guid: str, db: Session = Depends(get_db)):
db_unloading = crud.get_unloading(db, guid=guid)
if db_unloading is None:
raise HTTPException(status_code=404, detail="Unloading not found")
return db_unloading

View file

@ -0,0 +1,59 @@
from pydantic import BaseModel
from datetime import datetime
from typing import Optional, Dict, List
import uuid
class UnloadingBase(BaseModel):
name: str
unloading_time_seconds: int
warnings: Optional[Dict] = {}
row_count: int
file_count: int
computer_name: Optional[str] = None
class UnloadingCreate(UnloadingBase):
guid: Optional[str] = None
class Unloading(UnloadingBase):
guid: str
created_at: datetime
class Config:
from_attributes = True
class StageBase(BaseModel):
fio: str
is_completed: bool = False
class UnloadCheckCreate(StageBase):
pass
class TestLoadCreate(StageBase):
pass
class TestCheckCreate(StageBase):
pass
class ProdTransferCreate(StageBase):
pass
class Stage(StageBase):
id: int
date: datetime
product_guid: str
class Config:
from_attributes = True
class ProductBase(BaseModel):
name: str
class Product(ProductBase):
guid: str
unload_checks: List[Stage] = []
test_loads: List[Stage] = []
test_checks: List[Stage] = []
prod_transfers: List[Stage] = []
class Config:
from_attributes = True

View file

@ -0,0 +1,26 @@
[tool.poetry]
name = "fastapi-unloading-app"
version = "0.1.0"
package-mode = false
description = "FastAPI application for unloading management"
authors = ["Your Name <your.email@example.com>"]
[tool.poetry.dependencies]
python = "^3.9"
fastapi = "^0.104.0"
sqlalchemy = "1.4.42"
databases = "0.8.0"
aiosqlite = "^0.19.0"
sqladmin = "^0.15.0"
jinja2 = "^3.1.0"
python-multipart = "^0.0.6"
uvicorn = {extras = ["standard"], version = "^0.24.0"}
itsdangerous = "^2.2.0"
[tool.poetry.group.dev.dependencies]
alembic = "^1.12.0"
black = "^23.11.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

1
frontend/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules

21
frontend/app/layout.tsx Normal file
View file

@ -0,0 +1,21 @@
// src/app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { Provider } from '@/components/ui/provider'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'My Chakra App',
description: 'Приложение на Next.js с Chakra UI',
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<Provider>{children}</Provider>
</body>
</html>
)
}

28
frontend/app/page.tsx Normal file
View file

@ -0,0 +1,28 @@
// src/app/page.tsx
import { Button, Container, Heading, Text, VStack } from '@chakra-ui/react'
import LineBox from '@/components/LineBox'
export default function Home() {
return (
<Container maxW="100%" py={1} px={3}>
<VStack spacing={1} align="stretch">
<Heading textAlign="center" color="teal.600">
Добро пожаловать в Chakra UI + Next.js
</Heading>
{/* <Text fontSize="xl" textAlign="center">
Приложение настроено по официальной документации
</Text> */}
{/* <Button colorScheme="teal" size="lg" alignSelf="center">
Начать работу
</Button> */}
<LineBox
title="Мой первый компонент"
description="Это переиспользуемый компонент с настраиваемыми пропсами"
buttonText="Нажми меня"
//onButtonClick={handleClick}
/>
</VStack>
</Container>
)
}

24
frontend/package.json Normal file
View file

@ -0,0 +1,24 @@
{
"dependencies": {
"@chakra-ui/react": "^3.32.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"framer-motion": "^12.31.0",
"next": "^16.1.6",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-icons": "^5.5.0"
},
"scripts": {
"dev": "next dev --webpack",
"build": "next build --webpack",
"start": "next start",
"lint": "next lint"
},
"devDependencies": {
"@types/node": "25.2.0",
"@types/react": "19.2.11",
"typescript": "5.9.3"
}
}

View file

@ -0,0 +1,54 @@
// src/components/CustomBox.tsx
import { Box, BoxProps, Heading, Text, Button, HStack } from '@chakra-ui/react'
interface CustomBoxProps extends BoxProps {
title?: string
description?: string
buttonText?: string
onButtonClick?: () => void
}
export default function CustomBox({
title = "Заголовок по умолчанию",
description = "Описание по умолчанию",
buttonText = "Кнопка",
onButtonClick,
children,
...rest
}: CustomBoxProps) {
return (
<Box
p={4}
borderWidth="1px"
borderRadius="lg"
boxShadow="lg"
bg="white"
width="100%"
_dark={{ bg: "gray.700" }}
{...rest}
>
<HStack spacing={4} align="stretch">
{description && (
<Text fontSize="md" color="gray.600" _dark={{ color: "gray.300" }}>
{description}
</Text>
)}
<Text fontSize="md" color="gray.600" _dark={{ color: "gray.300" }}>test</Text>
{/* Дочерние элементы (если переданы) */}
{/* {children}
// {buttonText && (
// <Button
// colorScheme="teal"
// onClick={onButtonClick}
// alignSelf="flex-start"
// >
// {buttonText}
// </Button>
// )} */}
</HStack>
</Box>
)
}

View file

@ -0,0 +1,90 @@
'use client'
import { ClientOnly, IconButton, Skeleton, Span } from '@chakra-ui/react'
import { ThemeProvider, useTheme } from 'next-themes'
import * as React from 'react'
import { LuMoon, LuSun } from 'react-icons/lu'
export function ColorModeProvider(props) {
return (
<ThemeProvider attribute='class' disableTransitionOnChange {...props} />
)
}
export function useColorMode() {
const { resolvedTheme, setTheme, forcedTheme } = useTheme()
const colorMode = forcedTheme || resolvedTheme
const toggleColorMode = () => {
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')
}
return {
colorMode: colorMode,
setColorMode: setTheme,
toggleColorMode,
}
}
export function useColorModeValue(light, dark) {
const { colorMode } = useColorMode()
return colorMode === 'dark' ? dark : light
}
export function ColorModeIcon() {
const { colorMode } = useColorMode()
return colorMode === 'dark' ? <LuMoon /> : <LuSun />
}
export const ColorModeButton = React.forwardRef(
function ColorModeButton(props, ref) {
const { toggleColorMode } = useColorMode()
return (
<ClientOnly fallback={<Skeleton boxSize='9' />}>
<IconButton
onClick={toggleColorMode}
variant='ghost'
aria-label='Toggle color mode'
size='sm'
ref={ref}
{...props}
css={{
_icon: {
width: '5',
height: '5',
},
}}
>
<ColorModeIcon />
</IconButton>
</ClientOnly>
)
},
)
export const LightMode = React.forwardRef(function LightMode(props, ref) {
return (
<Span
color='fg'
display='contents'
className='chakra-theme light'
colorPalette='gray'
colorScheme='light'
ref={ref}
{...props}
/>
)
})
export const DarkMode = React.forwardRef(function DarkMode(props, ref) {
return (
<Span
color='fg'
display='contents'
className='chakra-theme dark'
colorPalette='gray'
colorScheme='dark'
ref={ref}
{...props}
/>
)
})

View file

@ -0,0 +1,12 @@
'use client'
import { ChakraProvider, defaultSystem } from '@chakra-ui/react'
import { ColorModeProvider } from './color-mode'
export function Provider(props) {
return (
<ChakraProvider value={defaultSystem}>
<ColorModeProvider {...props} />
</ChakraProvider>
)
}

View file

@ -0,0 +1,43 @@
'use client'
import {
Toaster as ChakraToaster,
Portal,
Spinner,
Stack,
Toast,
createToaster,
} from '@chakra-ui/react'
export const toaster = createToaster({
placement: 'bottom-end',
pauseOnPageIdle: true,
})
export const Toaster = () => {
return (
<Portal>
<ChakraToaster toaster={toaster} insetInline={{ mdDown: '4' }}>
{(toast) => (
<Toast.Root width={{ md: 'sm' }}>
{toast.type === 'loading' ? (
<Spinner size='sm' color='blue.solid' />
) : (
<Toast.Indicator />
)}
<Stack gap='1' flex='1' maxWidth='100%'>
{toast.title && <Toast.Title>{toast.title}</Toast.Title>}
{toast.description && (
<Toast.Description>{toast.description}</Toast.Description>
)}
</Stack>
{toast.action && (
<Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
)}
{toast.closable && <Toast.CloseTrigger />}
</Toast.Root>
)}
</ChakraToaster>
</Portal>
)
}

View file

@ -0,0 +1,35 @@
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react'
import * as React from 'react'
export const Tooltip = React.forwardRef(function Tooltip(props, ref) {
const {
showArrow,
children,
disabled,
portalled = true,
content,
contentProps,
portalRef,
...rest
} = props
if (disabled) return children
return (
<ChakraTooltip.Root {...rest}>
<ChakraTooltip.Trigger asChild>{children}</ChakraTooltip.Trigger>
<Portal disabled={!portalled} container={portalRef}>
<ChakraTooltip.Positioner>
<ChakraTooltip.Content ref={ref} {...contentProps}>
{showArrow && (
<ChakraTooltip.Arrow>
<ChakraTooltip.ArrowTip />
</ChakraTooltip.Arrow>
)}
{content}
</ChakraTooltip.Content>
</ChakraTooltip.Positioner>
</Portal>
</ChakraTooltip.Root>
)
})

42
frontend/tsconfig.json Normal file
View file

@ -0,0 +1,42 @@
{
"compilerOptions": {
"target": "ESNext", // ЗАМЕНИТЬ es5 на ESNext
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "ESNext", // ЗАМЕНИТЬ esnext на ESNext (капитализация)
"moduleResolution": "Bundler", // ЗАМЕНИТЬ node на Bundler
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
] // ЕСЛИ используете src/ структуру
// или "@/": ["./*"] если без src/
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}