Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a450e2ac6 | |||
| 9fc87a705f | |||
| 305864c1ed | |||
| ad83d9cc77 | |||
| 226c7f9473 |
@@ -1,3 +1,30 @@
|
||||
# v2025.7.1-1
|
||||
|
||||
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
|
||||
|
||||
✨ Новые функции
|
||||
- [#276](https://github.com/coopenomics/mono/issues/276): Реализовать путь возврата паевого взноса из кошелька пайщика
|
||||
- [#281](https://github.com/coopenomics/mono/issues/281): Генерация заявления на возврат паевого взноса
|
||||
- [#282](https://github.com/coopenomics/mono/issues/282): Генерация решения совета на возврат паевого взноса
|
||||
- [#278](https://github.com/coopenomics/mono/issues/278): Введение методов управления возвратами паевых взносов в контроллере
|
||||
- [#279](https://github.com/coopenomics/mono/issues/279): Отобразить исходящие платежи с кнопками управления в реестре платежей для совета
|
||||
|
||||
🐛 Исправления ошибок
|
||||
- [#286](https://github.com/coopenomics/mono/issues/286): Поправить ошибки склонений времени
|
||||
- [#262](https://github.com/coopenomics/mono/issues/262): Баг: рабочий стол совета включается, однако, страница всегда открывается со стола пайщика
|
||||
- [#261](https://github.com/coopenomics/mono/issues/261): Баг: первая загрузка переадресует на главную страницу всегда - прямой переход на собрание становится недоступен.
|
||||
|
||||
🔧 Улучшения
|
||||
- [#285](https://github.com/coopenomics/mono/issues/285): Корректировка дизайна кошелька, профиля, повестки совета, реестра документов, реестра платежей
|
||||
- [#284](https://github.com/coopenomics/mono/issues/284): Разместить кошелек на главную вместо профиля
|
||||
- [#283](https://github.com/coopenomics/mono/issues/283): Ввести лоадер на переходе между рабочими столами
|
||||
- [#280](https://github.com/coopenomics/mono/issues/280): Мигрировать имеющиеся данные о входящих платежах в новую модель
|
||||
- [#277](https://github.com/coopenomics/mono/issues/277): Рефакторинг модуля платежей: переход на унифицированную модель входящего и исходящего платежа
|
||||
|
||||
#releases
|
||||
|
||||
---
|
||||
|
||||
# v2025.6.14
|
||||
|
||||
Разработан модуль для проведения очередных общих собраний пайщиков. Исправлены баги, внесены улучшения пользовательского интерфейса.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@coopenomics/boot",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/cleos",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/contracts",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:all": "./build-all.sh",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@a2seven/yoo-checkout": "^1.1.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/controller",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"description": "",
|
||||
"private": false,
|
||||
"bin": "bin/createNodejsApp.js",
|
||||
|
||||
@@ -50,6 +50,12 @@ export class MeetTrackerService {
|
||||
// Основная функция проверки собраний
|
||||
async checkMeets(): Promise<void> {
|
||||
try {
|
||||
// Подтягиваем актуальный конфиг из базы
|
||||
const repo = await this.extensionRepository.findByName(this.extensionName);
|
||||
if (repo) {
|
||||
this.pluginConfig = repo;
|
||||
}
|
||||
|
||||
// Получаем все собрания из блокчейна через порт
|
||||
const meets = await this.meetPort.getMeets({ coopname: config.coopname }, undefined);
|
||||
if (!meets || meets.length === 0) {
|
||||
@@ -139,6 +145,8 @@ export class MeetTrackerService {
|
||||
if (closedMeetIds.includes(meetID)) {
|
||||
continue;
|
||||
}
|
||||
// Удаляем все старые собрания с тем же id перед добавлением нового
|
||||
this.pluginConfig.config.trackedMeets = this.pluginConfig.config.trackedMeets.filter((tm) => tm.id !== meetID);
|
||||
// Создаем новое отслеживаемое собрание
|
||||
const newTrackedMeet: TrackedMeet = {
|
||||
id: meetID,
|
||||
@@ -207,6 +215,15 @@ export class MeetTrackerService {
|
||||
if (statusChanged) {
|
||||
this.logger.info(`Изменение статуса собрания ${meetHash} (№${meetID}): ${oldStatus} -> ${extendedStatus}`);
|
||||
|
||||
if (
|
||||
oldStatus === ExtendedMeetStatus.ONRESTART &&
|
||||
extendedStatus === ExtendedMeetStatus.WAITING_FOR_OPENING &&
|
||||
!trackedMeet.notifications.restartNotification
|
||||
) {
|
||||
await this.notificationSender.sendRestartNotification(trackedMeet);
|
||||
trackedMeet.notifications.restartNotification = true;
|
||||
}
|
||||
|
||||
// При переходе с created на waitingForOpening отправляем начальное уведомление
|
||||
if (
|
||||
oldStatus === ExtendedMeetStatus.CREATED &&
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import { Inject, Injectable, OnModuleDestroy } from '@nestjs/common';
|
||||
import Redis from 'ioredis';
|
||||
import { REDIS_PROVIDER } from './redis.provider';
|
||||
import { WinstonLoggerService } from '~/modules/logger/logger-app.service';
|
||||
|
||||
export interface StreamMessage {
|
||||
messageId: string;
|
||||
fields: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface StreamConsumerOptions {
|
||||
stream: string;
|
||||
group: string;
|
||||
consumer: string;
|
||||
count?: number;
|
||||
block?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сервис для работы с Redis Streams
|
||||
* Обеспечивает надежное потребление событий от parser
|
||||
*/
|
||||
@Injectable()
|
||||
export class RedisStreamService implements OnModuleDestroy {
|
||||
private consumers: Map<string, boolean> = new Map();
|
||||
|
||||
constructor(
|
||||
@Inject(REDIS_PROVIDER) private readonly redisClient: { subscriber: Redis; publisher: Redis },
|
||||
private readonly logger: WinstonLoggerService
|
||||
) {
|
||||
this.logger.setContext(RedisStreamService.name);
|
||||
}
|
||||
|
||||
onModuleDestroy() {
|
||||
// Останавливаем всех потребителей
|
||||
this.consumers.forEach((_, key) => {
|
||||
this.consumers.set(key, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание группы потребителей
|
||||
*/
|
||||
async createConsumerGroup(stream: string, group: string, startId = '0'): Promise<void> {
|
||||
try {
|
||||
await this.redisClient.subscriber.xgroup('CREATE', stream, group, startId, 'MKSTREAM');
|
||||
this.logger.log(`Consumer group created: ${group} for stream: ${stream}`);
|
||||
} catch (error: any) {
|
||||
if (error.message.includes('BUSYGROUP')) {
|
||||
this.logger.log(`Consumer group ${group} already exists for stream ${stream}`);
|
||||
} else {
|
||||
this.logger.error(`Error creating consumer group: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Потребление сообщений из stream
|
||||
*/
|
||||
async startConsumer(
|
||||
options: StreamConsumerOptions,
|
||||
messageHandler: (message: StreamMessage) => Promise<void>
|
||||
): Promise<void> {
|
||||
const { stream, group, consumer, count = 1, block = 1000 } = options;
|
||||
const consumerKey = `${stream}:${group}:${consumer}`;
|
||||
|
||||
// Отмечаем потребителя как активного
|
||||
this.consumers.set(consumerKey, true);
|
||||
|
||||
this.logger.log(`Starting consumer: ${consumerKey}`);
|
||||
|
||||
while (this.consumers.get(consumerKey)) {
|
||||
try {
|
||||
const result: any = await this.redisClient.subscriber.xreadgroup(
|
||||
'GROUP',
|
||||
group,
|
||||
consumer,
|
||||
'COUNT',
|
||||
count.toString(),
|
||||
'BLOCK',
|
||||
block.toString(),
|
||||
'STREAMS',
|
||||
stream,
|
||||
'>'
|
||||
);
|
||||
|
||||
if (result && result.length > 0) {
|
||||
for (const [streamName, messages] of result) {
|
||||
if (streamName === stream) {
|
||||
for (const [messageId, messageData] of messages) {
|
||||
const fields: Record<string, string> = {};
|
||||
|
||||
// Преобразуем массив [key, value, key, value] в объект
|
||||
for (let i = 0; i < messageData.length; i += 2) {
|
||||
fields[messageData[i]] = messageData[i + 1];
|
||||
}
|
||||
|
||||
try {
|
||||
await messageHandler({ messageId, fields });
|
||||
await this.acknowledgeMessage(stream, group, messageId);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error processing message ${messageId}: ${error.message}`);
|
||||
// Не подтверждаем сообщение при ошибке - оно останется в pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (this.consumers.get(consumerKey)) {
|
||||
this.logger.error(`Consumer ${consumerKey} error: ${error.message}`);
|
||||
// Небольшая пауза перед повторной попыткой
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(`Consumer stopped: ${consumerKey}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Подтверждение обработки сообщения
|
||||
*/
|
||||
async acknowledgeMessage(stream: string, group: string, messageId: string): Promise<void> {
|
||||
try {
|
||||
await this.redisClient.subscriber.xack(stream, group, messageId);
|
||||
this.logger.debug(`Message acknowledged: ${messageId} in stream ${stream}`);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error acknowledging message ${messageId} in stream ${stream}, group ${group}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановка потребителя
|
||||
*/
|
||||
stopConsumer(stream: string, group: string, consumer: string): void {
|
||||
const consumerKey = `${stream}:${group}:${consumer}`;
|
||||
this.consumers.set(consumerKey, false);
|
||||
this.logger.log(`Stopping consumer: ${consumerKey}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение информации о pending сообщениях
|
||||
*/
|
||||
async getPendingMessages(stream: string, group: string): Promise<any> {
|
||||
try {
|
||||
return await this.redisClient.subscriber.xpending(stream, group);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error getting pending messages: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение информации о группах потребителей
|
||||
*/
|
||||
async getConsumerGroups(stream: string): Promise<any> {
|
||||
try {
|
||||
return await this.redisClient.subscriber.xinfo('GROUPS', stream);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error getting consumer groups: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { RedisService } from './redis.service';
|
||||
import { RedisStreamService } from './redis-stream.service';
|
||||
import { RedisProvider } from './redis.provider';
|
||||
|
||||
@Module({
|
||||
providers: [RedisProvider, RedisService],
|
||||
exports: [RedisService], // Экспортируем RedisService для использования в других модулях
|
||||
providers: [RedisProvider, RedisService, RedisStreamService],
|
||||
exports: [RedisService, RedisStreamService], // Экспортируем оба сервиса
|
||||
})
|
||||
export class RedisModule {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cooptypes",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"description": "_description_",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/desktop",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"description": "A Desktop Project",
|
||||
"productName": "Desktop App",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
@@ -68,6 +68,7 @@
|
||||
"qrcode": "^1.5.4",
|
||||
"quasar": "^2.16.0",
|
||||
"serialize-javascript": "^6.0.2",
|
||||
"socket.io": "^4.8.1",
|
||||
"swiper": "^11.1.7",
|
||||
"vite": "2.9.16",
|
||||
"vue": "^3.4.18",
|
||||
|
||||
@@ -11,18 +11,19 @@
|
||||
const { configure } = require('quasar/wrappers');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
module.exports = configure(function (ctx) {
|
||||
|
||||
const isSSR = ctx.mode.ssr;
|
||||
const isDev = ctx.dev;
|
||||
|
||||
// Загружаем переменные окружения всегда в режиме разработки
|
||||
// или только для клиентской части в продакшн
|
||||
const env = (isDev || !isSSR) ? require('dotenv').config().parsed : {
|
||||
CLIENT: process.env.CLIENT,
|
||||
SERVER: process.env.SERVER,
|
||||
};
|
||||
const env =
|
||||
isDev || !isSSR
|
||||
? require('dotenv').config().parsed
|
||||
: {
|
||||
CLIENT: process.env.CLIENT,
|
||||
SERVER: process.env.SERVER,
|
||||
};
|
||||
|
||||
return {
|
||||
htmlVariables: {
|
||||
@@ -39,10 +40,7 @@ module.exports = configure(function (ctx) {
|
||||
// app boot file (/src/boot)
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||
boot: [
|
||||
'init',
|
||||
'i18n', 'axios', 'sentry'
|
||||
],
|
||||
boot: ['init', 'i18n', 'axios', 'sentry'],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||
css: [
|
||||
@@ -118,14 +116,14 @@ module.exports = configure(function (ctx) {
|
||||
eslint: {
|
||||
lintCommand: 'eslint "./**/*.{js,ts,mjs,cjs,vue}"',
|
||||
},
|
||||
overlay: true
|
||||
overlay: true,
|
||||
},
|
||||
{ server: false },
|
||||
],
|
||||
],
|
||||
|
||||
optimizeDeps: {
|
||||
include: ['@dicebear/core', '@dicebear/collection']
|
||||
include: ['@dicebear/core', '@dicebear/collection'],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -135,10 +133,10 @@ module.exports = configure(function (ctx) {
|
||||
vueDevtools: false,
|
||||
open: false, // opens browser window automatically
|
||||
port: 3005,
|
||||
hmr:{
|
||||
hmr: {
|
||||
// clientPort: 3005,
|
||||
// overlay: false
|
||||
}
|
||||
// overlay: false
|
||||
},
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
||||
@@ -204,7 +202,7 @@ module.exports = configure(function (ctx) {
|
||||
],
|
||||
|
||||
// Не обрабатывать эти модули для SSR
|
||||
noExternal: ['@dicebear/core', '@dicebear/collection']
|
||||
noExternal: ['@dicebear/core', '@dicebear/collection'],
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||
import "quasar/dist/types/feature-flag";
|
||||
|
||||
declare module "quasar/dist/types/feature-flag" {
|
||||
interface QuasarFeatureFlags {
|
||||
pwa: true;
|
||||
}
|
||||
}
|
||||
@@ -10,22 +10,22 @@ export default ssrMiddleware(({ app }) => {
|
||||
app.use((req, res, next) => {
|
||||
// Получаем переменные из process.env, которые нужны клиенту
|
||||
const envForClient: EnvVars = {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
BACKEND_URL: process.env.BACKEND_URL,
|
||||
CHAIN_URL: process.env.CHAIN_URL,
|
||||
CHAIN_ID: process.env.CHAIN_ID,
|
||||
CURRENCY: process.env.CURRENCY,
|
||||
COOP_SHORT_NAME: process.env.COOP_SHORT_NAME,
|
||||
SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
|
||||
SITE_IMAGE: process.env.SITE_IMAGE,
|
||||
STORAGE_URL: process.env.STORAGE_URL,
|
||||
UPLOAD_URL: process.env.UPLOAD_URL,
|
||||
NODE_ENV: process.env.NODE_ENV as string,
|
||||
BACKEND_URL: process.env.BACKEND_URL as string,
|
||||
CHAIN_URL: process.env.CHAIN_URL as string,
|
||||
CHAIN_ID: process.env.CHAIN_ID as string,
|
||||
CURRENCY: process.env.CURRENCY as string,
|
||||
COOP_SHORT_NAME: process.env.COOP_SHORT_NAME as string,
|
||||
SITE_DESCRIPTION: process.env.SITE_DESCRIPTION as string,
|
||||
SITE_IMAGE: process.env.SITE_IMAGE as string,
|
||||
STORAGE_URL: process.env.STORAGE_URL as string,
|
||||
UPLOAD_URL: process.env.UPLOAD_URL as string,
|
||||
TIMEZONE: process.env.TIMEZONE || 'Europe/Moscow',
|
||||
VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE,
|
||||
VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE,
|
||||
NOVU_APP_ID: process.env.NOVU_APP_ID,
|
||||
NOVU_BACKEND_URL: process.env.NOVU_BACKEND_URL,
|
||||
NOVU_SOCKET_URL: process.env.NOVU_SOCKET_URL,
|
||||
VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE as string,
|
||||
VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE as string,
|
||||
NOVU_APP_ID: process.env.NOVU_APP_ID as string,
|
||||
NOVU_BACKEND_URL: process.env.NOVU_BACKEND_URL as string,
|
||||
NOVU_SOCKET_URL: process.env.NOVU_SOCKET_URL as string,
|
||||
};
|
||||
|
||||
// Создаем скрипт, который добавит переменные в window.__ENV__
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
// Типы для переменных окружения
|
||||
export interface EnvVars {
|
||||
NODE_ENV?: string;
|
||||
BACKEND_URL?: string;
|
||||
CHAIN_URL?: string;
|
||||
CHAIN_ID?: string;
|
||||
CURRENCY?: string;
|
||||
COOP_SHORT_NAME?: string;
|
||||
SITE_DESCRIPTION?: string;
|
||||
SITE_IMAGE?: string;
|
||||
STORAGE_URL?: string;
|
||||
UPLOAD_URL?: string;
|
||||
TIMEZONE?: string;
|
||||
NODE_ENV: string;
|
||||
BACKEND_URL: string;
|
||||
CHAIN_URL: string;
|
||||
CHAIN_ID: string;
|
||||
CURRENCY: string;
|
||||
COOP_SHORT_NAME: string;
|
||||
SITE_DESCRIPTION: string;
|
||||
SITE_IMAGE: string;
|
||||
STORAGE_URL: string;
|
||||
UPLOAD_URL: string;
|
||||
TIMEZONE: string;
|
||||
CLIENT?: boolean;
|
||||
SERVER?: boolean;
|
||||
VUE_ROUTER_MODE?: string;
|
||||
VUE_ROUTER_BASE?: string;
|
||||
NOVU_APP_ID?: string;
|
||||
NOVU_BACKEND_URL?: string;
|
||||
NOVU_SOCKET_URL?: string;
|
||||
VUE_ROUTER_MODE: string;
|
||||
VUE_ROUTER_BASE: string;
|
||||
NOVU_APP_ID: string;
|
||||
NOVU_BACKEND_URL: string;
|
||||
NOVU_SOCKET_URL: string;
|
||||
[key: string]: string | boolean | undefined;
|
||||
}
|
||||
|
||||
@@ -41,24 +41,24 @@ function getEnv(): EnvVars {
|
||||
// Для SSR сервера - берем реальные переменные
|
||||
// Для SPA - эти значения заменятся при сборке
|
||||
return {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
BACKEND_URL: process.env.BACKEND_URL,
|
||||
CHAIN_URL: process.env.CHAIN_URL,
|
||||
CHAIN_ID: process.env.CHAIN_ID,
|
||||
CURRENCY: process.env.CURRENCY,
|
||||
COOP_SHORT_NAME: process.env.COOP_SHORT_NAME,
|
||||
SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
|
||||
SITE_IMAGE: process.env.SITE_IMAGE,
|
||||
STORAGE_URL: process.env.STORAGE_URL,
|
||||
UPLOAD_URL: process.env.UPLOAD_URL,
|
||||
NODE_ENV: process.env.NODE_ENV as string,
|
||||
BACKEND_URL: process.env.BACKEND_URL as string,
|
||||
CHAIN_URL: process.env.CHAIN_URL as string,
|
||||
CHAIN_ID: process.env.CHAIN_ID as string,
|
||||
CURRENCY: process.env.CURRENCY as string,
|
||||
COOP_SHORT_NAME: process.env.COOP_SHORT_NAME as string,
|
||||
SITE_DESCRIPTION: process.env.SITE_DESCRIPTION as string,
|
||||
SITE_IMAGE: process.env.SITE_IMAGE as string,
|
||||
STORAGE_URL: process.env.STORAGE_URL as string,
|
||||
UPLOAD_URL: process.env.UPLOAD_URL as string,
|
||||
TIMEZONE: process.env.TIMEZONE || 'Europe/Moscow',
|
||||
CLIENT: process.env.CLIENT as unknown as boolean,
|
||||
SERVER: process.env.SERVER as unknown as boolean,
|
||||
VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE,
|
||||
VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE,
|
||||
NOVU_APP_ID: process.env.NOVU_APP_ID,
|
||||
NOVU_BACKEND_URL: process.env.NOVU_BACKEND_URL,
|
||||
NOVU_SOCKET_URL: process.env.NOVU_SOCKET_URL,
|
||||
VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE as string,
|
||||
VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE as string,
|
||||
NOVU_APP_ID: process.env.NOVU_APP_ID as string,
|
||||
NOVU_BACKEND_URL: process.env.NOVU_BACKEND_URL as string,
|
||||
NOVU_SOCKET_URL: process.env.NOVU_SOCKET_URL as string,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+214
-116
@@ -1,130 +1,228 @@
|
||||
<template lang="pug">
|
||||
div(v-if="userData.organization_data").q-gutter-sm.q-mt-md
|
||||
q-select(
|
||||
v-model="userData.organization_data.type"
|
||||
label="Выберите тип организации"
|
||||
standout="bg-teal text-white"
|
||||
:options="[{ label: 'Потребительский Кооператив', value: 'coop' }, { label: 'Производственный Кооператив', value: 'prodcoop' }, { label: 'ООО', value: 'ooo' }]"
|
||||
emit-value
|
||||
map-options).q-mb-md
|
||||
|
||||
q-input(v-model="userData.organization_data.short_name" standout="bg-teal text-white" hint="ПК Ромашка" label="Краткое наименование организации" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.full_name" standout="bg-teal text-white" hint="Потребительский Кооператив 'Ромашка'" label="Полное наименование организации" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.represented_by.last_name" standout="bg-teal text-white" label="Фамилия представителя" hint="" :rules="[val => notEmpty(val), val => validatePersonalName(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.represented_by.first_name" standout="bg-teal text-white" label="Имя представителя" hint="" :rules="[val => notEmpty(val), val => validatePersonalName(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.represented_by.middle_name" standout="bg-teal text-white" label="Отчество представителя" hint="" :rules="[val => validatePersonalName(val)]" autocomplete="off")
|
||||
|
||||
q-input(v-model="userData.organization_data.represented_by.based_on" standout="bg-teal text-white" label="Представитель действует на основании" hint="решения общего собрания №102 от 01.01.2025 г" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.represented_by.position" standout="bg-teal text-white" label="Должность представителя" hint="председатель совета" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
|
||||
q-input(v-model="userData.organization_data.phone" standout="bg-teal text-white" label="Номер телефона представителя" mask="+7 (###) ###-##-##" fill-mask hint="" :rules="[val => notEmpty(val), val => notEmptyPhone(val)]" autocomplete="off")
|
||||
|
||||
q-select(v-model="userData.organization_data.country" standout="bg-teal text-white" map-options emit-value option-label="label" option-value="value" label="Страна" :options="[{ label: 'Россия', value: 'Russia' }]" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.city" standout="bg-teal text-white" label="Город" hint="" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.full_address" standout="bg-teal text-white" hint="" label="Юридический адрес регистрации" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
q-input(v-model="userData.organization_data.fact_address" standout="bg-teal text-white" hint="" label="Фактический адрес" :rules="[val => notEmpty(val)]" autocomplete="off")
|
||||
template(v-slot:append)
|
||||
q-btn(dense size="sm" color="teal" @click="userData.organization_data.fact_address = userData.organization_data.full_address") совпадает
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.details.inn"
|
||||
standout="bg-teal text-white"
|
||||
mask="############"
|
||||
label="ИНН организации"
|
||||
hint="10 или 12 цифр"
|
||||
:rules="[val => notEmpty(val), val => (val.length === 10 || val.length === 12) || 'ИНН должен содержать 10 или 12 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.details.ogrn"
|
||||
standout="bg-teal text-white"
|
||||
mask="###############"
|
||||
label="ОГРН организации"
|
||||
hint="13 или 15 цифр"
|
||||
:rules="[val => notEmpty(val), val => (val.length === 13 || val.length === 15) || 'ОГРН должен содержать 13 или 15 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.details.kpp"
|
||||
standout="bg-teal text-white"
|
||||
mask="#########"
|
||||
label="КПП организации"
|
||||
hint="9 цифр"
|
||||
:rules="[val => notEmpty(val), val => val.length === 9 || 'КПП должен содержать 9 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.bank_account.bank_name"
|
||||
standout="bg-teal text-white"
|
||||
label="Наименование банка"
|
||||
hint="ПАО Сбербанк"
|
||||
:rules="[val => notEmpty(val)]"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.bank_account.details.corr"
|
||||
standout="bg-teal text-white"
|
||||
mask="####################"
|
||||
label="Корреспондентский счет"
|
||||
hint="20 цифр"
|
||||
:rules="[val => notEmpty(val), val => val.length === 20 || 'Корреспондентский счет должен содержать 20 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
q-input(
|
||||
v-model="userData.organization_data.bank_account.details.bik"
|
||||
standout="bg-teal text-white"
|
||||
mask="#########"
|
||||
label="БИК"
|
||||
hint="9 цифр"
|
||||
:rules="[val => notEmpty(val), val => val.length === 9 || 'БИК должен содержать 9 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.bank_account.details.kpp"
|
||||
standout="bg-teal text-white"
|
||||
mask="#########"
|
||||
label="КПП (банка)"
|
||||
hint="9 цифр"
|
||||
:rules="[val => notEmpty(val), val => val.length === 9 || 'КПП должен содержать 9 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model="userData.organization_data.bank_account.account_number"
|
||||
standout="bg-teal text-white"
|
||||
mask="####################"
|
||||
label="Номер счета"
|
||||
hint="20 цифр"
|
||||
:rules="[val => notEmpty(val), val => val.length === 20 || 'Номер счета должен содержать 20 цифр']"
|
||||
autocomplete="off"
|
||||
)
|
||||
|
||||
q-select(
|
||||
v-model="userData.organization_data.bank_account.currency"
|
||||
label="Валюта счёта"
|
||||
standout="bg-teal text-white"
|
||||
:options="[{ label: 'RUB', value: 'RUB' }]"
|
||||
emit-value
|
||||
:rules="[val => notEmpty(val)]"
|
||||
.q-gutter-sm.q-mt-md(v-if='userData.organization_data')
|
||||
q-select.q-mb-md(
|
||||
v-model='userData.organization_data.type',
|
||||
label='Выберите тип организации',
|
||||
standout='bg-teal text-white',
|
||||
:options='[ { label: "Потребительский Кооператив", value: Zeus.OrganizationType.COOP }, { label: "Производственный Кооператив", value: Zeus.OrganizationType.PRODCOOP }, { label: "ООО", value: Zeus.OrganizationType.OOO }, ]',
|
||||
emit-value,
|
||||
map-options
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.short_name',
|
||||
standout='bg-teal text-white',
|
||||
hint='ПК Ромашка',
|
||||
label='Краткое наименование организации',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.full_name',
|
||||
standout='bg-teal text-white',
|
||||
hint='Потребительский Кооператив \'Ромашка\'',
|
||||
label='Полное наименование организации',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.represented_by.last_name',
|
||||
standout='bg-teal text-white',
|
||||
label='Фамилия представителя',
|
||||
hint='',
|
||||
:rules='[(val) => notEmpty(val), (val) => validatePersonalName(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.represented_by.first_name',
|
||||
standout='bg-teal text-white',
|
||||
label='Имя представителя',
|
||||
hint='',
|
||||
:rules='[(val) => notEmpty(val), (val) => validatePersonalName(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.represented_by.middle_name',
|
||||
standout='bg-teal text-white',
|
||||
label='Отчество представителя',
|
||||
hint='',
|
||||
:rules='[(val) => validatePersonalName(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.represented_by.based_on',
|
||||
standout='bg-teal text-white',
|
||||
label='Представитель действует на основании',
|
||||
hint='решения общего собрания №102 от 01.01.2025 г',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.represented_by.position',
|
||||
standout='bg-teal text-white',
|
||||
label='Должность представителя',
|
||||
hint='председатель совета',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.phone',
|
||||
standout='bg-teal text-white',
|
||||
label='Номер телефона представителя',
|
||||
mask='+7 (###) ###-##-##',
|
||||
fill-mask,
|
||||
hint='',
|
||||
:rules='[(val) => notEmpty(val), (val) => notEmptyPhone(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-select(
|
||||
v-model='userData.organization_data.country',
|
||||
standout='bg-teal text-white',
|
||||
map-options,
|
||||
emit-value,
|
||||
option-label='label',
|
||||
option-value='value',
|
||||
label='Страна',
|
||||
:options='[{ label: "Россия", value: "Russia" }]',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.city',
|
||||
standout='bg-teal text-white',
|
||||
label='Город',
|
||||
hint='',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.full_address',
|
||||
standout='bg-teal text-white',
|
||||
hint='',
|
||||
label='Юридический адрес регистрации',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.fact_address',
|
||||
standout='bg-teal text-white',
|
||||
hint='',
|
||||
label='Фактический адрес',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
template(v-slot:append)
|
||||
q-btn(
|
||||
dense,
|
||||
size='sm',
|
||||
color='teal',
|
||||
@click='userData.organization_data.fact_address = userData.organization_data.full_address'
|
||||
) совпадает
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.details.inn',
|
||||
standout='bg-teal text-white',
|
||||
mask='############',
|
||||
label='ИНН организации',
|
||||
hint='10 или 12 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 10 || val.length === 12 || "ИНН должен содержать 10 или 12 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.details.ogrn',
|
||||
standout='bg-teal text-white',
|
||||
mask='###############',
|
||||
label='ОГРН организации',
|
||||
hint='13 или 15 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 13 || val.length === 15 || "ОГРН должен содержать 13 или 15 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.details.kpp',
|
||||
standout='bg-teal text-white',
|
||||
mask='#########',
|
||||
label='КПП организации',
|
||||
hint='9 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 9 || "КПП должен содержать 9 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.bank_account.bank_name',
|
||||
standout='bg-teal text-white',
|
||||
label='Наименование банка',
|
||||
hint='ПАО Сбербанк',
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.bank_account.details.corr',
|
||||
standout='bg-teal text-white',
|
||||
mask='####################',
|
||||
label='Корреспондентский счет',
|
||||
hint='20 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 20 || "Корреспондентский счет должен содержать 20 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
q-input(
|
||||
v-model='userData.organization_data.bank_account.details.bik',
|
||||
standout='bg-teal text-white',
|
||||
mask='#########',
|
||||
label='БИК',
|
||||
hint='9 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 9 || "БИК должен содержать 9 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.bank_account.details.kpp',
|
||||
standout='bg-teal text-white',
|
||||
mask='#########',
|
||||
label='КПП (банка)',
|
||||
hint='9 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 9 || "КПП должен содержать 9 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model='userData.organization_data.bank_account.account_number',
|
||||
standout='bg-teal text-white',
|
||||
mask='####################',
|
||||
label='Номер счета',
|
||||
hint='20 цифр',
|
||||
:rules='[(val) => notEmpty(val), (val) => val.length === 20 || "Номер счета должен содержать 20 цифр"]',
|
||||
autocomplete='off'
|
||||
)
|
||||
|
||||
q-select(
|
||||
v-model='userData.organization_data.bank_account.currency',
|
||||
label='Валюта счёта',
|
||||
standout='bg-teal text-white',
|
||||
:options='[{ label: "RUB", value: "RUB" }]',
|
||||
emit-value,
|
||||
:rules='[(val) => notEmpty(val)]',
|
||||
map-options
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { validatePersonalName, notEmpty, notEmptyPhone } from 'src/shared/lib/utils';
|
||||
import {
|
||||
validatePersonalName,
|
||||
notEmpty,
|
||||
notEmptyPhone,
|
||||
} from 'src/shared/lib/utils';
|
||||
|
||||
import type { IUserData } from 'src/shared/lib/types/user/IUserData';
|
||||
import { ref } from 'vue';
|
||||
import { Zeus } from '@coopenomics/sdk';
|
||||
|
||||
const props = defineProps<{ userData: IUserData }>();
|
||||
|
||||
const userData = ref<IUserData>(props.userData)
|
||||
|
||||
const userData = ref<IUserData>(props.userData);
|
||||
</script>
|
||||
|
||||
@@ -10,7 +10,15 @@ q-header.header(bordered, :class='headerClass')
|
||||
@click='emitToggleLeftDrawer'
|
||||
)
|
||||
BackButton(v-if='loggedIn')
|
||||
|
||||
q-toolbar-title
|
||||
q-btn(
|
||||
v-if='!loggedIn && coopTitle',
|
||||
stretch,
|
||||
flat,
|
||||
@click='emitToggleLeftDrawer',
|
||||
:size='isMobile ? "sm" : "lg"'
|
||||
) {{ coopTitle }}
|
||||
|
||||
// Добавляем компонент уведомлений, если пользователь авторизован
|
||||
NotificationCenter(v-if='loggedIn && isClient')
|
||||
@@ -56,7 +64,6 @@ import { BackButton } from 'src/widgets/Header/BackButton';
|
||||
import { NotificationCenter } from 'src/widgets/NotificationCenter';
|
||||
import './HeaderStyles.scss';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
// import { env } from 'src/shared/config'
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
@@ -66,12 +73,11 @@ const session = useSessionStore();
|
||||
const { isMobile } = useWindowSize();
|
||||
const emit = defineEmits(['toggle-left-drawer']);
|
||||
|
||||
const isClient = computed(
|
||||
() => typeof process !== 'undefined' && process.env.CLIENT,
|
||||
);
|
||||
|
||||
// Получаем информацию для навигации назад
|
||||
// const coopTitle = computed(() => env.COOP_SHORT_NAME)
|
||||
const coopTitle = computed(() => info.vars?.name);
|
||||
|
||||
const isClient = computed(() => process.env.CLIENT);
|
||||
|
||||
const isDark = computed(() => $q.dark.isActive);
|
||||
const headerClass = computed(() =>
|
||||
|
||||
@@ -1,35 +1,44 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
</template>
|
||||
<!-- NotificationCenter.vue
|
||||
<template lang="pug">
|
||||
div#notification-inbox.notification-container
|
||||
#notification-inbox.notification-container
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { NovuUI } from '@novu/js/ui'
|
||||
import { dark } from '@novu/js/themes'
|
||||
import { useCurrentUser } from 'src/entities/Session'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { env } from 'src/shared/config'
|
||||
const currentUser = useCurrentUser()
|
||||
import { useCurrentUser } from 'src/entities/Session';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { env } from 'src/shared/config';
|
||||
import { computed, onMounted, watch } from 'vue';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
|
||||
const $q = useQuasar()
|
||||
const isDark = computed(() => $q.dark.isActive)
|
||||
let novu: any = null
|
||||
const currentUser = useCurrentUser();
|
||||
const $q = useQuasar();
|
||||
const isDark = computed(() => $q.dark.isActive);
|
||||
const { info } = useSystemStore();
|
||||
|
||||
function mountNovu() {
|
||||
const subscriberId = currentUser.username || '68481006a874d6592b28c530'
|
||||
const el = document.getElementById('notification-inbox')
|
||||
let novu: any = null;
|
||||
|
||||
if (el) el.innerHTML = ''
|
||||
async function mountNovu() {
|
||||
// Динамический импорт только на клиенте
|
||||
const { NovuUI } = await import('@novu/js/ui');
|
||||
const { dark } = await import('@novu/js/themes');
|
||||
|
||||
const subscriberId = `${info.coopname}-${currentUser.username}`;
|
||||
const el = document.getElementById('notification-inbox');
|
||||
console.log('novuOptions, ', {
|
||||
applicationIdentifier: env.NOVU_APP_ID,
|
||||
subscriberId,
|
||||
// Теперь используем наш бэкенд в качестве прокси
|
||||
backendUrl: `${env.BACKEND_URL}/notifications`,
|
||||
socketUrl: `${env.BACKEND_URL}/notifications`,
|
||||
});
|
||||
|
||||
if (el) el.innerHTML = '';
|
||||
novu = new NovuUI({
|
||||
options: {
|
||||
applicationIdentifier: env.NOVU_APP_ID,
|
||||
subscriberId,
|
||||
backendUrl: env.NOVU_BACKEND_URL,
|
||||
socketUrl: env.NOVU_SOCKET_URL,
|
||||
// Используем наш бэкенд в качестве прокси
|
||||
backendUrl: `${env.BACKEND_URL}/notifications`,
|
||||
socketUrl: `${env.BACKEND_URL}/notifications`,
|
||||
},
|
||||
appearance: {
|
||||
baseTheme: isDark.value ? dark : undefined,
|
||||
@@ -45,7 +54,8 @@ function mountNovu() {
|
||||
'notifications.actions.readAll': 'Прочитать все',
|
||||
'notifications.actions.archiveAll': 'Архивировать все',
|
||||
'notifications.actions.archiveRead': 'Архивировать прочитанные',
|
||||
'notifications.newNotifications': '{{notificationCount}} новое уведомление(ий)',
|
||||
'notifications.newNotifications':
|
||||
'{{notificationCount}} новое уведомление(ий)',
|
||||
'notification.actions.read.tooltip': 'Отметить как прочитанное',
|
||||
'notification.actions.unread.tooltip': 'Отметить как непрочитанное',
|
||||
'notification.actions.archive.tooltip': 'В архив',
|
||||
@@ -53,28 +63,32 @@ function mountNovu() {
|
||||
'preferences.title': 'Настройки',
|
||||
'preferences.emptyNotice': 'Пока нет настроек уведомлений.',
|
||||
'preferences.global': 'Общие настройки',
|
||||
'preferences.workflow.disabled.notice': 'Обратитесь к администратору для управления подпиской на это важное уведомление.',
|
||||
'preferences.workflow.disabled.tooltip': 'Для изменения обратитесь к администратору',
|
||||
'locale': 'ru-RU',
|
||||
'dynamic': {
|
||||
'comment-on-post': 'Комментарии к записи'
|
||||
}
|
||||
}
|
||||
})
|
||||
'preferences.workflow.disabled.notice':
|
||||
'Обратитесь к администратору для управления подпиской на это важное уведомление.',
|
||||
'preferences.workflow.disabled.tooltip':
|
||||
'Для изменения обратитесь к администратору',
|
||||
locale: 'ru-RU',
|
||||
dynamic: {
|
||||
'comment-on-post': 'Комментарии к записи',
|
||||
},
|
||||
},
|
||||
});
|
||||
novu.mountComponent({
|
||||
name: 'Inbox',
|
||||
props: {},
|
||||
element: el as HTMLDivElement,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
mountNovu()
|
||||
})
|
||||
if (process.env.CLIENT) {
|
||||
mountNovu();
|
||||
}
|
||||
});
|
||||
|
||||
watch(isDark, () => {
|
||||
mountNovu()
|
||||
})
|
||||
mountNovu();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -89,4 +103,4 @@ watch(isDark, () => {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
</style> -->
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@coopenomics/docs",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.9.0",
|
||||
"description": "Документация для монорепозитория",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@coopenomics/factory",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
"author": "Alex Ant <chairman.voskhod@gmail.com>",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "migrator",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coop-notificator",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.1.1",
|
||||
"description": "_description_",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pkg-placeholder",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pkg-placeholder",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.16.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@coopenomics/parser",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": false,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@coopenomics/sdk",
|
||||
"type": "module",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": false,
|
||||
"packageManager": "pnpm@9.9.0",
|
||||
"description": "",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/setup",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "2025.7.1",
|
||||
"version": "2025.7.2",
|
||||
"packages": [
|
||||
"components/*",
|
||||
"components/controller/src/plugins/*"
|
||||
|
||||
Generated
+1
@@ -10214,6 +10214,7 @@ packages:
|
||||
|
||||
lodash.get@4.4.2:
|
||||
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
||||
deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
|
||||
|
||||
lodash.includes@4.3.0:
|
||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||
|
||||
+59
-24
@@ -1,7 +1,7 @@
|
||||
Сгенерируй changelog для версии v2025.6.14.
|
||||
Сгенерируй changelog для версии v2025.7.1-1.
|
||||
|
||||
Правила:
|
||||
- Начинай changelog заголовком: "# v2025.6.14"
|
||||
- Начинай changelog заголовком: "# v2025.7.1-1"
|
||||
- Затем добавь краткое текстовое описание релиза (1–3 строки), основанное на комментарии разработчика. Не копируй комментарий дословно. Используй его только как ориентир для формулировки описания.
|
||||
- Не добавляй ничего от себя, не используй мотивационные или маркетинговые фразы.
|
||||
- После описания сгруппируй изменения по категориям:
|
||||
@@ -14,10 +14,65 @@
|
||||
- В конце поставь тег: #releases
|
||||
|
||||
Комментарий разработчика для описания релиза:
|
||||
Реализован модуль проведения очередных общих собраний пайщиков.
|
||||
Релиз содержит изменения, реализующие функциональность возврата паевого взноса пайщика из цифрового кошелька и методы управления этим процессом для членов совета. Также, релиз содержит визуальные изменения в интерфейсы, направленные на улучшение визуального восприятия информации.
|
||||
|
||||
Ниже список закрытых issues, начиная с 2025-05-14:
|
||||
Ниже список закрытых issues, начиная с 2025-05-22T10:00:00+03:00:
|
||||
|
||||
#286: Поправить ошибки склонений времени
|
||||
|
||||
|
||||
|
||||
---
|
||||
#285: Корректировка дизайна кошелька, профиля, повестки совета, реестра документов, реестра платежей
|
||||
|
||||
|
||||
|
||||
---
|
||||
#284: Разместить кошелек на главную вместо профиля
|
||||
|
||||
|
||||
|
||||
---
|
||||
#283: Ввести лоадер на переходе между рабочими столами
|
||||
|
||||
|
||||
|
||||
---
|
||||
#282: Генерация решения совета на возврат паевого взноса
|
||||
|
||||
|
||||
|
||||
---
|
||||
#281: Генерация заявления на возврат паевого взноса
|
||||
|
||||
|
||||
|
||||
---
|
||||
#280: Мигрировать имеющиеся данные о входящих платежах в новую модель
|
||||
|
||||
|
||||
|
||||
---
|
||||
#279: Отобразить исходящие платежи с кнопками управления в реестре платежей для совета
|
||||
|
||||
|
||||
|
||||
---
|
||||
#278: Введение методов управления возвратами паевых взносов в контроллере
|
||||
|
||||
|
||||
|
||||
---
|
||||
#277: Рефакторинг модуля платежей: переход на унифицированную модель входящего и исходящего платежа
|
||||
|
||||
|
||||
|
||||
---
|
||||
#276: Реализовать путь возврата паевого взноса из кошелька пайщика
|
||||
|
||||
|
||||
|
||||
---
|
||||
#274: Настроить рассылку оповещений на почту при получении решения о проведении собрания
|
||||
|
||||
|
||||
@@ -92,24 +147,4 @@
|
||||
|
||||
|
||||
|
||||
---
|
||||
#259: Поправить отступы в мобильной карточке пайщика на странице пайщиков
|
||||
|
||||
|
||||
|
||||
---
|
||||
#258: Убрать hover-эффект при наведении на документ или пайщика в таблице
|
||||
|
||||
|
||||
|
||||
---
|
||||
#256: Отобразить статус членства каждого пайщика в разделе Пайщики
|
||||
|
||||
|
||||
|
||||
---
|
||||
#255: Отобразить ФИО/Наименование плательщика в разделе Платежи
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
# v2025.6.14
|
||||
# v2025.7.1-1
|
||||
|
||||
Разработан модуль для проведения очередных общих собраний пайщиков. Исправлены баги, внесены улучшения пользовательского интерфейса.
|
||||
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
|
||||
|
||||
✨ Новые функции
|
||||
- [#264](https://github.com/coopenomics/mono/issues/264): Разработан смарт-контракт общего собрания пайщиков (meet)
|
||||
- [#263](https://github.com/coopenomics/mono/issues/263): Реализован модуль оповещений на электронные почты по жизненному циклу общего собрания пайщиков
|
||||
- [#273](https://github.com/coopenomics/mono/issues/273): Встроены реальные шаблоны документов общего собрания
|
||||
- [#268](https://github.com/coopenomics/mono/issues/268): Добавлена страница результатов общего собрания
|
||||
- [#267](https://github.com/coopenomics/mono/issues/267): Добавлена страница просмотра и скачивания бюллетеней и уведомлений по собранию
|
||||
- [#270](https://github.com/coopenomics/mono/issues/270): Ссылка в оповещении ведет на страницу собрания с документом-уведомлением для подписи
|
||||
- [#276](https://github.com/coopenomics/mono/issues/276): Реализовать путь возврата паевого взноса из кошелька пайщика
|
||||
- [#281](https://github.com/coopenomics/mono/issues/281): Генерация заявления на возврат паевого взноса
|
||||
- [#282](https://github.com/coopenomics/mono/issues/282): Генерация решения совета на возврат паевого взноса
|
||||
- [#278](https://github.com/coopenomics/mono/issues/278): Введение методов управления возвратами паевых взносов в контроллере
|
||||
- [#279](https://github.com/coopenomics/mono/issues/279): Отобразить исходящие платежи с кнопками управления в реестре платежей для совета
|
||||
|
||||
🐛 Исправления ошибок
|
||||
- [#262](https://github.com/coopenomics/mono/issues/262): Исправлено некорректное открытие рабочего стола совета
|
||||
- [#261](https://github.com/coopenomics/mono/issues/261): Исправлена ошибка с редиректом при первой загрузке и прямом переходе на собрание
|
||||
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика
|
||||
- [#258](https://github.com/coopenomics/mono/issues/258): Убран hover-эффект на документе и пайщике в таблице
|
||||
- [#286](https://github.com/coopenomics/mono/issues/286): Поправить ошибки склонений времени
|
||||
- [#262](https://github.com/coopenomics/mono/issues/262): Баг: рабочий стол совета включается, однако, страница всегда открывается со стола пайщика
|
||||
- [#261](https://github.com/coopenomics/mono/issues/261): Баг: первая загрузка переадресует на главную страницу всегда - прямой переход на собрание становится недоступен.
|
||||
|
||||
🔧 Улучшения
|
||||
- [#274](https://github.com/coopenomics/mono/issues/274): Настроена рассылка оповещений на почту при получении решения о проведении собрания
|
||||
- [#272](https://github.com/coopenomics/mono/issues/272): Подписанные уведомления сохраняются и извлекаются из реестра по Graph-QL
|
||||
- [#271](https://github.com/coopenomics/mono/issues/271): Ведется подсчет количества пайщиков в каждом кооперативе при добавлении и удалении
|
||||
- [#260](https://github.com/coopenomics/mono/issues/260): Введен счетчик количества пайщиков в кооперативе на контракте регистратора
|
||||
- [#256](https://github.com/coopenomics/mono/issues/256): Отображается статус членства каждого пайщика
|
||||
- [#255](https://github.com/coopenomics/mono/issues/255): В разделе Платежи отображается ФИО/Наименование плательщика
|
||||
- [#269](https://github.com/coopenomics/mono/issues/269): Реализована рассылка уведомлений перед началом собрания
|
||||
- [#265](https://github.com/coopenomics/mono/issues/265): Проведена отладка и тестирование процесса общего собрания пайщиков
|
||||
- [#266](https://github.com/coopenomics/mono/issues/266): Протестированы все процессы общего собрания
|
||||
- [#285](https://github.com/coopenomics/mono/issues/285): Корректировка дизайна кошелька, профиля, повестки совета, реестра документов, реестра платежей
|
||||
- [#284](https://github.com/coopenomics/mono/issues/284): Разместить кошелек на главную вместо профиля
|
||||
- [#283](https://github.com/coopenomics/mono/issues/283): Ввести лоадер на переходе между рабочими столами
|
||||
- [#280](https://github.com/coopenomics/mono/issues/280): Мигрировать имеющиеся данные о входящих платежах в новую модель
|
||||
- [#277](https://github.com/coopenomics/mono/issues/277): Рефакторинг модуля платежей: переход на унифицированную модель входящего и исходящего платежа
|
||||
|
||||
#releases
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION: v2025.6.14
|
||||
FROM DATE: 2025-05-14T10:00:00+03:00
|
||||
VERSION: v2025.7.1-1
|
||||
FROM DATE: 2025-05-22T10:00:00+03:00
|
||||
COMMENT:
|
||||
Реализован модуль проведения очередных общих собраний пайщиков.
|
||||
Релиз содержит изменения, реализующие функциональность возврата паевого взноса пайщика из цифрового кошелька и методы управления этим процессом для членов совета. Также, релиз содержит визуальные изменения в интерфейсы, направленные на улучшение визуального восприятия информации.
|
||||
|
||||
Reference in New Issue
Block a user