Compare commits

..

6 Commits
main ... map

@ -10,8 +10,6 @@
# Production # Production
- add domain to *Caddyfile* - add domain to *Caddyfile*
- generate new database password and put it into appropriate places in *docker/docker-compose.yml*
- generate new `SECRET_KEY` with `openssl rand -base64 32` and put it into *docker/docker-compose.yml* geodata: and frontend: environment variables
# Run # Run
- `docker-compose --file docker/docker-compose.yml up --build -d` - `docker-compose --file docker/docker-compose.yml up --build -d`

@ -1,5 +1,3 @@
import os
from base64 import b64decode from base64 import b64decode
from datetime import datetime, timedelta from datetime import datetime, timedelta
from re import IGNORECASE, sub as substitute from re import IGNORECASE, sub as substitute
@ -18,7 +16,7 @@ from .database import SessionLocal, engine
# Security # Security
# take it from env # take it from env
SECRET_KEY = b64decode(os.environ["SECRET_KEY"]) SECRET_KEY = b64decode("iYg7wB+sPihtjz50iJTsD0XmOeUwKy2TJtfNLcqFRM8=").hex()
ALGORITHM = "HS256" ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 3600 ACCESS_TOKEN_EXPIRE_MINUTES = 3600
@ -201,15 +199,7 @@ def create_items(
for row in sheet["data"] for row in sheet["data"]
] ]
# dump all the data into database # dump all the data into database
try: accepted, processed = crud.insert_items(db=db, items=spreadsheet_item_list)
accepted, processed = crud.insert_items(db=db, items=spreadsheet_item_list)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="{}".format(
e
),
)
# transform spreadsheet coordinates data into spacial # transform spreadsheet coordinates data into spacial
crud.add_spacial_data(db=db) crud.add_spacial_data(db=db)

@ -5,7 +5,6 @@ from sqlalchemy import pool
from alembic import context from alembic import context
# this is the Alembic Config object, which provides # this is the Alembic Config object, which provides
# access to the values within the .ini file in use. # access to the values within the .ini file in use.
config = context.config config = context.config
@ -46,7 +45,6 @@ def run_migrations_offline() -> None:
target_metadata=target_metadata, target_metadata=target_metadata,
literal_binds=True, literal_binds=True,
dialect_opts={"paramstyle": "named"}, dialect_opts={"paramstyle": "named"},
compare_type=True,
) )
with context.begin_transaction(): with context.begin_transaction():
@ -67,11 +65,7 @@ def run_migrations_online() -> None:
) )
with connectable.connect() as connection: with connectable.connect() as connection:
context.configure( context.configure(connection=connection, target_metadata=target_metadata)
connection=connection,
target_metadata=target_metadata,
compare_type=True,
)
with context.begin_transaction(): with context.begin_transaction():
context.run_migrations() context.run_migrations()

@ -1,38 +0,0 @@
"""Cast resolution as float
Revision ID: 15b3c7a8e804
Revises: 4968a333a100
Create Date: 2022-10-31 23:55:04.806055
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "15b3c7a8e804"
down_revision = "4968a333a100"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands MANUALLY composed for Alembic ###
op.execute(
sa.text(
"ALTER TABLE geodata ALTER COLUMN resolution TYPE FLOAT USING resolution::double precision"
)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic ###
op.alter_column(
"geodata",
"resolution",
existing_type=sa.Float(),
type_=sa.VARCHAR(),
existing_nullable=True,
)
# ### end Alembic commands ###

@ -89,25 +89,28 @@ def upgrade() -> None:
[ [
{"database": "fadr", "spreadsheet": "fadr"}, {"database": "fadr", "spreadsheet": "fadr"},
{"database": "internal_id", "spreadsheet": "Уникальный номер образца"}, {"database": "internal_id", "spreadsheet": "Уникальный номер образца"},
{"database": "x_coord", "spreadsheet": "Широта"}, {"database": "x_coord", "spreadsheet": "кордината X"},
{"database": "y_coord", "spreadsheet": "Долгота"}, {"database": "y_coord", "spreadsheet": "координата Y"},
{ {
"database": "category", "database": "gis_category",
"spreadsheet": "Категория", "spreadsheet": """Категория в ГИС
Geology - керн, каменный материал
Soil - почва, грунты, морские осадки
Material - искуственные материалы, минералы""",
}, },
{ {
"database": "gis_category", "database": "category",
"spreadsheet": """Категория в ГИС""", "spreadsheet": "Категория (Археология, Биология, Минералы)",
}, },
{"database": "basin", "spreadsheet": "Бассейн"}, {"database": "basin", "spreadsheet": "Бассейн"},
{"database": "deposit", "spreadsheet": "Месторождение"}, {"database": "deposit", "spreadsheet": "Месторождение"},
{"database": "well", "spreadsheet": "№ скважины"}, {"database": "well", "spreadsheet": "№ скважины"},
{"database": "depth", "spreadsheet": "Глубины"}, {"database": "depth", "spreadsheet": "Глубина, интервал, привязка"},
{"database": "stratum", "spreadsheet": "Свита / пласт"}, {"database": "stratum", "spreadsheet": "Свита\пласт"},
{"database": "owner", "spreadsheet": "Владелец"}, {"database": "owner", "spreadsheet": "Хозяин (ФИО)"},
{"database": "org", "spreadsheet": "Организация"}, {"database": "org", "spreadsheet": "Организация"},
{"database": "ownercontacts", "spreadsheet": "Контакты"}, {"database": "ownercontacts", "spreadsheet": "Контакты хозяина"},
{"database": "samplelist", "spreadsheet": "Перечень объектов / образцов"}, {"database": "samplelist", "spreadsheet": "перечень объектов/образцов"},
{"database": "description", "spreadsheet": "Описание объекта"}, {"database": "description", "spreadsheet": "Описание объекта"},
{"database": "form_dimentions", "spreadsheet": "Форма, размер"}, {"database": "form_dimentions", "spreadsheet": "Форма, размер"},
{"database": "datalist", "spreadsheet": "Перечень данных"}, {"database": "datalist", "spreadsheet": "Перечень данных"},
@ -115,7 +118,7 @@ def upgrade() -> None:
{"database": "date", "spreadsheet": "Дата съёмки"}, {"database": "date", "spreadsheet": "Дата съёмки"},
{"database": "additional_info", "spreadsheet": "Дополнительная информация"}, {"database": "additional_info", "spreadsheet": "Дополнительная информация"},
{"database": "scanner", "spreadsheet": "Томограф"}, {"database": "scanner", "spreadsheet": "Томограф"},
{"database": "comment", "spreadsheet": "Комментарий"}, {"database": "comment", "spreadsheet": "комментарий"},
], ],
) )

@ -9,9 +9,8 @@ from sqlalchemy.dialects.postgresql import TSVECTOR
# if we are run as a module or as a script # if we are run as a module or as a script
# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912 # https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912
import sys import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
parent_module = sys.modules[".".join(__name__.split(".")[:-1]) or "__main__"] if __name__ == '__main__' or parent_module.__name__ == '__main__':
if __name__ == "__main__" or parent_module.__name__ == "__main__":
from database import Base from database import Base
else: else:
from .database import Base from .database import Base
@ -46,7 +45,7 @@ class ItemBase(Base):
description = Column(String) description = Column(String)
form_dimentions = Column(String) form_dimentions = Column(String)
datalist = Column(String) datalist = Column(String)
resolution = Column(Float) resolution = Column(String)
date = Column(String) date = Column(String)
additional_info = Column(String) additional_info = Column(String)
scanner = Column(String) scanner = Column(String)

@ -69,7 +69,7 @@ class ItemBase(BaseModel):
description: Optional[str] = None description: Optional[str] = None
form_dimentions: Optional[str] = None form_dimentions: Optional[str] = None
datalist: Optional[str] = None datalist: Optional[str] = None
resolution: Optional[float] = None resolution: Optional[str] = None
date: Optional[Union[datetime, str]] = None date: Optional[Union[datetime, str]] = None
additional_info: Optional[str] = None additional_info: Optional[str] = None
scanner: Optional[str] = None scanner: Optional[str] = None

@ -1,47 +1,36 @@
{
order jwtauth before basicauth
}
:80 { :80 {
encode zstd gzip encode zstd gzip
handle_path /api/v1/* { handle_path /api/v1/* {
rewrite * {path} rewrite * {path}
reverse_proxy geodata:8000 reverse_proxy geodata:8000
} }
redir /openapi.json /api/v1/openapi.json permanent redir /openapi.json /api/v1/openapi.json permanent
handle_path /martin/* {
rewrite * {path}
reverse_proxy martin:3000
}
@is_admin { handle_path /martin/* {
vars {http.auth.user.id} "demo" rewrite * {path}
} reverse_proxy martin:3000
}
handle_path /pgweb/* {
jwtauth {
sign_key {$SECRET_KEY}
from_cookies user_session
}
rewrite * {path} handle_path /pgweb/* {
reverse_proxy @is_admin pgweb:8081 rewrite * {path}
redir /login/ 401 reverse_proxy pgweb:8081
} }
handle_path /static/previews/* { handle_path /static/previews/* {
rewrite * {path} rewrite * {path}
file_server file_server
} }
# play nice with vue-router # play nice with vue-router
# https://caddy.community/t/caddy-with-vue-router/12352 # https://caddy.community/t/caddy-with-vue-router/12352
handle { handle {
root * /usr/share/caddy root * /usr/share/caddy
try_files {path}.html {path} /index.html try_files {path}.html {path} /index.html
file_server file_server
} }
} }

@ -1,18 +1,12 @@
# build stage # build stage
FROM caddy:2-builder-alpine as xcaddy-stage
RUN xcaddy build --with github.com/ggicci/caddy-jwt
FROM node:12.22.12-bullseye as build-stage FROM node:12.22.12-bullseye as build-stage
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
RUN npm install RUN npm install
COPY . . COPY . .
RUN npx browserslist@latest --update-db
RUN npm run build RUN npm run build
# production stage # production stage
FROM caddy:2-alpine as production-stage FROM caddy:2-alpine as production-stage
COPY --from=xcaddy-stage /usr/bin/caddy /usr/bin/caddy
COPY --from=build-stage /app/dist /usr/share/caddy COPY --from=build-stage /app/dist /usr/share/caddy
EXPOSE 80 EXPOSE 80

@ -8,8 +8,6 @@ services:
dockerfile: ../../docker/Dockerfile.backend dockerfile: ../../docker/Dockerfile.backend
volumes: volumes:
- tmp_vol:/tmp - tmp_vol:/tmp
environment:
- SECRET_KEY="iYg7wB+sPihtjz50iJTsD0XmOeUwKy2TJtfNLcqFRM8="
postgres: postgres:
image: "postgis/postgis:13-3.2" image: "postgis/postgis:13-3.2"
@ -34,18 +32,17 @@ services:
dockerfile: ../docker/Dockerfile.frontend dockerfile: ../docker/Dockerfile.frontend
ports: ports:
- "80:80" - "80:80"
- "443:443"
volumes: volumes:
- caddy_data:/data - caddy_data:/data
- caddy_config:/config - caddy_config:/config
- ../caddy:/etc/caddy - ../caddy:/etc/caddy
- ../previews:/srv - ../previews:/srv
environment:
- SECRET_KEY="iYg7wB+sPihtjz50iJTsD0XmOeUwKy2TJtfNLcqFRM8="
pgweb: pgweb:
restart: always restart: always
image: sosedoff/pgweb:0.11.12 image: sosedoff/pgweb:0.11.12
ports:
- "8081:8081"
environment: environment:
DATABASE_URL: "postgres://geodata:QAKvBKvLe4bS9U@postgres/geodata?sslmode=disable" # TODO: change to real password DATABASE_URL: "postgres://geodata:QAKvBKvLe4bS9U@postgres/geodata?sslmode=disable" # TODO: change to real password

@ -11,10 +11,9 @@
"maplibre-gl": "^2.1.9", "maplibre-gl": "^2.1.9",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-router": "^4.1.5", "vue-router": "^4.1.5",
"vuestic-ui": "^1.4.13" "vuestic-ui": "^1.4.7"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"nodemon": "^2.0.19", "nodemon": "^2.0.19",
"vue-cli-plugin-vuestic-ui": "~1.0.8" "vue-cli-plugin-vuestic-ui": "~1.0.8"
@ -39,6 +38,7 @@
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.9"
@ -73,6 +73,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz",
"integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.1.0", "@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
@ -103,6 +104,7 @@
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true, "dev": true,
"peer": true,
"bin": { "bin": {
"json5": "lib/cli.js" "json5": "lib/cli.js"
}, },
@ -115,6 +117,7 @@
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz",
"integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.10", "@babel/types": "^7.18.10",
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
@ -129,6 +132,7 @@
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.0.1", "@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/sourcemap-codec": "^1.4.10",
@ -161,6 +165,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@ -170,6 +175,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz",
"integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/template": "^7.18.6", "@babel/template": "^7.18.6",
"@babel/types": "^7.18.9" "@babel/types": "^7.18.9"
@ -183,6 +189,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
}, },
@ -195,6 +202,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
"integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
}, },
@ -207,6 +215,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz",
"integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-module-imports": "^7.18.6", "@babel/helper-module-imports": "^7.18.6",
@ -226,6 +235,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz",
"integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
}, },
@ -238,6 +248,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
}, },
@ -250,6 +261,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz",
"integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@ -277,6 +289,7 @@
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz",
"integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/template": "^7.18.6", "@babel/template": "^7.18.6",
"@babel/traverse": "^7.18.9", "@babel/traverse": "^7.18.9",
@ -378,6 +391,7 @@
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
"integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
"@babel/parser": "^7.18.10", "@babel/parser": "^7.18.10",
@ -392,6 +406,7 @@
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz",
"integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==", "integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
"@babel/generator": "^7.18.10", "@babel/generator": "^7.18.10",
@ -413,6 +428,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz",
"integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.18.10", "@babel/helper-string-parser": "^7.18.10",
"@babel/helper-validator-identifier": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6",
@ -442,6 +458,7 @@
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.0.0", "@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
@ -2333,6 +2350,7 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"safe-buffer": "~5.1.1" "safe-buffer": "~5.1.1"
} }
@ -2341,7 +2359,8 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true "dev": true,
"peer": true
}, },
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.4.2", "version": "0.4.2",
@ -3568,6 +3587,7 @@
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@ -3646,6 +3666,7 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
@ -4281,6 +4302,7 @@
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true, "dev": true,
"peer": true,
"bin": { "bin": {
"jsesc": "bin/jsesc" "jsesc": "bin/jsesc"
}, },
@ -7275,6 +7297,7 @@
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
@ -7564,9 +7587,9 @@
"dev": true "dev": true
}, },
"node_modules/vuestic-ui": { "node_modules/vuestic-ui": {
"version": "1.4.13", "version": "1.4.7",
"resolved": "https://registry.npmjs.org/vuestic-ui/-/vuestic-ui-1.4.13.tgz", "resolved": "https://registry.npmjs.org/vuestic-ui/-/vuestic-ui-1.4.7.tgz",
"integrity": "sha512-5f2d3isSaXq7RKWHZvGuMaiJraMugAy8DLnGBF9nLjiCZFT3b6E2WQiXpxvsyA4ZA7ZQsaEChNuaymAHdlv+xA==", "integrity": "sha512-t1gKmaoE3lqDDMUWnHZsJTal8cyHTeG9F/cbB1ggMH+loh6Po5tP7KTDA/93iG7dWtuwzuQhwt3/QE2rcbTkTA==",
"dependencies": { "dependencies": {
"cleave.js": "^1.6.0", "cleave.js": "^1.6.0",
"colortranslator": "^1.9.2", "colortranslator": "^1.9.2",
@ -8120,6 +8143,7 @@
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.9"
@ -8145,6 +8169,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz",
"integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@ampproject/remapping": "^2.1.0", "@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
@ -8167,7 +8192,8 @@
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true "dev": true,
"peer": true
} }
} }
}, },
@ -8176,6 +8202,7 @@
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz",
"integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/types": "^7.18.10", "@babel/types": "^7.18.10",
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
@ -8187,6 +8214,7 @@
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@jridgewell/set-array": "^1.0.1", "@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/sourcemap-codec": "^1.4.10",
@ -8211,13 +8239,15 @@
"version": "7.18.9", "version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
"dev": true "dev": true,
"peer": true
}, },
"@babel/helper-function-name": { "@babel/helper-function-name": {
"version": "7.18.9", "version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz",
"integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/template": "^7.18.6", "@babel/template": "^7.18.6",
"@babel/types": "^7.18.9" "@babel/types": "^7.18.9"
@ -8228,6 +8258,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
} }
@ -8237,6 +8268,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
"integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
} }
@ -8246,6 +8278,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz",
"integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-module-imports": "^7.18.6", "@babel/helper-module-imports": "^7.18.6",
@ -8262,6 +8295,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz",
"integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
} }
@ -8271,6 +8305,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/types": "^7.18.6" "@babel/types": "^7.18.6"
} }
@ -8279,7 +8314,8 @@
"version": "7.18.10", "version": "7.18.10",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz",
"integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==",
"dev": true "dev": true,
"peer": true
}, },
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.18.6", "version": "7.18.6",
@ -8298,6 +8334,7 @@
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz",
"integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/template": "^7.18.6", "@babel/template": "^7.18.6",
"@babel/traverse": "^7.18.9", "@babel/traverse": "^7.18.9",
@ -8377,6 +8414,7 @@
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
"integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
"@babel/parser": "^7.18.10", "@babel/parser": "^7.18.10",
@ -8388,6 +8426,7 @@
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz",
"integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==", "integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
"@babel/generator": "^7.18.10", "@babel/generator": "^7.18.10",
@ -8406,6 +8445,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz",
"integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/helper-string-parser": "^7.18.10", "@babel/helper-string-parser": "^7.18.10",
"@babel/helper-validator-identifier": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6",
@ -8432,6 +8472,7 @@
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@jridgewell/set-array": "^1.0.0", "@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
@ -9966,6 +10007,7 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"safe-buffer": "~5.1.1" "safe-buffer": "~5.1.1"
}, },
@ -9974,7 +10016,8 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true "dev": true,
"peer": true
} }
} }
}, },
@ -10903,7 +10946,8 @@
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true "dev": true,
"peer": true
}, },
"geojson-vt": { "geojson-vt": {
"version": "3.2.1", "version": "3.2.1",
@ -10963,7 +11007,8 @@
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true "dev": true,
"peer": true
}, },
"globby": { "globby": {
"version": "11.1.0", "version": "11.1.0",
@ -11432,7 +11477,8 @@
"version": "2.5.2", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true "dev": true,
"peer": true
}, },
"json-parse-better-errors": { "json-parse-better-errors": {
"version": "1.0.2", "version": "1.0.2",
@ -13658,7 +13704,8 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true "dev": true,
"peer": true
}, },
"to-regex-range": { "to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
@ -13892,9 +13939,9 @@
"dev": true "dev": true
}, },
"vuestic-ui": { "vuestic-ui": {
"version": "1.4.13", "version": "1.4.7",
"resolved": "https://registry.npmjs.org/vuestic-ui/-/vuestic-ui-1.4.13.tgz", "resolved": "https://registry.npmjs.org/vuestic-ui/-/vuestic-ui-1.4.7.tgz",
"integrity": "sha512-5f2d3isSaXq7RKWHZvGuMaiJraMugAy8DLnGBF9nLjiCZFT3b6E2WQiXpxvsyA4ZA7ZQsaEChNuaymAHdlv+xA==", "integrity": "sha512-t1gKmaoE3lqDDMUWnHZsJTal8cyHTeG9F/cbB1ggMH+loh6Po5tP7KTDA/93iG7dWtuwzuQhwt3/QE2rcbTkTA==",
"requires": { "requires": {
"cleave.js": "^1.6.0", "cleave.js": "^1.6.0",
"colortranslator": "^1.9.2", "colortranslator": "^1.9.2",

@ -10,12 +10,11 @@
"maplibre-gl": "^2.1.9", "maplibre-gl": "^2.1.9",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-router": "^4.1.5", "vue-router": "^4.1.5",
"vuestic-ui": "^v1.8.6" "vuestic-ui": "^1.4.7"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"nodemon": "^2.0.19", "nodemon": "^2.0.19",
"vue-cli-plugin-vuestic-ui": "~1.0.8", "vue-cli-plugin-vuestic-ui": "~1.0.8"
"@babel/core": "^7.0.0"
} }
} }

@ -2,7 +2,9 @@
<div> <div>
<h1 style="font-size: 3rem; margin: 1rem;">Текущий формат</h1> <h1 style="font-size: 3rem; margin: 1rem;">Текущий формат</h1>
<h2 style="margin-left: 1rem;">(скоординированный)</h2> <h2 style="margin-left: 1rem;">(скоординированный)</h2>
<div class="va-table-responsive" style="margin: 1rem;"> <va-radio class="radio-switcher" v-for="(option, index) in viewOptions" :key="index" v-model="selectedOption"
:option="option" :label="viewLabels[index]"></va-radio>
<div class="va-table-responsive" v-if="selectedOption === 'table-view'">
<table class="va-table va-table--striped detail-table"> <table class="va-table va-table--striped detail-table">
<thead> <thead>
<tr> <tr>
@ -18,6 +20,24 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<va-list v-else-if="selectedOption === 'list-view'">
<va-list-item v-for="(_, header, index) in items[0]" :key="index" class="header-list">
<va-list-item-section icon>
<va-icon name="circle" color="gray" size="small"></va-icon>
</va-list-item-section>
<va-list-item-section>
<va-list-item-label class="header-item" :lines="2">{{ header }}</va-list-item-label>
</va-list-item-section>
</va-list-item>
</va-list>
<div v-else class="card-row">
<div class="card-item flex lg4" v-for="(_, header, index) in items[0]" :key="index">
<va-card :bordered="false">
<va-card-content>{{ header }}</va-card-content>
</va-card>
</div>
</div>
</div> </div>
</template> </template>
@ -28,7 +48,18 @@ export default defineComponent({
name: "active-schema-screen", name: "active-schema-screen",
data() { data() {
return { return {
columns: [] columns: [],
viewOptions: [
"table-view",
"list-view",
"mosaic-view",
],
viewLabels: [
"Табличный вид",
"Списочный вид",
"Мозаичный вид",
],
selectedOption: "table-view"
} }
}, },
methods: { methods: {
@ -58,9 +89,20 @@ export default defineComponent({
<style lang="css" scoped> <style lang="css" scoped>
.radio-switcher {
margin: 1rem;
}
.header-item { .header-item {
padding: 0.5rem; padding: 0.5rem;
} }
.card-row {
display: flex;
flex-wrap: wrap;
}
.card-item {
margin: 1rem;
}
</style> </style>

@ -1,41 +1,35 @@
<template> <template>
<div class="filter_panel"> <div class="filter_panel">
<div class="sidebar-item">
<h3>Проект / тема</h3>
<project-filter @filter="$emit('filter', $event)" />
</div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Категория</h3> <h3>Категория</h3>
<category-filter @filter="$emit('filter', $event)" /> <category-filter @filter="$emit('filter', $event)" />
</div> </div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Описание</h3> <h3>ГИС категория</h3>
<description-filter @filter="$emit('filter', $event)" /> <gis-category-filter @filter="$emit('filter', $event)" />
</div> </div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Свита / пласт</h3> <h3>Томограф</h3>
<stratum-filter @filter="$emit('filter', $event)" /> <scanner-filter @filter="$emit('filter', $event)" />
</div> </div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Размер образца</h3> <h3>Организация</h3>
<form-dimensions-filter @filter="$emit('filter', $event)" /> <org-filter @filter="$emit('filter', $event)" />
</div> </div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Перечень данных</h3> <h3>Бассейн</h3>
<datalist-filter @filter="$emit('filter', $event)" /> <basin-filter @filter="$emit('filter', $event)" />
</div> </div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Разрешение съемки</h3> <h3>Свита / пласт</h3>
<resolution-filter @filter="$emit('filter', $event)" /> <stratum-filter @filter="$emit('filter', $event)" />
</div> </div>
<div class="sidebar-item"> <div class="sidebar-item">
<h3>Глубины</h3> <h3>Глубины</h3>
<depth-filter @filter="$emit('filter', $event)" /> <depth-filter @filter="$emit('filter', $event)" />
@ -53,27 +47,9 @@ import OrgFilter from "@/components/filters/OrgFilter.vue";
import StratumFilter from "@/components/filters/StratumFilter.vue"; import StratumFilter from "@/components/filters/StratumFilter.vue";
import DepthFilter from "@/components/filters/DepthFilter.vue"; import DepthFilter from "@/components/filters/DepthFilter.vue";
import BasinFilter from "@/components/filters/BasinFilter.vue"; import BasinFilter from "@/components/filters/BasinFilter.vue";
import ProjectFilter from "@/components/filters/ProjectFilter.vue";
import DescriptionFilter from "@/components/filters/DescriptionFilter.vue";
import DatalistFilter from "@/components/filters/DatalistFilter.vue";
import ResolutionFilter from "@/components/filters/ResolutionFilter.vue";
import FormDimensionsFilter from "@/components/filters/FormDimensionsFilter.vue";
export default { export default {
name: "filter-panel", name: "filter-panel",
components: { components: { CategoryFilter, GisCategoryFilter, ScannerFilter, OrgFilter, StratumFilter, DepthFilter, BasinFilter },
CategoryFilter,
GisCategoryFilter,
ScannerFilter,
OrgFilter,
StratumFilter,
DepthFilter,
BasinFilter,
ProjectFilter,
DescriptionFilter,
DatalistFilter,
ResolutionFilter,
FormDimensionsFilter
},
methods: {}, methods: {},
data() { data() {
return {} return {}
@ -97,7 +73,7 @@ export default {
flex-direction: column; flex-direction: column;
} }
.sidebar-item>h3 { .sidebar-item > h3 {
margin-bottom: 0.4rem margin-bottom: 0.4rem
} }

@ -38,17 +38,9 @@
<va-card id="map" class="content-container map-container"> <va-card id="map" class="content-container map-container">
<item-map-component v-if="detailsLoaded" :internal_id="this.itemDetails.internal_id" <item-map-component v-if="detailsLoaded" :internal_id="this.itemDetails.internal_id"
:x_coord="this.xComputed" :y_coord="this.yComputed" /> :x_coord="this.itemDetails.x_coord" :y_coord="this.itemDetails.y_coord" />
<h3 v-else>Карта загружается</h3> <h3 v-else>Карта загружается</h3>
</va-card> </va-card>
<va-card id="similar" class="content-container" v-if="detailsLoaded">
<va-scroll-container vertical>
<va-card-title>Похожие образцы</va-card-title>
<va-data-table :items="filteredSimilarItems" :columns="filteredColumns" :hoverable="true"
:clickable="true" select-mode="single" @row:click="(e) => handleClick(e)"
class="similar-items-table" />
</va-scroll-container>
</va-card>
<va-card id="contact" class="content-container"> <va-card id="contact" class="content-container">
<va-card-title>Связь с представителем</va-card-title> <va-card-title>Связь с представителем</va-card-title>
</va-card> </va-card>
@ -63,27 +55,21 @@ export default {
data() { data() {
return { return {
itemDetails: {}, itemDetails: {},
shownDetails: [ hidden: [
"fadr",
"internal_id", "internal_id",
"category", "x_coord",
"basin", "y_coord",
"stratum", "owner",
"form_dimentions", "ownercontacts",
"resolution", "id",
"scanner", "depth_min",
"datalist", "depth_max",
"comment"
], ],
imageExtension: "bmp", imageExtension: "bmp",
hasImage: false, hasImage: false,
detailsLoaded: false, detailsLoaded: false,
columnHeaders: [], columnHeaders: []
showColumns: [
"description",
"deposit",
"category",
],
similarItems: [],
}; };
}, },
methods: { methods: {
@ -91,6 +77,7 @@ export default {
fetch(`/api/v1/item/${this.$route.params.id}`) fetch(`/api/v1/item/${this.$route.params.id}`)
.then(res => res.json()) .then(res => res.json())
.then(data => this.itemDetails = data) .then(data => this.itemDetails = data)
.then(() => this.detailsLoaded = true)
.catch((e) => console.log(e)); .catch((e) => console.log(e));
}, },
async fetchColumnHeaders() { async fetchColumnHeaders() {
@ -118,39 +105,11 @@ export default {
} }
return `/static/previews/${this.itemDetails.fadr}+/${this.itemDetails.internal_id}.${this.imageExtension}` return `/static/previews/${this.itemDetails.fadr}+/${this.itemDetails.internal_id}.${this.imageExtension}`
}, }
async fetchSimilarItems() {
fetch(`/api/v1/detailed_search/`,
{ method: 'POST', body: JSON.stringify({ fadr: this.itemDetails.fadr }), headers: { 'Content-Type': 'application/json' } })
.then(res => res.json())
.then(data => this.similarItems = data)
.then(() => this.detailsLoaded = true)
.catch((e) => console.log(e));
},
handleClick(e) {
if (e.event.ctrlKey) {
let routeData = this.$router.resolve({ name: 'item-details' });
window.open(routeData.href, '_blank');
} else {
this.detailsLoaded = false;
this.$router.replace(`/items/${e.item.id}`).then(a => {
this.fetchItemDetails();
});
}
},
}, },
mounted() { created() {
this.fetchItemDetails();
this.fetchColumnHeaders(); this.fetchColumnHeaders();
this.fetchItemDetails().then(
this.fetchSimilarItems()
);
},
watch: {
'itemDetails': function (_oldval, _newval) {
this.fetchSimilarItems()
}
}, },
computed: { computed: {
slotPreviewPath() { slotPreviewPath() {
@ -166,40 +125,9 @@ export default {
}, },
formattedDetails() { formattedDetails() {
return Object.entries(this.itemDetails) return Object.entries(this.itemDetails)
.filter(([k, _v]) => (this.shownDetails.includes(k))) .filter(([k, _v]) => !(this.hidden.includes(k)))
.map(([k, v]) => [this.dictHeaders[k], v]) .map(([k, v]) => [this.dictHeaders[k], v])
}, },
filteredColumns() {
return this.columnHeaders
.filter(header => this.showColumns.includes(header.database))
.sort((header1, header2) => {
return this.showColumns.indexOf(header1.database) - this.showColumns.indexOf(header2.database)
})
.map(header => {
return {
key: header.database,
label: header.spreadsheet,
sortable: true,
}
})
},
forceSimilarItemsReload() {
// https://stackoverflow.com/questions/48641295/async-computed-in-components-vuejs
this.fetchSimilarItems();
for (let _i in this.itemDetails) {
return this.itemDetails.fadr;
}
return false;
},
filteredSimilarItems() {
return [...this.similarItems].filter(item => item.internal_id !== this.itemDetails.internal_id)
},
xComputed() {
return this.itemDetails.x_coord ? this.itemDetails.x_coord : 55.75
},
yComputed() {
return this.itemDetails.y_coord ? this.itemDetails.y_coord : 37.62
},
}, },
components: { ItemMapComponent } components: { ItemMapComponent }
} }
@ -218,11 +146,6 @@ export default {
margin: 1rem auto; margin: 1rem auto;
} }
.similar-items-table {
width: 45rem;
overflow-x: hidden;
}
.item-description { .item-description {
margin-top: 2rem; margin-top: 2rem;
margin-bottom: 2rem; margin-bottom: 2rem;

@ -7,7 +7,7 @@
<script> <script>
import maplibregl from "maplibre-gl"; import maplibregl from "maplibre-gl";
import { markRaw, onMounted, onUnmounted, reactive, shallowRef, watchEffect, } from "vue"; import { markRaw, onMounted, onUnmounted, reactive, shallowRef, } from "vue";
export default { export default {
name: "item-map-component", name: "item-map-component",
props: { props: {
@ -25,31 +25,6 @@ export default {
setup(props, _context) { setup(props, _context) {
const mapContainer = shallowRef(null); const mapContainer = shallowRef(null);
const map = shallowRef(null); const map = shallowRef(null);
let isSampleLayerAdded = false;
const addSamplesLayer = () => {
map.value.addSource('samples', {
'type': 'vector',
"tiles": ["http://localhost/martin/public.geodata/{z}/{x}/{y}.pbf"],
'promoteId': 'fadr',
});
map.value.addLayer({
'id': 'samples-layer',
'source': 'samples',
'source-layer': 'public.geodata',
'type': 'circle',
'paint': {
'circle-stroke-width': 1,
'circle-stroke-color': '#FFFFFF',
'circle-color': '#d95f0e',
'circle-opacity': 0.8,
'circle-radius': 16
},
filter: ["==", ["get", "internal_id"], props.internal_id]
});
}
onMounted(() => { onMounted(() => {
const apiKey = "pk.eyJ1IjoiZ2hlcm1hbnQiLCJhIjoiY2pncDUwcnRmNDQ4ZjJ4czdjZXMzaHZpNyJ9.3rFyYRRtvLUngHm027HZ7A"; const apiKey = "pk.eyJ1IjoiZ2hlcm1hbnQiLCJhIjoiY2pncDUwcnRmNDQ4ZjJ4czdjZXMzaHZpNyJ9.3rFyYRRtvLUngHm027HZ7A";
@ -83,11 +58,25 @@ export default {
} }
); );
watchEffect(() => { map.value.addSource('samples', {
if (!isSampleLayerAdded && typeof props.internal_id !== typeof undefined) { 'type': 'vector',
isSampleLayerAdded = true; "tiles": ["http://localhost:8080/martin/public.geodata/{z}/{x}/{y}.pbf"],
addSamplesLayer(); 'promoteId': 'fadr',
} });
map.value.addLayer({
'id': 'samples-layer',
'source': 'samples',
'source-layer': 'public.geodata',
'type': 'circle',
'paint': {
'circle-stroke-width': 1,
'circle-stroke-color': '#FFFFFF',
'circle-color': '#d95f0e',
'circle-opacity': 0.8,
'circle-radius': 16
},
filter: ["==", ["get", "internal_id"], props.internal_id]
}); });
}); });

@ -41,7 +41,6 @@ export default {
.then(data => { .then(data => {
if (data.access_token) { if (data.access_token) {
localStorage.setItem('user', JSON.stringify(data)); localStorage.setItem('user', JSON.stringify(data));
document.cookie = `user_session=${JSON.stringify(data.access_token)}; path=/; max-age=${60 * 60 * 24 * 365}; SameSite=strict`
this.$router.push('/admin') this.$router.push('/admin')
} }
}) })

@ -1,304 +1,303 @@
<template> <template>
<div class="map-wrap"> <div class="map-wrap">
<div id="layer-control" class="d-flex flex-direction-column"> <div id="layer-control" class="d-flex flex-direction-column">
<va-radio class="radio-switcher" v-for="(option, index) in viewOptions" :key="index" <va-radio
v-model="selectedOption" :option="option" :label="viewLabels[index]"></va-radio> v-for="(option, index) in options"
<va-checkbox class="mb-4 fields-switch" v-model="valuecb" label="Месторождения" /> :key="index"
</div> v-model="selectedOption"
<div id="map" class="map" ref="mapContainer"></div> :option="option"
:label="labels[index]"
/>
<va-checkbox class="mb-4" v-model="valuecb" :label="label" />
</div> </div>
<div id="map" class="map" ref="mapContainer"></div>
</div>
</template> </template>
<script> <script>
import maplibregl from "maplibre-gl"; import maplibregl from "maplibre-gl";
import { markRaw, onMounted, onUnmounted, reactive, ref, shallowRef, watch } from "vue"; import {
markRaw,
onMounted,
onUnmounted,
reactive,
shallowRef,
watch,
} from "vue";
export default { export default {
name: "map-component", name: "map-component",
props: { props: {
idlist: { idlist: {
type: Array, type: Array,
required: true, required: true,
}
}, },
setup(props, context) { },
const mapContainer = shallowRef(null); data() {
const map = shallowRef(null); return {
let currentFadr = reactive({ "fadr": [] }); valuebm: "",
const valuecb = ref(true); options: ["Карта", "Спутник", "Монохром"],
const selectedOption = ref("topo"); valuecb: true,
const viewOptions = ["topo", "satellite", "mono"]; label: "Месторождения",
const viewLabels = ["Карта", "Спутник", "Монохром"]; };
},
const apiKey = "pk.eyJ1IjoiZ2hlcm1hbnQiLCJhIjoiY2pncDUwcnRmNDQ4ZjJ4czdjZXMzaHZpNyJ9.3rFyYRRtvLUngHm027HZ7A"; setup(props, context) {
const mapContainer = shallowRef(null);
const map = shallowRef(null);
let currentFadr = reactive({ fadr: [] });
const apiKey =
"pk.eyJ1IjoiZ2hlcm1hbnQiLCJhIjoiY2pncDUwcnRmNDQ4ZjJ4czdjZXMzaHZpNyJ9.3rFyYRRtvLUngHm027HZ7A";
watch(
() => [...props.idlist],
(_currentValue, _oldValue) => {
updateSamplesLayer();
}
);
const buildMap = () => {
map.value = markRaw(
new maplibregl.Map({
container: mapContainer.value, // container id
// DOCS: https://maplibre.org/maplibre-gl-js-docs/style-spec/
style: {
version: 8,
sources: {},
layers: [],
},
center: [80, 40], // starting position [lng, lat]
zoom: 2, // starting zoom
maxZoom: 10,
})
);
map.value.on("load", () => {
const basemapTiles = { const basemapTiles = {
"topo": [`https://api.mapbox.com/styles/v1/ghermant/cl9vd1nji002n15s2xxj25f4m/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`], "topo": [`https://api.mapbox.com/styles/v1/ghermant/cl9vd1nji002n15s2xxj25f4m/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`],
"satellite": [`https://api.mapbox.com/styles/v1/ghermant/cl9olarp0002i14vq9a2d0e7g/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`], "satellite": [`https://api.mapbox.com/styles/v1/ghermant/cl9vd1nji002n15s2xxj25f4m/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`],
"mono": [`https://api.mapbox.com/styles/v1/ghermant/cl9vd7xwi004u14nxu3j83scj/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`] "mono": [`https://api.mapbox.com/styles/v1/ghermant/cl9olarp0002i14vq9a2d0e7g/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`]
}
};
watch(() => [...props.idlist], (_currentValue, _oldValue) => { map.value.addSource("basemap", {
updateSamplesLayer() type: "raster",
tiles: [
`https://api.mapbox.com/styles/v1/ghermant/cl9vd1nji002n15s2xxj25f4m/tiles/256/{z}/{x}/{y}@2x?access_token=${apiKey}`,
],
}); });
watch(() => valuecb.value, (_currentValue, _oldValue) => { map.value.addLayer({
updateFieldsLayer() id: "basemap-layer",
type: "raster",
source: "topo-basemap",
paint: {},
}); });
watch(() => selectedOption.value, (_currentValue, _oldValue) => { map.value.addSource("fields", {
updateBaseLayer() type: "vector",
}) tiles: [
`https://api.mapbox.com/v4/ghermant.cpc0xaaw/{z}/{x}/{y}.mvt?access_token=${apiKey}`,
const buildMap = () => { ],
map.value = markRaw(new maplibregl.Map({ });
container: mapContainer.value, // container id map.value.addLayer({
// DOCS: https://maplibre.org/maplibre-gl-js-docs/style-spec/ id: "fields-layer",
style: { type: "fill",
version: 8, source: "fields",
sources: {}, "source-layer": "mygeomapZoom2-2utkfs",
layers: [] paint: {
}, "fill-color": "#fc9272",
center: [80, 40], // starting position [lng, lat] "fill-opacity": 0.5,
zoom: 2, // starting zoom },
maxZoom: 10 layout: {
})); visibility: "visible",
},
map.value.on('load', () => { minzoom: 4
map.value.addSource('basemap', {
'type': 'raster',
'tiles': basemapTiles[selectedOption.value]
})
map.value.addLayer(
{
'id': 'basemap-layer',
'type': 'raster',
'source': 'basemap',
'paint': {}
}
);
map.value.addSource("fields", {
type: "vector",
tiles: [`https://api.mapbox.com/v4/ghermant.cpc0xaaw/{z}/{x}/{y}.mvt?access_token=${apiKey}`]
})
map.value.addLayer({
'id': 'fields-layer',
'type': 'fill',
'source': 'fields',
'source-layer': 'mygeomapZoom2-2utkfs',
'paint': {
'fill-color': '#fc9272',
'fill-opacity': 0.5
},
'layout': {
'visibility': 'visible'
}
})
map.value.addSource('samples', {
'type': 'vector',
"tiles": ["http://localhost/martin/public.geodata/{z}/{x}/{y}.pbf"],
'promoteId': 'fadr',
});
map.value.addLayer({
'id': 'samples-layer',
'source': 'samples',
'source-layer': 'public.geodata',
'type': 'circle',
'paint': {
'circle-stroke-width': 1,
'circle-stroke-color': '#FFFFFF',
'circle-color': [
'case',
['boolean', ['feature-state', 'beenClicked'], false],
'#fec44f',
'#d95f0e'
],
'circle-opacity': 0.8,
'circle-radius': 8
},
filter: ["match", ["get", "internal_id"], props.idlist, true, false]
});
});
const popup = new maplibregl.Popup({
closeButton: false,
closeOnClick: false,
});
map.value.on("click", "fields-layer", (e) => {
new maplibregl.Popup({})
.setLngLat(e.lngLat)
.setHTML(e.features[0].properties["descriptio"])
.addTo(map.value);
});
map.value.on("mouseover", "fields-layer", (e) => {
map.value.getCanvas().style.cursor = "pointer";
});
map.value.on("mouseleave", "fields-layer", (e) => {
map.value.getCanvas().style.cursor = "";
});
map.value.on('mouseover', 'samples-layer', (e) => {
// Change the cursor style as a UI indicator.
map.value.getCanvas().style.cursor = 'pointer';
// Populate the popup and set its coordinates
// based on the feature found.
popup
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(`Записей в точке: <b>${e.features.length}</b>`)
.addTo(map.value);
});
map.value.on('mouseleave', 'samples-layer', (e) => {
map.value.getCanvas().style.cursor = '';
popup.remove();
});
map.value.on('click', 'samples-layer', (e) => {
let newFadr = e.features[0].properties["fadr"];
let newFadrFound = currentFadr["fadr"].indexOf(newFadr)
if (newFadrFound > -1) {
//remove old paint
map.value.setFeatureState(
{
source: 'samples',
sourceLayer: 'public.geodata',
id: newFadr
},
{ beenClicked: false }
);
// remove from filters
currentFadr["fadr"].splice(newFadrFound, 1)
} else {
// apply new paint
map.value.setFeatureState(
{
source: 'samples',
sourceLayer: 'public.geodata',
id: newFadr
},
{ beenClicked: true }
);
// add to filters
currentFadr["fadr"].push(newFadr);
}
context.emit('mapClick', currentFadr);
});
map.value.on('click', (e) => {
const features = map.value.queryRenderedFeatures(e.point)
if (!features.length) {
currentFadr["fadr"].forEach((oldFadr) => {
map.value.setFeatureState(
{
source: 'samples',
sourceLayer: 'public.geodata',
id: oldFadr
},
{ beenClicked: false }
);
})
currentFadr["fadr"].length = 0;
}
context.emit('mapClick', currentFadr);
});
};
const updateSamplesLayer = () => {
if (props.idlist.length < 2000) {
map.value.setFilter('samples-layer', ["in", "internal_id", ...props.idlist])
} else {
map.value.setFilter('samples-layer', null)
}
};
const updateFieldsLayer = () => {
const visibility = valuecb.value ? 'visible' : 'none';
map.value.setLayoutProperty('fields-layer', 'visibility', visibility);
};
const updateBaseLayer = () => {
const selectedBasemap = selectedOption.value;
// https://stackoverflow.com/questions/38631344/recommended-way-to-switch-tile-urls-in-mapbox-gl-js
// Set the tile url to a cache-busting url (to circumvent browser caching behaviour):
map.value.getSource('basemap').tiles = basemapTiles[selectedBasemap]
// Remove the tiles for a particular source
map.value.style.sourceCaches['basemap'].clearTiles()
// Load the new tiles for the current viewport (map.value.transform -> viewport)
map.value.style.sourceCaches['basemap'].update(map.value.transform)
// Force a repaint, so that the map will be repainted without you having to touch the map
map.value.triggerRepaint()
}
onMounted(() => {
buildMap()
}); });
onUnmounted(() => { map.value.addSource("samples", {
// TODO: remove even listeners too type: "vector",
map.value?.remove(); tiles: [
}) "http://localhost:8080/martin/public.geodata/{z}/{x}/{y}.pbf",
],
promoteId: "fadr",
});
map.value.addLayer({
id: "samples-layer",
source: "samples",
"source-layer": "public.geodata",
type: "circle",
paint: {
"circle-stroke-width": 1,
"circle-stroke-color": "#FFFFFF",
"circle-color": [
"case",
["boolean", ["feature-state", "beenClicked"], false],
"#fec44f",
"#d95f0e",
],
"circle-opacity": 0.8,
"circle-radius": 8,
},
filter: ["match", ["get", "internal_id"], props.idlist, true, false],
});
});
const popup = new maplibregl.Popup({
closeButton: false,
closeOnClick: false,
})
map.value.on("click", "fields-layer", (e) => {
new maplibregl.Popup({})
.setLngLat(e.lngLat)
.setHTML(e.features[0].properties["descriptio"])
.addTo(map.value);
})
map.value.on("mouseover", "fields-layer", (e) => {
map.value.getCanvas().style.cursor = "pointer";
})
map.value.on("mouseleave", "fields-layer", (e) => {
map.value.getCanvas().style.cursor = "";
})
map.value.on("mouseover", "samples-layer", (e) => {
// Change the cursor style as a UI indicator.
map.value.getCanvas().style.cursor = "pointer";
// Populate the popup and set its coordinates
// based on the feature found.
popup
.setLngLat(e.lngLat)
.setHTML(`Записей в точке: <b>${e.features.length}</b>`)
.addTo(map.value);
});
map.value.on("mouseleave", "samples-layer", (e) => {
map.value.getCanvas().style.cursor = "";
popup.remove();
});
map.value.on("click", "samples-layer", (e) => {
let newFadr = e.features[0].properties["fadr"];
let newFadrFound = currentFadr["fadr"].indexOf(newFadr);
if (newFadrFound > -1) {
//remove old paint
map.value.setFeatureState(
{
source: "samples",
sourceLayer: "public.geodata",
id: newFadr,
},
{ beenClicked: false }
);
// remove from filters
currentFadr["fadr"].splice(newFadrFound, 1);
} else {
// apply new paint
map.value.setFeatureState(
{
source: "samples",
sourceLayer: "public.geodata",
id: newFadr,
},
{ beenClicked: true }
);
// add to filters
currentFadr["fadr"].push(newFadr);
}
return { context.emit("mapClick", currentFadr);
map, mapContainer, currentFadr, valuecb, selectedOption, viewOptions, viewLabels });
map.value.on("click", (e) => {
const features = map.value.queryRenderedFeatures(e.point);
if (!features.length) {
currentFadr["fadr"].forEach((oldFadr) => {
map.value.setFeatureState(
{
source: "samples",
sourceLayer: "public.geodata",
id: oldFadr,
},
{ beenClicked: false }
);
});
currentFadr["fadr"].length = 0;
} }
}
}
context.emit("mapClick", currentFadr);
});
};
const updateSamplesLayer = () => {
if (map.value.getLayer("samples-layer")) {
map.value.removeLayer("samples-layer");
}
if (props.idlist.length) {
map.value.addLayer({
id: "samples-layer",
source: "samples",
"source-layer": "public.geodata",
type: "circle",
paint: {
"circle-stroke-width": 1,
"circle-stroke-color": "#FFFFFF",
"circle-color": [
"case",
["boolean", ["feature-state", "beenClicked"], false],
"#fec44f",
"#d95f0e",
],
"circle-opacity": 0.8,
"circle-radius": 8,
},
filter: ["match", ["get", "internal_id"], props.idlist, true, false],
});
}
};
onMounted(() => {
buildMap();
});
onUnmounted(() => {
// TODO: remove even listeners too
map.value?.remove();
});
return {
map,
mapContainer,
currentFadr,
};
},
};
</script> </script>
<style lang="css"> <style lang="css">
.map-wrap { .map-wrap {
position: relative; position: relative;
height: 100vh; height: 100vh;
width: 100%; width: 100%;
} }
.map { .map {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
/* z-index: -1; */ z-index: -1;
} }
#layer-control { #layer-control {
position: absolute; position: absolute;
top: 1rem; top: 1rem;
right: 1rem; right: 1rem;
z-index: 5;
flex-direction: column;
}
.radio-switcher {
padding-left: 0.3rem;
}
.fields-switch {
margin-top: 1rem;
}
.va-radio__text, .va-checkbox__label {
color: white;
} }
</style> </style>

@ -3,56 +3,16 @@
<va-navbar shape color="#79589f" class="navbar"> <va-navbar shape color="#79589f" class="navbar">
<template #left> <template #left>
<va-navbar-item class="top-icon"> <va-navbar-item class="top-icon">
<va-popover message="Карта" prevent-overflow stick-to-edges> <va-popover message="Главная страница">
<va-icon size="3rem" name="public" @click="$router.push('/')" /> <va-icon size="3rem" color="gray" name="public" @click="$router.push('/')" />
</va-popover> </va-popover>
</va-navbar-item> </va-navbar-item>
<slot></slot> <slot></slot>
</template> </template>
<template #right> <template #right>
<va-dropdown :close-on-content-click="false" placement="bottom-end" prevent-overflow>
<template #anchor>
<va-navbar-item class="top-icon">
<va-popover message="Корзина" prevent-overflow stick-to-edges>
<va-icon size="3rem" name="shopping_basket" @click="" />
</va-popover>
</va-navbar-item>
</template>
<va-dropdown-content>
<va-card class="flex">
<va-card-title>Корзина</va-card-title>
<va-card-content v-if="!itemsInCart.length" class="cart-inner cart-empty">
<p class="cart-empty__text">Корзина пуста</p>
<p class="cart-empty__text">Попробуйте перейти на страницу образца</p>
<p v-if="isItemPath" class="cart-empty__text">
и <va-button @click="addToCart">Добавить в корзину</va-button>
</p>
</va-card-content>
<template v-else>
<va-card-actions align="between">
<template v-if="isItemPath">
<va-button v-if="!isPathInCart" @click="addToCart">Добавить в корзину
</va-button>
<va-button v-else @click="removeFromCart">Убрать из корзины</va-button>
</template>
<va-button @click="copyCart">{{ copyCartText }}</va-button>
<va-button color="warning" @click="emptyCart">Очистить корзину</va-button>
</va-card-actions>
<va-card-content class="cart-inner">
<va-data-table :items="itemsInCart" :columns="filteredColumns" :hoverable="true"
:clickable="true" select-mode="single" @row:click="(e) => handleClick(e)"
class="similar-items-table" />
</va-card-content>
</template>
</va-card>
</va-dropdown-content>
</va-dropdown>
<va-navbar-item class="top-icon"> <va-navbar-item class="top-icon">
<va-popover message="Аккаунт" prevent-overflow stick-to-edges> <va-popover message="Аккаунт">
<va-icon size="3rem" name="account_box" @click="$emit('accountClick')" /> <va-icon size="3rem" color="gray" name="account_box" @click="$emit('accountClick')" />
</va-popover> </va-popover>
</va-navbar-item> </va-navbar-item>
</template> </template>
@ -64,98 +24,6 @@
<script> <script>
export default { export default {
name: "navbar", name: "navbar",
data() {
return {
columnHeaders: [],
showColumns: [
"internal_id",
"description",
],
itemsInCart: [],
cartListCopied: false,
}
},
methods: {
addToCart() {
if (!this.isPathInCart) {
fetch(`/api/v1/item/${this.$route.params.id}`)
.then(res => res.json())
.then(data => {
this.itemsInCart.push(data);
localStorage.setItem("cart", JSON.stringify(this.itemsInCart));
this.cartListCopied = false;
})
.catch((e) => console.log(e));
}
},
removeFromCart() {
const routeId = Number.parseInt(this.$route.params.id);
this.itemsInCart = this.itemsInCart.filter(item => item.id !== routeId)
localStorage.setItem("cart", JSON.stringify(this.itemsInCart));
this.cartListCopied = false;
},
copyCart() {
const textToCopy = this.itemsInCart
.map(item => item.internal_id)
.reduce((list, item_id) => list + "," + item_id)
navigator.clipboard.writeText(textToCopy)
.then(() => this.cartListCopied = true)
.catch(e => console.log("Error copying item list ("
+ textToCopy
+ ") from cart: "
+ e))
},
emptyCart() {
this.itemsInCart = [];
localStorage.removeItem("cart");
},
restoreCart() {
if (localStorage.getItem("cart")) {
this.itemsInCart = JSON.parse(localStorage.getItem("cart"))
}
},
async fetchColumnHeaders() {
fetch(`/api/v1/headers/`)
.then(res => res.json())
.then(data => this.columnHeaders = data)
.catch((e) => console.log(e));
},
handleClick(e) {
if (String(e.item.id) !== this.$route.params.id) {
window.open(`/items/${e.item.id}`, '_blank')
}
}
},
mounted() {
this.fetchColumnHeaders();
this.restoreCart()
},
computed: {
isItemPath() {
return this.$route.name === "item-details";
},
filteredColumns() {
return this.columnHeaders
.filter(header => this.showColumns.includes(header.database))
.sort((header1, header2) => {
return this.showColumns.indexOf(header1.database) - this.showColumns.indexOf(header2.database)
})
.map(header => {
return {
key: header.database,
label: header.spreadsheet,
sortable: true,
}
})
},
isPathInCart() {
const routeId = Number.parseInt(this.$route.params.id);
return this.itemsInCart.some(item => item.id === routeId)
},
copyCartText() {
return (this.cartListCopied ? 'Скопировано ✓✓' : 'Скопировать список образцов')
}
},
} }
</script> </script>
@ -164,19 +32,4 @@ export default {
.navbar { .navbar {
position: relative; position: relative;
} }
.cart-inner {
display: flex;
flex-direction: column;
min-width: 30rem;
min-height: 30rem;
}
.cart-empty {
justify-content: center;
}
.cart-empty__text {
margin: 1rem auto;
}
</style> </style>

@ -5,12 +5,11 @@
@mapClick="$emit('mapClick', $event)" /> @mapClick="$emit('mapClick', $event)" />
</va-card> </va-card>
<va-card class="content-container"> <va-card class="content-container">
<va-scroll-container vertical> <va-data-table v-if="items.length" :items="items" :columns="columns" :hoverable="true" :clickable="true"
<va-data-table virtual-scroller sticky-header v-if="items.length" :items="items" :columns="columns" :hoverable="true" :clickable="true" :per-page="perPage" :current-page="currentPage" @row:click="(e) => $router.push(`/items/${e.item.id}`)"
@row:click="(e) => $router.push(`/items/${e.item.id}`)" no-data-filtered-html="Не найдено образцов, соответствующих данному запросу" />
no-data-filtered-html="Не найдено образцов, соответствующих данному запросу" /> <va-pagination v-if="items.length" v-model="currentPage" :pages="pages" input style="margin: 0.5rem auto" />
<h1 v-else class="no-items">Не найдено образцов, соответствующих данному запросу</h1> <h1 v-else class="no-items">Не найдено образцов, соответствующих данному запросу</h1>
</va-scroll-container>
</va-card> </va-card>
</div> </div>
</template> </template>
@ -33,12 +32,23 @@ export default {
}, },
data() { data() {
return { return {
perPage: 10,
currentPage: 1,
settleFinished: false, settleFinished: false,
} }
}, },
methods: {}, methods: {},
computed: { computed: {
pages() {
this.settleFinished = true;
this.currentPage = 1;
return (this.perPage && this.perPage !== 0)
? Math.ceil(this.items.length / this.perPage)
: this.items.length
},
itemIdList() { itemIdList() {
return this.items.map(item => item.internal_id) return this.items.map(item => item.internal_id)
}, },

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<va-sidebar class="sidebar-proper" width="18rem"> <va-sidebar color="white" text-color="black" class="sidebar-proper" width="18rem">
<slot></slot> <slot></slot>
</va-sidebar> </va-sidebar>
</div> </div>
@ -17,7 +17,7 @@ export default {
<style lang="css"> <style lang="css">
.sidebar-proper { .sidebar-proper {
height: max(100vh, 100%); height: max(100vh, 100%);
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); box-shadow: 0 0 15px rgba(0, 0, 0, 0.25);
clip-path: inset(0px -15px 0px 0px); clip-path: inset(0px -15px 0px 0px);
overflow-y: hidden; overflow-y: hidden;
} }

@ -1,40 +0,0 @@
<template>
<div>
<va-select class="multi-selector" :options="datalistOptions" v-model="selectedOptions"
@update:model-value="applyFilter" multiple searchable>
</va-select>
</div>
</template>
<script>
export default {
name: "datalist-filter",
data() {
return {
datalistOptions: [
// SELECT DISTINCT BTRIM(UNNEST(STRING_TO_ARRAY(datalist,','))) FROM geodata;
"Project",
"Rec",
"Data",
"Raw",
"Report",
],
selectedOptions: [],
}
},
methods: {
applyFilter() {
const filter = { "datalist": this.selectedOptions }
this.$emit('filter', filter, this.name)
},
},
}
</script>
<style lang="css">
.multi-selector {
--va-select-min-width: 12 rem;
}
</style>

@ -1,43 +0,0 @@
<template>
<div>
<va-select class="multi-selector" :options="descriptionOptions" v-model="selectedOptions"
@update:model-value="applyFilter" multiple searchable>
</va-select>
</div>
</template>
<script>
export default {
name: "description-filter",
data() {
return {
descriptionOptions: [
"терригенная порода-коллектор",
"черви",
"бусины из скорлупы страуса",
"осколки скорлупы яиц страуса",
"лопатка лощади ",
"грунты",
"глинисто-карбонатная порода",
"Искусстенная модель осадочных пород",
"угли",
],
selectedOptions: [],
}
},
methods: {
applyFilter() {
const filter = { "description": this.selectedOptions }
this.$emit('filter', filter, this.name)
},
},
}
</script>
<style lang="css">
.multi-selector {
--va-select-min-width: 12 rem;
}
</style>

@ -1,42 +0,0 @@
<template>
<div>
<va-select class="multi-selector" :options="formDimentionsOptions" v-model="selectedOptions"
@update:model-value="applyFilter" multiple searchable>
</va-select>
</div>
</template>
<script>
export default {
name: "form-dimentions-filter",
data() {
return {
formDimentionsOptions: [
"фрагмент 20 см",
"фрагменты 5-7 см",
"полноразмерный керн, 65 мм",
"цилиндр, 8 мм",
"цилиндр, 10 мм",
"цилиндр, 30 мм",
"полноразмерный керн",
"полноразмерный керн, 100 мм",
],
selectedOptions: [],
}
},
methods: {
applyFilter() {
const filter = { "form_dimentions": this.selectedOptions }
this.$emit('filter', filter, this.name)
},
},
}
</script>
<style lang="css">
.multi-selector {
--va-select-min-width: 12 rem;
}
</style>

@ -1,42 +0,0 @@
<template>
<div>
<va-select class="multi-selector" :options="projectOptions" v-model="selectedOptions"
@update:model-value="applyFilter" multiple searchable>
</va-select>
</div>
</template>
<script>
export default {
name: "project-filter",
data() {
return {
projectOptions: [
"AAK",
"AAM",
"ABB",
"ABE",
"AER",
"AJL",
"ASX",
"ATM",
],
selectedOptions: [],
}
},
methods: {
applyFilter() {
const filter = { "project": this.selectedOptions }
this.$emit('filter', filter, this.name)
},
},
}
</script>
<style lang="css">
.multi-selector {
--va-select-min-width: 12 rem;
}
</style>

@ -1,43 +0,0 @@
<template>
<div class="range-input">
<va-input placeholder="1.59" v-model="reso_min" input-class="text--center"
:mask="{ numeral: true, delimiter: '', numeralDecimalMark: ',' }" @change="applyFilter" />
<va-input placeholder="264" v-model="reso_max" input-class="text--center"
:mask="{ numeral: true, delimiter: '', numeralDecimalMark: ',' }" @change="applyFilter" />
</div>
</template>
<script>
export default {
name: "resolution-filter",
data() {
return {
// SELECT MIN(resolution), MAX(resolution) FROM geodata;
reso_min: "1.59",
reso_max: "264",
}
},
methods: {
applyFilter() {
const filter = { "resolution": [this.reso_min, this.reso_max] }
this.$emit('filter', filter, this.name)
},
},
}
</script>
<style lang="css">
.range-input {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 5rem
}
.range-input>div {
--va-input-wrapper-min-width: 4rem;
width: 4rem;
}
</style>

@ -22,7 +22,6 @@ import {
VaCard, VaCard,
VaCardTitle, VaCardTitle,
VaCardContent, VaCardContent,
VaCardActions,
VaImage, VaImage,
VaSelect, VaSelect,
VaPagination, VaPagination,
@ -31,10 +30,6 @@ import {
VaChip, VaChip,
VaModal, VaModal,
VaPopover, VaPopover,
VaScrollContainer,
VaDropdown,
VaDropdownContent,
VaCheckbox,
} from 'vuestic-ui' } from 'vuestic-ui'
import 'vuestic-ui/dist/styles/essential.css' import 'vuestic-ui/dist/styles/essential.css'
import 'vuestic-ui/dist/styles/grid.css' import 'vuestic-ui/dist/styles/grid.css'
@ -71,7 +66,6 @@ app.use(createVuesticEssential({
VaCard, VaCard,
VaCardTitle, VaCardTitle,
VaCardContent, VaCardContent,
VaCardActions,
VaImage, VaImage,
VaSelect, VaSelect,
VaPagination, VaPagination,
@ -80,10 +74,6 @@ app.use(createVuesticEssential({
VaChip, VaChip,
VaModal, VaModal,
VaPopover, VaPopover,
VaScrollContainer,
VaDropdown,
VaDropdownContent,
VaCheckbox,
} }
})); }));

@ -1,5 +1,5 @@
<template> <template>
<navbar class="navbar" @accountClick="$router.push('/admin/')"> <navbar @accountClick="$router.push('/admin/')">
<va-input placeholder="Начните вводить запрос" v-model="searchQuery"></va-input> <va-input placeholder="Начните вводить запрос" v-model="searchQuery"></va-input>
</navbar> </navbar>
<div class="central-view"> <div class="central-view">
@ -10,7 +10,7 @@
<overview-screen :items="filteredAndSearchedItems" :columns="filteredColumns" @mapClick="applyFilters" /> <overview-screen :items="filteredAndSearchedItems" :columns="filteredColumns" @mapClick="applyFilters" />
</div> </div>
</div> </div>
<login-modal :showModal="showModal" @closeModal="showModal = false" /> <login-modal :showModal="showModal" @closeModal="showModal=false" />
</template> </template>
@ -39,15 +39,14 @@ export default {
"description", "description",
"form_dimentions", "form_dimentions",
"datalist", "datalist",
"resolution",
], ],
showColumns: [ showColumns: [
"internal_id",
"category",
"description", "description",
"stratum", "deposit",
"form_dimentions", "org",
"resolution", "category",
"datalist" "scanner",
], ],
showModal: false, showModal: false,
} }
@ -90,28 +89,6 @@ export default {
if (item.depth_max < filterValues[0] || item.depth_min > filterValues[1]) { if (item.depth_max < filterValues[0] || item.depth_min > filterValues[1]) {
return false; return false;
} }
} else if (filterParam === "resolution") {
if (
// we control resolution input format, so we can be reasonbly sure
// it has upto one comma to denote decimal point and no dots
// therefore it's safe to normalize it into
// JavaScript definition of a decimal literal
item.resolution < parseFloat(filterValues[0].replace(/,/, '.'))
|| item.resolution > parseFloat(filterValues[1].replace(/,/, '.'))
) {
return false;
}
} else if (filterParam === "datalist") {
// for all selected datalist options check if we can find it in the datalist string
for (let datalistOption of filterValues) {
if (item.datalist.search(datalistOption) === -1) {
return false;
}
}
} else if (filterParam === "project") {
if (!filterValues.includes(item["fadr"])) {
return false;
}
} else if (!filterValues.includes(item[filterParam])) { } else if (!filterValues.includes(item[filterParam])) {
return false; return false;
} }
@ -175,8 +152,4 @@ export default {
.main-view { .main-view {
width: 100%; width: 100%;
} }
.navbar {
z-index: 100;
}
</style> </style>

@ -8,7 +8,7 @@
<item-details /> <item-details />
</div> </div>
</div> </div>
<login-modal :showModal="showModal" @closeModal="showModal = false" /> <login-modal :showModal="showModal" @closeModal="showModal=false" />
</template> </template>
@ -47,12 +47,6 @@ export default {
route: "item-details", route: "item-details",
hash: "#map", hash: "#map",
}, },
{
title: "Похожие образцы",
icon: "view_list",
route: "item-details",
hash: "#similar",
},
{ {
title: "Связь с представителем", title: "Связь с представителем",
icon: "contact_page", icon: "contact_page",

Loading…
Cancel
Save