@uniai-fe/uds-templates 0.5.4 → 0.5.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -12,7 +12,7 @@
12
12
  "publishConfig": {
13
13
  "access": "public"
14
14
  },
15
- "packageManager": "pnpm@10.33.0",
15
+ "packageManager": "pnpm@10.33.2",
16
16
  "engines": {
17
17
  "node": ">=24",
18
18
  "pnpm": ">=10"
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "devDependencies": {
72
72
  "@svgr/webpack": "^8.1.0",
73
- "@tanstack/react-query": "^5.99.0",
73
+ "@tanstack/react-query": "^5.100.1",
74
74
  "@types/node": "^24.10.2",
75
75
  "@types/react": "^19.2.14",
76
76
  "@types/react-dom": "^19.2.3",
@@ -89,7 +89,7 @@
89
89
  "jotai": "^2.19.1",
90
90
  "next": "^15.5.11",
91
91
  "prettier": "^3.8.3",
92
- "react-hook-form": "^7.72.1",
92
+ "react-hook-form": "^7.73.1",
93
93
  "sass": "^1.99.0",
94
94
  "typescript": "5.9.3"
95
95
  }
@@ -6,31 +6,22 @@ import type {
6
6
  API_Res_CctvCompany,
7
7
  API_Res_CctvRtcToken,
8
8
  } from "../types";
9
- import { getQueryString } from "@uniai-fe/util-functions";
10
9
 
11
10
  export const getClientCctvCompanyList = async ({
12
- username,
13
11
  url,
14
12
  }: {
15
- username: string;
16
13
  url?: string;
17
- }) =>
18
- await (
19
- await fetch(
20
- `${url ?? "/api/cctv/company-list"}${getQueryString({ username })}`,
21
- )
22
- ).json();
14
+ }): Promise<API_Res_CctvCompany> =>
15
+ await (await fetch(url ?? "/api/cctv/company-list")).json();
23
16
 
24
17
  export const useQueryCctvCompanyList = ({
25
- username,
26
18
  url,
27
19
  }: {
28
- username: string;
29
20
  url?: string;
30
21
  }): UseQueryResult<API_Res_CctvCompany> =>
31
22
  useQuery({
32
- queryKey: ["cctv_company_list", username, url],
33
- queryFn: () => getClientCctvCompanyList({ username, url }),
23
+ queryKey: ["cctv_company_list", url],
24
+ queryFn: () => getClientCctvCompanyList({ url }),
34
25
  });
35
26
 
36
27
  export const postCctvRtcToken = async ({
@@ -132,46 +132,39 @@ export function classifyGroups(
132
132
  /**
133
133
  * CCTV; company-list API route.ts 로직
134
134
  * @api
135
- * @route /v1/users/{username}/cctvs
135
+ * @route /common/cctvs/{me|all}
136
136
  * @param {API_Req_GetCompanyListParams} params
137
137
  * @property {string} domain API 요청 도메인(서버)
138
138
  * @property {string} routeUrl Next.js app/api 이하 경로 url
139
139
  * @property {string} [queryUrl] 백엔드 요청 url
140
- * @property {URLSearchParams | object} searchParams 요청 searchParams
140
+ * @property {"me" | "all"} [scope] common/cctvs 조회 범위
141
141
  */
142
142
  export async function getServerCompanyList({
143
143
  domain,
144
144
  routeUrl,
145
145
  queryUrl,
146
- searchParams,
146
+ scope = "me",
147
147
  }: API_Req_GetCompanyListParams): Promise<{
148
148
  res: API_Res_CctvCompany;
149
149
  domain: string;
150
150
  queryUrl: string;
151
151
  options?: ResponseInit;
152
152
  }> {
153
- const username = searchParams.get("username") || "";
154
- const query_url = queryUrl || `/v1/users/${username}/cctvs`;
153
+ const query_url = queryUrl || `/common/cctvs/${scope}`;
155
154
 
156
- const alternateResponse = {
157
- data: [],
158
- total_count: 0,
155
+ const alternateResponse: API_Res_CctvCompany = {
156
+ status: "error",
157
+ data: {
158
+ items: [],
159
+ total_count: 0,
160
+ },
161
+ errors: [],
159
162
  };
160
163
  const API_OPTION = {
161
164
  domain,
162
165
  queryUrl: query_url,
163
- // searchParams,
164
166
  };
165
167
 
166
- if (!queryUrl && !username) {
167
- nextAPILog("GET", routeUrl, query_url, { searchParams });
168
- return {
169
- res: alternateResponse,
170
- ...API_OPTION,
171
- options: { status: 400, statusText: "유저 아이디를 확인할 수 없습니다." },
172
- };
173
- }
174
-
175
168
  // 요청 URL 구성
176
169
  const url = generateQueryUrl({
177
170
  routeUrl,
@@ -181,12 +174,9 @@ export async function getServerCompanyList({
181
174
  try {
182
175
  const originRes: API_Res_CctvCompany = await (await fetch(url)).json();
183
176
 
184
- // 응답 그룹/농장
185
- const originGroups = originRes?.data || [];
186
- const resGourps = classifyGroups(originGroups);
187
-
177
+ // 변경: server helper는 common/cctvs raw 응답을 유지하고 template 변환은 hook에서 처리한다.
188
178
  return {
189
- res: { data: resGourps, total_count: originRes.total_count || 0 },
179
+ res: originRes,
190
180
  ...API_OPTION,
191
181
  };
192
182
  } catch (err) {
@@ -2,17 +2,25 @@
2
2
 
3
3
  import clsx from "clsx";
4
4
  import CCTVCamListItem from "./Item";
5
- import type { CctvCompanyCameraList } from "../../types";
5
+ import type { CctvCamListContainerProps } from "../../types";
6
6
  import { useCctvContext } from "../../hooks";
7
7
  import { useMemo } from "react";
8
8
 
9
+ /**
10
+ * CCTV; cam-list container
11
+ * @component
12
+ * @param {CctvCamListContainerProps} props
13
+ * @param {string} [props.className] 루트 컨테이너 클래스
14
+ * @param {CctvCompanyCameraList[]} [props.list] 외부에서 직접 주입하는 카메라 리스트
15
+ * @param {CctvVideoRenderOverlay} [props.renderOverlay] 각 item video overlay를 교체하는 custom render 함수
16
+ * @example
17
+ * <CCTV.CamList.Container renderOverlay={({ cam }) => <div>{cam?.cam_name}</div>} />
18
+ */
9
19
  export default function CCTVCamListContainer({
10
20
  className,
11
21
  list,
12
- }: {
13
- className?: string;
14
- list?: CctvCompanyCameraList[];
15
- }) {
22
+ renderOverlay,
23
+ }: CctvCamListContainerProps) {
16
24
  const { cams } = useCctvContext();
17
25
  const camList = useMemo(() => list ?? cams, [list, cams]);
18
26
  // console.log("CCTVCamListContainer camList:", { list, camList });
@@ -28,7 +36,12 @@ export default function CCTVCamListContainer({
28
36
  <div className={clsx("cctv-cam-list-container", className)}>
29
37
  <ul className="cctv-cam-list-track">
30
38
  {camList.map(({ renderKey, ...cam }) => (
31
- <CCTVCamListItem key={renderKey} {...cam} />
39
+ <CCTVCamListItem
40
+ key={renderKey}
41
+ {...cam}
42
+ // 변경 설명: container에서 받은 overlay seam을 item까지 그대로 전달한다.
43
+ renderOverlay={renderOverlay}
44
+ />
32
45
  ))}
33
46
  </ul>
34
47
  </div>
@@ -1,22 +1,30 @@
1
1
  "use client";
2
2
 
3
3
  import clsx from "clsx";
4
- import type { CctvCompanyCameraData } from "../../types";
5
4
  import { useCctvRtcStream } from "../../hooks";
6
5
  import { useMemo } from "react";
7
6
  import CCTVVideoTemplate from "../video/Template";
7
+ import type { CctvCamListItemProps } from "../../types";
8
8
  import {
9
9
  getOverlayMessage,
10
10
  getIsLive,
11
11
  getIsError,
12
12
  } from "../../utils/video-state";
13
13
 
14
+ /**
15
+ * CCTV; cam-list item
16
+ * @component
17
+ * @param {CctvCamListItemProps} props
18
+ * @param {string} [props.className] 루트 아이템 클래스
19
+ * @param {CctvVideoRenderOverlay} [props.renderOverlay] item video overlay를 교체하는 custom render 함수
20
+ * @example
21
+ * <CCTV.CamList.Item {...cam} renderOverlay={({ headerOptions }) => <div>{headerOptions?.title}</div>} />
22
+ */
14
23
  export default function CCTVCamListItem({
15
24
  className,
25
+ renderOverlay,
16
26
  ...cam
17
- }: {
18
- className?: string;
19
- } & CctvCompanyCameraData) {
27
+ }: CctvCamListItemProps) {
20
28
  const { videoRef, ...rtcCtx } = useCctvRtcStream({ cam });
21
29
 
22
30
  const isLive = useMemo(() => getIsLive({ cam, ...rtcCtx }), [cam, rtcCtx]);
@@ -37,6 +45,7 @@ export default function CCTVCamListItem({
37
45
  >
38
46
  <CCTVVideoTemplate
39
47
  ref={videoRef}
48
+ cam={cam}
40
49
  className="cctv-cam-list-video-container"
41
50
  headerOptions={{
42
51
  activeLiveState: true,
@@ -46,6 +55,8 @@ export default function CCTVCamListItem({
46
55
  title: cam.cam_name,
47
56
  }}
48
57
  footerOptions={{ activeTitle: true, activeOpenButton: true, cam }}
58
+ // 변경 설명: list item custom overlay는 template seam 하나만 따라간다.
59
+ renderOverlay={renderOverlay}
49
60
  {...{ isError, overlayMessage, isLive }}
50
61
  />
51
62
  </li>
@@ -15,32 +15,54 @@ import type { CctvVideoTemplateProps } from "../../types/props";
15
15
  * @property {CctvVideoOverlayHeaderProps} [headerOptions]
16
16
  * @property {CctvVideoOverlayFooterProps} [footerOptions]
17
17
  * @property {boolean} [isError]
18
- * @property {string | null} [overlayMessage]
18
+ * @property {React.ReactNode} [overlayMessage]
19
19
  * @property {boolean} [isLive]
20
+ * @property {CctvCompanyCameraData} [cam]
21
+ * @property {CctvVideoRenderOverlay} [renderOverlay]
20
22
  * @property {React.Ref<HTMLVideoElement>} ref
21
23
  */
22
24
  const CCTVVideoTemplate = forwardRef<HTMLVideoElement, CctvVideoTemplateProps>(
23
25
  (
24
26
  {
27
+ cam,
25
28
  className,
26
29
  headerOptions,
27
30
  footerOptions,
28
31
  isError,
29
32
  isLive,
30
33
  overlayMessage,
34
+ renderOverlay,
31
35
  },
32
36
  ref,
33
37
  ) => {
38
+ // 변경 설명: custom overlay가 없을 때는 기존 overlay 조립을 그대로 유지한다.
39
+ const defaultOverlay = (
40
+ <CCTVVideoOverlayContainer>
41
+ <CCTVVideoOverlayHeader {...headerOptions} />
42
+ <CCTVVideoOverlayBody className={isError ? "is-error" : undefined}>
43
+ {!isLive ? <CCTVVideoError>{overlayMessage}</CCTVVideoError> : null}
44
+ </CCTVVideoOverlayBody>
45
+ <CCTVVideoOverlayFooter {...footerOptions} isLive={isLive} />
46
+ </CCTVVideoOverlayContainer>
47
+ );
48
+
49
+ // 변경 설명: service는 기존 public 타입 기반 ctx만 받아 overlay를 교체할 수 있다.
50
+ const overlayNode =
51
+ typeof renderOverlay === "function"
52
+ ? renderOverlay({
53
+ cam,
54
+ headerOptions,
55
+ footerOptions,
56
+ isError,
57
+ isLive,
58
+ overlayMessage,
59
+ })
60
+ : defaultOverlay;
61
+
34
62
  return (
35
63
  <CCTVVideoContainer className={className} isError={isError}>
36
64
  <CCTVVideoContents ref={ref} muted />
37
- <CCTVVideoOverlayContainer>
38
- <CCTVVideoOverlayHeader {...headerOptions} />
39
- <CCTVVideoOverlayBody className={isError ? "is-error" : undefined}>
40
- {!isLive ? <CCTVVideoError>{overlayMessage}</CCTVVideoError> : null}
41
- </CCTVVideoOverlayBody>
42
- <CCTVVideoOverlayFooter {...footerOptions} isLive={isLive} />
43
- </CCTVVideoOverlayContainer>
65
+ {overlayNode}
44
66
  </CCTVVideoContainer>
45
67
  );
46
68
  },
@@ -1,9 +1,11 @@
1
1
  import CCTVVideoContainer from "./Container";
2
2
  import { CCTVVideoOverlay } from "./overlay";
3
+ import CCTVVideoTemplate from "./Template";
3
4
  import CCTVVideoContents from "./Video";
4
5
 
5
6
  export const CCTVVideo = {
6
7
  Container: CCTVVideoContainer,
7
8
  Contents: CCTVVideoContents,
9
+ Template: CCTVVideoTemplate,
8
10
  Overlay: CCTVVideoOverlay,
9
11
  };
@@ -2,22 +2,27 @@ import clsx from "clsx";
2
2
  import CCTVViewerContainer from "../Container";
3
3
  import CCTVViewerDesktopPagination from "./Pagination";
4
4
  import CCTVViewerDesktopVideo from "./Video";
5
- import type {
6
- CctvCompanyCameraData,
7
- CctvCompanyCameraList,
8
- } from "../../../types";
5
+ import type { CctvViewerDesktopContainerProps } from "../../../types";
9
6
 
7
+ /**
8
+ * CCTV; viewer desktop container
9
+ * @component
10
+ * @param {CctvViewerDesktopContainerProps} props
11
+ * @param {string} [props.className] 루트 컨테이너 클래스
12
+ * @param {React.ReactNode} [props.children] 기본 조립 대신 직접 주입하는 children
13
+ * @param {CctvCompanyCameraData} [props.selectedCam] 외부에서 직접 주입하는 선택 카메라 데이터
14
+ * @param {CctvCompanyCameraList[]} [props.camList] 외부에서 직접 주입하는 pagination 카메라 리스트
15
+ * @param {CctvVideoRenderOverlay} [props.renderOverlay] main video overlay를 교체하는 custom render 함수
16
+ * @example
17
+ * <CCTV.Viewer.Desktop.Container renderOverlay={({ isLive }) => <div>{String(isLive)}</div>} />
18
+ */
10
19
  export default function CCTVViewerDesktopContainer({
11
20
  className,
12
21
  children,
13
22
  selectedCam,
14
23
  camList,
15
- }: {
16
- className?: string;
17
- children?: React.ReactNode;
18
- selectedCam?: CctvCompanyCameraData;
19
- camList?: CctvCompanyCameraList[];
20
- }) {
24
+ renderOverlay,
25
+ }: CctvViewerDesktopContainerProps) {
21
26
  return (
22
27
  <CCTVViewerContainer
23
28
  className={clsx("cctv-viewer-desktop-container", className)}
@@ -30,6 +35,8 @@ export default function CCTVViewerDesktopContainer({
30
35
  {...(typeof selectedCam !== "undefined"
31
36
  ? { cam: selectedCam }
32
37
  : {})}
38
+ // 변경 설명: container에서 받은 seam을 main viewer video로 전달한다.
39
+ renderOverlay={renderOverlay}
33
40
  />
34
41
  <CCTVViewerDesktopPagination
35
42
  {...(typeof camList !== "undefined" ? { list: camList } : {})}
@@ -3,7 +3,7 @@
3
3
  import { useMemo } from "react";
4
4
 
5
5
  import { useCctvContext, useCctvRtcStream } from "../../../hooks";
6
- import type { CctvCompanyCameraData } from "../../../types";
6
+ import type { CctvViewerDesktopVideoProps } from "../../../types";
7
7
  import CCTVVideoTemplate from "../../video/Template";
8
8
  import {
9
9
  getOverlayMessage,
@@ -11,11 +11,19 @@ import {
11
11
  getIsError,
12
12
  } from "../../../utils/video-state";
13
13
 
14
+ /**
15
+ * CCTV; viewer desktop video
16
+ * @component
17
+ * @param {CctvViewerDesktopVideoProps} props
18
+ * @param {CctvCompanyCameraData} [props.cam] 외부에서 직접 주입하는 선택 카메라 데이터
19
+ * @param {CctvVideoRenderOverlay} [props.renderOverlay] main video overlay를 교체하는 custom render 함수
20
+ * @example
21
+ * <CCTV.Viewer.Desktop.Video renderOverlay={({ cam }) => <div>{cam?.cam_name}</div>} />
22
+ */
14
23
  export default function CCTVViewerDesktopVideo({
15
24
  cam: propsCam,
16
- }: {
17
- cam?: CctvCompanyCameraData;
18
- }) {
25
+ renderOverlay,
26
+ }: CctvViewerDesktopVideoProps) {
19
27
  const { selectedCam, isFetching } = useCctvContext();
20
28
 
21
29
  // props의 cam이 들어오면 우선 적용
@@ -36,6 +44,7 @@ export default function CCTVViewerDesktopVideo({
36
44
  return (
37
45
  <CCTVVideoTemplate
38
46
  ref={videoRef}
47
+ cam={cam}
39
48
  className="cctv-viewer-desktop-video-container"
40
49
  headerOptions={{
41
50
  activeLiveState: true,
@@ -46,6 +55,8 @@ export default function CCTVViewerDesktopVideo({
46
55
  title: cam?.cam_name,
47
56
  }}
48
57
  footerOptions={{ activeTitle: false }}
58
+ // 변경 설명: viewer video는 service custom overlay를 직접 수용하는 첫 public entry다.
59
+ renderOverlay={renderOverlay}
49
60
  {...{ isError, overlayMessage, isLive }}
50
61
  />
51
62
  );
@@ -1,47 +1,48 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useMemo } from "react";
4
- import { useQueryCctvCompanyList } from "../apis";
4
+ import { classifyGroups, useQueryCctvCompanyList } from "../apis";
5
5
  import { useCctvApiUrl } from "../components/Provider";
6
6
  import useCctvContext from "./useContext";
7
- import type { UseCctvCompanyDataReturn } from "../types";
7
+ import type {
8
+ UseCctvCompanyDataParams,
9
+ UseCctvCompanyDataReturn,
10
+ } from "../types";
8
11
 
9
12
  /**
10
13
  * CCTV 회사 리스트 API를 호출해 데이터를 가져오는 훅.
11
14
  * @hook
12
15
  * @param {UseCctvCompanyDataParams} [params] 훅 파라미터
13
- * @property {string} [username] 사용자 계정 아이디 (Provider에서 주입된 기본값 대신 사용)
14
16
  * @property {string} [url] 회사 리스트 API URL (Provider에서 주입된 기본값 대신 사용)
15
17
  * @return {UseCctvCompanyDataReturn} 회사 리스트 쿼리 반환값
16
18
  * @desc
17
19
  * return {
18
- * data, // 회사 리스트 API 응답 데이터
20
+ * data, // common/cctvs 원본 API 응답 데이터
19
21
  * isFetching, // 데이터 로딩 중 상태
20
22
  * isError, // 데이터 로딩 에러 상태
21
23
  * ...rest, // react-query useQuery 반환값
22
24
  * }
23
25
  */
24
- export default function useCctvCompanyData(params?: {
25
- username?: string;
26
- url?: string;
27
- }): UseCctvCompanyDataReturn {
28
- // 외부에서 username/url 덮어쓸 있도록 받고, 없으면 Provider 값을 사용한다.
29
- const { username, url } = params || {};
26
+ export default function useCctvCompanyData(
27
+ params?: UseCctvCompanyDataParams,
28
+ ): UseCctvCompanyDataReturn {
29
+ // 변경: list 조회 계약은 URL만 override 대상으로 유지한다.
30
+ const { url } = params || {};
30
31
 
31
32
  const { listUrl } = useCctvApiUrl();
32
33
  const resolvedUrl = useMemo(() => url ?? listUrl, [url, listUrl]);
33
- const { username: contextUsername, setValue } = useCctvContext();
34
+ const { setValue } = useCctvContext();
34
35
 
35
- // 사용자/URL에 맞춰 회사 리스트 API를 호출한다.
36
+ // URL에 맞춰 회사 리스트 API를 호출한다.
36
37
  const { data, isFetching, isError, ...rest } = useQueryCctvCompanyList({
37
- username: username ?? contextUsername,
38
38
  url: resolvedUrl,
39
39
  });
40
40
 
41
41
  // 응답 데이터를 react-hook-form rawData 필드로 반영한다.
42
42
  useEffect(() => {
43
- if (data?.data) {
44
- setValue("rawData", data?.data);
43
+ if (data?.data?.items) {
44
+ // 변경: common/cctvs raw items를 template form data 계약에 맞게 재분류한다.
45
+ setValue("rawData", classifyGroups(data.data.items));
45
46
  }
46
47
  }, [data, setValue]);
47
48
 
@@ -1,3 +1,4 @@
1
+ import type { API_Res_Base } from "@uniai-fe/util-api";
1
2
  import type { UtilQueryBaseParams } from "@uniai-fe/util-functions";
2
3
  import type { NextRequest } from "next/server";
3
4
 
@@ -6,20 +7,25 @@ import type { NextRequest } from "next/server";
6
7
  * @property {string} domain API 요청 도메인(서버)
7
8
  * @property {string} routeUrl Next.js app/api 이하 경로 url
8
9
  * @property {string} [queryUrl] 백엔드 요청 url
9
- * @property {URLSearchParams} searchParams api 요청 search params
10
+ * @property {"me" | "all"} [scope] common/cctvs 조회 범위
10
11
  */
11
- export interface API_Req_GetCompanyListParams extends Omit<
12
- UtilQueryBaseParams,
13
- "queryUrl"
14
- > {
12
+ export interface API_Req_GetCompanyListParams {
13
+ /**
14
+ * API 도메인
15
+ */
16
+ domain: string;
17
+ /**
18
+ * Next.js app/api 이하 경로 url
19
+ */
20
+ routeUrl: string;
15
21
  /**
16
22
  * 백엔드 요청 url
17
23
  */
18
24
  queryUrl?: string;
19
25
  /**
20
- * api 요청 search params
26
+ * common/cctvs 조회 범위
21
27
  */
22
- searchParams: URLSearchParams;
28
+ scope?: "me" | "all";
23
29
  }
24
30
 
25
31
  /**
@@ -115,15 +121,27 @@ export interface API_Res_CctvCompanyGroup {
115
121
  }
116
122
 
117
123
  /**
118
- * CCTV; API 업체 목록 조회 응답
119
- * @property {API_Res_CctvCompanyGroup[]} data
120
- * @property {number} total_count
124
+ * CCTV; common/cctvs 원본 응답 데이터
125
+ * @property {API_Res_CctvCompanyGroup[]} items 백엔드 원본 업체 그룹 목록
126
+ * @property {number} total_count 전체 업체 그룹 수
121
127
  */
122
- export interface API_Res_CctvCompany {
123
- data: API_Res_CctvCompanyGroup[];
128
+ export interface API_Res_CctvCompanyData {
129
+ /**
130
+ * 백엔드 원본 업체 그룹 목록
131
+ */
132
+ items: API_Res_CctvCompanyGroup[];
133
+ /**
134
+ * 전체 업체 그룹 수
135
+ */
124
136
  total_count: number;
125
137
  }
126
138
 
139
+ /**
140
+ * CCTV; common/cctvs 원본 응답
141
+ * @typedef {API_Res_Base<API_Res_CctvCompanyData>} API_Res_CctvCompany
142
+ */
143
+ export type API_Res_CctvCompany = API_Res_Base<API_Res_CctvCompanyData>;
144
+
127
145
  /**
128
146
  * CCTV; 실시간 스트리밍 토큰 요청 (Next.js API 요청)
129
147
  * @property {string} company_id 업체 id코드
@@ -92,14 +92,9 @@ export interface UseCctvRtcStreamReturn extends UseCctvRtcStreamState {
92
92
 
93
93
  /**
94
94
  * CCTV; useCctvCompanyData params
95
- * @property {string} [username] 사용자 계정 아이디
96
95
  * @property {string} [url] 업체 리스트 API URL
97
96
  */
98
97
  export interface UseCctvCompanyDataParams {
99
- /**
100
- * 사용자 계정 아이디
101
- */
102
- username?: string;
103
98
  /**
104
99
  * 업체 리스트 API URL
105
100
  */
@@ -108,7 +103,7 @@ export interface UseCctvCompanyDataParams {
108
103
 
109
104
  /**
110
105
  * CCTV; useCctvCompanyData return
111
- * @property {API_Res_CctvCompany | undefined} data 업체 리스트 API 응답 데이터
106
+ * @property {API_Res_CctvCompany | undefined} data common/cctvs 원본 API 응답 데이터
112
107
  * @property {boolean} isFetching 데이터 로딩 중 상태
113
108
  * @property {boolean} isError 데이터 로딩 에러 상태
114
109
  */
@@ -1,4 +1,47 @@
1
- import type { CctvCompanyCameraData } from "./list";
1
+ import type { CctvCompanyCameraData, CctvCompanyCameraList } from "./list";
2
+
3
+ /**
4
+ * CCTV; custom overlay render context
5
+ * @property {CctvCompanyCameraData | undefined} [cam] 현재 overlay가 참조하는 카메라 데이터
6
+ * @property {CctvVideoOverlayHeaderProps | undefined} [headerOptions] 기본 헤더 표시 옵션
7
+ * @property {CctvVideoOverlayFooterProps | undefined} [footerOptions] 기본 푸터 표시 옵션
8
+ * @property {boolean | undefined} [isError] 영상 에러 상태 여부
9
+ * @property {boolean | undefined} [isLive] 영상 live 상태 여부
10
+ * @property {React.ReactNode} [overlayMessage] 기본 안내/에러 메시지 콘텐츠
11
+ */
12
+ export interface CctvVideoRenderOverlayContext {
13
+ /**
14
+ * 현재 overlay가 참조하는 카메라 데이터
15
+ */
16
+ cam?: CctvCompanyCameraData;
17
+ /**
18
+ * 기본 헤더 표시 옵션
19
+ */
20
+ headerOptions?: CctvVideoOverlayHeaderProps;
21
+ /**
22
+ * 기본 푸터 표시 옵션
23
+ */
24
+ footerOptions?: CctvVideoOverlayFooterProps;
25
+ /**
26
+ * 영상 에러 상태 여부
27
+ */
28
+ isError?: boolean;
29
+ /**
30
+ * 영상 live 상태 여부
31
+ */
32
+ isLive?: boolean;
33
+ /**
34
+ * 기본 안내/에러 메시지 콘텐츠
35
+ */
36
+ overlayMessage?: React.ReactNode;
37
+ }
38
+
39
+ /**
40
+ * CCTV; custom overlay render 함수
41
+ */
42
+ export type CctvVideoRenderOverlay = (
43
+ context: CctvVideoRenderOverlayContext,
44
+ ) => React.ReactNode;
2
45
 
3
46
  /**
4
47
  * CCTV; <CctvVideoOverlayHeader /> props
@@ -96,8 +139,13 @@ export interface CctvVideoStateProps {
96
139
  * @property {boolean} [isError] 에러 상태 여부
97
140
  * @property {boolean} [isLive] 영상이 정상적으로 재생 중인지 여부
98
141
  * @property {React.ReactNode} [overlayMessage] 에러/안내 메시지 콘텐츠
142
+ * @property {CctvVideoRenderOverlay} [renderOverlay] custom overlay 렌더 함수
99
143
  */
100
144
  export interface CctvVideoTemplateProps extends CctvVideoStateProps {
145
+ /**
146
+ * 현재 overlay가 참조하는 카메라 데이터
147
+ */
148
+ cam?: CctvCompanyCameraData;
101
149
  className?: string;
102
150
  /**
103
151
  * <CCtvVideoOverlayHeader /> 속성
@@ -107,4 +155,102 @@ export interface CctvVideoTemplateProps extends CctvVideoStateProps {
107
155
  * <CctvVideoOverlayFooter /> 속성
108
156
  */
109
157
  footerOptions?: CctvVideoOverlayFooterProps;
158
+ /**
159
+ * custom overlay 렌더 함수
160
+ */
161
+ renderOverlay?: CctvVideoRenderOverlay;
162
+ }
163
+
164
+ /**
165
+ * CCTV; cam-list item props
166
+ * @property {string} [className] 루트 아이템 클래스
167
+ * @property {CctvVideoRenderOverlay} [renderOverlay] custom overlay 렌더 함수
168
+ * @property {boolean} selected 카메라 선택 상태
169
+ * @property {() => void} [onSelect] 카메라 선택 핸들러
170
+ * @property {string} cam_id 카메라 id코드
171
+ * @property {string} cam_name 카메라 별칭
172
+ * @property {boolean} cam_online 카메라 online 여부
173
+ * @property {string} [cam_rtc] 카메라 rtc 경로
174
+ * @property {string} [cam_rtcp] 카메라 rtcp 경로
175
+ * @property {boolean} [cam_shared] 카메라 외부공유 여부
176
+ * @property {string} [company_id] 소속 업체 id
177
+ * @property {string} [company_name] 소속 업체명
178
+ */
179
+ export interface CctvCamListItemProps extends CctvCompanyCameraData {
180
+ /**
181
+ * 루트 아이템 클래스
182
+ */
183
+ className?: string;
184
+ /**
185
+ * custom overlay 렌더 함수
186
+ */
187
+ renderOverlay?: CctvVideoRenderOverlay;
188
+ }
189
+
190
+ /**
191
+ * CCTV; cam-list container props
192
+ * @property {string} [className] 루트 컨테이너 클래스
193
+ * @property {CctvCompanyCameraList[]} [list] 외부 주입 카메라 리스트
194
+ * @property {CctvVideoRenderOverlay} [renderOverlay] custom overlay 렌더 함수
195
+ */
196
+ export interface CctvCamListContainerProps {
197
+ /**
198
+ * 루트 컨테이너 클래스
199
+ */
200
+ className?: string;
201
+ /**
202
+ * 외부 주입 카메라 리스트
203
+ */
204
+ list?: CctvCompanyCameraList[];
205
+ /**
206
+ * custom overlay 렌더 함수
207
+ */
208
+ renderOverlay?: CctvVideoRenderOverlay;
209
+ }
210
+
211
+ /**
212
+ * CCTV; viewer desktop video props
213
+ * @property {CctvCompanyCameraData} [cam] 외부에서 직접 주입하는 카메라 데이터
214
+ * @property {CctvVideoRenderOverlay} [renderOverlay] custom overlay 렌더 함수
215
+ */
216
+ export interface CctvViewerDesktopVideoProps {
217
+ /**
218
+ * 외부에서 직접 주입하는 카메라 데이터
219
+ */
220
+ cam?: CctvCompanyCameraData;
221
+ /**
222
+ * custom overlay 렌더 함수
223
+ */
224
+ renderOverlay?: CctvVideoRenderOverlay;
225
+ }
226
+
227
+ /**
228
+ * CCTV; viewer desktop container props
229
+ * @property {string} [className] 루트 컨테이너 클래스
230
+ * @property {React.ReactNode} [children] 기본 video/pagination 대신 직접 주입할 children
231
+ * @property {CctvCompanyCameraData} [selectedCam] 외부에서 직접 주입하는 선택 카메라 데이터
232
+ * @property {CctvCompanyCameraData[]} [camList] 외부에서 직접 주입하는 카메라 리스트
233
+ * @property {CctvVideoRenderOverlay} [renderOverlay] viewer video용 custom overlay 렌더 함수
234
+ */
235
+ export interface CctvViewerDesktopContainerProps {
236
+ /**
237
+ * 루트 컨테이너 클래스
238
+ */
239
+ className?: string;
240
+ /**
241
+ * 기본 video/pagination 대신 직접 주입할 children
242
+ */
243
+ children?: React.ReactNode;
244
+ /**
245
+ * 외부에서 직접 주입하는 선택 카메라 데이터
246
+ */
247
+ selectedCam?: CctvCompanyCameraData;
248
+ /**
249
+ * 외부에서 직접 주입하는 카메라 리스트
250
+ */
251
+ camList?: CctvCompanyCameraList[];
252
+ /**
253
+ * viewer video용 custom overlay 렌더 함수
254
+ */
255
+ renderOverlay?: CctvVideoRenderOverlay;
110
256
  }