@uniai-fe/uds-templates 0.4.0 → 0.4.2

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
@@ -746,6 +746,46 @@
746
746
  transition: none;
747
747
  }
748
748
  }
749
+ .auth-stage-header {
750
+ display: flex;
751
+ flex-direction: column;
752
+ gap: var(--spacing-padding-5, 20px);
753
+ padding: 0 var(--spacing-padding-1, 4px);
754
+ }
755
+
756
+ .auth-stage-step {
757
+ display: flex;
758
+ justify-content: flex-start;
759
+ }
760
+
761
+ .auth-stage-step-pagination {
762
+ --pagination-carousel-gap: 6px;
763
+ }
764
+
765
+ .auth-stage-headline {
766
+ display: flex;
767
+ flex-direction: column;
768
+ gap: var(--spacing-padding-1, 4px);
769
+ }
770
+
771
+ .auth-stage-headline-text {
772
+ margin: 0;
773
+ font-size: 24px;
774
+ font-weight: 600;
775
+ line-height: 1.4;
776
+ letter-spacing: 0.2px;
777
+ color: var(--color-label-standard);
778
+ font-family: "Pretendard JP Variable", "Pretendard", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
779
+ }
780
+
781
+ .auth-stage-headline-description {
782
+ margin: 0;
783
+ font-size: 14px;
784
+ line-height: 1.4;
785
+ color: var(--color-label-assistive);
786
+ font-family: "Pretendard", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
787
+ }
788
+
749
789
 
750
790
 
751
791
  .auth-container {
@@ -826,6 +866,10 @@
826
866
  margin-top: var(--spacing-padding-4, 16px);
827
867
  }
828
868
 
869
+ .auth-code-field {
870
+ margin-top: var(--spacing-gap-6);
871
+ }
872
+
829
873
  .auth-login-form {
830
874
  display: flex;
831
875
  flex-direction: column;
@@ -1067,6 +1111,59 @@
1067
1111
  margin: 0;
1068
1112
  }
1069
1113
 
1114
+ .password-confirm-field {
1115
+ margin-top: var(--spacing-gap-6);
1116
+ }
1117
+
1118
+ .auth-password-helper {
1119
+ display: flex;
1120
+ flex-wrap: wrap;
1121
+ align-items: center;
1122
+ gap: var(--spacing-gap-3);
1123
+ margin-top: var(--spacing-gap-2);
1124
+ }
1125
+
1126
+ .auth-password-helper-column {
1127
+ flex-direction: column;
1128
+ align-items: flex-start;
1129
+ gap: var(--spacing-gap-1);
1130
+ }
1131
+
1132
+ .auth-set-password-icon {
1133
+ display: flex;
1134
+ align-items: center;
1135
+ justify-content: center;
1136
+ font-size: 0;
1137
+ }
1138
+ .auth-set-password-icon svg path {
1139
+ fill: var(--color-label-assistive);
1140
+ }
1141
+
1142
+ .auth-password-helper-text {
1143
+ font-size: var(--font-label-small-size);
1144
+ line-height: var(--font-label-small-line-height);
1145
+ font-weight: var(--font-label-small-weight);
1146
+ color: var(--color-label-assistive);
1147
+ }
1148
+
1149
+ .auth-password-helper-item {
1150
+ display: flex;
1151
+ align-items: center;
1152
+ gap: var(--spacing-gap-1);
1153
+ }
1154
+ .auth-password-helper-item[data-state=complete] .auth-password-helper-text {
1155
+ color: var(--color-success);
1156
+ }
1157
+ .auth-password-helper-item[data-state=complete] .auth-set-password-icon svg path {
1158
+ fill: var(--color-success);
1159
+ }
1160
+ .auth-password-helper-item[data-state=error] .auth-password-helper-text {
1161
+ color: var(--color-error);
1162
+ }
1163
+ .auth-password-helper-item[data-state=error] .auth-set-password-icon svg path {
1164
+ fill: var(--color-error);
1165
+ }
1166
+
1070
1167
  .auth-signup-form {
1071
1168
  display: flex;
1072
1169
  flex-direction: column;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
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.32.0",
15
+ "packageManager": "pnpm@10.33.0",
16
16
  "engines": {
17
17
  "node": ">=24",
18
18
  "pnpm": ">=10"
@@ -66,11 +66,11 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "clsx": "^2.1.1",
69
- "dayjs": "^1.11.19"
69
+ "dayjs": "^1.11.20"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@svgr/webpack": "^8.1.0",
73
- "@tanstack/react-query": "^5.90.21",
73
+ "@tanstack/react-query": "^5.95.2",
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.18.1",
89
+ "jotai": "^2.19.0",
90
90
  "next": "^15.5.11",
91
91
  "prettier": "^3.8.1",
92
- "react-hook-form": "^7.71.2",
93
- "sass": "^1.97.3",
92
+ "react-hook-form": "^7.72.0",
93
+ "sass": "^1.98.0",
94
94
  "typescript": "~5.9.3"
95
95
  }
96
96
  }
@@ -1,3 +1,5 @@
1
+ @use "./header/stage-header.scss";
2
+
1
3
  :root {
2
4
  --auth-container-max-width: 335px;
3
5
  --auth-container-gap: var(--spacing-padding-7, 28px);
@@ -1,5 +1,4 @@
1
1
  import "./index.scss";
2
- import "./header/stage-header.scss";
3
2
 
4
3
  export { AuthStageHeader } from "./header";
5
4
  export { AuthContainer } from "./AuthContainer";
@@ -91,7 +91,8 @@ export function AuthFindAccountInfoStep({
91
91
  <div className="auth-find-account-fields">
92
92
  <AuthFindAccountFieldBase
93
93
  className={clsx("auth-find-field-name", nameField.className)}
94
- label="이름"
94
+ // 첫 번째 필드 라벨도 서비스 앱이 주입한 값을 우선 따르도록 preset 하드코딩을 제거한다.
95
+ label={nameField.label ?? "이름"}
95
96
  templateProps={nameField.templateProps}
96
97
  inputProps={{
97
98
  name: nameFieldName,
@@ -24,7 +24,7 @@ export default function FindIdStepComplete({
24
24
  title={title ?? "아이디 찾기 완료"}
25
25
  description={description ?? "로그인 페이지로 이동하여 로그인합니다."}
26
26
  cta={{
27
- label: cta?.label ?? "로그인하기",
27
+ label: cta?.label ?? "확인",
28
28
  buttonProps: cta?.buttonProps,
29
29
  }}
30
30
  >
@@ -1 +1,3 @@
1
1
  @use "../common/find/styles/find-account.scss";
2
+ // StepReset password helper는 외부 앱의 `@uniai-fe/uds-templates/css` 엔트리에도 포함돼야 한다.
3
+ @use "../common/password/styles/set-password.scss";
@@ -20,7 +20,7 @@ export default function FindPasswordStepComplete({
20
20
  title={title ?? "비밀번호 변경 완료"}
21
21
  description={description ?? "로그인 페이지로 이동하여 로그인해 주세요."}
22
22
  cta={{
23
- label: cta?.label ?? "로그인하기",
23
+ label: cta?.label ?? "확인",
24
24
  buttonProps: cta?.buttonProps,
25
25
  }}
26
26
  />
@@ -0,0 +1,7 @@
1
+ @use "./common/container/index.scss" as authCommonContainer;
2
+ @use "./common/complete/index.scss" as authCommonComplete;
3
+ @use "./common/auth-code/styles/index.scss" as authCommonAuthCode;
4
+ @use "./login/index.scss" as authLogin;
5
+ @use "./find-id/index.scss" as authFindId;
6
+ @use "./find-password/index.scss" as authFindPassword;
7
+ @use "./signup/styles/signup.scss" as authSignup;
@@ -1,4 +1,4 @@
1
- import "./login/index.scss";
1
+ import "./index.scss";
2
2
 
3
3
  import {
4
4
  AuthContainer,
@@ -81,7 +81,10 @@ export default function AuthLoginFormField({
81
81
  register={register.id}
82
82
  helper={idHelper}
83
83
  label={idField?.label ?? "아이디"}
84
- placeholder={idField?.placeholder ?? "아이디를 입력해 주세요"}
84
+ placeholder={
85
+ idField?.placeholder ??
86
+ `${idField?.label ?? "아이디"}를 입력해 주세요`
87
+ }
85
88
  inputProps={idField?.inputProps}
86
89
  templateProps={idField?.templateProps}
87
90
  />
@@ -51,6 +51,58 @@ export interface API_Res_LoginUserFarm {
51
51
  user_access_role: string;
52
52
  }
53
53
 
54
+ /**
55
+ * 로그인 API; 응답 - 소속 부서 정보
56
+ * @route /auth/user/login
57
+ * @property {number} org_id 소속 조직 인덱스
58
+ * @property {string} org_name 소속 조직명
59
+ * @property {string} org_type 소속 조직 유형 코드
60
+ * @property {number} parent_org_id 상위 조직 인덱스
61
+ * @property {string} parent_org_name 상위 조직명
62
+ * @property {number} root_org_id 최상위 조직 인덱스
63
+ * @property {string} root_org_name 최상위 조직명
64
+ * @property {number} position_id 직책 인덱스
65
+ * @property {string} position_name 직책명
66
+ */
67
+ export interface API_Res_LoginUserAuth {
68
+ /**
69
+ * 소속 조직 인덱스
70
+ */
71
+ org_id: number;
72
+ /**
73
+ * 소속 조직명
74
+ */
75
+ org_name: string;
76
+ /**
77
+ * 소속 조직 유형 코드
78
+ */
79
+ org_type: string;
80
+ /**
81
+ * 상위 조직 인덱스
82
+ */
83
+ parent_org_id: number;
84
+ /**
85
+ * 상위 조직명
86
+ */
87
+ parent_org_name: string;
88
+ /**
89
+ * 최상위 조직 인덱스
90
+ */
91
+ root_org_id: number;
92
+ /**
93
+ * 최상위 조직명
94
+ */
95
+ root_org_name: string;
96
+ /**
97
+ * 직책 인덱스
98
+ */
99
+ position_id: number;
100
+ /**
101
+ * 직책명
102
+ */
103
+ position_name: string;
104
+ }
105
+
54
106
  /**
55
107
  * 로그인 API; 응답 - 유저 정보
56
108
  * @route /auth/user/login
@@ -120,6 +172,19 @@ export interface API_Res_LoginUserInfo {
120
172
  * @property {string} user_access_role 계정 권한/직책 코드 (OWNER: 농장주, EMPLOYEE: 농장직원, REGIONAL_MANAGER: 지역소장)
121
173
  */
122
174
  user_farms: API_Res_LoginUserFarm[];
175
+ /**
176
+ * (avic 전용) 소속부서 정보
177
+ * @property {number} org_id 소속 조직 인덱스
178
+ * @property {string} org_name 소속 조직명
179
+ * @property {string} org_type 소속 조직 유형 코드
180
+ * @property {number} parent_org_id 상위 조직 인덱스
181
+ * @property {string} parent_org_name 상위 조직명
182
+ * @property {number} root_org_id 최상위 조직 인덱스
183
+ * @property {string} root_org_name 최상위 조직명
184
+ * @property {number} position_id 직책 인덱스
185
+ * @property {string} position_name 직책명
186
+ */
187
+ avic_organizations: API_Res_LoginUserAuth[];
123
188
  /**
124
189
  * 가입일 (UTC)
125
190
  * - yyyy-MM-ddTHH:mm:ss
@@ -1,5 +1,3 @@
1
- import "./index.scss";
2
-
3
1
  /**
4
2
  * CCTV; viewer, cam-list, pagination, api/hook/jotai 도구를 함께 제공하는 엔트리
5
3
  */
package/src/index.scss CHANGED
@@ -3,15 +3,9 @@
3
3
 
4
4
  /* 템플릿 레벨 스타일을 통합해 서비스 앱이 단일 엔트리만 import하도록 구성한다. */
5
5
  /* namespace 충돌을 막기 위해 모든 @use 경로에 고유 alias를 부여한다. */
6
- @use "./page-frame/desktop/index.scss" as pageFrameDesktop;
7
- @use "./page-frame/mobile/index.scss" as pageFrameMobile;
6
+ @use "./page-frame/index.scss" as pageFrameStyles;
8
7
  @use "./modal/index.scss" as modalStyles;
9
8
 
10
- @use "./auth/common/container/index.scss" as authCommonContainer;
11
- @use "./auth/common/complete/index.scss" as authCommonComplete;
12
- @use "./auth/login/index.scss" as authLogin;
13
- @use "./auth/find-id/index.scss" as authFindId;
14
- @use "./auth/find-password/index.scss" as authFindPassword;
15
- @use "./auth/signup/styles/signup.scss" as authSignup;
9
+ @use "./auth/index.scss" as authStyles;
16
10
  @use "./weather/index.scss" as weatherStyles;
17
11
  @use "./cctv/index.scss" as cctvStyles;
package/src/index.tsx CHANGED
@@ -1,3 +1,6 @@
1
+ import "./modal/index.scss";
2
+ import "./cctv/index.scss";
3
+
1
4
  export * from "./page-frame";
2
5
  export * from "./auth";
3
6
  export * from "./modal";
@@ -1,5 +1,3 @@
1
- import "./index.scss";
2
-
3
1
  import { modalStackAtom } from "./jotai/atoms";
4
2
  import {
5
3
  ModalNamespace,
@@ -64,8 +64,9 @@ export default function PageHeaderSettingButton({
64
64
  }, [menuItems]);
65
65
 
66
66
  const isMatchRouteGroup = useMemo(
67
- () => pathname.startsWith(commonRoute),
68
- [pathname, commonRoute],
67
+ // Storybook의 next/navigation mock에서는 pathname이 null일 수 있어 fallback 경로를 재사용한다.
68
+ () => resolvedPath.startsWith(commonRoute),
69
+ [resolvedPath, commonRoute],
69
70
  );
70
71
 
71
72
  const dropdownItems: DropdownTemplateItem[] = useMemo(
@@ -1,5 +1,3 @@
1
- import "./index.scss";
2
-
3
1
  import Frame from "./components";
4
2
 
5
3
  /**
@@ -0,0 +1,2 @@
1
+ @use "./desktop/index.scss" as pageFrameDesktop;
2
+ @use "./mobile/index.scss" as pageFrameMobile;
@@ -1,3 +1,5 @@
1
+ import "./index.scss";
2
+
1
3
  import { DesktopFrame } from "./desktop";
2
4
  import { MobileFrame } from "./mobile";
3
5
 
@@ -2,7 +2,6 @@ import clsx from "clsx";
2
2
  import type { ReactNode } from "react";
3
3
 
4
4
  import IconBackward from "../../img/chevron-backward.svg";
5
- import "../../styles/header.scss";
6
5
 
7
6
  export interface MobileFrameHeaderProps {
8
7
  className?: string;
@@ -1,3 +1,5 @@
1
+ // MobileFrameHeader direct barrel 소비에서도 header 스타일을 함께 로드한다.
2
+ import "../../styles/header.scss";
1
3
  import { MobileFrameHeader } from "./Header";
2
4
 
3
5
  const Header = {
@@ -1,5 +1,3 @@
1
- import "./index.scss";
2
-
3
1
  import { MobileFrame } from "./components/page/Frame";
4
2
 
5
3
  /**
@@ -1,8 +1,12 @@
1
+ "use client";
2
+
1
3
  import Image from "next/image";
2
4
  import clsx from "clsx";
3
5
 
4
6
  import assetUrl from "../../../asset-url";
5
7
 
8
+ // Storybook의 next/image mock과 동일한 client boundary에서 weather icon을 렌더한다.
9
+
6
10
  /**
7
11
  * 날씨; 아이콘
8
12
  * @desc
@@ -1,5 +1,5 @@
1
- // Weather UI 컴포넌트 전용 스타일을 로드한다.
2
- import "./styles/weather.scss";
1
+ // Weather 엔트리는 category stylesheet index만 통해 공용 스타일을 로드한다.
2
+ import "./index.scss";
3
3
 
4
4
  /**
5
5
  * Weather; page-header 템플릿과 api/hook/jotai/mock 도구를 함께 제공하는 엔트리