@@ -593,7 +647,7 @@ const SeweragePenetrationRateInp = () => {
return errors
.map((e) => {
const prefix = e.vrfcCd === 'IMR' ? 'IMR검증 - ' : '일반검증 - ';
- return `${prefix}${e.rsnExpln}`;
+ return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
})
.filter(Boolean)
.join('\n');
@@ -614,6 +668,25 @@ const SeweragePenetrationRateInp = () => {
submitMode={isExceptionSubmitFlow ? 'exception' : 'normal'}
isLoading={isLoading}
/>
+
+
>
);
};
diff --git a/apps/st/src/components/statistics/management/LogHistory.tsx b/apps/st/src/components/statistics/management/LogHistory.tsx
index ff1747c33..e2abaf60d 100644
--- a/apps/st/src/components/statistics/management/LogHistory.tsx
+++ b/apps/st/src/components/statistics/management/LogHistory.tsx
@@ -7,7 +7,7 @@ import useSWR from "swr";
import { useAlertStore } from "@/shared/lib";
import { CodeSelectWithAll } from "@/entities/code";
-import { statisticsAPi } from "@/entities/statistics/api/statistics-service";
+import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import type {
ApiResponse,
SearchCondition,
@@ -36,13 +36,13 @@ import { toGridRows } from "@/components/util/grid/gridFunction";
const PAGE_SIZE = 100;
const getSidoListFetcher = async (): Promise
> => {
- return statisticsAPi.getSidoList();
+ return statisticsApi.getSidoList();
};
const getSggListFetcher = async (
key: [string, string],
): Promise> => {
- return statisticsAPi.getSggList(key[1]);
+ return statisticsApi.getSggList(key[1]);
};
const LogHistory = () => {
@@ -158,7 +158,7 @@ const LogHistory = () => {
try {
setIsLoading(true);
- const response = await statisticsAPi.getLogList(param);
+ const response = await statisticsApi.getLogList(param);
if (response.status === "success") {
const lostList = response.data?.statsLogList ?? [];
diff --git a/apps/st/src/components/statistics/management/LogHistoryDetailDialog.tsx b/apps/st/src/components/statistics/management/LogHistoryDetailDialog.tsx
index b451b3a14..09667bc88 100644
--- a/apps/st/src/components/statistics/management/LogHistoryDetailDialog.tsx
+++ b/apps/st/src/components/statistics/management/LogHistoryDetailDialog.tsx
@@ -12,7 +12,7 @@ import {
DialogTitle,
} from "@common/components";
-import { statisticsAPi } from "@/entities/statistics/api/statistics-service";
+import { statisticsApi } from "@/entities/statistics/api/statistics-service";
export interface LogHistoryDetailDialogProps {
open: boolean;
@@ -30,7 +30,7 @@ export function LogHistoryDetailDialog({
const sn = row.statsLogSn;
void (async () => {
- const response = await statisticsAPi.getLogDetail(
+ const response = await statisticsApi.getLogDetail(
sn !== undefined && sn !== null ? String(sn) : "",
);
console.log(response.data);
diff --git a/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx b/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx
index 3af748260..b0f38ed02 100644
--- a/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx
+++ b/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx
@@ -13,7 +13,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { SeweragePenetrationRateInfoList } from "@/components/util/column/review/SeweragePenetrationRateRvwColumn";
import type { SewerageSupplyRateReviewItem, statsStatusInfo } from "@/entities/statistics/model/statistics-sewerage-penetration-review-interface";
import type { ReviewOpinionSaveRequest } from "@/entities/statistics/model/statistics-interface";
-import { statisticsAPi } from "@/entities/statistics/api/statistics-service";
+import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
@@ -141,7 +141,7 @@ const SeweragePenetrationRateRvw = () => {
try {
const userInfo = await getUserInfo();
- const response = await statisticsAPi.submitReview({
+ const response = await statisticsApi.submitReview({
statsYr: reviewOpinionContext.statsYr,
sewerBzmnCd: reviewOpinionContext.sewerBzmnCd,
statsCd: reviewOpinionContext.statsCd,
@@ -169,7 +169,7 @@ const SeweragePenetrationRateRvw = () => {
}
try {
- const detailRes = await statisticsAPi.getReviewOpinionDetail({
+ const detailRes = await statisticsApi.getReviewOpinionDetail({
statsYr: reviewOpinionContext.statsYr,
sewerBzmnCd: reviewOpinionContext.sewerBzmnCd,
statsCd: reviewOpinionContext.statsCd,
@@ -191,7 +191,7 @@ const SeweragePenetrationRateRvw = () => {
const saveReviewOpinion = async (param: ReviewOpinionSaveRequest) => {
try {
- const response = await statisticsAPi.saveReviewOpinion(param);
+ const response = await statisticsApi.saveReviewOpinion(param);
if (response.status !== "success") {
alert(response.message || "저장에 실패했습니다.");
return;
@@ -220,7 +220,7 @@ const SeweragePenetrationRateRvw = () => {
if (await confirm('반려 처리 하시겠습니까?')) {
try {
const userInfo = await getUserInfo();
- const response = await statisticsAPi.saveReviewReturn({
+ const response = await statisticsApi.saveReviewReturn({
statsYr: reviewOpinionContext.statsYr,
sewerBzmnCd: reviewOpinionContext.sewerBzmnCd,
statsCd: reviewOpinionContext.statsCd,
diff --git a/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts b/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts
index 32dfb9211..6adccc917 100644
--- a/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts
+++ b/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts
@@ -7,7 +7,8 @@ import {
type SewerageSupplyRateApiResponse,
type CheckInputPeriodResponse,
type SewerageSupplyRateSaveRequest,
- type SewerageSupplyRateSaveApiResponse,
+ type SewerageSupplyRateSubmitResponse,
+ type SewerageSupplyRateSaveResponse,
} from "../../model/input/statistics-sewage-supply-rate-input-interface";
export class StatisticsInputSewSupplyRateApiService implements StatisticsInputSewSupplyRateService {
@@ -26,16 +27,16 @@ export class StatisticsInputSewSupplyRateApiService implements StatisticsInputSe
return response.data;
}
- public async saveSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise {
- const response = await stClient.post(
+ public async saveSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise {
+ const response = await stClient.post(
`${ENDPOINT.ST_SERVICE.INPUT}/sewerage-supply-rate/save`,
params,
);
return response.data;
}
- public async submitSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise {
- const response = await stClient.post(
+ public async submitSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise {
+ const response = await stClient.post(
`${ENDPOINT.ST_SERVICE.INPUT}/sewerage-supply-rate/submit`,
params,
);
diff --git a/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts b/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts
index af6aad7fb..02d0144c4 100644
--- a/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts
+++ b/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts
@@ -11,7 +11,7 @@ import type {
SeweragePipeInputPipeDiameterResponse,
SeweragePipeInputPipeTypeResponse,
SeweragePipeInputRegionalFactStatusSaveRequest,
- SeweragePipeInputRegionalFactStatusSaveApiResponse,
+ SeweragePipeInputRegionalFactStatusSaveResponse,
SeweragePipeInputRegionalResponse,
SeweragePipeRequestParams,
StatisticsInputSewPipeService,
@@ -82,8 +82,8 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS
return response.data;
}
- public async saveRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSaveRequest): Promise {
- const response = await stClient.post(
+ public async saveRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSaveRequest): Promise {
+ const response = await stClient.post(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/regional-facility-status/save`,
param,
);
diff --git a/apps/st/src/entities/statistics/api/statistics-service.ts b/apps/st/src/entities/statistics/api/statistics-service.ts
index 648853663..b7aec86ac 100644
--- a/apps/st/src/entities/statistics/api/statistics-service.ts
+++ b/apps/st/src/entities/statistics/api/statistics-service.ts
@@ -14,6 +14,8 @@ import {
type ReviewReturnSaveResponse,
type ReviewSubmitRequest,
type ReviewSubmitResponse,
+ type VerificationResultListRequest,
+ type VerificationResultListResponse,
type SearchCondition,
type Sgg,
type StatisticsLogDetailResponse,
@@ -22,9 +24,11 @@ import {
type StatisticsService,
type SubmitExceptionRequest,
type SubmitExceptionResponse,
+ type ImrResponse,
+ type ImrStatusResponse,
} from "../model/statistics-interface";
-export class StatisticsAPiService implements StatisticsService {
+export class StatisticsApiService implements StatisticsService {
public async getSidoList(params?: {
instSeCd?: string;
}): Promise> {
@@ -68,14 +72,21 @@ export class StatisticsAPiService implements StatisticsService {
return response.data;
}
- public async callImrService(param: ImrRequest): Promise {
- const response = await client.post(
+ public async callImrService(param: ImrRequest): Promise {
+ const response = await client.post(
`${ENDPOINT.STATVERIFY_SERVICE.IMR}`,
param,
);
return response.data;
}
+ public async getImrStatus(param: string): Promise {
+ const response = await client.get(
+ `${ENDPOINT.STATVERIFY_SERVICE.IMR}/status/${param}`,
+ );
+ return response.data;
+ }
+
// 예외 제출
public async submitException(
param: SubmitExceptionRequest,
@@ -145,6 +156,14 @@ export class StatisticsAPiService implements StatisticsService {
);
return response.data;
}
+
+ public async getVerificationResultList(param: VerificationResultListRequest): Promise {
+ const response = await stClient.get(
+ `${ENDPOINT.ST_SERVICE.INPUTVERIFY}/results`,
+ { params: param },
+ );
+ return response.data;
+ }
}
-export const statisticsAPi = new StatisticsAPiService();
+export const statisticsApi = new StatisticsApiService();
diff --git a/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts
index 79f9fd1db..299078872 100644
--- a/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts
+++ b/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts
@@ -1,5 +1,3 @@
-import type { SewerageSupplyRateSaveApiResponse } from "./statistics-sewage-supply-rate-input-interface";
-
export interface SeweragePipeRequestParams {
ctpvNm?: string;
sggNm?: string;
@@ -119,8 +117,11 @@ export interface SeweragePipeInputRegionalFactStatusSaveRequest {
saveList: SeweragePipeInputRegionalFactStatusSaveItem[];
}
-/** 지역별 시설현황 저장 API 응답 — 보급률 저장과 동일 래핑 */
-export type SeweragePipeInputRegionalFactStatusSaveApiResponse = SewerageSupplyRateSaveApiResponse;
+export interface SeweragePipeInputRegionalFactStatusSaveResponse {
+ data: string;
+ message?: string;
+ status: string;
+}
/** 시설별 설치 현황 — 목록 항목 */
export interface SeweragePipeInputFactInstallStatusItem {
@@ -519,5 +520,5 @@ export interface StatisticsInputSewPipeService {
getFacilityAdminAreaList(param: SeweragePipeFacilityAdminAreaListRequest): Promise;
saveRegionalFactStatus(
param: SeweragePipeInputRegionalFactStatusSaveRequest,
- ): Promise;
+ ): Promise;
}
\ No newline at end of file
diff --git a/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts
index 59481dc5b..1c14dfab8 100644
--- a/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts
+++ b/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts
@@ -36,6 +36,8 @@ export interface SewerageSupplyRateCommonInfo {
export interface VerificationResultItem {
vrfcCd: string;
rsnExpln: string;
+ nowVlCn: string;
+ compVlCn: string;
}
/** 필드명 → 검증 결과 배열. 예: { popltnSm: [{ type, rsn }], wstrPrmTrt: [...] } */
@@ -150,12 +152,20 @@ export interface SewerageSupplyRateSaveRequest {
superAdmin?: string;
}
-export interface SewerageSupplyRateSaveData {
- httpStatus?: number;
- code?: string;
+export interface SewerageSupplyRateSaveResponse {
+ data: string;
+ message?: string;
+ status: string;
}
-export type SewerageSupplyRateSaveApiResponse = ApiResponse;
+export interface SewerageSupplyRateSubmitResponse {
+ data: {
+ finalStatus: string;
+ nvpassed: boolean;
+ }
+ message?: string;
+ status: string;
+}
// 하수도 통계 입력 기간 조회
export interface CheckInputPeriodResponse {
@@ -167,6 +177,6 @@ export interface CheckInputPeriodResponse {
export interface StatisticsInputSewSupplyRateService {
getSewerageSupplyRateList(params: SewerageSupplyRateRequestParams): Promise;
checkInputPeriod(params: string): Promise;
- saveSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise;
- submitSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise;
+ saveSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise;
+ submitSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise;
}
\ No newline at end of file
diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts
index c20b175e0..5c95fb8bf 100644
--- a/apps/st/src/entities/statistics/model/statistics-interface.ts
+++ b/apps/st/src/entities/statistics/model/statistics-interface.ts
@@ -41,6 +41,91 @@ export interface ImrRequest {
stats_cd: string;
stats_yr: string;
lae_sbmsn_stts_cd: string;
+ gen_verify_stts_cd?: string;
+}
+
+export interface ImrResponse {
+ anls_sn: number;
+ job_id: string;
+ total_otlr_cnt: number;
+ status_code: string;
+ error_msg: string;
+ processing_time_ms: number;
+ job_prog: number;
+ queue_position: number;
+}
+
+export interface ImrStatusResponse {
+ job_id: string;
+ anls_sn: number;
+ status: string;
+ job_prog: number;
+ total_otlr_cnt: number;
+ total_nml_cnt: number;
+ total_excl_cnt: number;
+ processing_time_ms: number;
+ error_msg: string;
+ queue_position: string;
+}
+
+export interface VerificationResultListRequest {
+ statsYr: string;
+ sewerBzmnCd?: string;
+ ctpvNm?: string;
+ sggNm?: string;
+ statsCd?: string;
+ /** 검증 방법 코드 (예: IMR) */
+ vrfcMthdCd?: string;
+ /** 처리여부 코드 목록, 콤마 구분 */
+ prcsYnList?: string;
+ pbcpAprvYn?: string;
+ page?: number;
+ size?: number;
+}
+
+/** 검증 결과 리포트 목록 한 행 (`verifyResultList` 원소) */
+export interface VerificationResultReportItem {
+ statsYr: string;
+ statsCd: string;
+ statsNm: string;
+ sewerBzmnCd: string;
+ ctpvNm: string;
+ sggNm: string;
+ admdstNm: string;
+ vrfcSn: number;
+ vrfcMthdCd: string;
+ vrfcMthdNm: string;
+ vrfcTrgtNm: string;
+ regDt: string;
+ rsnExpln: string;
+ nowVlCn: string;
+ compVlCn: string | null;
+ prcsYn: string;
+ prcsOpnnExpln: string | null;
+ prcrId: string | null;
+ rjctOpnnCn: string | null;
+ pbcpAprvYn: string;
+ mdfrId: string | null;
+ mdfcnDt: string;
+}
+
+export interface VerificationResultListPageInfo {
+ totalElements: number;
+ totalPages: number;
+ currentPage: number;
+ size: number;
+}
+
+export interface VerificationResultListData {
+ verifyResultList: VerificationResultReportItem[];
+ pageInfo: VerificationResultListPageInfo;
+}
+
+/** 검증 결과 리포트 조회 API 응답 */
+export interface VerificationResultListResponse {
+ status: string;
+ message?: string;
+ data?: VerificationResultListData;
}
export interface SubmitExceptionRequest {
@@ -188,7 +273,7 @@ export interface StatisticsService {
getSggList(sidoName: string, params?: { instSeCd?: string; gugunSkipYn?: "Y" | "N" }): Promise>;
getSggAllList(sidoName: string, params?: { instSeCd?: string; gugunSkipYn?: "Y" | "N" }): Promise>;
cancelSubmit(param: cancelSubmitRequest): Promise;
- callImrService(param: ImrRequest): Promise;
+ callImrService(param: ImrRequest): Promise;
submitException(param: SubmitExceptionRequest): Promise;
getLogList(param: StatisticsLogRequest): Promise;
getLogDetail(param: string): Promise;
@@ -196,4 +281,6 @@ export interface StatisticsService {
getReviewOpinionDetail(param: getReviewOpinionDetailRequest): Promise;
saveReviewReturn(param: ReviewReturnSaveRequest): Promise;
submitReview(param :ReviewSubmitRequest): Promise;
+ getVerificationResultList(param: VerificationResultListRequest): Promise;
+ getImrStatus(param: string): Promise;
}
\ No newline at end of file
diff --git a/apps/st/src/shared/lib/index.ts b/apps/st/src/shared/lib/index.ts
index 5ac79bf08..e2ddab13d 100644
--- a/apps/st/src/shared/lib/index.ts
+++ b/apps/st/src/shared/lib/index.ts
@@ -2,6 +2,7 @@ export { getCookie, setCookie, removeCookie } from "./cookie";
export { useMutationWrapper } from "./use-mutation-wrapper";
export { useAlertStore } from "./use-alert-store";
export { useDataTable } from "./use-data-table";
+export { useGlobalProgressStore } from "./use-global-progress-store";
export { useMenuStore } from "./use-menu-store";
export { useUserStore } from "./use-user-store";
export { usePrompt } from "./use-prompt";
diff --git a/apps/st/src/shared/lib/use-global-progress-store.ts b/apps/st/src/shared/lib/use-global-progress-store.ts
new file mode 100644
index 000000000..4161ad1d4
--- /dev/null
+++ b/apps/st/src/shared/lib/use-global-progress-store.ts
@@ -0,0 +1,16 @@
+import { useContext } from "react";
+import { useStore } from "zustand";
+import type { GlobalProgressStore } from "../store/global-progress-store";
+import { StoreContext } from "../providers/store-provider";
+
+export const useGlobalProgressStore = (
+ selector: (store: GlobalProgressStore) => T,
+): T => {
+ const storeContext = useContext(StoreContext);
+
+ if (!storeContext) {
+ throw new Error("useGlobalProgressStore must be used within StoreProvider");
+ }
+
+ return useStore(storeContext.globalProgressStore, selector);
+};
diff --git a/apps/st/src/shared/providers/store-provider.tsx b/apps/st/src/shared/providers/store-provider.tsx
index a0ec17b61..44d6d89f0 100644
--- a/apps/st/src/shared/providers/store-provider.tsx
+++ b/apps/st/src/shared/providers/store-provider.tsx
@@ -4,6 +4,7 @@ import { createContext, useRef } from "react";
import {
createAlertStore,
createAttachmentStore,
+ createGlobalProgressStore,
createMenuStore,
createUserStore,
} from "../store";
@@ -11,6 +12,7 @@ import {
type StoreContextType = {
alertStore: ReturnType;
attachmentStore: ReturnType;
+ globalProgressStore: ReturnType;
menuStore: ReturnType;
userStore: ReturnType;
};
@@ -25,6 +27,7 @@ export const StoreProvider = ({ children }: { children: React.ReactNode }) => {
storeRef.current = {
alertStore: createAlertStore(),
attachmentStore: createAttachmentStore(),
+ globalProgressStore: createGlobalProgressStore(),
menuStore: createMenuStore(),
userStore: createUserStore(),
};
diff --git a/apps/st/src/shared/store/global-progress-store.ts b/apps/st/src/shared/store/global-progress-store.ts
new file mode 100644
index 000000000..70a08debf
--- /dev/null
+++ b/apps/st/src/shared/store/global-progress-store.ts
@@ -0,0 +1,45 @@
+import { createStore } from "zustand/vanilla";
+
+export type GlobalProgressState = {
+ visible: boolean;
+ label: string;
+ progress: number;
+};
+
+export type GlobalProgressActions = {
+ show: (payload?: { label?: string; progress?: number }) => void;
+ setLabel: (label: string) => void;
+ setProgress: (progress: number) => void;
+ hide: () => void;
+};
+
+export type GlobalProgressStore = GlobalProgressState & GlobalProgressActions;
+
+export const defaultGlobalProgressState: GlobalProgressState = {
+ visible: false,
+ label: "진행 중...",
+ progress: 0,
+};
+
+export const createGlobalProgressStore = (
+ initState: GlobalProgressState = defaultGlobalProgressState,
+) => {
+ return createStore()((set) => ({
+ ...initState,
+ show: (payload) =>
+ set(() => ({
+ visible: true,
+ label: payload?.label ?? defaultGlobalProgressState.label,
+ progress: Math.max(0, Math.min(100, payload?.progress ?? 0)),
+ })),
+ setLabel: (label) => set(() => ({ label })),
+ setProgress: (progress) =>
+ set(() => ({ progress: Math.max(0, Math.min(100, progress)) })),
+ hide: () =>
+ set(() => ({
+ visible: false,
+ progress: 0,
+ label: defaultGlobalProgressState.label,
+ })),
+ }));
+};
diff --git a/apps/st/src/shared/store/index.ts b/apps/st/src/shared/store/index.ts
index a41d6f928..c5014150e 100644
--- a/apps/st/src/shared/store/index.ts
+++ b/apps/st/src/shared/store/index.ts
@@ -1,4 +1,5 @@
export { createAlertStore } from "./alert-store";
export { createAttachmentStore } from "./attachment-store";
+export { createGlobalProgressStore } from "./global-progress-store";
export { createMenuStore, type BbsType } from "./menu-store";
export { createUserStore, type IUserInfo } from "./user-store";
diff --git a/apps/st/src/shared/ui/global-progress-overlay.tsx b/apps/st/src/shared/ui/global-progress-overlay.tsx
new file mode 100644
index 000000000..c9007148b
--- /dev/null
+++ b/apps/st/src/shared/ui/global-progress-overlay.tsx
@@ -0,0 +1,23 @@
+"use client";
+
+import { Progress } from "@common/components";
+import { useGlobalProgressStore } from "@/shared/lib";
+
+export const GlobalProgressOverlay = () => {
+ const visible = useGlobalProgressStore((state) => state.visible);
+ const label = useGlobalProgressStore((state) => state.label);
+ const progress = useGlobalProgressStore((state) => state.progress);
+
+ if (!visible) return null;
+
+ return (
+
+
+
+ {label} {progress}%
+
+
+
+
+ );
+};