import axios from "axios"; import { useMutation, useQuery } from "@tanstack/react-query"; import { STATUSES } from "./config"; import { usePointSelection } from "./stores/usePointSelection"; import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "./stores/usePendingPointsFilters"; import { appendFiltersInUse } from "./utils.js"; import { useMode } from "./stores/useMode.js"; import { useMemo } from "react"; import { useUpdateLayerCounter } from "./stores/useUpdateLayerCounter.js"; import { keycloak } from "./keycloak.js"; import { useKeycloak } from "@react-keycloak/web"; export const BASE_URL = import.meta.env.VITE_API_URL; export const api = axios.create({ baseURL: import.meta.env.MODE === "development" ? "http://localhost:5173/" : BASE_URL, }); api.interceptors.request.use(async function (config) { const token = keycloak.token; if (token) { config.headers.Authorization = `Bearer ${ token }`; } return config; }); export const useDbTableName = () => { const {isImportMode} = useMode(); if (isImportMode) return "pre_placement_points"; return "placement_points"; } export const useSourceLayerName = () => { const {isImportMode} = useMode(); if (isImportMode) return "public.prepoints_with_dist"; return "public.points_with_dist"; } const enrichParamsWithRegionFilter = (params, region) => { const resultParams = params ? params : new URLSearchParams(); if (region) { if (region.type === "ao") { resultParams.append("district[]", region.id); } if (region.type === "rayon") { resultParams.append("area[]", region.id); } } return resultParams; }; export const getPoints = async (params, region, dbTable = "placement_points", signal) => { const resultParams = enrichParamsWithRegionFilter(params, region); const { data } = await api.get( `/api/${dbTable}/?${resultParams.toString()}`, {signal} ); return data; }; export const exportPoints = async (params, region, dbTable = "placement_points") => { const resultParams = enrichParamsWithRegionFilter(params, region); const { data } = await api.get( `/api/${dbTable}/to_excel/?${resultParams.toString()}`, { responseType: "arraybuffer" } ); return data; }; export const downloadImportTemplate = async () => { const { data } = await api.get( '/api/pre_placement_points/download_template/', { responseType: "arraybuffer" } ); return data; }; export const uploadPointsFile = async (file, config) => { const formData = new FormData(); formData.append("file", file); const { data } = await api.post( `/api/pre_placement_points/load_matching_file/`, formData, config ); return data; }; export const importPoints = async (id) => { const formData = new FormData(); formData.append("id", id); const { data } = await api.post( `/api/pre_placement_points/start_matching/`, formData ); return data; }; export const getImportStatus = async () => { const { data } = await api.get( `/api/pre_placement_points/import_status/` ); return data; }; export const useGetTotalInitialPointsCount = () => { const dbTable = useDbTableName(); const { updateCounter } = useUpdateLayerCounter(); return useQuery( ["all-initial-count", dbTable, updateCounter], async ({signal}) => { const params = new URLSearchParams({ page: 1, page_size: 1, }); params.append("status[]", [STATUSES.pending, STATUSES.cancelled]); return await getPoints(params, null, dbTable, signal); }, { select: (data) => data.count, refetchOnWindowFocus: false } ); }; export const useGetFilteredPendingPointsCount = (isMerge) => { const { filters, ranges } = usePendingPointsFilters(); const { updateCounter } = useUpdateLayerCounter(); const { categories, region, } = filters; const { selection: { included }, } = usePointSelection(); const includedIds = [...included]; const getParams = () => { const params = new URLSearchParams({ page: 1, page_size: 1, "categories[]": categories, "included[]": includedIds, }); params.append("status[]", STATUSES.pending); appendFiltersInUse(params, filters, ranges); return params; } const getMergeParams = () => { return new URLSearchParams({ "matching_status": "New", }); } const dbTable = useDbTableName(); return useQuery( ["filtered-points", filters, dbTable, includedIds, updateCounter], async ({signal}) => { const params = isMerge ? getMergeParams() : getParams(); return await getPoints(params, region, dbTable, signal); }, { select: (data) => data.count, keepPreviousData: true, refetchOnWindowFocus: false } ); }; export const useGetPointsToMergeCount = () => { const getMergeParams = () => { return new URLSearchParams({ "matching_status": "New", }); } const dbTable = useDbTableName(); return useQuery( ["filtered-points", dbTable], async () => { const params = getMergeParams(); return await getPoints(params, null, dbTable); }, { select: (data) => data.count, keepPreviousData: true } ); }; export const useMergePointsToDb = () => { return useMutation({ mutationFn: () => { return api.post( `/api/pre_placement_points/move_points/` ); }, }); }; export const useGetPermissions = () => { const {setImportMode} = useMode(); return useQuery(["permissions"], async () => { const { data } = await api.get("/api/me/"); if (data?.groups?.includes("postnet_editor")) { return "editor"; } setImportMode(false); return "viewer"; }); }; const TASK_STATUSES = { finished: "Перерасчет ML завершен" } export const useCanEdit = () => { const { keycloak } = useKeycloak(); const { data: statusData } = useLastMLRun(); const hasFinishedUpdate = useMemo(() => { return statusData?.task_status === TASK_STATUSES.finished }, [statusData]); return keycloak.hasResourceRole("postnet_editor", "postnet") && hasFinishedUpdate; }; export const useUpdatePostamatId = () => { return useMutation({ mutationFn: (params) => { return api.put( `/api/placement_points/update_postamat_id/?${params.toString()}` ); }, }); }; export const getLastMLRun = async () => { const { data } = await api.get( `/api/placement_points/last_time_ml_run/` ); return data; }; export const startML = async () => { const { data } = await api.get( `/api/placement_points/start/` ); return data; }; export const getPostamatesAndPvzGroups = async () => { const { data } = await api.get( `/api/postamate_and_pvz_groups/` ); return data; }; export const usePostamatesAndPvzGroups = () => { return useQuery( ['groups'], async () => { return await getPostamatesAndPvzGroups(); }, ); }; export const getOtherGroups = async () => { const { data } = await api.get( `/api/other_object_groups/` ); return data; }; export const useOtherGroups = () => { return useQuery( ['other_groups'], async () => { return await getOtherGroups(); }, ); }; export const useLastMLRun = () => { return useQuery( ['last_time'], async () => { return await getLastMLRun(); }, { refetchInterval: 5000, } ); } export const useGetPendingPointsRange = (dbTable) => { const { isImportMode } = useMode(); const statusFilter = isImportMode ? '' : `?status[]=${STATUSES.pending}`; return useQuery( ["prediction-max-min", dbTable], async () => { const { data, isInitialLoading, isFetching } = await api.get( `/api/${dbTable}/filters/${statusFilter}` ); return {data, isLoading: isInitialLoading || isFetching}; }, { select: ({data, isLoading}) => { const distToGroupsArr = data.dist_to_groups.map((groupRange) => { return { [`d${groupRange.group_id}`]: [Math.floor(groupRange.dist[0]), Math.min(Math.ceil(groupRange.dist[1]), 4000)], } }); const distToGroups = Object.assign({}, ...distToGroupsArr); const rangesArr = RANGE_FILTERS_KEYS.map((key) => { if ((/d[0-9]/.test(key))) return; return { [key]: [Math.floor(data[key][0]), Math.ceil(data[key][1])] } }).filter(item => !!item); const ranges = Object.assign({}, ...rangesArr); return { fullRange: { prediction: data.prediction_current, ...ranges, ...distToGroups }, isLoading: isLoading }; }, } ); }; export const useGetPopupPoints = (features) => { const pointsIds = features.map(f => f.properties.id); const dbTable = useDbTableName(); const { data, isInitialLoading, isFetching } = useQuery( ["popup_data", features], async () => { const params = new URLSearchParams({ "location_ids[]": pointsIds, }); const { data } = await api.get( `/api/${dbTable}/?${params.toString()}` ); return data.results; }, { refetchOnWindowFocus: false, refetchOnMount: false } ); return { data, isLoading: isInitialLoading || isFetching }; }; export const deletePoint = async (id) => { const formData = new FormData(); formData.append("ids", id); await api.delete( `/api/pre_placement_points/delete_points/`, { data: formData } ) }