38 lines
2.9 KiB
TypeScript
38 lines
2.9 KiB
TypeScript
"use client"
|
|
|
|
import {useEffect,useRef,useState} from "react"
|
|
import {normalizeAlertsConfig,type PutRulesBody} from "@mmapp/contracts/alerts"
|
|
import {can} from "@/lib/auth"
|
|
import {ApiClientError} from "@/shared/api/http-client"
|
|
import {saveAlertRules} from "@/shared/api/alerts"
|
|
const pending=new Set<string>()
|
|
const key=(source:string)=>"mm:alerts-rules-pending:"+source
|
|
function unresolved(source:string){try{return pending.has(source)||sessionStorage.getItem(key(source))==="true"}catch{return pending.has(source)}}
|
|
function checkpoint(source:string,value:boolean){if(value)pending.add(source);else pending.delete(source);try{if(value)sessionStorage.setItem(key(source),"true");else sessionStorage.removeItem(key(source))}catch{/* The memory guard survives section navigation. */}}
|
|
|
|
export function useAlertsConfigWrite(base:string,live:boolean,available:boolean,expectedRevision:string|null,onBegin:()=>boolean,onEnd:()=>void){
|
|
const source=JSON.stringify([live,base])
|
|
const [busy,setBusy]=useState(false),[error,setError]=useState<string|null>(null),[outcome,setOutcome]=useState<string|null>(null),[unknown,setUnknown]=useState(false),[ready,setReady]=useState(false)
|
|
const active=useRef(true),lock=useRef(false),controller=useRef<AbortController|null>(null)
|
|
useEffect(()=>{active.current=true;queueMicrotask(()=>{if(active.current){setReady(true);setUnknown(unresolved(source))}});return()=>{active.current=false;controller.current?.abort()}},[source])
|
|
const enabled=ready&&live&&available&&!!expectedRevision&&can("mm:alerts:write")
|
|
async function save(payload:PutRulesBody){
|
|
if(!enabled||lock.current||unknown||!active.current||!can("mm:alerts:write"))return null
|
|
let normalized:ReturnType<typeof normalizeAlertsConfig>
|
|
try{normalized=normalizeAlertsConfig(payload)}catch(e){setError(e instanceof Error?e.message:"Проверьте параметры правил");return null}
|
|
if(!onBegin())return null
|
|
lock.current=true;controller.current=new AbortController();setBusy(true);setError(null);setOutcome(null);checkpoint(source,true);setUnknown(true)
|
|
const current=()=>active.current&&can("mm:alerts:write")
|
|
try{
|
|
const receipt=await saveAlertRules(base,normalized,{signal:controller.current.signal,expectedRevision:expectedRevision??undefined});if(!current())return null
|
|
checkpoint(source,false);setUnknown(false);setOutcome("Правила и группы сохранены в БД");return receipt
|
|
}catch(e){
|
|
const rejected=e instanceof ApiClientError&&[400,401,403,409,412,422,428].includes(e.status)
|
|
if(rejected&¤t())checkpoint(source,false)
|
|
if(current()){setUnknown(!rejected);setError(e instanceof Error?e.message:"Результат сохранения правил не получен")};return null
|
|
}finally{lock.current=false;controller.current=null;if(active.current){setBusy(false);onEnd()}}
|
|
}
|
|
function review(){if(!enabled||busy)return;checkpoint(source,false);setUnknown(false)}
|
|
return {save,busy,error,outcome,unknown,enabled,review}
|
|
}
|