@uniai-fe/uds-templates 0.4.28 → 0.4.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -1519,6 +1519,9 @@
1519
1519
  border-radius: var(--cctv-video-radius);
1520
1520
  background: var(--cctv-video-bg);
1521
1521
  }
1522
+ .cctv-video-container[data-error=true] .cctv-video-box {
1523
+ visibility: hidden;
1524
+ }
1522
1525
 
1523
1526
  .cctv-video-box {
1524
1527
  width: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.4.28",
3
+ "version": "0.4.30",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "devDependencies": {
72
72
  "@svgr/webpack": "^8.1.0",
73
- "@tanstack/react-query": "^5.96.0",
73
+ "@tanstack/react-query": "^5.97.0",
74
74
  "@types/node": "^24.10.2",
75
75
  "@types/react": "^19.2.14",
76
76
  "@types/react-dom": "^19.2.3",
@@ -86,11 +86,11 @@
86
86
  "@uniai-fe/util-next": "workspace:*",
87
87
  "@uniai-fe/util-rtc": "workspace:*",
88
88
  "eslint": "^9.39.2",
89
- "jotai": "^2.19.0",
89
+ "jotai": "^2.19.1",
90
90
  "next": "^15.5.11",
91
- "prettier": "^3.8.1",
92
- "react-hook-form": "^7.72.0",
93
- "sass": "^1.98.0",
91
+ "prettier": "^3.8.2",
92
+ "react-hook-form": "^7.72.1",
93
+ "sass": "^1.99.0",
94
94
  "typescript": "~5.9.3"
95
95
  }
96
96
  }
@@ -24,6 +24,9 @@ export interface API_Req_Login {
24
24
  * @property {string} name 농장명
25
25
  * @property {string} farm_code 농장 식별번호
26
26
  * @property {string} user_access_role 계정 권한/직책 코드 (OWNER: 농장주, EMPLOYEE: 농장직원, REGIONAL_MANAGER: 지역소장)
27
+ * @property {string} address 농장주소
28
+ * @property {number} latitude 농장좌표 위도
29
+ * @property {number} longitude 농장좌표 경도
27
30
  */
28
31
  export interface API_Res_LoginUserFarm {
29
32
  /**
@@ -49,6 +52,18 @@ export interface API_Res_LoginUserFarm {
49
52
  * - REGIONAL_MANAGER: 지역소장
50
53
  */
51
54
  user_access_role: string;
55
+ /**
56
+ * 농장 주소
57
+ */
58
+ address: string;
59
+ /**
60
+ * 농장 좌표; 위도
61
+ */
62
+ latitude: number;
63
+ /**
64
+ * 농장 좌표; 경도
65
+ */
66
+ longitude: number;
52
67
  }
53
68
 
54
69
  /**
@@ -3,11 +3,18 @@ import clsx from "clsx";
3
3
  export default function CCTVVideoContainer({
4
4
  className,
5
5
  children,
6
+ isError,
6
7
  }: {
7
8
  className?: string;
8
9
  children: React.ReactNode;
10
+ isError?: boolean;
9
11
  }) {
10
12
  return (
11
- <div className={clsx("cctv-video-container", className)}>{children}</div>
13
+ <div
14
+ className={clsx("cctv-video-container", className)}
15
+ data-error={isError ? "true" : undefined}
16
+ >
17
+ {children}
18
+ </div>
12
19
  );
13
20
  }
@@ -32,7 +32,7 @@ const CCTVVideoTemplate = forwardRef<HTMLVideoElement, CctvVideoTemplateProps>(
32
32
  ref,
33
33
  ) => {
34
34
  return (
35
- <CCTVVideoContainer className={className}>
35
+ <CCTVVideoContainer className={className} isError={isError}>
36
36
  <CCTVVideoContents ref={ref} muted />
37
37
  <CCTVVideoOverlayContainer>
38
38
  <CCTVVideoOverlayHeader {...headerOptions} />
@@ -84,6 +84,12 @@ export function useCctvRtcStream({
84
84
  useEffect(() => {
85
85
  const currentVideo = videoRef.current;
86
86
 
87
+ // 토큰/카메라 에러로 전환되면 직전 MediaStream이 화면에 남지 않도록 비운다.
88
+ if (tokenQuery.isError || !cam?.cam_online) {
89
+ if (currentVideo) currentVideo.srcObject = null;
90
+ return;
91
+ }
92
+
87
93
  // 필수 값이 없으면 스트림을 시작하지 않는다.
88
94
  if (!tokenQuery.data?.token || !endpoint || !currentVideo) return;
89
95
 
@@ -110,6 +116,8 @@ export function useCctvRtcStream({
110
116
  handle = streamHandle;
111
117
  })
112
118
  .catch(error => {
119
+ // 스트림 실패 시에도 이전 프레임이 에러 오버레이 뒤에 남지 않도록 정리한다.
120
+ currentVideo.srcObject = null;
113
121
  // Error 객체 여부에 따라 메시지를 정규화한다.
114
122
  setStreamError(
115
123
  error instanceof Error
@@ -129,7 +137,13 @@ export function useCctvRtcStream({
129
137
  handle = null;
130
138
  currentVideo.srcObject = null;
131
139
  };
132
- }, [endpoint, tokenQuery.data?.token, cam?.cam_id]);
140
+ }, [
141
+ endpoint,
142
+ tokenQuery.data?.token,
143
+ tokenQuery.isError,
144
+ cam?.cam_id,
145
+ cam?.cam_online,
146
+ ]);
133
147
 
134
148
  const liveState = useMemo(
135
149
  () =>
@@ -7,6 +7,13 @@
7
7
 
8
8
  background: var(--cctv-video-bg);
9
9
  // overflow: hidden;
10
+
11
+ &[data-error="true"] {
12
+ // 에러 오버레이가 활성화되면 직전 video frame이 메시지 뒤에 보이지 않게 숨긴다.
13
+ .cctv-video-box {
14
+ visibility: hidden;
15
+ }
16
+ }
10
17
  }
11
18
 
12
19
  .cctv-video-box {
@@ -40,9 +40,10 @@ export function getOverlayMessage({
40
40
  }
41
41
 
42
42
  if (!cam.cam_online) return CCTV_MESSAGE.offline;
43
- if (isTokenLoading || isStreaming) return CCTV_MESSAGE.preparing;
43
+ // 에러 상태가 준비 상태와 겹칠 때는 에러 문구가 최종 표시 계약을 우선한다.
44
44
  if (isTokenError) return CCTV_MESSAGE.tokenError;
45
45
  if (streamError) return streamError;
46
+ if (isTokenLoading || isStreaming) return CCTV_MESSAGE.preparing;
46
47
  return null;
47
48
  }
48
49