@uniai-fe/uds-templates 0.5.9 → 0.5.11

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.
Files changed (30) hide show
  1. package/README.md +7 -0
  2. package/dist/styles.css +139 -79
  3. package/package.json +1 -1
  4. package/src/cctv/components/pagination/list/Item.tsx +9 -0
  5. package/src/weather/apis/korea/client.ts +50 -15
  6. package/src/weather/apis/korea/server.ts +2 -2
  7. package/src/weather/apis/open-weather-map/client.ts +45 -15
  8. package/src/weather/apis/open-weather-map/server.ts +8 -2
  9. package/src/weather/components/icon/Address.tsx +7 -6
  10. package/src/weather/components/icon/Weather.tsx +4 -5
  11. package/src/weather/components/page-header/Address.tsx +36 -2
  12. package/src/weather/components/page-header/Alert.tsx +43 -16
  13. package/src/weather/components/page-header/Container.tsx +33 -5
  14. package/src/weather/components/page-header/Forecast.tsx +48 -7
  15. package/src/weather/components/page-header/NextDays.tsx +25 -22
  16. package/src/weather/components/page-header/Today.tsx +134 -91
  17. package/src/weather/hooks/useOpenWeatherMap.ts +22 -3
  18. package/src/weather/hooks/useWeatherKorea.ts +16 -4
  19. package/src/weather/hooks/useWeatherKoreaAlert.ts +13 -4
  20. package/src/weather/img/marker.svg +4 -0
  21. package/src/weather/index.scss +1 -0
  22. package/src/weather/styles/variables.scss +30 -0
  23. package/src/weather/styles/weather.scss +116 -109
  24. package/src/weather/types/base.ts +20 -0
  25. package/src/weather/types/index.ts +2 -0
  26. package/src/weather/types/page-header.ts +277 -0
  27. package/src/weather/types/provider.ts +34 -0
  28. package/src/weather/utils/index.ts +1 -0
  29. package/src/weather/utils/locale.ts +110 -0
  30. package/src/weather/utils/weather.ts +112 -0
@@ -0,0 +1,110 @@
1
+ import type {
2
+ WeatherPageHeaderTexts,
3
+ WeatherProviderCapabilityRegistry,
4
+ WeatherProviderKey,
5
+ } from "../types";
6
+
7
+ const WEATHER_PAGE_HEADER_DEFAULT_TEXTS: WeatherPageHeaderTexts = {
8
+ currentWeatherLoading: "현재 날씨 불러오는 중...",
9
+ forecastWeatherLoading: "예보 날씨 불러오는 중...",
10
+ alertLoading: "특보 불러오는 중...",
11
+ currentLabel: "현재",
12
+ maxTemperatureLabel: "최고:",
13
+ minTemperatureLabel: "최저:",
14
+ humidityLabel: "습도",
15
+ tomorrowLabel: "내일",
16
+ dayAfterTomorrowLabel: "모레",
17
+ weatherIconAlt: "날씨 아이콘",
18
+ addressIconAlt: "날씨 위치",
19
+ conditionFallbackLabel: "-",
20
+ alertPreliminaryLevelLabel: "예비특보",
21
+ alertWatchLevelLabel: "주의보",
22
+ alertCancelCommandLabel: "해제",
23
+ alertIssueCommandLabel: "발령",
24
+ };
25
+
26
+ const OPEN_WEATHER_MAP_LANG_BY_LOCALE: Record<string, string> = {
27
+ ko: "kr",
28
+ en: "en",
29
+ ja: "ja",
30
+ };
31
+
32
+ const WEATHER_DEFAULT_PROVIDER: WeatherProviderKey = "openWeatherMap";
33
+
34
+ const WEATHER_PROVIDER_BY_LOCALE_KEY: Record<string, WeatherProviderKey> = {
35
+ ko: "kma",
36
+ };
37
+
38
+ export const WEATHER_PROVIDER_CAPABILITIES: WeatherProviderCapabilityRegistry =
39
+ {
40
+ kma: {
41
+ now: true,
42
+ forecast: true,
43
+ alert: true,
44
+ },
45
+ openWeatherMap: {
46
+ now: true,
47
+ forecast: true,
48
+ alert: false,
49
+ },
50
+ };
51
+
52
+ /**
53
+ * Weather Locale; locale 기준 키 정규화
54
+ * @param {string} [locale] locale 코드
55
+ * @return {string} 전달된 locale의 기본 언어 코드
56
+ * @example
57
+ * getWeatherLocaleKey("ko-KR")
58
+ */
59
+ export const getWeatherLocaleKey = (locale?: string): string =>
60
+ locale?.trim().toLowerCase().split(/[-_]/)[0] || "ko";
61
+
62
+ /**
63
+ * Weather Locale; locale 기준 provider 선택
64
+ * @param {string} [locale] locale 코드
65
+ * @return {WeatherProviderKey} 현재 locale에 사용할 weather provider
66
+ * @example
67
+ * resolveWeatherProvider("ko-KR")
68
+ */
69
+ export const resolveWeatherProvider = (locale?: string): WeatherProviderKey => {
70
+ const localeKey = getWeatherLocaleKey(locale);
71
+
72
+ return WEATHER_PROVIDER_BY_LOCALE_KEY[localeKey] ?? WEATHER_DEFAULT_PROVIDER;
73
+ };
74
+
75
+ /**
76
+ * Weather Locale; KMA provider 사용 여부
77
+ * @param {string} [locale] locale 코드
78
+ * @return {boolean} KMA provider 대상 locale 여부
79
+ * @example
80
+ * isKoreaWeatherLocale("ko-KR")
81
+ */
82
+ export const isKoreaWeatherLocale = (locale?: string): boolean =>
83
+ resolveWeatherProvider(locale) === "kma";
84
+
85
+ /**
86
+ * Weather Locale; page-header 텍스트 병합
87
+ * @param {Partial<WeatherPageHeaderTexts>} [texts] 서비스에서 주입한 텍스트
88
+ * @return {WeatherPageHeaderTexts} 기본 fallback과 서비스 주입 텍스트를 병합한 텍스트
89
+ * @example
90
+ * resolveWeatherPageHeaderTexts({ humidityLabel: "습도" })
91
+ */
92
+ export const resolveWeatherPageHeaderTexts = (
93
+ texts?: Partial<WeatherPageHeaderTexts>,
94
+ ): WeatherPageHeaderTexts => ({
95
+ ...WEATHER_PAGE_HEADER_DEFAULT_TEXTS,
96
+ ...texts,
97
+ });
98
+
99
+ /**
100
+ * Weather Locale; OpenWeatherMap API lang 값 변환
101
+ * @param {string} [locale] locale 코드
102
+ * @return {string} OpenWeatherMap lang 파라미터
103
+ * @example
104
+ * getOpenWeatherMapLang("ko")
105
+ */
106
+ export const getOpenWeatherMapLang = (locale?: string): string => {
107
+ const localeKey = getWeatherLocaleKey(locale);
108
+
109
+ return OPEN_WEATHER_MAP_LANG_BY_LOCALE[localeKey] ?? localeKey;
110
+ };
@@ -1,10 +1,13 @@
1
1
  import type {
2
2
  API_Res_WeatherKoreaBase,
3
3
  API_Res_WeatherKoreaForecast,
4
+ API_Res_WeatherKoreaNextDays,
4
5
  API_Res_WeatherKoreaNow,
5
6
  KMA_Res_WeatherItem,
6
7
  KMA_Res_WeatherForecast,
7
8
  KMA_Res_WeatherNow,
9
+ OWM_Res_Weather_Forecast,
10
+ OWM_Res_Weather_Forecast_Item,
8
11
  } from "../types";
9
12
  import { getMoments } from "./date-time";
10
13
 
@@ -217,6 +220,115 @@ const getWeatherCondition = ({
217
220
  };
218
221
  };
219
222
 
223
+ /**
224
+ * 날씨 도구; OpenWeatherMap 아이콘 코드를 weather icon 코드로 변환
225
+ * @param {string} [icon] OpenWeatherMap icon 코드
226
+ * @return {string} weather icon 코드
227
+ */
228
+ export const getOpenWeatherMapConditionCode = (icon?: string): string => {
229
+ switch (icon) {
230
+ case "01d":
231
+ case "01n":
232
+ return "sky-1";
233
+ case "02d":
234
+ case "02n":
235
+ case "03d":
236
+ case "03n":
237
+ case "04d":
238
+ case "04n":
239
+ return "sky-2";
240
+ case "09d":
241
+ case "09n":
242
+ return "drop-rain-3";
243
+ case "10d":
244
+ case "10n":
245
+ return "drop-rain-1";
246
+ case "11d":
247
+ case "11n":
248
+ return "drop-rain-thunder";
249
+ case "13d":
250
+ case "13n":
251
+ return "drop-snow";
252
+ case "50d":
253
+ case "50n":
254
+ return "sky-4";
255
+ default:
256
+ return "sky-1";
257
+ }
258
+ };
259
+
260
+ const getLocalDateKey = (date: Date): string => {
261
+ const month = String(date.getMonth() + 1).padStart(2, "0");
262
+ const day = String(date.getDate()).padStart(2, "0");
263
+
264
+ return `${date.getFullYear()}-${month}-${day}`;
265
+ };
266
+
267
+ const getForecastItemDateKey = (item: OWM_Res_Weather_Forecast_Item): string =>
268
+ item.dt_txt?.slice(0, 10) || getLocalDateKey(new Date(item.dt * 1000));
269
+
270
+ const toOpenWeatherMapNextDay = (
271
+ items: OWM_Res_Weather_Forecast_Item[],
272
+ ): API_Res_WeatherKoreaNextDays | undefined => {
273
+ if (items.length === 0) return undefined;
274
+
275
+ const conditionItem =
276
+ items.find(item => item.dt_txt?.includes("12:00")) ||
277
+ items[Math.floor(items.length / 2)];
278
+ const temperatures = items.flatMap(item => [
279
+ item.main.temp_min,
280
+ item.main.temp_max,
281
+ item.main.temp,
282
+ ]);
283
+
284
+ return {
285
+ sky: null,
286
+ drop: null,
287
+ rainAmount: null,
288
+ snowAmount: null,
289
+ windSpeed: null,
290
+ condition: getOpenWeatherMapConditionCode(
291
+ conditionItem?.weather?.[0]?.icon,
292
+ ),
293
+ conditionName: conditionItem?.weather?.[0]?.description || null,
294
+ max_temperature:
295
+ temperatures.length > 0 ? Math.round(Math.max(...temperatures)) : null,
296
+ min_temperature:
297
+ temperatures.length > 0 ? Math.round(Math.min(...temperatures)) : null,
298
+ };
299
+ };
300
+
301
+ /**
302
+ * 날씨 도구; OpenWeatherMap 예보 응답을 page-header next days 형태로 변환
303
+ * @param {OWM_Res_Weather_Forecast} [forecast] OpenWeatherMap 예보 응답
304
+ * @return {{ day_1?: API_Res_WeatherKoreaNextDays; day_2?: API_Res_WeatherKoreaNextDays }} 내일/모레 예보
305
+ */
306
+ export const getOpenWeatherMapNextDays = (
307
+ forecast?: OWM_Res_Weather_Forecast,
308
+ ): {
309
+ day_1?: API_Res_WeatherKoreaNextDays;
310
+ day_2?: API_Res_WeatherKoreaNextDays;
311
+ } => {
312
+ if (!Array.isArray(forecast?.list)) return {};
313
+
314
+ const todayKey = getLocalDateKey(new Date());
315
+ const grouped = forecast.list.reduce<
316
+ Record<string, OWM_Res_Weather_Forecast_Item[]>
317
+ >((days, item) => {
318
+ const dateKey = getForecastItemDateKey(item);
319
+ if (dateKey === todayKey) return days;
320
+
321
+ days[dateKey] = [...(days[dateKey] || []), item];
322
+ return days;
323
+ }, {});
324
+ const [day1Key, day2Key] = Object.keys(grouped).sort();
325
+
326
+ return {
327
+ day_1: day1Key ? toOpenWeatherMapNextDay(grouped[day1Key]) : undefined,
328
+ day_2: day2Key ? toOpenWeatherMapNextDay(grouped[day2Key]) : undefined,
329
+ };
330
+ };
331
+
220
332
  export function now(
221
333
  outputResponse: API_Res_WeatherKoreaNow,
222
334
  apiResponse: KMA_Res_WeatherNow,