Срез 0: скелет tabular-parser — без фронта и БД, воркер NATS без JetStream, парсер-заглушка, контракт v1.0 #3
1
.gitignore
vendored
1
.gitignore
vendored
@ -322,7 +322,6 @@ dist
|
|||||||
|
|
||||||
# Custom ignore
|
# Custom ignore
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Dockerfile
|
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
.dockerignore
|
.dockerignore
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
149
api/alembic.ini
149
api/alembic.ini
@ -1,149 +0,0 @@
|
|||||||
# A generic, single database configuration.
|
|
||||||
|
|
||||||
[alembic]
|
|
||||||
# path to migration scripts.
|
|
||||||
# this is typically a path given in POSIX (e.g. forward slashes)
|
|
||||||
# format, relative to the token %(here)s which refers to the location of this
|
|
||||||
# ini file
|
|
||||||
script_location = %(here)s/alembic
|
|
||||||
|
|
||||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
|
||||||
# Uncomment the line below if you want the files to be prepended with date and time
|
|
||||||
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
|
||||||
# for all available tokens
|
|
||||||
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
|
||||||
# Or organize into date-based subdirectories (requires recursive_version_locations = true)
|
|
||||||
# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s
|
|
||||||
|
|
||||||
# sys.path path, will be prepended to sys.path if present.
|
|
||||||
# defaults to the current working directory. for multiple paths, the path separator
|
|
||||||
# is defined by "path_separator" below.
|
|
||||||
prepend_sys_path = .
|
|
||||||
|
|
||||||
|
|
||||||
# timezone to use when rendering the date within the migration file
|
|
||||||
# as well as the filename.
|
|
||||||
# If specified, requires the tzdata library which can be installed by adding
|
|
||||||
# `alembic[tz]` to the pip requirements.
|
|
||||||
# string value is passed to ZoneInfo()
|
|
||||||
# leave blank for localtime
|
|
||||||
# timezone =
|
|
||||||
|
|
||||||
# max length of characters to apply to the "slug" field
|
|
||||||
# truncate_slug_length = 40
|
|
||||||
|
|
||||||
# set to 'true' to run the environment during
|
|
||||||
# the 'revision' command, regardless of autogenerate
|
|
||||||
# revision_environment = false
|
|
||||||
|
|
||||||
# set to 'true' to allow .pyc and .pyo files without
|
|
||||||
# a source .py file to be detected as revisions in the
|
|
||||||
# versions/ directory
|
|
||||||
# sourceless = false
|
|
||||||
|
|
||||||
# version location specification; This defaults
|
|
||||||
# to <script_location>/versions. When using multiple version
|
|
||||||
# directories, initial revisions must be specified with --version-path.
|
|
||||||
# The path separator used here should be the separator specified by "path_separator"
|
|
||||||
# below.
|
|
||||||
# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions
|
|
||||||
|
|
||||||
# path_separator; This indicates what character is used to split lists of file
|
|
||||||
# paths, including version_locations and prepend_sys_path within configparser
|
|
||||||
# files such as alembic.ini.
|
|
||||||
# The default rendered in new alembic.ini files is "os", which uses os.pathsep
|
|
||||||
# to provide os-dependent path splitting.
|
|
||||||
#
|
|
||||||
# Note that in order to support legacy alembic.ini files, this default does NOT
|
|
||||||
# take place if path_separator is not present in alembic.ini. If this
|
|
||||||
# option is omitted entirely, fallback logic is as follows:
|
|
||||||
#
|
|
||||||
# 1. Parsing of the version_locations option falls back to using the legacy
|
|
||||||
# "version_path_separator" key, which if absent then falls back to the legacy
|
|
||||||
# behavior of splitting on spaces and/or commas.
|
|
||||||
# 2. Parsing of the prepend_sys_path option falls back to the legacy
|
|
||||||
# behavior of splitting on spaces, commas, or colons.
|
|
||||||
#
|
|
||||||
# Valid values for path_separator are:
|
|
||||||
#
|
|
||||||
# path_separator = :
|
|
||||||
# path_separator = ;
|
|
||||||
# path_separator = space
|
|
||||||
# path_separator = newline
|
|
||||||
#
|
|
||||||
# Use os.pathsep. Default configuration used for new projects.
|
|
||||||
path_separator = os
|
|
||||||
|
|
||||||
# set to 'true' to search source files recursively
|
|
||||||
# in each "version_locations" directory
|
|
||||||
# new in Alembic version 1.10
|
|
||||||
# recursive_version_locations = false
|
|
||||||
|
|
||||||
# the output encoding used when revision files
|
|
||||||
# are written from script.py.mako
|
|
||||||
# output_encoding = utf-8
|
|
||||||
|
|
||||||
# database URL. This is consumed by the user-maintained env.py script only.
|
|
||||||
# other means of configuring database URLs may be customized within the env.py
|
|
||||||
# file.
|
|
||||||
sqlalchemy.url = driver://user:pass@localhost/dbname
|
|
||||||
|
|
||||||
|
|
||||||
[post_write_hooks]
|
|
||||||
# post_write_hooks defines scripts or Python functions that are run
|
|
||||||
# on newly generated revision scripts. See the documentation for further
|
|
||||||
# detail and examples
|
|
||||||
|
|
||||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
|
||||||
# hooks = black
|
|
||||||
# black.type = console_scripts
|
|
||||||
# black.entrypoint = black
|
|
||||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
|
||||||
|
|
||||||
# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module
|
|
||||||
# hooks = ruff
|
|
||||||
# ruff.type = module
|
|
||||||
# ruff.module = ruff
|
|
||||||
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
|
|
||||||
|
|
||||||
# Alternatively, use the exec runner to execute a binary found on your PATH
|
|
||||||
# hooks = ruff
|
|
||||||
# ruff.type = exec
|
|
||||||
# ruff.executable = ruff
|
|
||||||
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
|
|
||||||
|
|
||||||
# Logging configuration. This is also consumed by the user-maintained
|
|
||||||
# env.py script only.
|
|
||||||
[loggers]
|
|
||||||
keys = root,sqlalchemy,alembic
|
|
||||||
|
|
||||||
[handlers]
|
|
||||||
keys = console
|
|
||||||
|
|
||||||
[formatters]
|
|
||||||
keys = generic
|
|
||||||
|
|
||||||
[logger_root]
|
|
||||||
level = WARNING
|
|
||||||
handlers = console
|
|
||||||
qualname =
|
|
||||||
|
|
||||||
[logger_sqlalchemy]
|
|
||||||
level = WARNING
|
|
||||||
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
|
|
||||||
@ -1 +0,0 @@
|
|||||||
Generic single-database configuration.
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
from logging.config import fileConfig
|
|
||||||
|
|
||||||
from sqlalchemy import engine_from_config
|
|
||||||
from sqlalchemy import pool
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
||||||
from alembic import context
|
|
||||||
from src.core.config import settings
|
|
||||||
from src.models import Base # noqa: F401 — модели регистрируются в src/models/__init__.py
|
|
||||||
# this is the Alembic Config object, which provides
|
|
||||||
# access to the values within the .ini file in use.
|
|
||||||
config = context.config
|
|
||||||
|
|
||||||
# Interpret the config file for Python logging.
|
|
||||||
# This line sets up loggers basically.
|
|
||||||
if config.config_file_name is not None:
|
|
||||||
fileConfig(config.config_file_name)
|
|
||||||
|
|
||||||
config.set_main_option("sqlalchemy.url", settings.OPENBAO.DATABASE.DATABASE_URL)
|
|
||||||
# add your model's MetaData object here
|
|
||||||
# for 'autogenerate' support
|
|
||||||
# from myapp import mymodel
|
|
||||||
# target_metadata = mymodel.Base.metadata
|
|
||||||
target_metadata = Base.metadata
|
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
|
||||||
# can be acquired:
|
|
||||||
# my_important_option = config.get_main_option("my_important_option")
|
|
||||||
# ... etc.
|
|
||||||
|
|
||||||
def include_name(name, type_, parent_names):
|
|
||||||
if type_ == 'schema':
|
|
||||||
return name == 'dkb'
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def do_run_migrations(connection) -> None:
|
|
||||||
context.configure(
|
|
||||||
connection=connection,
|
|
||||||
target_metadata=target_metadata,
|
|
||||||
compare_type=True,
|
|
||||||
version_table_schema="dkb",
|
|
||||||
include_schemas=True,
|
|
||||||
include_name=include_name
|
|
||||||
)
|
|
||||||
|
|
||||||
with context.begin_transaction():
|
|
||||||
context.run_migrations()
|
|
||||||
|
|
||||||
def run_migrations_offline() -> None:
|
|
||||||
"""Run migrations in 'offline' mode.
|
|
||||||
|
|
||||||
This configures the context with just a URL
|
|
||||||
and not an Engine, though an Engine is acceptable
|
|
||||||
here as well. By skipping the Engine creation
|
|
||||||
we don't even need a DBAPI to be available.
|
|
||||||
|
|
||||||
Calls to context.execute() here emit the given string to the
|
|
||||||
script output.
|
|
||||||
|
|
||||||
"""
|
|
||||||
url = config.get_main_option("sqlalchemy.url")
|
|
||||||
context.configure(
|
|
||||||
url=url,
|
|
||||||
target_metadata=target_metadata,
|
|
||||||
literal_binds=True,
|
|
||||||
dialect_opts={"paramstyle": "named"},
|
|
||||||
version_table_schema="dkb",
|
|
||||||
include_schemas=True,
|
|
||||||
include_name=include_name
|
|
||||||
)
|
|
||||||
|
|
||||||
with context.begin_transaction():
|
|
||||||
context.run_migrations()
|
|
||||||
|
|
||||||
|
|
||||||
async def run_migrations_online() -> None:
|
|
||||||
"""Run migrations in 'online' mode.
|
|
||||||
|
|
||||||
In this scenario we need to create an Engine
|
|
||||||
and associate a connection with the context.
|
|
||||||
|
|
||||||
"""
|
|
||||||
connectable = AsyncEngine(
|
|
||||||
engine_from_config(
|
|
||||||
config.get_section(config.config_ini_section),
|
|
||||||
prefix="sqlalchemy.",
|
|
||||||
poolclass=pool.NullPool,
|
|
||||||
future=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async with connectable.connect() as connection:
|
|
||||||
await connection.run_sync(do_run_migrations)
|
|
||||||
|
|
||||||
await connectable.dispose()
|
|
||||||
|
|
||||||
|
|
||||||
if context.is_offline_mode():
|
|
||||||
run_migrations_offline()
|
|
||||||
else:
|
|
||||||
asyncio.run(run_migrations_online())
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
"""${message}
|
|
||||||
|
|
||||||
Revision ID: ${up_revision}
|
|
||||||
Revises: ${down_revision | comma,n}
|
|
||||||
Create Date: ${create_date}
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
${imports if imports else ""}
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = ${repr(up_revision)}
|
|
||||||
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
|
||||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
"""Upgrade schema."""
|
|
||||||
${upgrades if upgrades else "pass"}
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
"""Downgrade schema."""
|
|
||||||
${downgrades if downgrades else "pass"}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
from .db import get_async_session
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
from contextlib import asynccontextmanager
|
|
||||||
|
|
||||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine, AsyncSession
|
|
||||||
from fastapi import Depends
|
|
||||||
# from fastapi_users.db import SQLAlchemyUserDatabase, SQLAlchemyBaseUserTableUUID
|
|
||||||
from src.core.config import settings
|
|
||||||
|
|
||||||
engine = create_async_engine(settings.OPENBAO.DATABASE.DATABASE_URL, connect_args={"server_settings": {"search_path": 'dkb'}})
|
|
||||||
async_session_maker = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
|
||||||
|
|
||||||
|
|
||||||
# @asynccontextmanager
|
|
||||||
async def get_async_session():
|
|
||||||
async with async_session_maker() as session:
|
|
||||||
yield session
|
|
||||||
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
"""ORM-модели. Источник истины — docs/model/data-model-v5.13.dbml; заполняется в Срезе 0."""
|
|
||||||
from .base import Base, UUIDPrimaryMixin
|
|
||||||
|
|
||||||
__all__ = ["Base", "UUIDPrimaryMixin"]
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import uuid
|
|
||||||
|
|
||||||
from sqlalchemy import MetaData
|
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
|
||||||
from sqlalchemy.orm import DeclarativeBase, Mapped, declarative_mixin, mapped_column
|
|
||||||
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
|
||||||
metadata = MetaData(schema="dkb")
|
|
||||||
|
|
||||||
|
|
||||||
@declarative_mixin
|
|
||||||
class UUIDPrimaryMixin:
|
|
||||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
### Task 1: [1.1] Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `a
|
### Task 1: [1.1] Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `a
|
||||||
|
|
||||||
- [ ] Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `api/alembic.ini`, `api/src/db/`, `api/src/models/`, `api/src/repositories/`; из `.gitignore` убрать `Dockerfile`; проверка: `git status` показывает только удаления и `.gitignore`; `python -c "import src"` пока падает ожидаемо (чинится в 1.2)
|
- [x] Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `api/alembic.ini`, `api/src/db/`, `api/src/models/`, `api/src/repositories/`; из `.gitignore` убрать `Dockerfile`; проверка: `git status` показывает только удаления и `.gitignore`; `python -c "import src"` пока падает ожидаемо (чинится в 1.2)
|
||||||
|
|
||||||
### Task 2: [1.2] `src/app.py`
|
### Task 2: [1.2] `src/app.py`
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
## 1. Вырезать фронт и БД из шаблона
|
## 1. Вырезать фронт и БД из шаблона
|
||||||
|
|
||||||
- [ ] 1.1 Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `api/alembic.ini`, `api/src/db/`,
|
- [x] 1.1 Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `api/alembic.ini`, `api/src/db/`,
|
||||||
`api/src/models/`, `api/src/repositories/`; из `.gitignore` убрать `Dockerfile`; проверка: `git status` показывает
|
`api/src/models/`, `api/src/repositories/`; из `.gitignore` убрать `Dockerfile`; проверка: `git status` показывает
|
||||||
только удаления и `.gitignore`; `python -c "import src"` пока падает ожидаемо (чинится в 1.2)
|
только удаления и `.gitignore`; `python -c "import src"` пока падает ожидаемо (чинится в 1.2)
|
||||||
- [ ] 1.2 `src/app.py`: убрать mount SPA/`os.makedirs("web")`, CORS, SMB-сессию, импорт `raisa_fastapi_protected_api`;
|
- [ ] 1.2 `src/app.py`: убрать mount SPA/`os.makedirs("web")`, CORS, SMB-сессию, импорт `raisa_fastapi_protected_api`;
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
registry=http://nexus.dev.raisa.go.rshbank.ru/repository/npm/
|
|
||||||
strict-ssl=false
|
|
||||||
@ -9,15 +9,5 @@ source ./../api/.venv/bin/activate
|
|||||||
pip install -r ./../api/requirements.txt
|
pip install -r ./../api/requirements.txt
|
||||||
|
|
||||||
echo "complete setup development api"
|
echo "complete setup development api"
|
||||||
|
|
||||||
echo "setup web application"
|
|
||||||
|
|
||||||
cp ./.npmrc ./../web/.npmrc
|
|
||||||
cd ./../web
|
|
||||||
|
|
||||||
npm i
|
|
||||||
|
|
||||||
echo "complete setup web application"
|
|
||||||
echo "To start api, cd to api directory, and use venv, start run_server.py"
|
echo "To start api, cd to api directory, and use venv, start run_server.py"
|
||||||
echo "To start api, cd to web directory, and use command npm run start!"
|
echo "Happy Hacking! :)"
|
||||||
echo "Happy Hacking! :)"
|
|
||||||
|
|||||||
@ -1,91 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
||||||
"vcs": {
|
|
||||||
"enabled": true,
|
|
||||||
"clientKind": "git",
|
|
||||||
"useIgnoreFile": true
|
|
||||||
},
|
|
||||||
"files": {
|
|
||||||
"ignoreUnknown": true,
|
|
||||||
"ignore": ["node_modules"]
|
|
||||||
},
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"lineWidth": 140,
|
|
||||||
"formatWithErrors": true
|
|
||||||
},
|
|
||||||
"organizeImports": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"ignore": ["*.json"],
|
|
||||||
"enabled": true,
|
|
||||||
"rules": {
|
|
||||||
"recommended": true,
|
|
||||||
"a11y": {
|
|
||||||
"useButtonType": "off",
|
|
||||||
"noSvgWithoutTitle": "off"
|
|
||||||
},
|
|
||||||
"complexity": {
|
|
||||||
"noForEach": "off",
|
|
||||||
"noUselessTypeConstraint": "warn"
|
|
||||||
},
|
|
||||||
"correctness": {
|
|
||||||
"noUnusedVariables": "error",
|
|
||||||
"noUnusedImports": "error",
|
|
||||||
"useExhaustiveDependencies": "info"
|
|
||||||
},
|
|
||||||
"nursery": {
|
|
||||||
"noCommonJs": "error",
|
|
||||||
"noDuplicateElseIf": "error",
|
|
||||||
"noDuplicateProperties": "error",
|
|
||||||
"noNestedTernary": "error",
|
|
||||||
"useAtIndex": "error",
|
|
||||||
"useCollapsedIf": "error",
|
|
||||||
"useConsistentCurlyBraces": "error"
|
|
||||||
},
|
|
||||||
"performance": {
|
|
||||||
"noDelete": "off",
|
|
||||||
"noAccumulatingSpread": "off"
|
|
||||||
},
|
|
||||||
"style": {
|
|
||||||
"useDefaultSwitchClause": "error",
|
|
||||||
"useForOf": "error",
|
|
||||||
"useFragmentSyntax": "error",
|
|
||||||
"useConsistentArrayType": "error",
|
|
||||||
"useShorthandAssign": "error"
|
|
||||||
},
|
|
||||||
"suspicious": {
|
|
||||||
"noConsole": "warn",
|
|
||||||
"noEmptyBlockStatements": "error",
|
|
||||||
"noEvolvingTypes": "error",
|
|
||||||
"noExplicitAny": "warn",
|
|
||||||
"noSkippedTests": "warn",
|
|
||||||
"useAwait": "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"javascript": {
|
|
||||||
"formatter": {
|
|
||||||
"quoteStyle": "single",
|
|
||||||
"jsxQuoteStyle": "single",
|
|
||||||
"bracketSameLine": true
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"json": {
|
|
||||||
"parser": {
|
|
||||||
"allowComments": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"css": {
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"parser": {
|
|
||||||
"cssModules": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frontend",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"build": "rsbuild build",
|
|
||||||
"start": "rsbuild dev --open",
|
|
||||||
"preview": "rsbuild preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@ant-design/icons": "^6.0.0",
|
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
|
||||||
"antd": "^6.5.0",
|
|
||||||
"markdown-to-jsx": "^9.1.2",
|
|
||||||
"pdfjs-dist": "^5.7.284",
|
|
||||||
"raisa-fastapi-protected-web": "^1.0.0",
|
|
||||||
"raisa-ui": "^1.0.0",
|
|
||||||
"react": "^18.3.1",
|
|
||||||
"react-daisyui": "^5.0.5",
|
|
||||||
"react-dom": "^18.3.1",
|
|
||||||
"react-redux": "^9.2.0",
|
|
||||||
"react-router-dom": "^7.8.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@biomejs/biome": "1.9.4",
|
|
||||||
"@biomejs/cli-linux-x64-musl": "^1.9.4",
|
|
||||||
"@rsbuild/core": "1.6.7",
|
|
||||||
"@rsbuild/plugin-react": "^1.1.0",
|
|
||||||
"@types/react": "^18.3.1",
|
|
||||||
"@types/react-dom": "^18.3.1",
|
|
||||||
"daisyui": "^4.12.10",
|
|
||||||
"tailwindcss": "^3.4.13",
|
|
||||||
"typescript": "^5.7.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru-RU">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<title>Пример React-приложения</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import { defineConfig, loadEnv } from '@rsbuild/core';
|
|
||||||
import { pluginReact } from '@rsbuild/plugin-react';
|
|
||||||
|
|
||||||
const { publicVars, rawPublicVars } = loadEnv({prefixes: ['REACT_APP_']});
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
server: {
|
|
||||||
base: (rawPublicVars['REACT_APP_ROOT_PATH'] !== undefined) ? rawPublicVars['REACT_APP_ROOT_PATH'] : '/'
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: {
|
|
||||||
js: '[name]_script.[contenthash:8].js',
|
|
||||||
css: '[name]_style.[contenthash:8].css',
|
|
||||||
},
|
|
||||||
distPath: {
|
|
||||||
favicon: 'static/image',
|
|
||||||
},
|
|
||||||
assetPrefix: (rawPublicVars['REACT_APP_ROOT_PATH'] !== undefined) ? rawPublicVars['REACT_APP_ROOT_PATH'] : './'
|
|
||||||
},
|
|
||||||
html: {
|
|
||||||
template: './public/index.html',
|
|
||||||
favicon: './src/assets/favicon.ico',
|
|
||||||
},
|
|
||||||
source: {
|
|
||||||
include: [{ not: /[\\/]core-js[\\/]/ }],
|
|
||||||
define: {
|
|
||||||
...publicVars,
|
|
||||||
'process.env': JSON.stringify(rawPublicVars),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: [pluginReact()],
|
|
||||||
});
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import './App.css';
|
|
||||||
import { Layout } from 'antd';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|
||||||
import { Config } from './conf/config';
|
|
||||||
import { Main } from './layouts/Main';
|
|
||||||
import { store } from './store/store';
|
|
||||||
import { Sidebar } from './widgets/Sidebar/Sidebar';
|
|
||||||
|
|
||||||
// Каркас: экраны по docs/context/10-user-journey.md добавляются срезами.
|
|
||||||
function App() {
|
|
||||||
return (
|
|
||||||
<Provider store={store}>
|
|
||||||
<BrowserRouter basename={Config.APP_ADDRESS}>
|
|
||||||
<Layout hasSider={true} style={{ height: '100vh' }}>
|
|
||||||
<Sidebar />
|
|
||||||
<Layout.Content>
|
|
||||||
<Routes>
|
|
||||||
<Route path='/' element={<Main />} />
|
|
||||||
</Routes>
|
|
||||||
</Layout.Content>
|
|
||||||
</Layout>
|
|
||||||
</BrowserRouter>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
@ -1,7 +0,0 @@
|
|||||||
// biome-ignore lint/suspicious/noExplicitAny: process.env подставляется rsbuild на этапе сборки
|
|
||||||
const environment = (process as any).env;
|
|
||||||
|
|
||||||
export const Config = {
|
|
||||||
API_URL: (environment.REACT_APP_API_URL ?? environment.REACT_APP_ROOT_PATH ?? '') as string,
|
|
||||||
APP_ADDRESS: `${environment.REACT_APP_ROOT_PATH || '/aurora/apps/fastapi-dkb-armka'}` as string,
|
|
||||||
};
|
|
||||||
1
web/src/env.d.ts
vendored
1
web/src/env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="@rsbuild/core/types" />
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
/* :root {
|
|
||||||
--text: #6b6375;
|
|
||||||
--text-h: #08060d;
|
|
||||||
--bg: #fff;
|
|
||||||
--border: #e5e4e7;
|
|
||||||
--code-bg: #f4f3ec;
|
|
||||||
--accent: #aa3bff;
|
|
||||||
--accent-bg: rgba(170, 59, 255, 0.1);
|
|
||||||
--accent-border: rgba(170, 59, 255, 0.5);
|
|
||||||
--social-bg: rgba(244, 243, 236, 0.5);
|
|
||||||
--shadow:
|
|
||||||
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
|
|
||||||
|
|
||||||
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
--mono: ui-monospace, Consolas, monospace;
|
|
||||||
|
|
||||||
font: 18px/145% var(--sans);
|
|
||||||
letter-spacing: 0.18px;
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: var(--text);
|
|
||||||
background: var(--bg);
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--text: #9ca3af;
|
|
||||||
--text-h: #f3f4f6;
|
|
||||||
--bg: #16171d;
|
|
||||||
--border: #2e303a;
|
|
||||||
--code-bg: #1f2028;
|
|
||||||
--accent: #c084fc;
|
|
||||||
--accent-bg: rgba(192, 132, 252, 0.15);
|
|
||||||
--accent-border: rgba(192, 132, 252, 0.5);
|
|
||||||
--social-bg: rgba(47, 48, 58, 0.5);
|
|
||||||
--shadow:
|
|
||||||
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#social .button-icon {
|
|
||||||
filter: invert(1) brightness(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#root {
|
|
||||||
width: 1126px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
text-align: center;
|
|
||||||
border-inline: 1px solid var(--border);
|
|
||||||
min-height: 100svh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2 {
|
|
||||||
font-family: var(--heading);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-h);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 56px;
|
|
||||||
letter-spacing: -1.68px;
|
|
||||||
margin: 32px 0;
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
font-size: 36px;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 118%;
|
|
||||||
letter-spacing: -0.24px;
|
|
||||||
margin: 0 0 8px;
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
code,
|
|
||||||
.counter {
|
|
||||||
font-family: var(--mono);
|
|
||||||
display: inline-flex;
|
|
||||||
border-radius: 4px;
|
|
||||||
color: var(--text-h);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 135%;
|
|
||||||
padding: 4px 8px;
|
|
||||||
background: var(--code-bg);
|
|
||||||
} */
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { createRoot } from 'react-dom/client';
|
|
||||||
import './index.css';
|
|
||||||
import { ConfigProvider } from 'antd';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
const rootElement = document.getElementById('root');
|
|
||||||
if (!rootElement) {
|
|
||||||
throw new Error('Элемент #root не найден');
|
|
||||||
}
|
|
||||||
|
|
||||||
createRoot(rootElement).render(
|
|
||||||
// <StrictMode>
|
|
||||||
<ConfigProvider
|
|
||||||
theme={{
|
|
||||||
token: {
|
|
||||||
colorPrimary: 'rgba(0, 255, 0, 0.5)',
|
|
||||||
colorPrimaryHover: 'rgba(0, 255, 0, 1)',
|
|
||||||
colorBgLayout: 'rgb(247, 248, 250)',
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Layout: {
|
|
||||||
siderBg: '#fff',
|
|
||||||
},
|
|
||||||
Button: {
|
|
||||||
// textHoverBg: "green",
|
|
||||||
colorBorder: 'rgba(0,0,0,0.5)',
|
|
||||||
defaultActiveBorderColor: 'rgba(0,0,0,0.5)',
|
|
||||||
defaultBorderColor: 'rgba(0,0,0,0.5)',
|
|
||||||
},
|
|
||||||
Input: {
|
|
||||||
activeBorderColor: 'rgba(46, 166, 75, 0.35)',
|
|
||||||
activeShadow: '0 0 0 1px #2ea64b59,0 1px 2px #0000000f',
|
|
||||||
hoverBorderColor: 'rgba(46, 166, 75, 0.35)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<App />
|
|
||||||
</ConfigProvider>,
|
|
||||||
// </StrictMode>,
|
|
||||||
);
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
// Каркас: стартовый экран. Содержимое — по docs/context/10-user-journey.md.
|
|
||||||
export function Main() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
margin: 10,
|
|
||||||
padding: 16,
|
|
||||||
background: 'white',
|
|
||||||
borderRadius: 10,
|
|
||||||
height: 'calc(100% - 20px)',
|
|
||||||
}}>
|
|
||||||
<h3>АРМ КА</h3>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
// Каркас: слайсы добавляются срезами в ./slices.
|
|
||||||
export const store = configureStore({
|
|
||||||
reducer: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type RootState = ReturnType<typeof store.getState>;
|
|
||||||
export type AppDispatch = typeof store.dispatch;
|
|
||||||
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
|
|
||||||
export const useAppSelector = useSelector.withTypes<RootState>();
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
import { HomeOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
|
|
||||||
import { Divider, Layout, Tooltip } from 'antd';
|
|
||||||
import { type ReactNode, memo, useState } from 'react';
|
|
||||||
import { NavLink } from 'react-router-dom';
|
|
||||||
import styles from './styles.module.css';
|
|
||||||
|
|
||||||
type NavItem = {
|
|
||||||
link: string;
|
|
||||||
label: string;
|
|
||||||
icon: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Group = {
|
|
||||||
title: string;
|
|
||||||
key: string;
|
|
||||||
children: NavItem[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Каркас: разделы меню — по карте экранов docs/context/10-user-journey.md, добавляются срезами.
|
|
||||||
const config: Group[] = [
|
|
||||||
{
|
|
||||||
title: 'Главная',
|
|
||||||
key: 'home',
|
|
||||||
children: [{ link: '/', icon: <HomeOutlined />, label: 'Главная' }],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function CollapsedIcon({ icon, title, to }: { icon: ReactNode; title: string; to: string }) {
|
|
||||||
return (
|
|
||||||
<Tooltip placement='right' title={title}>
|
|
||||||
<NavLink to={to} className={({ isActive }) => `${styles.iconBtn} ${isActive ? styles.iconBtnActive : styles.iconBtnNav}`}>
|
|
||||||
{icon}
|
|
||||||
</NavLink>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function NavLinkStyled({ to, children }: { to: string; children: ReactNode }) {
|
|
||||||
return (
|
|
||||||
<NavLink className={({ isActive }) => `${styles.linkBase} ${isActive ? styles.linkActive : ''}`} to={to}>
|
|
||||||
{children}
|
|
||||||
</NavLink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Section = memo(function Section({ isCollapsed, group }: { isCollapsed: boolean; group: Group }) {
|
|
||||||
return !isCollapsed ? (
|
|
||||||
<div className={styles.section}>
|
|
||||||
<div className={styles.sectionTitle}>{group.title}</div>
|
|
||||||
{group.children.map((i) => (
|
|
||||||
<NavLinkStyled key={i.link} to={i.link}>
|
|
||||||
{i.icon}
|
|
||||||
{i.label}
|
|
||||||
</NavLinkStyled>
|
|
||||||
))}
|
|
||||||
<Divider size='small' />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{group.children.map((i) => (
|
|
||||||
<CollapsedIcon key={i.link} icon={i.icon} to={i.link} title={i.label} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export function Sidebar() {
|
|
||||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout.Sider
|
|
||||||
collapsed={isCollapsed}
|
|
||||||
style={{ height: 'auto', margin: '10px', borderRadius: 10 }}
|
|
||||||
width={280}
|
|
||||||
collapsedWidth={64}
|
|
||||||
collapsible
|
|
||||||
trigger={null}>
|
|
||||||
<div className={`${styles.wrapper} ${isCollapsed ? styles.wrapperCollapsed : ''}`}>
|
|
||||||
<div className={styles.topbar}>
|
|
||||||
<button type='button' className={styles.collapseBtn} onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
||||||
{isCollapsed ? <RightOutlined /> : <LeftOutlined />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{config.map((g) => (
|
|
||||||
<Section key={g.key} group={g} isCollapsed={isCollapsed} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Layout.Sider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,188 +0,0 @@
|
|||||||
.company_group_item {
|
|
||||||
border: 2px solid grey;
|
|
||||||
border-radius: 2px;
|
|
||||||
height: 50px;
|
|
||||||
width: 90%;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: lightgray;
|
|
||||||
}
|
|
||||||
.active_cg {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
font-weight: 700;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsedList {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sectionTitle {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #8c8c8c;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subgroupToggle {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
padding: 6px 10px;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(0, 0, 0, 0.88);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subgroup {
|
|
||||||
/* padding-left: 8px; */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
/* gap: 10px; */
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkBase {
|
|
||||||
display: inline-flex;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 8px 10px;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: rgba(55, 65, 81, 0.88);
|
|
||||||
text-decoration: none;
|
|
||||||
background: transparent;
|
|
||||||
transition: background 0.2s, color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .linkActive:hover {
|
|
||||||
color: rgb(31, 114, 55);
|
|
||||||
} */
|
|
||||||
|
|
||||||
.linkBase:hover {
|
|
||||||
color: rgba(55, 65, 81, 0.88);
|
|
||||||
background: rgb(249, 250, 251);
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkBase.linkActive {
|
|
||||||
color: rgb(31, 114, 55);
|
|
||||||
background: #edf8ee;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.state {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.emptyWrap {
|
|
||||||
padding-top: 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: width 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapperCollapsed {
|
|
||||||
padding: 12px 0;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsedList {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconBtn {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(0, 0, 0, 0.75);
|
|
||||||
transition: background 0.2s, color 0.2s;
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconBtnActive,
|
|
||||||
.iconBtnActive:hover {
|
|
||||||
background: rgba(46, 166, 75, 0.16);
|
|
||||||
color: rgb(31, 114, 55);
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconBtnNav:hover {
|
|
||||||
color: #101828;
|
|
||||||
background: #f2f4f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconBtnAction {
|
|
||||||
background: linear-gradient(160deg, #2ea64b, #1f7237);
|
|
||||||
font-size: small;
|
|
||||||
box-shadow: 0 1px 3px #1f723740;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconBtnAction:hover {
|
|
||||||
background: linear-gradient(160deg, #268f40, #185c2b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .iconBtn. */
|
|
||||||
|
|
||||||
.collapseBtn {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
|
||||||
border-radius: 12px;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(0, 0, 0, 0.75);
|
|
||||||
transition: background 0.2s, color 0.2s;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: [
|
|
||||||
"./src/**/*.{html,js,jsx,ts,tsx}",
|
|
||||||
"node_modules/daisyui/dist/**/*.js",
|
|
||||||
"node_modules/react-daisyui/dist/**/*.js",
|
|
||||||
"node_modules/raisa-ui/dist/**/*.js"
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [require('daisyui')],
|
|
||||||
daisyui: {
|
|
||||||
themes: [
|
|
||||||
{
|
|
||||||
rshb: {
|
|
||||||
...require('daisyui/src/theming/themes')['light'],
|
|
||||||
|
|
||||||
"primary": "#171A1C",
|
|
||||||
"primary-content": "#fcfcfd",
|
|
||||||
"secondary": "#ECEDEF",
|
|
||||||
"secondary-content": "#687178",
|
|
||||||
"accent": "#8ecaff",
|
|
||||||
"accent-content": "#fcfcfd",
|
|
||||||
"neutral": "#ecedef",
|
|
||||||
"neutral-content": "#171A1C",
|
|
||||||
"base-100": "#FCFCFD",
|
|
||||||
"base-200": "#F6F7F8",
|
|
||||||
"base-300": "#ECEDEF",
|
|
||||||
"base-content": "#171A1C",
|
|
||||||
"info": "#0894FF",
|
|
||||||
"info-content": "#E3E5E8",
|
|
||||||
"success": "#39B33D",
|
|
||||||
"success-content": "#E3E5E8",
|
|
||||||
"warning": "#E6B200",
|
|
||||||
"warning-content": "#fcfcfd",
|
|
||||||
"error": "#FF371A",
|
|
||||||
"error-content": "#fcfcfd",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": ["DOM", "ES2020"],
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"target": "ES2020",
|
|
||||||
"noEmit": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
|
|
||||||
/* modules */
|
|
||||||
"module": "ESNext",
|
|
||||||
"isolatedModules": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
|
|
||||||
/* type checking */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user