Compare commits
No commits in common. "4e7cc9b3bd92a6b550fbf18254f3d2d15ccb3496" and "e28f6a38871238fd4e8201753c9c6da15c8bbb54" have entirely different histories.
4e7cc9b3bd
...
e28f6a3887
@ -920,6 +920,7 @@ def upgrade() -> None:
|
|||||||
sa.Column('vsp_type', sa.String(100)),
|
sa.Column('vsp_type', sa.String(100)),
|
||||||
sa.Column('notes', sa.Text()),
|
sa.Column('notes', sa.Text()),
|
||||||
sa.Column('location_form', sa.String()),
|
sa.Column('location_form', sa.String()),
|
||||||
|
sa.Column('numbers', sa.Integer()),
|
||||||
sa.Column('rent_contract_num', sa.String()),
|
sa.Column('rent_contract_num', sa.String()),
|
||||||
sa.Column('rent_end_date', sa.Date()),
|
sa.Column('rent_end_date', sa.Date()),
|
||||||
schema='v3',
|
schema='v3',
|
||||||
|
|||||||
@ -1123,9 +1123,9 @@ END;
|
|||||||
$function$
|
$function$
|
||||||
;
|
;
|
||||||
|
|
||||||
-- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, date);
|
-- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, int4, varchar, date);
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date)
|
CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_numbers integer DEFAULT NULL::integer, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date)
|
||||||
RETURNS integer
|
RETURNS integer
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $function$
|
AS $function$
|
||||||
@ -1153,12 +1153,14 @@ BEGIN
|
|||||||
(branch_id, reg_number, address, format, opened_at,
|
(branch_id, reg_number, address, format, opened_at,
|
||||||
placement_type, staff_count, total_area, closed_at,
|
placement_type, staff_count, total_area, closed_at,
|
||||||
is_active, is_deleted, system_code, created_by,
|
is_active, is_deleted, system_code, created_by,
|
||||||
vsp_type, notes, rent_contract_num, rent_end_date)
|
vsp_type, notes, location_form, numbers,
|
||||||
|
rent_contract_num, rent_end_date)
|
||||||
VALUES
|
VALUES
|
||||||
(p_branch_id, p_reg_number, p_address, p_format, p_opened_at,
|
(p_branch_id, p_reg_number, p_address, p_format, p_opened_at,
|
||||||
p_placement_type, p_staff_count, p_total_area, p_closed_at,
|
p_placement_type, p_staff_count, p_total_area, p_closed_at,
|
||||||
p_is_active, p_is_deleted, p_system_code, p_created_by,
|
p_is_active, p_is_deleted, p_system_code, p_created_by,
|
||||||
p_vsp_type, p_notes, p_rent_contract_num, p_rent_end_date)
|
p_vsp_type, p_notes, p_location_form, p_numbers,
|
||||||
|
p_rent_contract_num, p_rent_end_date)
|
||||||
RETURNING id INTO v_id;
|
RETURNING id INTO v_id;
|
||||||
|
|
||||||
PERFORM v3.log_event(
|
PERFORM v3.log_event(
|
||||||
@ -1182,6 +1184,8 @@ BEGIN
|
|||||||
'created_by', p_created_by,
|
'created_by', p_created_by,
|
||||||
'vsp_type', p_vsp_type,
|
'vsp_type', p_vsp_type,
|
||||||
'notes', p_notes,
|
'notes', p_notes,
|
||||||
|
'location_form', p_location_form,
|
||||||
|
'numbers', p_numbers,
|
||||||
'rent_contract_num', p_rent_contract_num,
|
'rent_contract_num', p_rent_contract_num,
|
||||||
'rent_end_date', v3._diff_val(p_rent_end_date),
|
'rent_end_date', v3._diff_val(p_rent_end_date),
|
||||||
'user_email', v_actor_email,
|
'user_email', v_actor_email,
|
||||||
@ -1570,7 +1574,7 @@ AS $function$
|
|||||||
DECLARE
|
DECLARE
|
||||||
v_report_id INT;
|
v_report_id INT;
|
||||||
v_eid INT;
|
v_eid INT;
|
||||||
v_form_id INT;
|
v_org_id INT;
|
||||||
v_ei_path INT[];
|
v_ei_path INT[];
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Извлекаем report_id + ei ДО DELETE (нужен для path)
|
-- Извлекаем report_id + ei ДО DELETE (нужен для path)
|
||||||
@ -1584,33 +1588,21 @@ BEGIN
|
|||||||
|
|
||||||
v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]);
|
v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]);
|
||||||
|
|
||||||
SELECT bf.id
|
|
||||||
INTO v_form_id
|
|
||||||
FROM v3.rf_project_report r
|
|
||||||
JOIN v3.project p
|
|
||||||
ON p.id = r.project_id
|
|
||||||
LEFT JOIN v3.budget_form bf
|
|
||||||
ON bf.form_type_code = 'FORM_3'
|
|
||||||
AND bf.year = r.year
|
|
||||||
AND bf.org_unit_id = p.org_unit_id
|
|
||||||
WHERE r.id = v_report_id
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
-- Каскад (FK без ON DELETE CASCADE)
|
-- Каскад (FK без ON DELETE CASCADE)
|
||||||
DELETE FROM v3.rf_project_report_quarter WHERE rf_project_report_line_id = p_line_id;
|
DELETE FROM v3.rf_project_report_quarter WHERE rf_project_report_line_id = p_line_id;
|
||||||
DELETE FROM v3.rf_project_report_line WHERE id = p_line_id;
|
DELETE FROM v3.rf_project_report_line WHERE id = p_line_id;
|
||||||
|
|
||||||
|
SELECT org_unit_id INTO v_org_id FROM v3.project WHERE id = v_report_id;
|
||||||
-- Аудит: ROW_DELETE
|
-- Аудит: ROW_DELETE
|
||||||
PERFORM v3.log_event(
|
PERFORM v3.log_event(
|
||||||
'ROW_DELETE', 'ROW',
|
'ROW_DELETE', 'ROW',
|
||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
'sheet', 'FORM_3',
|
'sheet', 'FORM_3',
|
||||||
'line_id', p_line_id,
|
'line_id', p_line_id,
|
||||||
'form_id', v_form_id,
|
'entity_id', p_line_id,
|
||||||
'report_id', v_report_id,
|
'report_id', v_report_id,
|
||||||
'expense_item_id', v_eid
|
'expense_item_id', v_eid
|
||||||
),
|
), null, null, v_org_id
|
||||||
v_form_id
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Возврат: только иерархия по пути удалённой (INPUT уже нет)
|
-- Возврат: только иерархия по пути удалённой (INPUT уже нет)
|
||||||
@ -2746,9 +2738,9 @@ END;
|
|||||||
$function$
|
$function$
|
||||||
;
|
;
|
||||||
|
|
||||||
-- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, date);
|
-- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, int4, varchar, date);
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date)
|
CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_numbers integer DEFAULT NULL::integer, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date)
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $function$
|
AS $function$
|
||||||
@ -2766,7 +2758,8 @@ BEGIN
|
|||||||
SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at,
|
SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at,
|
||||||
v.placement_type, v.staff_count, v.total_area, v.closed_at,
|
v.placement_type, v.staff_count, v.total_area, v.closed_at,
|
||||||
v.is_active, v.is_deleted, v.system_code,
|
v.is_active, v.is_deleted, v.system_code,
|
||||||
v.vsp_type, v.notes, v.rent_contract_num, v.rent_end_date
|
v.vsp_type, v.notes, v.location_form, v.numbers,
|
||||||
|
v.rent_contract_num, v.rent_end_date
|
||||||
INTO v_old
|
INTO v_old
|
||||||
FROM v3.vsp v
|
FROM v3.vsp v
|
||||||
WHERE v.id = p_vsp_id;
|
WHERE v.id = p_vsp_id;
|
||||||
@ -2899,6 +2892,20 @@ BEGIN
|
|||||||
'after', v3._diff_val(p_notes)));
|
'after', v3._diff_val(p_notes)));
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
IF p_location_form IS NOT NULL AND p_location_form IS DISTINCT FROM v_old.location_form THEN
|
||||||
|
UPDATE v3.vsp SET location_form = p_location_form WHERE id = p_vsp_id;
|
||||||
|
v_changes := v_changes || jsonb_build_object('location_form',
|
||||||
|
jsonb_build_object('before', v3._diff_val(v_old.location_form),
|
||||||
|
'after', v3._diff_val(p_location_form)));
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF p_numbers IS NOT NULL AND p_numbers IS DISTINCT FROM v_old.numbers THEN
|
||||||
|
UPDATE v3.vsp SET numbers = p_numbers WHERE id = p_vsp_id;
|
||||||
|
v_changes := v_changes || jsonb_build_object('numbers',
|
||||||
|
jsonb_build_object('before', v3._diff_val(v_old.numbers),
|
||||||
|
'after', v3._diff_val(p_numbers)));
|
||||||
|
END IF;
|
||||||
|
|
||||||
IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN
|
IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN
|
||||||
UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id;
|
UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id;
|
||||||
v_changes := v_changes || jsonb_build_object('rent_contract_num',
|
v_changes := v_changes || jsonb_build_object('rent_contract_num',
|
||||||
@ -4078,12 +4085,26 @@ $function$
|
|||||||
|
|
||||||
-- DROP FUNCTION v3.v_form1_smeta(int4, int4);
|
-- DROP FUNCTION v3.v_form1_smeta(int4, int4);
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION v3.v_form1_smeta(p_year integer, p_org_unit_id integer)
|
|
||||||
RETURNS TABLE(row_type character varying, depth integer, section_code character varying, name character varying, supp_plan_q1 numeric, supp_plan_q2 numeric, supp_plan_q3 numeric, supp_plan_q4 numeric, supp_plan_year numeric, dev_plan_q1 numeric, dev_plan_q2 numeric, dev_plan_q3 numeric, dev_plan_q4 numeric, dev_plan_year numeric, total_plan_year numeric, supp_appr_q1 numeric, supp_appr_q2 numeric, supp_appr_q3 numeric, supp_appr_q4 numeric, supp_appr_year numeric, dev_appr_q1 numeric, dev_appr_q2 numeric, dev_appr_q3 numeric, dev_appr_q4 numeric, dev_appr_year numeric, total_appr_year numeric, supp_act_q1 numeric, supp_act_q2 numeric, supp_act_q3 numeric, supp_act_q4 numeric, supp_act_year numeric, dev_act_q1 numeric, dev_act_q2 numeric, dev_act_q3 numeric, dev_act_q4 numeric, dev_act_year numeric, total_act_year numeric, supp_corr_q2 numeric, supp_corr_q3 numeric, supp_corr_q4 numeric, dev_corr_q2 numeric, dev_corr_q3 numeric, dev_corr_q4 numeric)
|
CREATE FUNCTION v3.v_form1_smeta(p_year INT, p_org_unit_id INT)
|
||||||
LANGUAGE plpgsql
|
RETURNS TABLE (
|
||||||
STABLE
|
row_type VARCHAR,
|
||||||
SET search_path TO 'v3', 'pg_catalog'
|
depth INT,
|
||||||
AS $function$
|
section_code VARCHAR,
|
||||||
|
name VARCHAR,
|
||||||
|
supp_plan_q1 NUMERIC, supp_plan_q2 NUMERIC, supp_plan_q3 NUMERIC, supp_plan_q4 NUMERIC, supp_plan_year NUMERIC,
|
||||||
|
dev_plan_q1 NUMERIC, dev_plan_q2 NUMERIC, dev_plan_q3 NUMERIC, dev_plan_q4 NUMERIC, dev_plan_year NUMERIC,
|
||||||
|
total_plan_year NUMERIC,
|
||||||
|
supp_appr_q1 NUMERIC, supp_appr_q2 NUMERIC, supp_appr_q3 NUMERIC, supp_appr_q4 NUMERIC, supp_appr_year NUMERIC,
|
||||||
|
dev_appr_q1 NUMERIC, dev_appr_q2 NUMERIC, dev_appr_q3 NUMERIC, dev_appr_q4 NUMERIC, dev_appr_year NUMERIC,
|
||||||
|
total_appr_year NUMERIC,
|
||||||
|
supp_act_q1 NUMERIC, supp_act_q2 NUMERIC, supp_act_q3 NUMERIC, supp_act_q4 NUMERIC, supp_act_year NUMERIC,
|
||||||
|
dev_act_q1 NUMERIC, dev_act_q2 NUMERIC, dev_act_q3 NUMERIC, dev_act_q4 NUMERIC, dev_act_year NUMERIC,
|
||||||
|
total_act_year NUMERIC,
|
||||||
|
supp_corr_q2 NUMERIC, supp_corr_q3 NUMERIC, supp_corr_q4 NUMERIC,
|
||||||
|
dev_corr_q2 NUMERIC, dev_corr_q3 NUMERIC, dev_corr_q4 NUMERIC
|
||||||
|
)
|
||||||
|
AS $$
|
||||||
#variable_conflict use_column
|
#variable_conflict use_column
|
||||||
DECLARE
|
DECLARE
|
||||||
v_ahr INT; v_cap INT; v_oper INT;
|
v_ahr INT; v_cap INT; v_oper INT;
|
||||||
@ -4213,8 +4234,8 @@ BEGIN
|
|||||||
FROM merged m
|
FROM merged m
|
||||||
ORDER BY m.sc;
|
ORDER BY m.sc;
|
||||||
END;
|
END;
|
||||||
$function$
|
$$ LANGUAGE plpgsql STABLE SET search_path = v3, pg_catalog;
|
||||||
;
|
|
||||||
-- DROP FUNCTION v3.v_form2_sheet_sections(int4, varchar, _text);
|
-- DROP FUNCTION v3.v_form2_sheet_sections(int4, varchar, _text);
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION v3.v_form2_sheet_sections(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[])
|
CREATE OR REPLACE FUNCTION v3.v_form2_sheet_sections(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[])
|
||||||
|
|||||||
@ -14,4 +14,3 @@ aiosqlite==0.20.0
|
|||||||
asyncpg==0.30.0
|
asyncpg==0.30.0
|
||||||
pytest==8.3.2
|
pytest==8.3.2
|
||||||
pytest-asyncio==0.24.0
|
pytest-asyncio==0.24.0
|
||||||
openpyxl==3.1.5
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ from src.core.errors import AccessDeniedException
|
|||||||
from src.api.v1.deps import get_current_active_user, require_admin
|
from src.api.v1.deps import get_current_active_user, require_admin
|
||||||
from src.db.models.app_user import AppUser
|
from src.db.models.app_user import AppUser
|
||||||
from src.db.session import get_db
|
from src.db.session import get_db
|
||||||
from src.domain.schemas import BaseListResponse, BaseSingleResponse, OrgUnitCreateSchema, OrgUnitListSchema, OrgUnitSchema, OrgUnitUpdateSchema, ResponseBase
|
from src.domain.schemas import BaseListResponse, BaseSingleResponse, OrgUnitBaseSchema, OrgUnitListSchema, OrgUnitSchema, OrgUnitUpdateSchema, ResponseBase
|
||||||
from src.services.org_unit_service import OrgUnitService
|
from src.services.org_unit_service import OrgUnitService
|
||||||
|
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ async def org_unit_id(
|
|||||||
|
|
||||||
@router.post("/")
|
@router.post("/")
|
||||||
async def create_ssp(
|
async def create_ssp(
|
||||||
org_data: OrgUnitCreateSchema,
|
org_data: OrgUnitBaseSchema,
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
current_user: AppUser = Depends(require_admin),
|
current_user: AppUser = Depends(require_admin),
|
||||||
) -> BaseSingleResponse[OrgUnitSchema]:
|
) -> BaseSingleResponse[OrgUnitSchema]:
|
||||||
|
|||||||
@ -27,9 +27,9 @@ async def get_vsp(
|
|||||||
ssp_id: int | None = None,
|
ssp_id: int | None = None,
|
||||||
open_date_start: date | None = None,
|
open_date_start: date | None = None,
|
||||||
open_date_end: date | None = None,
|
open_date_end: date | None = None,
|
||||||
placement_type: str | None = None,
|
location_form: str | None = None,
|
||||||
staff_count_min: int | None = None,
|
numbers_min: int | None = None,
|
||||||
staff_count_max: int | None = None,
|
numbers_max: int | None = None,
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
current_user: AppUser = Depends(require_executor),
|
current_user: AppUser = Depends(require_executor),
|
||||||
):
|
):
|
||||||
@ -42,9 +42,9 @@ async def get_vsp(
|
|||||||
ssp_id=ssp_id,
|
ssp_id=ssp_id,
|
||||||
open_date_start=open_date_start,
|
open_date_start=open_date_start,
|
||||||
open_date_end=open_date_end,
|
open_date_end=open_date_end,
|
||||||
placement_type=placement_type,
|
location_form=location_form,
|
||||||
staff_count_min=staff_count_min,
|
numbers_min=numbers_min,
|
||||||
staff_count_max=staff_count_max,
|
numbers_max=numbers_max,
|
||||||
load_org_unit=True,
|
load_org_unit=True,
|
||||||
with_count=True,
|
with_count=True,
|
||||||
)
|
)
|
||||||
@ -89,9 +89,9 @@ async def export_info(
|
|||||||
ssp_ids=body.ssp_ids,
|
ssp_ids=body.ssp_ids,
|
||||||
open_date_start=body.open_date_start,
|
open_date_start=body.open_date_start,
|
||||||
open_date_end=body.open_date_end,
|
open_date_end=body.open_date_end,
|
||||||
placement_type=body.placement_type,
|
location_form=body.location_form,
|
||||||
staff_count_min=body.staff_count_min,
|
numbers_min=body.numbers_min,
|
||||||
staff_count_max=body.staff_count_max,
|
numbers_max=body.numbers_max,
|
||||||
)
|
)
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
stream,
|
stream,
|
||||||
|
|||||||
@ -45,6 +45,8 @@ class Vsp(Base):
|
|||||||
)
|
)
|
||||||
vsp_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
vsp_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||||
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
location_form: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||||
|
numbers: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||||
rent_contract_num: Mapped[str | None] = mapped_column(String, nullable=True)
|
rent_contract_num: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||||
rent_end_date : Mapped[date | None]= mapped_column(Date, nullable=True)
|
rent_end_date : Mapped[date | None]= mapped_column(Date, nullable=True)
|
||||||
|
|
||||||
|
|||||||
@ -233,10 +233,6 @@ class OrgUnitBaseSchema(BaseModel):
|
|||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
class OrgUnitCreateSchema(OrgUnitBaseSchema):
|
|
||||||
title: str = Field(..., min_length=1)
|
|
||||||
|
|
||||||
|
|
||||||
class OrgUnitUpdateSchema(OrgUnitBaseSchema):
|
class OrgUnitUpdateSchema(OrgUnitBaseSchema):
|
||||||
title: Optional[str] = None
|
title: Optional[str] = None
|
||||||
is_ssp: Optional[bool] = None
|
is_ssp: Optional[bool] = None
|
||||||
@ -494,8 +490,8 @@ class VSPBase(BaseModel):
|
|||||||
serialization_alias='approved_format',
|
serialization_alias='approved_format',
|
||||||
)
|
)
|
||||||
notes: Optional[str] = Field(None, description="Примечания")
|
notes: Optional[str] = Field(None, description="Примечания")
|
||||||
placement_type: Optional[str] = Field(None, description="Тип размещения")
|
location_form: Optional[str] = Field(None, description="Форма расположения")
|
||||||
staff_count: Optional[int] = Field(None, description="Штатная численность")
|
numbers: Optional[int] = Field(None, description="Штатная численность")
|
||||||
area: Optional[float] = Field(
|
area: Optional[float] = Field(
|
||||||
None,
|
None,
|
||||||
description="Арендная площадь",
|
description="Арендная площадь",
|
||||||
@ -543,8 +539,8 @@ class VSPEditBase(BaseModel):
|
|||||||
serialization_alias='format',
|
serialization_alias='format',
|
||||||
)
|
)
|
||||||
notes: Optional[str] = Field(None, description="Примечания")
|
notes: Optional[str] = Field(None, description="Примечания")
|
||||||
placement_type: Optional[str] = Field(None, description="Тип размещения")
|
location_form: Optional[str] = Field(None, description="Форма расположения")
|
||||||
staff_count: Optional[int] = Field(None, description="Штатная численность")
|
numbers: Optional[int] = Field(None, description="Штатная численность")
|
||||||
total_area: Optional[float] = Field(
|
total_area: Optional[float] = Field(
|
||||||
None,
|
None,
|
||||||
description="Арендная площадь",
|
description="Арендная площадь",
|
||||||
@ -615,9 +611,9 @@ class VSPExportRequest(BaseModel):
|
|||||||
ssp_ids: Optional[List[int]] = None
|
ssp_ids: Optional[List[int]] = None
|
||||||
open_date_start: Optional[date] = None
|
open_date_start: Optional[date] = None
|
||||||
open_date_end: Optional[date] = None
|
open_date_end: Optional[date] = None
|
||||||
placement_type: Optional[str] = None
|
location_form: Optional[str] = None
|
||||||
staff_count_min: Optional[int] = None
|
numbers_min: Optional[int] = None
|
||||||
staff_count_max: Optional[int] = None
|
numbers_max: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
class ExpenseItemBaseSchema(BaseModel):
|
class ExpenseItemBaseSchema(BaseModel):
|
||||||
|
|||||||
@ -36,9 +36,9 @@ class VSPRepository:
|
|||||||
ssp_ids: list[int] | None = None,
|
ssp_ids: list[int] | None = None,
|
||||||
open_date_start: date | None = None,
|
open_date_start: date | None = None,
|
||||||
open_date_end: date | None = None,
|
open_date_end: date | None = None,
|
||||||
placement_type: str | None = None,
|
location_form: str | None = None,
|
||||||
staff_count_min: int | None = None,
|
numbers_min: int | None = None,
|
||||||
staff_count_max: int | None = None,
|
numbers_max: int | None = None,
|
||||||
is_active: bool | None = True,
|
is_active: bool | None = True,
|
||||||
close_date_is_null: bool | None = None,
|
close_date_is_null: bool | None = None,
|
||||||
load_org_unit: bool = False,
|
load_org_unit: bool = False,
|
||||||
@ -53,11 +53,11 @@ class VSPRepository:
|
|||||||
filters = [
|
filters = [
|
||||||
("registration_number", registration_number),
|
("registration_number", registration_number),
|
||||||
("address", address),
|
("address", address),
|
||||||
("placement_type", placement_type),
|
("location_form", location_form),
|
||||||
("open_date_start", open_date_start),
|
("open_date_start", open_date_start),
|
||||||
("open_date_end", open_date_end),
|
("open_date_end", open_date_end),
|
||||||
("staff_count_min", staff_count_min),
|
("numbers_min", numbers_min),
|
||||||
("staff_count_max", staff_count_max),
|
("numbers_max", numbers_max),
|
||||||
("is_active", is_active),
|
("is_active", is_active),
|
||||||
]
|
]
|
||||||
for filter_name, filter_value in filters:
|
for filter_name, filter_value in filters:
|
||||||
@ -67,16 +67,16 @@ class VSPRepository:
|
|||||||
queries.append(Vsp.reg_number.ilike(f"%{value}%"))
|
queries.append(Vsp.reg_number.ilike(f"%{value}%"))
|
||||||
case ("address", str(value)) if value:
|
case ("address", str(value)) if value:
|
||||||
queries.append(Vsp.address.ilike(f"%{value}%"))
|
queries.append(Vsp.address.ilike(f"%{value}%"))
|
||||||
case ("placement_type", str(value)) if value:
|
case ("location_form", str(value)) if value:
|
||||||
queries.append(Vsp.placement_type.ilike(f"%{value}%"))
|
queries.append(Vsp.location_form.ilike(f"%{value}%"))
|
||||||
case ("open_date_start", value) if value is not None:
|
case ("open_date_start", value) if value is not None:
|
||||||
queries.append(Vsp.opened_at >= value)
|
queries.append(Vsp.opened_at >= value)
|
||||||
case ("open_date_end", value) if value is not None:
|
case ("open_date_end", value) if value is not None:
|
||||||
queries.append(Vsp.opened_at <= value)
|
queries.append(Vsp.opened_at <= value)
|
||||||
case ("staff_count_min", value) if value is not None:
|
case ("numbers_min", value) if value is not None:
|
||||||
queries.append(Vsp.staff_count >= value)
|
queries.append(Vsp.numbers >= value)
|
||||||
case ("staff_count_max", value) if value is not None:
|
case ("numbers_max", value) if value is not None:
|
||||||
queries.append(Vsp.staff_count <= value)
|
queries.append(Vsp.numbers <= value)
|
||||||
case ("is_active", value) if value is not None:
|
case ("is_active", value) if value is not None:
|
||||||
queries.append(Vsp.is_active == value)
|
queries.append(Vsp.is_active == value)
|
||||||
case _:
|
case _:
|
||||||
@ -116,7 +116,6 @@ class VSPRepository:
|
|||||||
|
|
||||||
async def create(self, payload: VSPCreate, created_by: int, load_org_unit: bool = False) -> Vsp:
|
async def create(self, payload: VSPCreate, created_by: int, load_org_unit: bool = False) -> Vsp:
|
||||||
data = payload.model_dump()
|
data = payload.model_dump()
|
||||||
|
|
||||||
vsp_id = (
|
vsp_id = (
|
||||||
await self.db.execute(
|
await self.db.execute(
|
||||||
text("""
|
text("""
|
||||||
@ -125,7 +124,8 @@ class VSPRepository:
|
|||||||
:p_opened_at, :p_placement_type, :p_staff_count,
|
:p_opened_at, :p_placement_type, :p_staff_count,
|
||||||
:p_total_area, :p_closed_at, :p_is_active, :p_is_deleted,
|
:p_total_area, :p_closed_at, :p_is_active, :p_is_deleted,
|
||||||
:p_system_code, :p_created_by, :p_vsp_type, :p_notes,
|
:p_system_code, :p_created_by, :p_vsp_type, :p_notes,
|
||||||
:p_rent_contract_num, :p_rent_end_date
|
:p_location_form, :p_numbers, :p_rent_contract_num,
|
||||||
|
:p_rent_end_date
|
||||||
)
|
)
|
||||||
"""),
|
"""),
|
||||||
{
|
{
|
||||||
@ -144,6 +144,8 @@ class VSPRepository:
|
|||||||
"p_created_by": created_by,
|
"p_created_by": created_by,
|
||||||
"p_vsp_type": data.get("vsp_type"),
|
"p_vsp_type": data.get("vsp_type"),
|
||||||
"p_notes": data.get("notes"),
|
"p_notes": data.get("notes"),
|
||||||
|
"p_location_form": data.get("location_form"),
|
||||||
|
"p_numbers": data.get("numbers"),
|
||||||
"p_rent_contract_num": data.get("rent_contract_num"),
|
"p_rent_contract_num": data.get("rent_contract_num"),
|
||||||
"p_rent_end_date": data.get("rent_end_date"),
|
"p_rent_end_date": data.get("rent_end_date"),
|
||||||
}
|
}
|
||||||
@ -174,9 +176,9 @@ class VSPRepository:
|
|||||||
"opened_at": "p_opened_at",
|
"opened_at": "p_opened_at",
|
||||||
"closed_at": "p_closed_at",
|
"closed_at": "p_closed_at",
|
||||||
"format": "p_format",
|
"format": "p_format",
|
||||||
"placement_type": "p_placement_type",
|
|
||||||
"staff_count": "p_staff_count",
|
|
||||||
"notes": "p_notes",
|
"notes": "p_notes",
|
||||||
|
"location_form": "p_location_form",
|
||||||
|
"numbers": "p_numbers",
|
||||||
"total_area": "p_total_area",
|
"total_area": "p_total_area",
|
||||||
"rent_contract_num": "p_rent_contract_num",
|
"rent_contract_num": "p_rent_contract_num",
|
||||||
"rent_end_date": "p_rent_end_date",
|
"rent_end_date": "p_rent_end_date",
|
||||||
@ -188,7 +190,8 @@ class VSPRepository:
|
|||||||
params = {"p_vsp_id": vsp.id, "p_updated_by": updated_by}
|
params = {"p_vsp_id": vsp.id, "p_updated_by": updated_by}
|
||||||
for field, param in FIELD_TO_PARAM.items():
|
for field, param in FIELD_TO_PARAM.items():
|
||||||
params[param] = data.get(field)
|
params[param] = data.get(field)
|
||||||
|
params.setdefault("p_placement_type", data.get("placement_type"))
|
||||||
|
params.setdefault("p_staff_count", data.get("staff_count"))
|
||||||
await self.db.execute(
|
await self.db.execute(
|
||||||
text("""
|
text("""
|
||||||
SELECT v3.upd_vsp(
|
SELECT v3.upd_vsp(
|
||||||
@ -196,7 +199,8 @@ class VSPRepository:
|
|||||||
:p_format, :p_opened_at, :p_placement_type, :p_staff_count,
|
:p_format, :p_opened_at, :p_placement_type, :p_staff_count,
|
||||||
:p_total_area, :p_closed_at, :p_is_active, :p_is_deleted,
|
:p_total_area, :p_closed_at, :p_is_active, :p_is_deleted,
|
||||||
:p_system_code, :p_updated_by, :p_vsp_type, :p_notes,
|
:p_system_code, :p_updated_by, :p_vsp_type, :p_notes,
|
||||||
:p_rent_contract_num, :p_rent_end_date
|
:p_location_form, :p_numbers, :p_rent_contract_num,
|
||||||
|
:p_rent_end_date
|
||||||
)
|
)
|
||||||
"""),
|
"""),
|
||||||
params,
|
params,
|
||||||
|
|||||||
@ -20,8 +20,8 @@ _info_close_sync_lock = asyncio.Lock()
|
|||||||
|
|
||||||
class VSPService:
|
class VSPService:
|
||||||
EXECUTOR_EDITABLE_FIELDS = {
|
EXECUTOR_EDITABLE_FIELDS = {
|
||||||
"placement_type",
|
"location_form",
|
||||||
"staff_count",
|
"numbers",
|
||||||
"total_area",
|
"total_area",
|
||||||
"rent_contract_num",
|
"rent_contract_num",
|
||||||
"rent_end_date",
|
"rent_end_date",
|
||||||
@ -54,9 +54,9 @@ class VSPService:
|
|||||||
ssp_ids: list[int] | None = None,
|
ssp_ids: list[int] | None = None,
|
||||||
open_date_start: date | None = None,
|
open_date_start: date | None = None,
|
||||||
open_date_end: date | None = None,
|
open_date_end: date | None = None,
|
||||||
placement_type: str | None = None,
|
location_form: str | None = None,
|
||||||
staff_count_min: int | None = None,
|
numbers_min: int | None = None,
|
||||||
staff_count_max: int | None = None,
|
numbers_max: int | None = None,
|
||||||
is_active: bool | None = None,
|
is_active: bool | None = None,
|
||||||
load_org_unit: bool = False,
|
load_org_unit: bool = False,
|
||||||
with_count: bool = False,
|
with_count: bool = False,
|
||||||
@ -84,9 +84,9 @@ class VSPService:
|
|||||||
ssp_ids=effective_ssp_ids,
|
ssp_ids=effective_ssp_ids,
|
||||||
open_date_start=open_date_start,
|
open_date_start=open_date_start,
|
||||||
open_date_end=open_date_end,
|
open_date_end=open_date_end,
|
||||||
placement_type=placement_type,
|
location_form=location_form,
|
||||||
staff_count_min=staff_count_min,
|
numbers_min=numbers_min,
|
||||||
staff_count_max=staff_count_max,
|
numbers_max=numbers_max,
|
||||||
is_active=is_active,
|
is_active=is_active,
|
||||||
load_org_unit=load_org_unit,
|
load_org_unit=load_org_unit,
|
||||||
with_count=with_count,
|
with_count=with_count,
|
||||||
@ -154,9 +154,9 @@ class VSPService:
|
|||||||
ssp_ids: list[int] | None = None,
|
ssp_ids: list[int] | None = None,
|
||||||
open_date_start: date | None = None,
|
open_date_start: date | None = None,
|
||||||
open_date_end: date | None = None,
|
open_date_end: date | None = None,
|
||||||
placement_type: str | None = None,
|
location_form: str | None = None,
|
||||||
staff_count_min: int | None = None,
|
numbers_min: int | None = None,
|
||||||
staff_count_max: int | None = None,
|
numbers_max: int | None = None,
|
||||||
) -> tuple[io.BytesIO, str]:
|
) -> tuple[io.BytesIO, str]:
|
||||||
data = await self.get_list(
|
data = await self.get_list(
|
||||||
user=user,
|
user=user,
|
||||||
@ -165,9 +165,9 @@ class VSPService:
|
|||||||
ssp_ids=ssp_ids,
|
ssp_ids=ssp_ids,
|
||||||
open_date_start=open_date_start,
|
open_date_start=open_date_start,
|
||||||
open_date_end=open_date_end,
|
open_date_end=open_date_end,
|
||||||
placement_type=placement_type,
|
location_form=location_form,
|
||||||
staff_count_min=staff_count_min,
|
numbers_min=numbers_min,
|
||||||
staff_count_max=staff_count_max,
|
numbers_max=numbers_max,
|
||||||
is_active=None,
|
is_active=None,
|
||||||
load_org_unit=True,
|
load_org_unit=True,
|
||||||
)
|
)
|
||||||
@ -188,8 +188,8 @@ class VSPService:
|
|||||||
"open_date",
|
"open_date",
|
||||||
"close_date",
|
"close_date",
|
||||||
"notes",
|
"notes",
|
||||||
"placement_type",
|
"location_form",
|
||||||
"staff_count",
|
"numbers",
|
||||||
"area",
|
"area",
|
||||||
"rent_contract_num",
|
"rent_contract_num",
|
||||||
"rent_end_date",
|
"rent_end_date",
|
||||||
@ -215,8 +215,8 @@ class VSPService:
|
|||||||
item.opened_at.isoformat() if item.opened_at else None,
|
item.opened_at.isoformat() if item.opened_at else None,
|
||||||
item.closed_at.isoformat() if item.closed_at else None,
|
item.closed_at.isoformat() if item.closed_at else None,
|
||||||
item.notes or None,
|
item.notes or None,
|
||||||
item.placement_type or None,
|
item.location_form or None,
|
||||||
item.staff_count if item.staff_count is not None else None,
|
item.numbers if item.numbers is not None else None,
|
||||||
float(item.total_area) if item.total_area is not None else None,
|
float(item.total_area) if item.total_area is not None else None,
|
||||||
item.rent_contract_num or None,
|
item.rent_contract_num or None,
|
||||||
item.rent_end_date.isoformat() if item.rent_end_date else None,
|
item.rent_end_date.isoformat() if item.rent_end_date else None,
|
||||||
|
|||||||
@ -40,10 +40,10 @@ def test_vsp_dropdown_smoke(client, admin_tokens, auth_headers):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"params, count",
|
"params, count",
|
||||||
[
|
[
|
||||||
([("ssp_id", 2)], 2),
|
([("ssp_id", 2)], 1),
|
||||||
([("form_id", 1)], 0),
|
([("form_id", 1)], 0),
|
||||||
([("form_id", 2)], 2),
|
([("form_id", 2)], 1),
|
||||||
([("ssp_id", 2), ("form_id", 1)], 2),
|
([("ssp_id", 2), ("form_id", 1)], 1),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_vsp_dropdown_with_filter(client, admin_tokens, auth_headers, params, count):
|
def test_vsp_dropdown_with_filter(client, admin_tokens, auth_headers, params, count):
|
||||||
@ -110,8 +110,8 @@ def test_vsp_create_with_all_fields(client, admin_tokens, auth_headers):
|
|||||||
"close_date": "2025-12-31",
|
"close_date": "2025-12-31",
|
||||||
"approved_format": "Стандарт",
|
"approved_format": "Стандарт",
|
||||||
"notes": "Тестовое примечание",
|
"notes": "Тестовое примечание",
|
||||||
"placement_type": "Аренда",
|
"location_form": "Встроенное",
|
||||||
"staff_count": 10,
|
"numbers": 10,
|
||||||
"area": 150.5,
|
"area": 150.5,
|
||||||
"rent_contract_num": "Д-123/24",
|
"rent_contract_num": "Д-123/24",
|
||||||
"rent_end_date": "2025-12-31",
|
"rent_end_date": "2025-12-31",
|
||||||
@ -127,8 +127,8 @@ def test_vsp_create_with_all_fields(client, admin_tokens, auth_headers):
|
|||||||
assert payload["result"]["open_date"] == "2024-01-15"
|
assert payload["result"]["open_date"] == "2024-01-15"
|
||||||
assert payload["result"]["close_date"] == "2025-12-31"
|
assert payload["result"]["close_date"] == "2025-12-31"
|
||||||
assert payload["result"]["approved_format"] == "Стандарт"
|
assert payload["result"]["approved_format"] == "Стандарт"
|
||||||
assert payload["result"]["placement_type"] == "Аренда"
|
assert payload["result"]["location_form"] == "Встроенное"
|
||||||
assert payload["result"]["staff_count"] == 10
|
assert payload["result"]["numbers"] == 10
|
||||||
assert payload["result"]["area"] == 150.5
|
assert payload["result"]["area"] == 150.5
|
||||||
assert payload["result"]["rent_contract_num"] == "Д-123/24"
|
assert payload["result"]["rent_contract_num"] == "Д-123/24"
|
||||||
assert payload["result"]["rent_end_date"] == "2025-12-31"
|
assert payload["result"]["rent_end_date"] == "2025-12-31"
|
||||||
|
|||||||
@ -26,7 +26,7 @@ SELECT setval('v3.budget_line_id_seq', 1);
|
|||||||
INSERT INTO v3.form_phase (budget_form_id,sheet,phase_code,"role",column_keys,opens_at,closes_at) VALUES
|
INSERT INTO v3.form_phase (budget_form_id,sheet,phase_code,"role",column_keys,opens_at,closes_at) VALUES
|
||||||
(1,'AHR','test','DFIP','{{plan.q1}}','2026-05-05 03:00:00+03','2026-06-06 03:00:00+03');
|
(1,'AHR','test','DFIP','{{plan.q1}}','2026-05-05 03:00:00+03','2026-06-06 03:00:00+03');
|
||||||
|
|
||||||
INSERT INTO v3.vsp (id,branch_id,reg_number,address,format,opened_at,placement_type,staff_count,total_area,closed_at,is_active,updated_at,is_deleted,created_at,created_by,system_code,updated_by,vsp_type,notes,location_form,rent_contract_num,rent_end_date) VALUES
|
INSERT INTO v3.vsp (id,branch_id,reg_number,address,format,opened_at,placement_type,staff_count,total_area,closed_at,is_active,updated_at,is_deleted,created_at,created_by,system_code,updated_by,vsp_type,notes,location_form,numbers,rent_contract_num,rent_end_date) VALUES
|
||||||
(1,2,'Тестовый всп 1','Тестовая 12','укукк','2026-05-07','ывс',2026,230,'2026-06-16',false,'2026-06-18 15:46:38.611903',false,'2026-06-04 14:36:59.737727',1,'1233',91,'','','встроенное помещение','',NULL),
|
(1,2,'Тестовый всп 1','Тестовая 12','укукк','2026-05-07','ывс',2026,230,'2026-06-16',false,'2026-06-18 15:46:38.611903',false,'2026-06-04 14:36:59.737727',1,'1233',91,'','','встроенное помещение',NULL,'',NULL),
|
||||||
(2,2,'Test12 -----','Тестовая 125','формат 15 ','2026-06-08',NULL,NULL,58,NULL,true,'2026-06-16 18:53:17.986651',false,'2026-06-09 09:04:33.29858',91,'123-1234',91,'субаренда','Работает ','аренда','12-45','2026-10-02');
|
(2,2,'Test12 -----','Тестовая 125','формат 15 ','2026-06-08',NULL,NULL,58,NULL,true,'2026-06-16 18:53:17.986651',false,'2026-06-09 09:04:33.29858',91,'123-1234',91,'субаренда','Работает ','аренда',155,'12-45','2026-10-02');
|
||||||
SELECT setval('v3.vsp_id_seq', 2);
|
SELECT setval('v3.vsp_id_seq', 2);
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export const ProjectsApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
update: (id, data) => {
|
update: (id, data) => {
|
||||||
return api.patch(`/projects/${id}`, data).then((r) => r.data);
|
return api.put(`/projects/${id}`, data).then((r) => r.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
delete: (id) => {
|
delete: (id) => {
|
||||||
|
|||||||
@ -41,7 +41,6 @@ export const AppRoutes = () => {
|
|||||||
<Route path="/projects" element={<ProjectsPage />}/>
|
<Route path="/projects" element={<ProjectsPage />}/>
|
||||||
<Route path="/forms" element={<FormsPage />} />
|
<Route path="/forms" element={<FormsPage />} />
|
||||||
<Route path="/task/:taskId" element={<TaskPage />} />
|
<Route path="/task/:taskId" element={<TaskPage />} />
|
||||||
<Route path="/project/:projectId" element={<TaskPage />} />
|
|
||||||
<Route path="/table/:tableId" element={<TablePage />} />
|
<Route path="/table/:tableId" element={<TablePage />} />
|
||||||
<Route path="/table-temp/:tableId" element={<TableTempPage />} />
|
<Route path="/table-temp/:tableId" element={<TableTempPage />} />
|
||||||
<Route path="/admin_panel/users" element={<UsersPage />} />
|
<Route path="/admin_panel/users" element={<UsersPage />} />
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { NavLink } from 'react-router-dom';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { TasksSvg, AdminPanelSvg, DictSvg } from '../common/icons/icons';
|
import { TasksSvg, AdminPanelSvg, DictSvg } from '../common/icons/icons';
|
||||||
import { useAuth } from '../../app/context/AuthProvider';
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
import { ROLES_NAME_ID } from '../../constants/constants';
|
import { ROLES_NAME_ID } from '../../constants';
|
||||||
|
|
||||||
const Switch = styled.div`
|
const Switch = styled.div`
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import { useRealtime } from './contexts/RealtimeContext';
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { CircularProgress } from '@mui/material';
|
import { CircularProgress } from '@mui/material';
|
||||||
import { additionVspRowTable } from './constants/addingRowConfig';
|
import { additionVspRowTable } from './constants/addingRowConfig';
|
||||||
import { SelectVspModal } from './Modals/SelectVspModal';
|
import { SelectVspModal } from './Modals/selectVspModal';
|
||||||
import { getRowId } from './utils/rowUtils';
|
import { getRowId } from './utils/rowUtils';
|
||||||
|
|
||||||
const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import { StageStatusChip } from './StageStatusChip';
|
|||||||
import { formatDateForApi } from './utils';
|
import { formatDateForApi } from './utils';
|
||||||
import CollapsibleTree from '../common/CollapsibleTree';
|
import CollapsibleTree from '../common/CollapsibleTree';
|
||||||
import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils';
|
import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils';
|
||||||
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants/constants';
|
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants';
|
||||||
|
|
||||||
export const AddStagesModal = ({
|
export const AddStagesModal = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Box, Divider, Stack } from '@mui/material';
|
import { Box, Divider, Stack } from '@mui/material';
|
||||||
import { UnLockedSvg } from '../common/icons/icons';
|
import { UnLockedSvg } from '../common/icons/icons';
|
||||||
import { ROLES_ID_RUSSIAN_NAME, EDIT_ACCESS_RUSSIAN } from '../../constants/constants';
|
import { ROLES_ID_RUSSIAN_NAME, EDIT_ACCESS_RUSSIAN } from '../../constants';
|
||||||
|
|
||||||
export const StageInfoForTooltip = ({ stageData = [] }) => {
|
export const StageInfoForTooltip = ({ stageData = [] }) => {
|
||||||
const accessData = stageData[0] || [];
|
const accessData = stageData[0] || [];
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import {
|
|||||||
import { getStageStatus, getStatusColors } from './utils';
|
import { getStageStatus, getStatusColors } from './utils';
|
||||||
import { formatDate } from '../../../utils/formatDate';
|
import { formatDate } from '../../../utils/formatDate';
|
||||||
import { StageStatusChip } from '../StageStatusChip';
|
import { StageStatusChip } from '../StageStatusChip';
|
||||||
import { ROLES_NAME_ID, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../constants/constants';
|
import { ROLES_NAME_ID, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../constants';
|
||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
|
|
||||||
export const StagesTable = ({ stages, onEdit, onDelete }) => {
|
export const StagesTable = ({ stages, onEdit, onDelete }) => {
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export function PaginatedList({
|
|||||||
extraActions = null,
|
extraActions = null,
|
||||||
enableExport = true,
|
enableExport = true,
|
||||||
limitOptions = [10, 20, 50, 100, 200],
|
limitOptions = [10, 20, 50, 100, 200],
|
||||||
maxHeight = 'calc(100vh - 300px)',
|
maxHeight = 'calc(110vh - 300px)',
|
||||||
enableScroll = true,
|
enableScroll = true,
|
||||||
}) {
|
}) {
|
||||||
const [exportMode, setExportMode] = useState(false);
|
const [exportMode, setExportMode] = useState(false);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import { EDIT_ACCESS_RUSSIAN, ROLES_ID_RUSSIAN_NAME } from '../../../constants/constants';
|
import { EDIT_ACCESS_RUSSIAN, ROLES_ID_RUSSIAN_NAME } from '../../../constants';
|
||||||
import {
|
import {
|
||||||
SelectWrapper,
|
SelectWrapper,
|
||||||
SelectContainer,
|
SelectContainer,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants';
|
||||||
import { Switch, Buttons, StyledNavLink } from './SwitchFormTask.style';
|
import { Switch, Buttons, StyledNavLink } from './SwitchFormTask.style';
|
||||||
|
|
||||||
export function SwitchFormTask() {
|
export function SwitchFormTask() {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DIRECTION_TRANSLATE, FORM_TYPE_OPTIONS } from '../../../constants/constants';
|
import { DIRECTION_TRANSLATE, FORM_TYPE_OPTIONS } from '../../../constants';
|
||||||
import {
|
import {
|
||||||
Section,
|
Section,
|
||||||
SectionStartPart,
|
SectionStartPart,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React from 'react';
|
||||||
import { Box, Stack, Typography } from '@mui/material';
|
import { Box, Stack, Typography } from '@mui/material';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import TableCard from '../TableCard/TableCard';
|
import TableCard from '../TableCard/TableCard';
|
||||||
@ -12,9 +12,6 @@ const TablesList = ({
|
|||||||
formInfo
|
formInfo
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const isProject = useMemo(() => {
|
|
||||||
return !!projects;
|
|
||||||
}, [projects])
|
|
||||||
|
|
||||||
const handleNavigate = (path) => {
|
const handleNavigate = (path) => {
|
||||||
navigate(path);
|
navigate(path);
|
||||||
@ -25,7 +22,7 @@ const TablesList = ({
|
|||||||
if (!formInfo){
|
if (!formInfo){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const path = `${basePath}/form/${formInfo.id}/form-type/${isProject ? 'project' : formInfo.form_type_code}/${table.sheet}/${table?.direction}`;
|
const path = `${basePath}/form/${formInfo.id}/form-type/${formInfo.form_type_code}/${table.sheet}/${table?.direction}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableCard
|
<TableCard
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import { formatDate } from '../../utils/formatDate';
|
|||||||
import { IconWithContent } from './IconWithContent';
|
import { IconWithContent } from './IconWithContent';
|
||||||
import { Chip } from '../styles/StyledChip';
|
import { Chip } from '../styles/StyledChip';
|
||||||
import { ExportButton } from '../common/Buttons/ButtonsActions';
|
import { ExportButton } from '../common/Buttons/ButtonsActions';
|
||||||
import { FORM_TYPE_TRANSLATE } from '../../constants/constants';
|
import { FORM_TYPE_TRANSLATE } from '../../constants';
|
||||||
|
|
||||||
const TaskCard = styled.div`
|
const TaskCard = styled.div`
|
||||||
width: 26.8rem;
|
width: 26.8rem;
|
||||||
|
|||||||
@ -1,90 +0,0 @@
|
|||||||
export const DEFAULT_PROJECT_CONFIG = {
|
|
||||||
'ССП/РФ': {
|
|
||||||
type: 'multiselect',
|
|
||||||
options: [],
|
|
||||||
placeholder: 'Выберите ССП/РФ',
|
|
||||||
defaultValue: null,
|
|
||||||
required_field: true,
|
|
||||||
},
|
|
||||||
'Год': {
|
|
||||||
type: 'number',
|
|
||||||
placeholder: 'Введите год',
|
|
||||||
defaultValue: null,
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
required_field: true,
|
|
||||||
},
|
|
||||||
'Тип проекта развития': {
|
|
||||||
type: 'multiselect',
|
|
||||||
options: [
|
|
||||||
'Открытие ВСП',
|
|
||||||
'Закрытие ВСП',
|
|
||||||
'Переезд ВСП',
|
|
||||||
'Реновация РФ',
|
|
||||||
'Открытие УРМ',
|
|
||||||
],
|
|
||||||
placeholder: 'Выберите тип проекта',
|
|
||||||
defaultValue: null,
|
|
||||||
required_field: false,
|
|
||||||
|
|
||||||
},
|
|
||||||
'Название проекта': {
|
|
||||||
type: 'text',
|
|
||||||
placeholder: 'Введите название проекта',
|
|
||||||
defaultValue: null,
|
|
||||||
required_field: true,
|
|
||||||
},
|
|
||||||
'Формат ВСП': {
|
|
||||||
type: 'multiselect',
|
|
||||||
options: [
|
|
||||||
'Фланговый',
|
|
||||||
'Типовой',
|
|
||||||
'Розничный',
|
|
||||||
'МСБ',
|
|
||||||
'Лёгкий',
|
|
||||||
'Мини',
|
|
||||||
'Розничный-киоск',
|
|
||||||
'МБО',
|
|
||||||
'Офис самообслуживания',
|
|
||||||
'Другое',
|
|
||||||
],
|
|
||||||
placeholder: 'Выберите формат ВСП',
|
|
||||||
defaultValue: null,
|
|
||||||
required_field: false,
|
|
||||||
|
|
||||||
},
|
|
||||||
'Адрес объекта': {
|
|
||||||
type: 'text',
|
|
||||||
placeholder: 'Введите адрес объекта',
|
|
||||||
defaultValue: '',
|
|
||||||
required_field: false,
|
|
||||||
|
|
||||||
},
|
|
||||||
'Целевая численность, чел.': {
|
|
||||||
type: 'number',
|
|
||||||
placeholder: 'Введите число',
|
|
||||||
defaultValue: 0,
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
required_field: false,
|
|
||||||
|
|
||||||
},
|
|
||||||
'Тип размещения': {
|
|
||||||
type: 'multiselect',
|
|
||||||
options: ['Собственность', 'Аренда', 'Субаренда'],
|
|
||||||
placeholder: 'Выберите тип размещения',
|
|
||||||
defaultValue: null,
|
|
||||||
required_field: false,
|
|
||||||
|
|
||||||
},
|
|
||||||
'Площадь размещения, м2': {
|
|
||||||
type: 'number',
|
|
||||||
placeholder: 'Введите число',
|
|
||||||
defaultValue: 0,
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
required_field: false,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|||||||
import { PageContainer } from '../../StyledForPage';
|
import { PageContainer } from '../../StyledForPage';
|
||||||
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch.jsx';
|
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch.jsx';
|
||||||
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants';
|
||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { Box, Typography, Link, Chip } from '@mui/material';
|
import { Box, Typography, Link, Chip } from '@mui/material';
|
||||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../../constants/constants';
|
import { ROLES_ID_RUSSIAN_NAME } from '../../../../constants';
|
||||||
import {
|
import {
|
||||||
ENTITY_RUSSIAN_NAMES,
|
ENTITY_RUSSIAN_NAMES,
|
||||||
getRussianNamesForType,
|
getRussianNamesForType,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { FormsApi } from '../../../../api/forms';
|
|||||||
import { SspApi } from '../../../../api/ssp';
|
import { SspApi } from '../../../../api/ssp';
|
||||||
import { TasksApi } from '../../../../api/tasks';
|
import { TasksApi } from '../../../../api/tasks';
|
||||||
import { UsersApi } from '../../../../api/users';
|
import { UsersApi } from '../../../../api/users';
|
||||||
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, ROLES_ID_RUSSIAN_NAME, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../../constants/constants';
|
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, ROLES_ID_RUSSIAN_NAME, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../../constants';
|
||||||
|
|
||||||
export const VALUE_TRANSFORMERS = {
|
export const VALUE_TRANSFORMERS = {
|
||||||
Роль: transformRole,
|
Роль: transformRole,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
|
|||||||
import { PageContainer } from '../../StyledForPage';
|
import { PageContainer } from '../../StyledForPage';
|
||||||
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch';
|
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch';
|
||||||
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants';
|
||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import Modal from '../../../components/common/Modal/Modal';
|
import Modal from '../../../components/common/Modal/Modal';
|
||||||
import { Box, Button, Typography } from '@mui/material';
|
import { Box, Button, Typography } from '@mui/material';
|
||||||
import SelectWithOptions from '../components/SelectWithOptions';
|
import SelectWithOptions from '../components/SelectWithOptions';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants';
|
||||||
import { AutocompleteForChangeOptions } from '../components/AutocompleteForChangeOptions';
|
import { AutocompleteForChangeOptions } from '../components/AutocompleteForChangeOptions';
|
||||||
|
|
||||||
export const ModalEditUserSsp = ({
|
export const ModalEditUserSsp = ({
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
|||||||
import {
|
import {
|
||||||
ROLES_ID_RUSSIAN_NAME_ARRAY as ROLES,
|
ROLES_ID_RUSSIAN_NAME_ARRAY as ROLES,
|
||||||
ROLES_ID_RUSSIAN_NAME,
|
ROLES_ID_RUSSIAN_NAME,
|
||||||
} from '../../../../constants/constants';
|
} from '../../../../constants';
|
||||||
import SelectWithOptions from '../../components/SelectWithOptions';
|
import SelectWithOptions from '../../components/SelectWithOptions';
|
||||||
import useAbbreviation from './hooks/useAbbreviation';
|
import useAbbreviation from './hooks/useAbbreviation';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -5,12 +5,12 @@ import {
|
|||||||
CheckedIcon,
|
CheckedIcon,
|
||||||
IndeterminateIcon,
|
IndeterminateIcon,
|
||||||
} from '../../IconsForTable/icons';
|
} from '../../IconsForTable/icons';
|
||||||
import { ROLES_NAME_ID } from '../../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../../constants';
|
||||||
import { createFilterData } from '../../utils/filters';
|
import { createFilterData } from '../../utils/filters';
|
||||||
import {
|
import {
|
||||||
ROLES_ID_RUSSIAN_NAME_ARRAY as ROLES,
|
ROLES_ID_RUSSIAN_NAME_ARRAY as ROLES,
|
||||||
ROLES_ID_NAME,
|
ROLES_ID_NAME,
|
||||||
} from '../../../../constants/constants';
|
} from '../../../../constants';
|
||||||
import SelectWithOptions from '../../components/SelectWithOptions';
|
import SelectWithOptions from '../../components/SelectWithOptions';
|
||||||
import RowActionsMenu from './RowActionMenu';
|
import RowActionsMenu from './RowActionMenu';
|
||||||
import DepartmentCell from './DepartmentCell';
|
import DepartmentCell from './DepartmentCell';
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { CheckIcon, CheckedIcon } from '../IconsForTable/icons';
|
import { CheckIcon, CheckedIcon } from '../IconsForTable/icons';
|
||||||
import { TrashSvg } from '../../../components/common/icons/icons';
|
import { TrashSvg } from '../../../components/common/icons/icons';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants';
|
||||||
|
|
||||||
const StyleForChecked = {
|
const StyleForChecked = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { Button, Stack } from '@mui/material';
|
|||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants/constants';
|
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants';
|
||||||
import { UserIcon, TrashSvg } from '../../../components/common/icons/icons';
|
import { UserIcon, TrashSvg } from '../../../components/common/icons/icons';
|
||||||
import {
|
import {
|
||||||
DangerOutlinedButton,
|
DangerOutlinedButton,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { PageContainer } from '../../StyledForPage';
|
import { PageContainer } from '../../StyledForPage';
|
||||||
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch';
|
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch';
|
||||||
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
||||||
import { ROLES_ID_RUSSIAN_NAME_ARRAY, ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_ID_RUSSIAN_NAME_ARRAY, ROLES_NAME_ID } from '../../../constants';
|
||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
import { Box, TextField, Autocomplete, FormLabel } from '@mui/material';
|
import { Box, TextField, Autocomplete, FormLabel } from '@mui/material';
|
||||||
import SearchComponent from '../../../components/common/SearchComponent';
|
import SearchComponent from '../../../components/common/SearchComponent';
|
||||||
@ -10,7 +10,7 @@ import { toast } from 'react-toastify';
|
|||||||
import { SspApi } from '../../../api/ssp';
|
import { SspApi } from '../../../api/ssp';
|
||||||
import { UsersApi } from '../../../api/users';
|
import { UsersApi } from '../../../api/users';
|
||||||
import UserTable from './UserTable/UserTable';
|
import UserTable from './UserTable/UserTable';
|
||||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants/constants';
|
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants';
|
||||||
import { ModalEditUserSsp } from './ModalEditUserSsp';
|
import { ModalEditUserSsp } from './ModalEditUserSsp';
|
||||||
import { ModalAddUsers } from './ModalAddUsers';
|
import { ModalAddUsers } from './ModalAddUsers';
|
||||||
import UsersActionBar from './UsersActionBar';
|
import UsersActionBar from './UsersActionBar';
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import TableDictVsp from './components/TableDictVsp';
|
|||||||
import { PageContainer } from '../StyledForPage';
|
import { PageContainer } from '../StyledForPage';
|
||||||
import { SspApi } from '../../api/ssp';
|
import { SspApi } from '../../api/ssp';
|
||||||
import { useAuth } from '../../app/context/AuthProvider';
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
import { ROLES_NAME_ID } from '../../constants/constants';
|
import { ROLES_NAME_ID } from '../../constants';
|
||||||
import DictVspFilters from './components/DictVspFilters';
|
import DictVspFilters from './components/DictVspFilters';
|
||||||
|
|
||||||
const DictVspInfo = () => {
|
const DictVspInfo = () => {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
} from '../../../../components/common/Buttons/Buttons';
|
} from '../../../../components/common/Buttons/Buttons';
|
||||||
import { useAuth } from '../../../../app/context/AuthProvider';
|
import { useAuth } from '../../../../app/context/AuthProvider';
|
||||||
import { ROLES_NAME_ID } from '../../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../../constants';
|
||||||
import {
|
import {
|
||||||
ModalContainer,
|
ModalContainer,
|
||||||
FieldContainer,
|
FieldContainer,
|
||||||
@ -95,7 +95,7 @@ const ModalEditAddVsp = ({
|
|||||||
: null,
|
: null,
|
||||||
notes: initialData.notes || '',
|
notes: initialData.notes || '',
|
||||||
location_form: initialData.location_form || '',
|
location_form: initialData.location_form || '',
|
||||||
numbers: initialData.staff_count?.toString() || '',
|
numbers: initialData.numbers?.toString() || '',
|
||||||
area: initialData.area || null,
|
area: initialData.area || null,
|
||||||
rent_contract_num: initialData.rent_contract_num || '',
|
rent_contract_num: initialData.rent_contract_num || '',
|
||||||
rent_end_date: initialData.rent_end_date
|
rent_end_date: initialData.rent_end_date
|
||||||
@ -215,7 +215,7 @@ const ModalEditAddVsp = ({
|
|||||||
|
|
||||||
const submitData = {
|
const submitData = {
|
||||||
...formData,
|
...formData,
|
||||||
numbers: formData.staff_count ? parseInt(formData.staff_count, 10) : null,
|
numbers: formData.numbers ? parseInt(formData.numbers, 10) : null,
|
||||||
area: formData.area ? parseFloat(formData.area) : null,
|
area: formData.area ? parseFloat(formData.area) : null,
|
||||||
open_date: formData.open_date
|
open_date: formData.open_date
|
||||||
? formData.open_date.toLocaleDateString('en-CA')
|
? formData.open_date.toLocaleDateString('en-CA')
|
||||||
@ -495,11 +495,11 @@ const ModalEditAddVsp = ({
|
|||||||
<FieldLabel>Штатная численность ВСП</FieldLabel>
|
<FieldLabel>Штатная численность ВСП</FieldLabel>
|
||||||
<StyledTextField
|
<StyledTextField
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.staff_count}
|
value={formData.numbers}
|
||||||
onChange={handleNumberChange('numbers')}
|
onChange={handleNumberChange('numbers')}
|
||||||
placeholder="Введите численность"
|
placeholder="Введите численность"
|
||||||
error={!!errors.staff_count}
|
error={!!errors.numbers}
|
||||||
helperText={errors.staff_count}
|
helperText={errors.numbers}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
htmlInput: { min: 1, step: 1 },
|
htmlInput: { min: 1, step: 1 },
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
PrimaryOutlinedButton,
|
PrimaryOutlinedButton,
|
||||||
} from '../../../components/common/Buttons/Buttons';
|
} from '../../../components/common/Buttons/Buttons';
|
||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants';
|
||||||
import ModalExport from './Modals/ModalExport';
|
import ModalExport from './Modals/ModalExport';
|
||||||
|
|
||||||
const TableDictVsp = ({
|
const TableDictVsp = ({
|
||||||
@ -204,7 +204,7 @@ const TableDictVsp = ({
|
|||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'staff_count',
|
accessorKey: 'numbers',
|
||||||
header: 'Штатная численность ВСП',
|
header: 'Штатная численность ВСП',
|
||||||
size: 170,
|
size: 170,
|
||||||
minSize: 140,
|
minSize: 140,
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { RealtimeProvider } from '../components/RealtimeTable/contexts/RealtimeContext';
|
import { RealtimeProvider } from '../components/RealtimeTable/contexts/RealtimeContext';
|
||||||
import { useAuth } from '../app/context/AuthProvider';
|
import { useAuth } from '../app/context/AuthProvider';
|
||||||
import { BackButton } from '../components/common/Buttons/BackButton';
|
import { BackButton } from '../components/common/Buttons/BackButton';
|
||||||
import { SHEET_NAME } from '../constants/constants';
|
import { SHEET_NAME } from '../constants';
|
||||||
|
|
||||||
const TableInfo = ({ formId, sheetName }) => {
|
const TableInfo = ({ formId, sheetName }) => {
|
||||||
const portalContent = (
|
const portalContent = (
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
// src/components/common/ProjectCardComponent/ModalCreateProject.jsx
|
import { useState } from 'react';
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import { Box, Button, Divider, TextField, Typography } from '@mui/material';
|
import { Box, Button, Divider, TextField, Typography } from '@mui/material';
|
||||||
import SelectWithOptions from '../AdminPanel/components/SelectWithOptions';
|
import SelectWithOptions from '../AdminPanel/components/SelectWithOptions';
|
||||||
import Modal from '../../components/common/Modal/Modal';
|
import Modal from '../../components/common/Modal/Modal';
|
||||||
@ -10,103 +9,25 @@ export const ModalCreateProject = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onCreate,
|
onCreate,
|
||||||
config = {},
|
config = {},
|
||||||
initialData = null,
|
|
||||||
isEdit = false,
|
|
||||||
projectId = null,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [projectData, setProjectData] = useState(() => {
|
const [projectData, setProjectData] = useState(() => {
|
||||||
// Если есть initialData, используем её, иначе создаем из конфига
|
const initialData = {};
|
||||||
if (initialData) {
|
|
||||||
return { ...initialData };
|
|
||||||
}
|
|
||||||
const initial = {};
|
|
||||||
Object.keys(config).forEach((key) => {
|
Object.keys(config).forEach((key) => {
|
||||||
initial[key] = config[key].defaultValue;
|
initialData[key] = config[key].defaultValue;
|
||||||
});
|
});
|
||||||
return initial;
|
return initialData;
|
||||||
});
|
});
|
||||||
|
|
||||||
const [errors, setErrors] = useState({});
|
|
||||||
|
|
||||||
// Обновляем данные при изменении initialData
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialData) {
|
|
||||||
setProjectData({ ...initialData });
|
|
||||||
} else {
|
|
||||||
const initial = {};
|
|
||||||
Object.keys(config).forEach((key) => {
|
|
||||||
initial[key] = config[key].defaultValue;
|
|
||||||
});
|
|
||||||
setProjectData(initial);
|
|
||||||
}
|
|
||||||
setErrors({});
|
|
||||||
}, [initialData, config]);
|
|
||||||
|
|
||||||
const handleChange = (field, value) => {
|
const handleChange = (field, value) => {
|
||||||
setProjectData((prev) => ({
|
setProjectData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}));
|
||||||
// Очищаем ошибку для поля при изменении
|
|
||||||
if (errors[field]) {
|
|
||||||
setErrors((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[field]: undefined,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateField = (fieldName, config) => {
|
|
||||||
if (config.required_field) {
|
|
||||||
const value = projectData[fieldName];
|
|
||||||
|
|
||||||
// Проверка для разных типов полей
|
|
||||||
if (config.type === 'multiselect') {
|
|
||||||
if (!value || (Array.isArray(value) && value.length === 0) || value === '') {
|
|
||||||
return `Поле "${fieldName}" обязательно для заполнения`;
|
|
||||||
}
|
|
||||||
} else if (config.type === 'text') {
|
|
||||||
if (!value || value.trim() === '') {
|
|
||||||
return `Поле "${fieldName}" обязательно для заполнения`;
|
|
||||||
}
|
|
||||||
} else if (config.type === 'number') {
|
|
||||||
if (value === null || value === undefined || value === '') {
|
|
||||||
return `Поле "${fieldName}" обязательно для заполнения`;
|
|
||||||
}
|
|
||||||
// Проверка на минимальное значение
|
|
||||||
if (config.min !== undefined && Number(value) < config.min) {
|
|
||||||
return `Значение должно быть не меньше ${config.min}`;
|
|
||||||
}
|
|
||||||
// Проверка на максимальное значение
|
|
||||||
if (config.max !== undefined && Number(value) > config.max) {
|
|
||||||
return `Значение должно быть не больше ${config.max}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateAllFields = () => {
|
|
||||||
const newErrors = {};
|
|
||||||
let hasErrors = false;
|
|
||||||
|
|
||||||
Object.entries(config).forEach(([fieldName, fieldConfig]) => {
|
|
||||||
const error = validateField(fieldName, fieldConfig);
|
|
||||||
if (error) {
|
|
||||||
newErrors[fieldName] = error;
|
|
||||||
hasErrors = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setErrors(newErrors);
|
|
||||||
return !hasErrors;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickCreate = () => {
|
const handleClickCreate = () => {
|
||||||
if (validateAllFields()) {
|
|
||||||
onCreate(projectData);
|
onCreate(projectData);
|
||||||
// Не закрываем модалку здесь, пусть это делает родительский компонент
|
onClose();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
@ -115,14 +36,10 @@ export const ModalCreateProject = ({
|
|||||||
resetData[key] = config[key].defaultValue;
|
resetData[key] = config[key].defaultValue;
|
||||||
});
|
});
|
||||||
setProjectData(resetData);
|
setProjectData(resetData);
|
||||||
setErrors({});
|
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderField = (fieldName, config) => {
|
const renderField = (fieldName, config) => {
|
||||||
const hasError = !!errors[fieldName];
|
|
||||||
const errorText = errors[fieldName];
|
|
||||||
|
|
||||||
switch (config.type) {
|
switch (config.type) {
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
return (
|
return (
|
||||||
@ -133,13 +50,9 @@ export const ModalCreateProject = ({
|
|||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
fontSize: '0.75rem',
|
fontSize: '0.75rem',
|
||||||
paddingBottom: '.25rem',
|
paddingBottom: '.25rem',
|
||||||
color: hasError ? 'var(--error-color, #d32f2f)' : 'var(--grey-dark)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fieldName}
|
{fieldName}
|
||||||
{config.required_field && (
|
|
||||||
<span style={{ color: 'var(--error-color, #d32f2f)', marginLeft: '4px' }}>*</span>
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<SelectWithOptions
|
<SelectWithOptions
|
||||||
className="sameSize"
|
className="sameSize"
|
||||||
@ -148,22 +61,7 @@ export const ModalCreateProject = ({
|
|||||||
placeholder={config.placeholder}
|
placeholder={config.placeholder}
|
||||||
onChange={(value) => handleChange(fieldName, value)}
|
onChange={(value) => handleChange(fieldName, value)}
|
||||||
width="28.25rem"
|
width="28.25rem"
|
||||||
style={{
|
|
||||||
borderColor: hasError ? 'var(--error-color, #d32f2f)' : undefined,
|
|
||||||
borderWidth: hasError ? '2px' : undefined,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{hasError && (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
color: 'var(--error-color, #d32f2f)',
|
|
||||||
fontSize: '0.75rem',
|
|
||||||
marginTop: '4px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{errorText}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -175,14 +73,11 @@ export const ModalCreateProject = ({
|
|||||||
sx={{
|
sx={{
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
fontSize: '0.75rem',
|
fontSize: '0.75rem',
|
||||||
color: hasError ? 'var(--error-color, #d32f2f)' : 'var(--grey-dark)',
|
color: 'var(--grey-dark)',
|
||||||
paddingBottom: '.25rem',
|
paddingBottom: '.25rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fieldName}
|
{fieldName}
|
||||||
{config.required_field && (
|
|
||||||
<span style={{ color: 'var(--error-color, #d32f2f)', marginLeft: '4px' }}>*</span>
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
className="sameSize"
|
className="sameSize"
|
||||||
@ -190,14 +85,6 @@ export const ModalCreateProject = ({
|
|||||||
onChange={(e) => handleChange(fieldName, e.target.value)}
|
onChange={(e) => handleChange(fieldName, e.target.value)}
|
||||||
placeholder={config.placeholder}
|
placeholder={config.placeholder}
|
||||||
size="small"
|
size="small"
|
||||||
error={hasError}
|
|
||||||
helperText={hasError ? errorText : ''}
|
|
||||||
FormHelperTextProps={{
|
|
||||||
sx: {
|
|
||||||
marginLeft: 0,
|
|
||||||
fontSize: '0.75rem',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@ -210,14 +97,11 @@ export const ModalCreateProject = ({
|
|||||||
sx={{
|
sx={{
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
fontSize: '0.75rem',
|
fontSize: '0.75rem',
|
||||||
color: hasError ? 'var(--error-color, #d32f2f)' : 'var(--grey-dark)',
|
color: 'var(--grey-dark)',
|
||||||
paddingBottom: '.25rem',
|
paddingBottom: '.25rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fieldName}
|
{fieldName}
|
||||||
{config.required_field && (
|
|
||||||
<span style={{ color: 'var(--error-color, #d32f2f)', marginLeft: '4px' }}>*</span>
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
className="sameSize"
|
className="sameSize"
|
||||||
@ -234,8 +118,6 @@ export const ModalCreateProject = ({
|
|||||||
}}
|
}}
|
||||||
placeholder={config.placeholder}
|
placeholder={config.placeholder}
|
||||||
size="small"
|
size="small"
|
||||||
error={hasError}
|
|
||||||
helperText={hasError ? errorText : ''}
|
|
||||||
slotProps={{
|
slotProps={{
|
||||||
htmlInput: {
|
htmlInput: {
|
||||||
min: config.min,
|
min: config.min,
|
||||||
@ -243,12 +125,6 @@ export const ModalCreateProject = ({
|
|||||||
step: config.step || 1,
|
step: config.step || 1,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
FormHelperTextProps={{
|
|
||||||
sx: {
|
|
||||||
marginLeft: 0,
|
|
||||||
fontSize: '0.75rem',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@ -260,7 +136,7 @@ export const ModalCreateProject = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={isEdit ? 'Редактирование проекта' : 'Создание проекта'}
|
title={'Создание проекта'}
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
customSize={'43.75'}
|
customSize={'43.75'}
|
||||||
@ -278,7 +154,7 @@ export const ModalCreateProject = ({
|
|||||||
onClick={handleClickCreate}
|
onClick={handleClickCreate}
|
||||||
style={{ height: '2.5rem' }}
|
style={{ height: '2.5rem' }}
|
||||||
>
|
>
|
||||||
{isEdit ? 'Сохранить' : 'Создать'}
|
Создать
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import {
|
|||||||
} from '../../components/common/icons/icons';
|
} from '../../components/common/icons/icons';
|
||||||
import { IconWithContent } from '../../components/common/IconWithContent';
|
import { IconWithContent } from '../../components/common/IconWithContent';
|
||||||
import { ExportButton } from '../../components/common/Buttons/ButtonsActions';
|
import { ExportButton } from '../../components/common/Buttons/ButtonsActions';
|
||||||
import { ModalCreateProject } from './ModalCreateProject';
|
|
||||||
|
|
||||||
const ProjectCard = styled.div`
|
const ProjectCard = styled.div`
|
||||||
width: 26.8rem;
|
width: 26.8rem;
|
||||||
@ -138,9 +137,17 @@ const getStatusLabel = (status) => {
|
|||||||
return statusMap[status] || status || 'Не указан';
|
return statusMap[status] || status || 'Не указан';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getLevelLabel = (level) => {
|
||||||
|
const levelMap = {
|
||||||
|
project: 'Проект',
|
||||||
|
subproject: 'Подпроект',
|
||||||
|
task: 'Задача',
|
||||||
|
};
|
||||||
|
return levelMap[level] || level || 'Не указан';
|
||||||
|
};
|
||||||
|
|
||||||
export const ProjectCardComponent = ({
|
export const ProjectCardComponent = ({
|
||||||
project,
|
project,
|
||||||
projectDataInit,
|
|
||||||
exportMode = false,
|
exportMode = false,
|
||||||
isLoadingFile = false,
|
isLoadingFile = false,
|
||||||
onAddForExport,
|
onAddForExport,
|
||||||
@ -165,41 +172,22 @@ export const ProjectCardComponent = ({
|
|||||||
} = project;
|
} = project;
|
||||||
|
|
||||||
const [isEditModalOpen, setEditModalOpen] = useState(false);
|
const [isEditModalOpen, setEditModalOpen] = useState(false);
|
||||||
|
const [editedName, setEditedName] = useState('');
|
||||||
const [currentName, setCurrentName] = useState(name || '');
|
const [currentName, setCurrentName] = useState(name || '');
|
||||||
const [isChecked, setIsChecked] = useState(false);
|
const [isChecked, setIsChecked] = useState(false);
|
||||||
const [editFormData, setEditFormData] = useState({});
|
|
||||||
const [projectData, setProjectData] = useState(projectDataInit);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsChecked(false);
|
setIsChecked(false);
|
||||||
}, [exportMode]);
|
}, [exportMode]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Функция для подготовки данных проекта к редактированию
|
|
||||||
const prepareProjectDataForEdit = () => {
|
|
||||||
return {
|
|
||||||
'ССП/РФ': project.org_unit_id || null,
|
|
||||||
'Год': year || null,
|
|
||||||
'Тип проекта развития': project_type || null,
|
|
||||||
'Название проекта': currentName,
|
|
||||||
'Формат ВСП': vsp_format || null,
|
|
||||||
'Адрес объекта': object_address || '',
|
|
||||||
'Целевая численность, чел.': staff_count || 0,
|
|
||||||
'Тип размещения': placement_type || null,
|
|
||||||
'Площадь размещения, м2': total_area || 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenEdit = (event) => {
|
const handleOpenEdit = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setEditFormData(prepareProjectDataForEdit());
|
setEditedName(currentName);
|
||||||
setEditModalOpen(true);
|
setEditModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseEdit = () => {
|
const handleCloseEdit = () => {
|
||||||
setEditModalOpen(false);
|
setEditModalOpen(false);
|
||||||
setEditFormData({});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCheckboxClick = (event) => {
|
const handleCheckboxClick = (event) => {
|
||||||
@ -223,39 +211,30 @@ export const ProjectCardComponent = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Обновленная функция обновления проекта
|
const updateProject = async (nextName) => {
|
||||||
const updateProject = async (formData) => {
|
|
||||||
try {
|
try {
|
||||||
const projectData = {
|
await ProjectsApi.update(id, { name: nextName });
|
||||||
name: formData['Название проекта'] || currentName,
|
setCurrentName(nextName);
|
||||||
project_type: formData['Тип проекта развития'] || null,
|
|
||||||
vsp_format: formData['Формат ВСП'] || null,
|
|
||||||
object_address: formData['Адрес объекта'] || '',
|
|
||||||
staff_count: Number(formData['Целевая численность, чел.']) || 0,
|
|
||||||
placement_type: formData['Тип размещения'] || null,
|
|
||||||
total_area: Number(formData['Площадь размещения, м2']) || 0,
|
|
||||||
year: formData['Год'] ? Number(formData['Год']) : null,
|
|
||||||
branch_id: formData['ССП/РФ'] || null,
|
|
||||||
};
|
|
||||||
|
|
||||||
await ProjectsApi.update(id, projectData);
|
|
||||||
|
|
||||||
// Обновляем локальные данные
|
|
||||||
setCurrentName(projectData.name);
|
|
||||||
setEditModalOpen(false);
|
setEditModalOpen(false);
|
||||||
toast.success('Данные проекта успешно обновлены');
|
toast.success('Название проекта обновлено');
|
||||||
|
|
||||||
// Можно добавить рефреш данных или перезагрузку страницы
|
|
||||||
window.location.reload(); // или используйте callback для обновления списка
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(
|
toast.error(
|
||||||
error?.response?.data?.detail || 'Не удалось обновить данные проекта',
|
error?.response?.data?.detail || 'Не удалось обновить название проекта',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveProject = async (formData) => {
|
const handleSaveName = async () => {
|
||||||
await updateProject(formData);
|
const nextName = editedName.trim();
|
||||||
|
if (!nextName) {
|
||||||
|
toast.error('Название не может быть пустым');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nextName === currentName) {
|
||||||
|
setEditModalOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await updateProject(nextName);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getInitials = (name) => {
|
const getInitials = (name) => {
|
||||||
@ -273,9 +252,7 @@ export const ProjectCardComponent = ({
|
|||||||
<Box sx={{ width: '100%' }}>
|
<Box sx={{ width: '100%' }}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<ProjectId>ID: {id}</ProjectId>
|
<ProjectId>ID: {id}</ProjectId>
|
||||||
<Stack direction={'row'} spacing={'.5rem'} sx={{
|
<Stack direction={'row'} spacing={'.5rem'} alignItems="center">
|
||||||
|
|
||||||
}} >
|
|
||||||
{!exportMode && (
|
{!exportMode && (
|
||||||
<>
|
<>
|
||||||
{status && (
|
{status && (
|
||||||
@ -285,9 +262,8 @@ export const ProjectCardComponent = ({
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {Пока нет редактирование + экспорт} */}
|
<ExportButton onClick={handleExportProject} />
|
||||||
{/* <ExportButton onClick={handleExportProject} />
|
<Edit onClick={handleOpenEdit} />
|
||||||
<Edit onClick={handleOpenEdit} /> */}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -296,13 +272,18 @@ export const ProjectCardComponent = ({
|
|||||||
<ProjectTitle>{currentName}</ProjectTitle>
|
<ProjectTitle>{currentName}</ProjectTitle>
|
||||||
|
|
||||||
<ProjectInfoGrid>
|
<ProjectInfoGrid>
|
||||||
|
{level && (
|
||||||
|
<LevelChip label={getLevelLabel(level)} size="small" />
|
||||||
|
)}
|
||||||
{project_type && (
|
{project_type && (
|
||||||
<InfoChip label={`Тип: ${project_type}`} size="small" />
|
<InfoChip label={`Тип: ${project_type}`} size="small" />
|
||||||
)}
|
)}
|
||||||
{report_count !== undefined && report_count !== null && (
|
{report_count !== undefined && report_count !== null && (
|
||||||
<InfoChip label={`Отчетов: ${report_count}`} size="small" />
|
<InfoChip label={`Отчетов: ${report_count}`} size="small" />
|
||||||
)}
|
)}
|
||||||
|
{parent_id && (
|
||||||
|
<InfoChip label={`Родитель: ${parent_id}`} size="small" />
|
||||||
|
)}
|
||||||
</ProjectInfoGrid>
|
</ProjectInfoGrid>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -321,6 +302,18 @@ export const ProjectCardComponent = ({
|
|||||||
<IconWithContent icon={<UserIcon />}>
|
<IconWithContent icon={<UserIcon />}>
|
||||||
{org_unit_name || 'Организация не указана'}
|
{org_unit_name || 'Организация не указана'}
|
||||||
</IconWithContent>
|
</IconWithContent>
|
||||||
|
{object_address && (
|
||||||
|
<IconWithContent >
|
||||||
|
{object_address}
|
||||||
|
</IconWithContent>
|
||||||
|
)}
|
||||||
|
{(staff_count || total_area) && (
|
||||||
|
<IconWithContent >
|
||||||
|
{staff_count && `👥 ${staff_count}`}
|
||||||
|
{staff_count && total_area && ' | '}
|
||||||
|
{total_area && `📐 ${total_area} м²`}
|
||||||
|
</IconWithContent>
|
||||||
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
||||||
{exportMode && (
|
{exportMode && (
|
||||||
@ -334,16 +327,6 @@ export const ProjectCardComponent = ({
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</ProjectCard>
|
</ProjectCard>
|
||||||
|
|
||||||
<ModalCreateProject
|
|
||||||
open={isEditModalOpen}
|
|
||||||
onClose={handleCloseEdit}
|
|
||||||
onCreate={handleSaveProject}
|
|
||||||
config={projectData}
|
|
||||||
initialData={editFormData}
|
|
||||||
isEdit={true}
|
|
||||||
projectId={id}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
// src/pages/ProjectsPage/ProjectsPage.jsx
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { ProjectsApi } from '../../api/projects';
|
import { ProjectsApi } from '../../api/projects';
|
||||||
import { CardsSkeleton } from '../../components/common/Skeleton';
|
import { CardsSkeleton } from '../../components/common/Skeleton';
|
||||||
@ -17,14 +18,12 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TextField,
|
|
||||||
Autocomplete,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { ModalCreateProject } from './ModalCreateProject';
|
import { ModalCreateProject } from './ModalCreateProject';
|
||||||
import {
|
import {
|
||||||
ROLES_NAME_ID,
|
ROLES_NAME_ID,
|
||||||
} from '../../constants/constants';
|
} from '../../constants';
|
||||||
import { useAuth } from '../../app/context/AuthProvider';
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
||||||
import {
|
import {
|
||||||
@ -35,20 +34,79 @@ import { exportBulkForms, exportSingleForm } from '../../utils/exportForm';
|
|||||||
import { WhitePlus } from '../../components/common/icons/icons';
|
import { WhitePlus } from '../../components/common/icons/icons';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { PaginatedList } from '../../components/common/PaginatedList';
|
import { PaginatedList } from '../../components/common/PaginatedList';
|
||||||
import { DEFAULT_PROJECT_CONFIG } from '../../constants/projectConfig';
|
|
||||||
import { SspApi } from '../../api/ssp';
|
|
||||||
|
|
||||||
export { DEFAULT_PROJECT_CONFIG } from '../../constants/projectConfig';
|
|
||||||
|
// Конфигурация для создания проекта
|
||||||
|
const defaultProjectConfig = {
|
||||||
|
'Тип проекта развития': {
|
||||||
|
type: 'multiselect',
|
||||||
|
options: [
|
||||||
|
'Открытие ВСП',
|
||||||
|
'Закрытие ВСП',
|
||||||
|
'Переезд ВСП',
|
||||||
|
'Реновация РФ',
|
||||||
|
'Открытие УРМ',
|
||||||
|
],
|
||||||
|
placeholder: 'Выберите тип проекта',
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
'Название проекта': {
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'Введите название проекта',
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
'Формат ВСП': {
|
||||||
|
type: 'multiselect',
|
||||||
|
options: [
|
||||||
|
'Фланговый',
|
||||||
|
'Типовой',
|
||||||
|
'Розничный',
|
||||||
|
'МСБ',
|
||||||
|
'Лёгкий',
|
||||||
|
'Мини',
|
||||||
|
'Розничный-киоск',
|
||||||
|
'МБО',
|
||||||
|
'Офис самообслуживания',
|
||||||
|
'Другое',
|
||||||
|
],
|
||||||
|
placeholder: 'Выберите формат ВСП',
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
'Адрес объекта': {
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'Введите адрес объекта',
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
'Целевая численность, чел.': {
|
||||||
|
type: 'number',
|
||||||
|
placeholder: 'Введите число',
|
||||||
|
defaultValue: 0,
|
||||||
|
min: 0,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
'Тип размещения': {
|
||||||
|
type: 'multiselect',
|
||||||
|
options: ['Собственность', 'Аренда', 'Субаренда'],
|
||||||
|
placeholder: 'Выберите тип размещения',
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
'Площадь размещения, м2': {
|
||||||
|
type: 'number',
|
||||||
|
placeholder: 'Введите число',
|
||||||
|
defaultValue: 0,
|
||||||
|
min: 0,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default function ProjectsPage() {
|
export default function ProjectsPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
// Новые фильтры
|
const [statusFilter, setStatusFilter] = useState('');
|
||||||
const [yearFilter, setYearFilter] = useState('');
|
const [levelFilter, setLevelFilter] = useState('');
|
||||||
const [branchFilter, setBranchFilter] = useState('');
|
|
||||||
|
|
||||||
// Pagination state
|
// Pagination state
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
@ -60,47 +118,11 @@ export default function ProjectsPage() {
|
|||||||
const [exportMode, setExportMode] = useState(false);
|
const [exportMode, setExportMode] = useState(false);
|
||||||
const [exportList, setExportList] = useState([]);
|
const [exportList, setExportList] = useState([]);
|
||||||
|
|
||||||
const [projectData, setProjectData] = useState(DEFAULT_PROJECT_CONFIG);
|
|
||||||
|
|
||||||
// Получаем список годов для фильтра (можно динамически или задать диапазон)
|
|
||||||
const yearOptions = useMemo(() => {
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
const years = [];
|
|
||||||
for (let year = currentYear - 5; year <= currentYear + 2; year++) {
|
|
||||||
years.push(year);
|
|
||||||
}
|
|
||||||
return years;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!user) return;
|
|
||||||
if (user.role_id == ROLES_NAME_ID.admin) {
|
|
||||||
SspApi.getAll().then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
setProjectData(prev => ({
|
|
||||||
...prev, 'ССП/РФ': {
|
|
||||||
...prev['ССП/РФ'],
|
|
||||||
options: data.result
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.error('Ошибка при получении ССП/РФ');
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProjectData(prev => ({
|
|
||||||
...prev, 'ССП/РФ': {
|
|
||||||
...prev['ССП/РФ'],
|
|
||||||
options: user.org_units,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}, [user])
|
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Маппинг полей из модалки в API
|
||||||
const mapFormDataToApi = (formData) => {
|
const mapFormDataToApi = (formData) => {
|
||||||
const apiData = {
|
const apiData = {
|
||||||
name: formData['Название проекта'] || '',
|
name: formData['Название проекта'] || '',
|
||||||
@ -110,9 +132,12 @@ export default function ProjectsPage() {
|
|||||||
staff_count: formData['Целевая численность, чел.'] || 0,
|
staff_count: formData['Целевая численность, чел.'] || 0,
|
||||||
placement_type: formData['Тип размещения'] || null,
|
placement_type: formData['Тип размещения'] || null,
|
||||||
total_area: formData['Площадь размещения, м2'] || 0,
|
total_area: formData['Площадь размещения, м2'] || 0,
|
||||||
year: formData['Год'] || null,
|
// Добавляем значения по умолчанию для обязательных полей
|
||||||
branch_id: formData['ССП/РФ'] || null,
|
year: new Date().getFullYear(),
|
||||||
|
org_unit_id: user?.org_unit_id || null,
|
||||||
|
form_type_code: 'project', // или другое значение по умолчанию
|
||||||
|
status: 'active',
|
||||||
|
level: 'project',
|
||||||
};
|
};
|
||||||
return apiData;
|
return apiData;
|
||||||
};
|
};
|
||||||
@ -144,9 +169,6 @@ export default function ProjectsPage() {
|
|||||||
const params = {
|
const params = {
|
||||||
offset: skip,
|
offset: skip,
|
||||||
limit: currentLimit,
|
limit: currentLimit,
|
||||||
// Добавляем фильтры в параметры запроса
|
|
||||||
...(yearFilter && { year: parseInt(yearFilter) }),
|
|
||||||
...(branchFilter && { branch_id: parseInt(branchFilter.id) }),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await ProjectsApi.list(params);
|
const res = await ProjectsApi.list(params);
|
||||||
@ -162,11 +184,10 @@ export default function ProjectsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Сбрасываем страницу при изменении любых фильтров
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
loadProjects(1, limit);
|
loadProjects(1, limit);
|
||||||
}, [yearFilter, branchFilter]);
|
}, [query, statusFilter, levelFilter]);
|
||||||
|
|
||||||
const handlePageChange = (event, value) => {
|
const handlePageChange = (event, value) => {
|
||||||
setPage(value);
|
setPage(value);
|
||||||
@ -180,13 +201,6 @@ export default function ProjectsPage() {
|
|||||||
loadProjects(1, newLimit);
|
loadProjects(1, newLimit);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Сброс всех фильтров
|
|
||||||
const handleClearFilters = () => {
|
|
||||||
setYearFilter('');
|
|
||||||
setBranchFilter('');
|
|
||||||
setPage(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadProjects(page, limit);
|
loadProjects(page, limit);
|
||||||
}, []);
|
}, []);
|
||||||
@ -239,68 +253,6 @@ export default function ProjectsPage() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Фильтры */}
|
|
||||||
<Box sx={{ mb: 3, display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'center' }}>
|
|
||||||
|
|
||||||
{/* Фильтр по году */}
|
|
||||||
<FormControl size="small" sx={{ minWidth: 150, height:'2.5rem' }}>
|
|
||||||
<Select
|
|
||||||
displayEmpty
|
|
||||||
value={yearFilter}
|
|
||||||
onChange={(e) => setYearFilter(e.target.value)}
|
|
||||||
>
|
|
||||||
<MenuItem value="">Все годы</MenuItem>
|
|
||||||
{yearOptions.map((year) => (
|
|
||||||
<MenuItem key={year} value={year}>
|
|
||||||
{year}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
{/* Фильтр по ССП/РФ */}
|
|
||||||
<FormControl size="small" sx={{ minWidth: '10rem' }}>
|
|
||||||
<Autocomplete
|
|
||||||
size="small"
|
|
||||||
options={projectData['ССП/РФ']?.options ?? []}
|
|
||||||
noOptionsText="Не найдено"
|
|
||||||
getOptionLabel={(option) => option.title || option.name || '-'}
|
|
||||||
value={
|
|
||||||
branchFilter
|
|
||||||
}
|
|
||||||
onChange={(event, newValue) => {
|
|
||||||
setBranchFilter(newValue);
|
|
||||||
}}
|
|
||||||
sx={{ height: '2.5rem', minWidth: '21.25rem' }}
|
|
||||||
renderInput={(params) => (
|
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
placeholder="Все"
|
|
||||||
sx={{
|
|
||||||
height: '2.5rem',
|
|
||||||
width: '21.25rem',
|
|
||||||
'& .MuiOutlinedInput-root': {
|
|
||||||
height: '2.5rem',
|
|
||||||
'& fieldset': {
|
|
||||||
borderRadius: '0.5rem',
|
|
||||||
borderColor: 'rgba(0,0,0,0.1)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
onClick={handleClearFilters}
|
|
||||||
sx={{ ml: 'auto' }}
|
|
||||||
>
|
|
||||||
Сбросить фильтры
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<PaginatedList
|
<PaginatedList
|
||||||
items={items}
|
items={items}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@ -315,7 +267,6 @@ export default function ProjectsPage() {
|
|||||||
<ProjectCardComponent
|
<ProjectCardComponent
|
||||||
key={project.id}
|
key={project.id}
|
||||||
project={project}
|
project={project}
|
||||||
projectDataInit={projectData}
|
|
||||||
{...exportProps}
|
{...exportProps}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -335,7 +286,7 @@ export default function ProjectsPage() {
|
|||||||
open={modalOpen}
|
open={modalOpen}
|
||||||
onClose={() => setModalOpen(false)}
|
onClose={() => setModalOpen(false)}
|
||||||
onCreate={handleCreateProject}
|
onCreate={handleCreateProject}
|
||||||
config={projectData}
|
config={defaultProjectConfig}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export default function TablePage() {
|
|||||||
|
|
||||||
const connection = connectToTable(tableId, user?.id, {
|
const connection = connectToTable(tableId, user?.id, {
|
||||||
onError: () => {
|
onError: () => {
|
||||||
toast.error('Ошибка подключения к серверу');
|
toast.error('Ошибка подключения к серверу реального времени');
|
||||||
},
|
},
|
||||||
onClose: (event) => {
|
onClose: (event) => {
|
||||||
if (isUnmountingRef.current) {
|
if (isUnmountingRef.current) {
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { createPortal } from 'react-dom';
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { TasksApi } from '../../api/tasks';
|
import { TasksApi } from '../../api/tasks';
|
||||||
import { TablesApi } from '../../api/tables';
|
import { TablesApi } from '../../api/tables';
|
||||||
import { ProjectsApi } from '../../api/projects'; // Добавьте импорт API для проектов
|
|
||||||
import {
|
import {
|
||||||
TaskInfoContainer,
|
TaskInfoContainer,
|
||||||
NameTask,
|
NameTask,
|
||||||
@ -22,13 +21,13 @@ import { FormsApi } from '../../api/forms';
|
|||||||
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
||||||
import { exportSingleForm } from '../../utils/exportForm';
|
import { exportSingleForm } from '../../utils/exportForm';
|
||||||
import TablesList from '../../components/common/TableList/TableList';
|
import TablesList from '../../components/common/TableList/TableList';
|
||||||
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, SHEET_NAME } from '../../constants/constants';
|
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, SHEET_NAME } from '../../constants';
|
||||||
|
|
||||||
const TaskInfo = ({ task, isProject }) => {
|
const TaskInfo = ({ task }) => {
|
||||||
const portalContent = (
|
const portalContent = (
|
||||||
<TaskInfoContainer>
|
<TaskInfoContainer>
|
||||||
<BackButton to={isProject ? "/projects" : "/tasks"} />
|
<BackButton to="/tasks" />
|
||||||
<NameTask>{isProject ? 'Проект' : FORM_TYPE_TRANSLATE[task.form_type_code] || ''}</NameTask>
|
<NameTask>{FORM_TYPE_TRANSLATE[task.form_type_code] || ''}</NameTask>
|
||||||
</TaskInfoContainer>
|
</TaskInfoContainer>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -40,13 +39,9 @@ const TaskInfo = ({ task, isProject }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function TaskPage() {
|
export default function TaskPage() {
|
||||||
const { taskId, projectId } = useParams();
|
const { taskId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const id = Number(taskId);
|
||||||
// Определяем, что у нас за ID и какой тип
|
|
||||||
const isProject = !!projectId;
|
|
||||||
const id = isProject ? Number(projectId) : Number(taskId);
|
|
||||||
|
|
||||||
const [task, setTask] = useState(null);
|
const [task, setTask] = useState(null);
|
||||||
const [tables, setTables] = useState([]);
|
const [tables, setTables] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@ -55,42 +50,23 @@ export default function TaskPage() {
|
|||||||
const [modalProjectOpen, setModalProjectOpen] = useState(false);
|
const [modalProjectOpen, setModalProjectOpen] = useState(false);
|
||||||
const [tempProjects, setTempProjects] = useState([]);
|
const [tempProjects, setTempProjects] = useState([]);
|
||||||
const [projects, setProjects] = useState([]);
|
const [projects, setProjects] = useState([]);
|
||||||
const [isProjectData, setIsProjectData] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getSheets = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
if (isProject) {
|
const data = await TasksApi.getSheets(id);
|
||||||
// Получаем информацию о проекте
|
// Преобразуем данные с учетом direction
|
||||||
const projectData = await ProjectsApi.get(id);
|
const mappedTables = data.result.all_sheets.map(sheet => ({
|
||||||
setTask(projectData.result);
|
|
||||||
setIsProjectData(true);
|
|
||||||
console.log(projectData.result.reports);
|
|
||||||
|
|
||||||
const mappedTables = projectData.result.reports.map(sheet => ({
|
|
||||||
name: SHEET_NAME[sheet.report_type] || sheet.report_type,
|
|
||||||
sheet: sheet.report_type,
|
|
||||||
}));
|
|
||||||
setTables(mappedTables);
|
|
||||||
} else {
|
|
||||||
// Получаем информацию о задаче
|
|
||||||
const taskData = await TasksApi.getById(id);
|
|
||||||
setTask(taskData.result);
|
|
||||||
setIsProjectData(false);
|
|
||||||
|
|
||||||
// Получаем таблицы задачи
|
|
||||||
const tablesData = await TasksApi.getSheets(id);
|
|
||||||
const mappedTables = tablesData.result.all_sheets.map(sheet => ({
|
|
||||||
name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
|
name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
|
||||||
sheet: sheet.sheet_name,
|
sheet: sheet.sheet_name,
|
||||||
direction: sheet.direction,
|
direction: sheet.direction,
|
||||||
}));
|
}));
|
||||||
setTables(mappedTables.filter(t => t.sheet !== "OTCH9F"));
|
//пока скрыт
|
||||||
}
|
setTables(mappedTables.filter(t => t.sheet !== "OTCH9F",));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(
|
toast.error(
|
||||||
error?.response?.data?.detail || `Ошибка получения данных ${isProject ? 'проекта' : 'задачи'}`,
|
error?.response?.data?.detail || 'Ошибка получения данных задачи',
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -98,11 +74,27 @@ export default function TaskPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (Number.isFinite(id)) {
|
if (Number.isFinite(id)) {
|
||||||
getData();
|
getSheets();
|
||||||
}
|
}
|
||||||
}, [id, isProject]);
|
}, [id]);
|
||||||
|
|
||||||
// Объединили два useEffect в один
|
useEffect(() => {
|
||||||
|
const getFormInfo = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const data = await TasksApi.getById(id);
|
||||||
|
setTask(data.result);
|
||||||
|
} catch (error) {
|
||||||
|
// обработка ошибки
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Number.isFinite(id)) {
|
||||||
|
getFormInfo();
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
const handleCreateProject = (projectData) => {
|
const handleCreateProject = (projectData) => {
|
||||||
// логика создания проекта
|
// логика создания проекта
|
||||||
@ -114,14 +106,6 @@ export default function TaskPage() {
|
|||||||
return searchString.includes(query.toLowerCase());
|
return searchString.includes(query.toLowerCase());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Функция для экспорта
|
|
||||||
const handleExport = () => {
|
|
||||||
const fileName = isProject
|
|
||||||
? `project_${task?.title || id}`
|
|
||||||
: `form_${task?.title || taskId}`;
|
|
||||||
exportSingleForm(id, fileName);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center pt-12">
|
<div className="flex justify-center pt-12">
|
||||||
@ -132,7 +116,7 @@ export default function TaskPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{task && <TaskInfo task={task} isProject={isProject} />}
|
{task && <TaskInfo task={task} />}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -158,7 +142,9 @@ export default function TaskPage() {
|
|||||||
onChange={setQuery}
|
onChange={setQuery}
|
||||||
/>
|
/>
|
||||||
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end', gap: '.5rem' }}>
|
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end', gap: '.5rem' }}>
|
||||||
<ExportWithTextButton onClick={handleExport} />
|
<ExportWithTextButton
|
||||||
|
onClick={() => exportSingleForm(taskId, task?.title ?? 'form')}
|
||||||
|
/>
|
||||||
<PrimaryOutlinedButton
|
<PrimaryOutlinedButton
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={() => setModalStageOpen(true)}
|
onClick={() => setModalStageOpen(true)}
|
||||||
@ -169,9 +155,8 @@ export default function TaskPage() {
|
|||||||
<SettingStageModal
|
<SettingStageModal
|
||||||
isOpen={modalStageOpen}
|
isOpen={modalStageOpen}
|
||||||
onClose={() => setModalStageOpen(false)}
|
onClose={() => setModalStageOpen(false)}
|
||||||
taskId={isProject ? undefined : taskId}
|
taskId={taskId}
|
||||||
projectId={isProject ? projectId : undefined}
|
mode="task"
|
||||||
mode={isProject ? "project" : "task"}
|
|
||||||
formTypeCode={task?.form_type_code || ''}
|
formTypeCode={task?.form_type_code || ''}
|
||||||
sheets={tables}
|
sheets={tables}
|
||||||
/>
|
/>
|
||||||
@ -183,9 +168,8 @@ export default function TaskPage() {
|
|||||||
query={query}
|
query={query}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
filteredTables={filteredTables}
|
filteredTables={filteredTables}
|
||||||
basePath={"/table"}
|
basePath="/table"
|
||||||
formInfo={task}
|
formInfo={task}
|
||||||
isProject={isProject}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import {
|
|||||||
ORG_UNIT_TYPE_OPTIONS,
|
ORG_UNIT_TYPE_OPTIONS,
|
||||||
ROLES_NAME_ID,
|
ROLES_NAME_ID,
|
||||||
FORM_TYPE_TRANSLATE
|
FORM_TYPE_TRANSLATE
|
||||||
} from '../../constants/constants';
|
} from '../../constants';
|
||||||
import { useAuth } from '../../app/context/AuthProvider';
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
import { SspApi } from '../../api/ssp';
|
import { SspApi } from '../../api/ssp';
|
||||||
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
||||||
@ -256,6 +256,7 @@ export default function TasksPage() {
|
|||||||
<>
|
<>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<HeaderSwitch />
|
<HeaderSwitch />
|
||||||
|
<HeaderSwitch />
|
||||||
|
|
||||||
<PaginatedList
|
<PaginatedList
|
||||||
items={items}
|
items={items}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user