@uniai-fe/uds-templates 0.5.28 → 0.6.0
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/README.md +3 -6
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/cctv/styles/variables.scss +1 -1
- package/src/weather/_legacy/apis/index.ts +4 -0
- package/src/weather/_legacy/data/response.ts +36 -0
- package/src/weather/_legacy/hooks/index.ts +5 -0
- package/src/weather/{hooks → _legacy/hooks}/useOpenWeatherMap.ts +1 -1
- package/src/weather/{hooks → _legacy/hooks}/useWeatherKorea.ts +4 -7
- package/src/weather/{hooks → _legacy/hooks}/useWeatherKoreaAlert.ts +2 -4
- package/src/weather/_legacy/types/api.ts +221 -0
- package/src/weather/_legacy/types/base.ts +70 -0
- package/src/weather/_legacy/types/index.ts +4 -0
- package/src/weather/_legacy/utils/index.ts +5 -0
- package/src/weather/_legacy/utils/locale.ts +28 -0
- package/src/weather/_legacy/utils/location.ts +139 -0
- package/src/weather/_legacy/utils/weather.ts +460 -0
- package/src/weather/apis/client.ts +339 -0
- package/src/weather/apis/index.ts +2 -4
- package/src/weather/apis/server.ts +264 -0
- package/src/weather/components/icon/Address.tsx +7 -0
- package/src/weather/components/icon/Weather.tsx +7 -6
- package/src/weather/components/page-header/Address.tsx +14 -0
- package/src/weather/components/page-header/Alert.tsx +17 -13
- package/src/weather/components/page-header/Container.tsx +12 -19
- package/src/weather/components/page-header/Forecast.tsx +21 -28
- package/src/weather/components/page-header/NextDays.tsx +10 -0
- package/src/weather/components/page-header/Today.tsx +86 -158
- package/src/weather/components/page-header/index.ts +5 -0
- package/src/weather/data/response.ts +4 -23
- package/src/weather/hooks/index.ts +3 -3
- package/src/weather/hooks/useWeather.ts +52 -0
- package/src/weather/hooks/useWeatherAlert.ts +35 -0
- package/src/weather/index.tsx +2 -2
- package/src/weather/jotai/coordinate.ts +4 -0
- package/src/weather/jotai/farm-idx.ts +4 -0
- package/src/weather/types/api.ts +393 -114
- package/src/weather/types/base.ts +15 -32
- package/src/weather/types/index.ts +0 -3
- package/src/weather/types/page-header.ts +118 -68
- package/src/weather/utils/index.ts +6 -4
- package/src/weather/utils/locale.ts +7 -69
- package/src/weather/utils/location.ts +6 -141
- package/src/weather/utils/weather.ts +53 -456
- package/src/weather/data/alert-regions-meta.json +0 -1286
- package/src/weather/data/weather-regions-meta.json +0 -9833
- package/src/weather/types/provider.ts +0 -34
- package/src/weather/utils/alert.ts +0 -30
- /package/src/weather/{apis → _legacy/apis}/korea/client.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/korea/server.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/open-weather-map/client.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/open-weather-map/server.ts +0 -0
- /package/src/weather/{types → _legacy/types}/korea.ts +0 -0
- /package/src/weather/{types → _legacy/types}/open-weather-map.ts +0 -0
- /package/src/weather/{utils → _legacy/utils}/date-time.ts +0 -0
- /package/src/weather/{utils → _legacy/utils}/validate.ts +0 -0
|
@@ -3,21 +3,28 @@
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
|
|
5
5
|
import { Alternate } from "@uniai-fe/uds-primitives";
|
|
6
|
-
import
|
|
6
|
+
import { useWeatherAlert } from "../../hooks";
|
|
7
7
|
import type { WeatherPageHeaderAlertProps } from "../../types";
|
|
8
|
-
import {
|
|
9
|
-
isKoreaWeatherLocale,
|
|
10
|
-
resolveWeatherPageHeaderTexts,
|
|
11
|
-
} from "../../utils";
|
|
8
|
+
import { resolveWeatherPageHeaderTexts } from "../../utils";
|
|
12
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Weather Page Header; 기상 특보를 표시하는 컴포넌트.
|
|
12
|
+
* @component
|
|
13
|
+
* @desc 통합 weather alert query 결과 또는 상위 weather props의 특보 목록을 표시한다.
|
|
14
|
+
* @param {WeatherPageHeaderAlertProps} [props] alert props
|
|
15
|
+
* @param {string} [props.locale] 표시 언어 코드
|
|
16
|
+
* @param {Partial<WeatherPageHeaderTexts>} [props.texts] 서비스에서 주입한 표시 문구
|
|
17
|
+
* @param {WeatherPageHeaderWeather} [props.weather] 상위 container에서 준비한 통합 weather query 결과
|
|
18
|
+
*/
|
|
13
19
|
export default function WeatherPageHeaderAlert({
|
|
14
|
-
locale,
|
|
15
20
|
texts: textOverrides,
|
|
21
|
+
weather,
|
|
16
22
|
}: WeatherPageHeaderAlertProps = {}) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
enabled: isKoreaProvider,
|
|
23
|
+
const alertFallback = useWeatherAlert({
|
|
24
|
+
enabled: !weather,
|
|
20
25
|
});
|
|
26
|
+
const alert = weather?.alert ?? alertFallback.alert;
|
|
27
|
+
const isFetching = weather?.isFetchingAlert ?? alertFallback.isFetching;
|
|
21
28
|
const texts = resolveWeatherPageHeaderTexts(textOverrides);
|
|
22
29
|
|
|
23
30
|
const notice = useMemo((): string => {
|
|
@@ -42,10 +49,7 @@ export default function WeatherPageHeaderAlert({
|
|
|
42
49
|
texts.alertWatchLevelLabel,
|
|
43
50
|
]);
|
|
44
51
|
|
|
45
|
-
if (
|
|
46
|
-
!isKoreaProvider ||
|
|
47
|
-
(!isFetching && (alert.length === 0 || !alert?.[0]?.alert_type))
|
|
48
|
-
) {
|
|
52
|
+
if (!isFetching && (alert.length === 0 || !alert?.[0]?.alert_type)) {
|
|
49
53
|
return null;
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -5,37 +5,30 @@ import WeatherPageHeaderForecast from "./Forecast";
|
|
|
5
5
|
import WeatherPageHeaderAddress from "./Address";
|
|
6
6
|
import PageHeaderUtilityItem from "../../../page-frame/desktop/components/header/util/Item";
|
|
7
7
|
import type { WeatherPageHeaderContainerProps } from "../../types";
|
|
8
|
-
import {
|
|
9
|
-
import { isKoreaWeatherLocale } from "../../utils";
|
|
8
|
+
import { useWeather } from "../../hooks";
|
|
10
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Weather Page Header; address/today/forecast를 조합하는 container 컴포넌트.
|
|
12
|
+
* @component
|
|
13
|
+
* @desc 통합 weather summary query를 한 번 실행하고 address, today, forecast leaf에 전달한다.
|
|
14
|
+
* @param {WeatherPageHeaderContainerProps} [props] container props
|
|
15
|
+
* @param {string} [props.locale] 표시 언어 코드
|
|
16
|
+
* @param {Partial<WeatherPageHeaderTexts>} [props.texts] 서비스에서 주입한 표시 문구
|
|
17
|
+
*/
|
|
11
18
|
export default function WeatherPageHeaderContainer({
|
|
12
19
|
locale,
|
|
13
20
|
texts,
|
|
14
21
|
}: WeatherPageHeaderContainerProps = {}) {
|
|
15
|
-
const
|
|
16
|
-
const koreaWeather = useWeatherKorea({
|
|
17
|
-
enabled: isKoreaProvider,
|
|
18
|
-
});
|
|
19
|
-
const openWeatherMapWeather = useOpenWeatherMap({
|
|
20
|
-
locale,
|
|
21
|
-
enabledNow: !isKoreaProvider,
|
|
22
|
-
enabledForecast: !isKoreaProvider,
|
|
23
|
-
});
|
|
22
|
+
const weather = useWeather({ locale });
|
|
24
23
|
|
|
25
24
|
return (
|
|
26
25
|
<PageHeaderUtilityItem className="weather-page-header">
|
|
27
26
|
<WeatherPageHeaderAddress locale={locale} texts={texts} />
|
|
28
|
-
<WeatherPageHeaderToday
|
|
29
|
-
locale={locale}
|
|
30
|
-
texts={texts}
|
|
31
|
-
koreaWeather={koreaWeather}
|
|
32
|
-
openWeatherMapWeather={openWeatherMapWeather}
|
|
33
|
-
/>
|
|
27
|
+
<WeatherPageHeaderToday locale={locale} texts={texts} weather={weather} />
|
|
34
28
|
<WeatherPageHeaderForecast
|
|
35
29
|
locale={locale}
|
|
36
30
|
texts={texts}
|
|
37
|
-
|
|
38
|
-
openWeatherMapWeather={openWeatherMapWeather}
|
|
31
|
+
weather={weather}
|
|
39
32
|
/>
|
|
40
33
|
</PageHeaderUtilityItem>
|
|
41
34
|
);
|
|
@@ -1,46 +1,39 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useMemo } from "react";
|
|
4
3
|
import { Alternate } from "@uniai-fe/uds-primitives";
|
|
5
|
-
import {
|
|
4
|
+
import { useWeather } from "../../hooks";
|
|
6
5
|
import type { WeatherPageHeaderForecastProps } from "../../types";
|
|
7
6
|
import {
|
|
8
|
-
|
|
9
|
-
isKoreaWeatherLocale,
|
|
7
|
+
isWeatherForecastResponse,
|
|
10
8
|
resolveWeatherPageHeaderTexts,
|
|
11
9
|
} from "../../utils";
|
|
12
10
|
import WeatherPageHeaderNextDays from "./NextDays";
|
|
13
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Weather Page Header; 내일/모레 예보를 표시하는 컴포넌트.
|
|
14
|
+
* @component
|
|
15
|
+
* @desc 통합 weather forecast 응답에서 day_1/day_2를 추출해 하루 단위 예보로 표시한다.
|
|
16
|
+
* @param {WeatherPageHeaderForecastProps} [props] forecast props
|
|
17
|
+
* @param {string} [props.locale] 표시 언어 코드
|
|
18
|
+
* @param {Partial<WeatherPageHeaderTexts>} [props.texts] 서비스에서 주입한 표시 문구
|
|
19
|
+
* @param {WeatherPageHeaderWeather} [props.weather] 상위 container에서 준비한 통합 weather query 결과
|
|
20
|
+
*/
|
|
14
21
|
export default function WeatherPageHeaderForecast({
|
|
15
22
|
locale,
|
|
16
23
|
texts: textOverrides,
|
|
17
|
-
|
|
18
|
-
openWeatherMapWeather,
|
|
24
|
+
weather,
|
|
19
25
|
}: WeatherPageHeaderForecastProps = {}) {
|
|
20
|
-
const
|
|
21
|
-
const koreaWeatherFallback = useWeatherKorea({
|
|
22
|
-
enabled: !koreaWeather && isKoreaProvider,
|
|
23
|
-
});
|
|
24
|
-
const openWeatherMapWeatherFallback = useOpenWeatherMap({
|
|
26
|
+
const weatherFallback = useWeather({
|
|
25
27
|
locale,
|
|
26
|
-
|
|
27
|
-
enabledForecast: !openWeatherMapWeather && !isKoreaProvider,
|
|
28
|
+
enabled: !weather,
|
|
28
29
|
});
|
|
29
|
-
const
|
|
30
|
-
const openWeatherMap = openWeatherMapWeather ?? openWeatherMapWeatherFallback;
|
|
30
|
+
const currentWeather = weather ?? weatherFallback;
|
|
31
31
|
const texts = resolveWeatherPageHeaderTexts(textOverrides);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
36
|
-
const forecastDays = isKoreaProvider
|
|
37
|
-
? korea.forecast
|
|
38
|
-
: openWeatherMapNextDays;
|
|
39
|
-
const isFetching = isKoreaProvider
|
|
40
|
-
? korea.isFetchingForecast
|
|
41
|
-
: openWeatherMap.isFetchingForecast;
|
|
32
|
+
const forecast = isWeatherForecastResponse(currentWeather.forecast)
|
|
33
|
+
? currentWeather.forecast
|
|
34
|
+
: undefined;
|
|
42
35
|
|
|
43
|
-
if (
|
|
36
|
+
if (currentWeather.isFetchingForecast) {
|
|
44
37
|
return (
|
|
45
38
|
<div className="weather-next-days-container">
|
|
46
39
|
<Alternate.LoadingDefault direction="horizontal">
|
|
@@ -54,12 +47,12 @@ export default function WeatherPageHeaderForecast({
|
|
|
54
47
|
<>
|
|
55
48
|
<WeatherPageHeaderNextDays
|
|
56
49
|
title={texts.tomorrowLabel}
|
|
57
|
-
data={
|
|
50
|
+
data={forecast?.day_1}
|
|
58
51
|
texts={textOverrides}
|
|
59
52
|
/>
|
|
60
53
|
<WeatherPageHeaderNextDays
|
|
61
54
|
title={texts.dayAfterTomorrowLabel}
|
|
62
|
-
data={
|
|
55
|
+
data={forecast?.day_2}
|
|
63
56
|
texts={textOverrides}
|
|
64
57
|
/>
|
|
65
58
|
</>
|
|
@@ -4,6 +4,16 @@ import type { WeatherPageHeaderNextDaysProps } from "../../types";
|
|
|
4
4
|
import { resolveWeatherPageHeaderTexts } from "../../utils";
|
|
5
5
|
import WeatherIcon from "../icon/Weather";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Weather Page Header; 하루 단위 예보를 표시하는 컴포넌트.
|
|
9
|
+
* @component
|
|
10
|
+
* @desc title과 normalized forecast day 데이터를 조합해 날씨 아이콘과 최저/최고 기온을 표시한다.
|
|
11
|
+
* @param {WeatherPageHeaderNextDaysProps} props next days props
|
|
12
|
+
* @param {string} props.title 날짜 라벨
|
|
13
|
+
* @param {API_Res_WeatherNextDays} [props.data] 예보 데이터
|
|
14
|
+
* @param {string} [props.locale] 표시 언어 코드
|
|
15
|
+
* @param {Partial<WeatherPageHeaderTexts>} [props.texts] 서비스에서 주입한 표시 문구
|
|
16
|
+
*/
|
|
7
17
|
export default function WeatherPageHeaderNextDays({
|
|
8
18
|
title,
|
|
9
19
|
data,
|
|
@@ -7,187 +7,115 @@ import { Alternate } from "@uniai-fe/uds-primitives";
|
|
|
7
7
|
import WeatherIcon from "../icon/Weather";
|
|
8
8
|
import WeatherPageHeaderAlert from "./Alert";
|
|
9
9
|
import type { WeatherPageHeaderTodayProps } from "../../types";
|
|
10
|
-
import {
|
|
10
|
+
import { useWeather } from "../../hooks";
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
isKoreaWeatherLocale,
|
|
12
|
+
isWeatherForecastResponse,
|
|
13
|
+
isWeatherNowResponse,
|
|
15
14
|
resolveWeatherPageHeaderTexts,
|
|
16
15
|
} from "../../utils";
|
|
17
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Weather Page Header; 표시 가능한 숫자값으로 변환.
|
|
19
|
+
* @util
|
|
20
|
+
* @desc weather API가 숫자 또는 문자열로 내려주는 수치 값을 number/null로 정규화한다.
|
|
21
|
+
* @param {number|string|null|undefined} value 변환할 날씨 수치 값
|
|
22
|
+
* @return {number | null} 표시 가능한 숫자 값
|
|
23
|
+
*/
|
|
24
|
+
const toWeatherNumber = (
|
|
25
|
+
value: number | string | null | undefined,
|
|
26
|
+
): number | null => {
|
|
27
|
+
if (value === null || typeof value === "undefined" || value === "") {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const nextValue = Number(value);
|
|
32
|
+
return Number.isNaN(nextValue) ? null : nextValue;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Weather Page Header; 현재 날씨와 당일 최고/최저/습도를 표시하는 컴포넌트.
|
|
37
|
+
* @component
|
|
38
|
+
* @desc 통합 weather now/forecast 응답을 조합해 현재 기온, 최고/최저 기온, 습도를 표시한다.
|
|
39
|
+
* @param {WeatherPageHeaderTodayProps} [props] today props
|
|
40
|
+
* @param {string} [props.locale] 표시 언어 코드
|
|
41
|
+
* @param {Partial<WeatherPageHeaderTexts>} [props.texts] 서비스에서 주입한 표시 문구
|
|
42
|
+
* @param {WeatherPageHeaderWeather} [props.weather] 상위 container에서 준비한 통합 weather query 결과
|
|
43
|
+
*/
|
|
18
44
|
export default function WeatherPageHeaderToday({
|
|
19
45
|
locale,
|
|
20
46
|
texts: textOverrides,
|
|
21
|
-
|
|
22
|
-
openWeatherMapWeather,
|
|
47
|
+
weather,
|
|
23
48
|
}: WeatherPageHeaderTodayProps = {}) {
|
|
24
|
-
const
|
|
25
|
-
const koreaWeatherFallback = useWeatherKorea({
|
|
26
|
-
enabled: !koreaWeather && isKoreaProvider,
|
|
27
|
-
});
|
|
28
|
-
const openWeatherMapWeatherFallback = useOpenWeatherMap({
|
|
49
|
+
const weatherFallback = useWeather({
|
|
29
50
|
locale,
|
|
30
|
-
|
|
31
|
-
enabledForecast: !openWeatherMapWeather && !isKoreaProvider,
|
|
51
|
+
enabled: !weather,
|
|
32
52
|
});
|
|
33
|
-
const
|
|
34
|
-
const openWeatherMap = openWeatherMapWeather ?? openWeatherMapWeatherFallback;
|
|
53
|
+
const currentWeather = weather ?? weatherFallback;
|
|
35
54
|
const texts = resolveWeatherPageHeaderTexts(textOverrides);
|
|
36
|
-
const
|
|
37
|
-
() =>
|
|
38
|
-
|
|
55
|
+
const now = useMemo(
|
|
56
|
+
() =>
|
|
57
|
+
isWeatherNowResponse(currentWeather.now) ? currentWeather.now : undefined,
|
|
58
|
+
[currentWeather.now],
|
|
59
|
+
);
|
|
60
|
+
const forecast = useMemo(
|
|
61
|
+
() =>
|
|
62
|
+
isWeatherForecastResponse(currentWeather.forecast)
|
|
63
|
+
? currentWeather.forecast
|
|
64
|
+
: undefined,
|
|
65
|
+
[currentWeather.forecast],
|
|
39
66
|
);
|
|
40
67
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return false;
|
|
46
|
-
}, [
|
|
47
|
-
isKoreaProvider,
|
|
48
|
-
korea.isFetchingNow,
|
|
49
|
-
openWeatherMap.isFetchingForecast,
|
|
50
|
-
openWeatherMap.isFetchingNow,
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
const iconCode = useMemo(() => {
|
|
54
|
-
if (isKoreaProvider) {
|
|
55
|
-
const kma_now = korea.now?.today?.condition;
|
|
56
|
-
if (kma_now && !kma_now.endsWith("null")) return kma_now;
|
|
57
|
-
|
|
58
|
-
const kma_forecast = korea.forecast?.today?.condition;
|
|
59
|
-
if (kma_forecast && !kma_forecast.endsWith("null")) return kma_forecast;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return getOpenWeatherMapConditionCode(
|
|
63
|
-
openWeatherMap.now?.weather?.[0]?.icon,
|
|
64
|
-
);
|
|
65
|
-
}, [
|
|
66
|
-
isKoreaProvider,
|
|
67
|
-
korea.forecast?.today?.condition,
|
|
68
|
-
korea.now?.today?.condition,
|
|
69
|
-
openWeatherMap.now?.weather,
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
const weatherStateName = useMemo(() => {
|
|
73
|
-
if (isKoreaProvider) {
|
|
74
|
-
const kma_now = korea.now?.today?.conditionName;
|
|
75
|
-
if (kma_now && !kma_now.endsWith("null"))
|
|
76
|
-
return kma_now || texts.conditionFallbackLabel;
|
|
77
|
-
|
|
78
|
-
const kma_forecast = korea.forecast?.today?.conditionName;
|
|
79
|
-
if (kma_forecast && !kma_forecast.endsWith("null"))
|
|
80
|
-
return kma_forecast || texts.conditionFallbackLabel;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
openWeatherMap.now?.weather?.[0]?.description ||
|
|
85
|
-
texts.conditionFallbackLabel
|
|
86
|
-
);
|
|
87
|
-
}, [
|
|
88
|
-
isKoreaProvider,
|
|
89
|
-
korea.forecast?.today?.conditionName,
|
|
90
|
-
korea.now?.today?.conditionName,
|
|
91
|
-
openWeatherMap.now?.weather,
|
|
92
|
-
texts.conditionFallbackLabel,
|
|
93
|
-
]);
|
|
94
|
-
|
|
95
|
-
const temperature = useMemo(() => {
|
|
96
|
-
if (isKoreaProvider) {
|
|
97
|
-
const kma_now = korea.now?.today?.temperature;
|
|
98
|
-
if (kma_now && !Number.isNaN(Number(kma_now))) return Number(kma_now);
|
|
99
|
-
|
|
100
|
-
const kma_forecast = korea.forecast?.today?.temperature;
|
|
101
|
-
if (kma_forecast && !Number.isNaN(Number(kma_forecast)))
|
|
102
|
-
return Number(kma_forecast);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const alt_now = openWeatherMap.now?.main?.temp;
|
|
106
|
-
return typeof alt_now === "number" ? Math.round(alt_now) : null;
|
|
107
|
-
}, [
|
|
108
|
-
isKoreaProvider,
|
|
109
|
-
korea.forecast?.today?.temperature,
|
|
110
|
-
korea.now?.today?.temperature,
|
|
111
|
-
openWeatherMap.now?.main?.temp,
|
|
112
|
-
]);
|
|
113
|
-
|
|
114
|
-
const humidity = useMemo(() => {
|
|
115
|
-
if (isKoreaProvider) {
|
|
116
|
-
const kma_now = korea.now?.today?.humidity;
|
|
117
|
-
if (kma_now && !Number.isNaN(Number(kma_now))) return Number(kma_now);
|
|
118
|
-
|
|
119
|
-
const kma_forecast = korea.forecast?.today?.humidity;
|
|
120
|
-
if (kma_forecast && !Number.isNaN(Number(kma_forecast)))
|
|
121
|
-
return Number(kma_forecast);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const alt_now = openWeatherMap.now?.main?.humidity;
|
|
125
|
-
return typeof alt_now === "number" ? Math.round(alt_now) : null;
|
|
126
|
-
}, [
|
|
127
|
-
isKoreaProvider,
|
|
128
|
-
korea.forecast?.today?.humidity,
|
|
129
|
-
korea.now?.today?.humidity,
|
|
130
|
-
openWeatherMap.now?.main?.humidity,
|
|
131
|
-
]);
|
|
132
|
-
|
|
133
|
-
const maxTemperature = useMemo(() => {
|
|
134
|
-
if (isKoreaProvider) {
|
|
135
|
-
const kma_forecast = korea.forecast?.today?.max_temperature;
|
|
136
|
-
if (
|
|
137
|
-
kma_forecast !== null &&
|
|
138
|
-
kma_forecast !== undefined &&
|
|
139
|
-
!Number.isNaN(Number(kma_forecast))
|
|
140
|
-
)
|
|
141
|
-
return Number(kma_forecast);
|
|
142
|
-
}
|
|
68
|
+
const iconCode = useMemo(
|
|
69
|
+
() => now?.today.condition || forecast?.today.condition || "sky-1",
|
|
70
|
+
[forecast?.today.condition, now?.today.condition],
|
|
71
|
+
);
|
|
143
72
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
73
|
+
const weatherStateName = useMemo(
|
|
74
|
+
() =>
|
|
75
|
+
now?.today.conditionName ||
|
|
76
|
+
forecast?.today.conditionName ||
|
|
77
|
+
texts.conditionFallbackLabel,
|
|
78
|
+
[
|
|
79
|
+
forecast?.today.conditionName,
|
|
80
|
+
now?.today.conditionName,
|
|
81
|
+
texts.conditionFallbackLabel,
|
|
82
|
+
],
|
|
83
|
+
);
|
|
151
84
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
85
|
+
const temperature = useMemo(
|
|
86
|
+
() =>
|
|
87
|
+
toWeatherNumber(now?.today.temperature) ??
|
|
88
|
+
toWeatherNumber(forecast?.today.temperature),
|
|
89
|
+
[forecast?.today.temperature, now?.today.temperature],
|
|
90
|
+
);
|
|
158
91
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
!Number.isNaN(Number(kma_forecast))
|
|
166
|
-
)
|
|
167
|
-
return Number(kma_forecast);
|
|
168
|
-
}
|
|
92
|
+
const humidity = useMemo(
|
|
93
|
+
() =>
|
|
94
|
+
toWeatherNumber(now?.today.humidity) ??
|
|
95
|
+
toWeatherNumber(forecast?.today.humidity),
|
|
96
|
+
[forecast?.today.humidity, now?.today.humidity],
|
|
97
|
+
);
|
|
169
98
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
!Number.isNaN(Number(alt_forecast))
|
|
175
|
-
)
|
|
176
|
-
return Number(alt_forecast);
|
|
99
|
+
const maxTemperature = useMemo(
|
|
100
|
+
() => toWeatherNumber(forecast?.today.max_temperature),
|
|
101
|
+
[forecast?.today.max_temperature],
|
|
102
|
+
);
|
|
177
103
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
openWeatherMapToday?.min_temperature,
|
|
183
|
-
]);
|
|
104
|
+
const minTemperature = useMemo(
|
|
105
|
+
() => toWeatherNumber(forecast?.today.min_temperature),
|
|
106
|
+
[forecast?.today.min_temperature],
|
|
107
|
+
);
|
|
184
108
|
const alert = (
|
|
185
|
-
<WeatherPageHeaderAlert
|
|
109
|
+
<WeatherPageHeaderAlert
|
|
110
|
+
locale={locale}
|
|
111
|
+
texts={textOverrides}
|
|
112
|
+
weather={currentWeather}
|
|
113
|
+
/>
|
|
186
114
|
);
|
|
187
115
|
|
|
188
116
|
return (
|
|
189
117
|
<div className="weather-today-container">
|
|
190
|
-
{
|
|
118
|
+
{currentWeather.isFetchingNow ? (
|
|
191
119
|
<>
|
|
192
120
|
{alert}
|
|
193
121
|
<Alternate.LoadingDefault direction="horizontal">
|
|
@@ -6,6 +6,11 @@ import WeatherPageHeaderNextDays from "./NextDays";
|
|
|
6
6
|
import WeatherPageHeaderToday from "./Today";
|
|
7
7
|
|
|
8
8
|
// 기존 Container 호출 계약을 유지하면서, 서비스 재조합용 하위 조각 접근도 함께 연다.
|
|
9
|
+
/**
|
|
10
|
+
* Weather Page Header; 조합형 page-header namespace 컴포넌트.
|
|
11
|
+
* @component
|
|
12
|
+
* @desc 기존 container 호출 계약을 유지하고 서비스 재조합용 하위 조각을 정적 속성으로 노출한다.
|
|
13
|
+
*/
|
|
9
14
|
export const WeatherPageHeader = Object.assign(WeatherPageHeaderContainer, {
|
|
10
15
|
Container: WeatherPageHeaderContainer,
|
|
11
16
|
Address: WeatherPageHeaderAddress,
|
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { API_Res_WeatherBase } from "../types";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
response: {
|
|
5
|
-
header: {
|
|
6
|
-
resultCode: "",
|
|
7
|
-
resultMsg: "",
|
|
8
|
-
},
|
|
9
|
-
body: {
|
|
10
|
-
dataType: "",
|
|
11
|
-
items: {
|
|
12
|
-
item: [],
|
|
13
|
-
},
|
|
14
|
-
pageNo: 0,
|
|
15
|
-
numOfRows: 0,
|
|
16
|
-
totalCount: 0,
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const API_RES_BASE: API_Res_WeatherKoreaBase = {
|
|
3
|
+
const API_RES_BASE: API_Res_WeatherBase = {
|
|
22
4
|
sky: null,
|
|
23
5
|
drop: null,
|
|
24
6
|
rainAmount: null,
|
|
@@ -28,9 +10,8 @@ const API_RES_BASE: API_Res_WeatherKoreaBase = {
|
|
|
28
10
|
conditionName: null,
|
|
29
11
|
};
|
|
30
12
|
|
|
31
|
-
const
|
|
32
|
-
API_RES_RAW,
|
|
13
|
+
const WEATHER_RESPONSE = {
|
|
33
14
|
API_RES_BASE,
|
|
34
15
|
};
|
|
35
16
|
|
|
36
|
-
export default
|
|
17
|
+
export default WEATHER_RESPONSE;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import useWeather from "./useWeather";
|
|
2
|
+
import useWeatherAlert from "./useWeatherAlert";
|
|
3
3
|
|
|
4
|
-
export {
|
|
4
|
+
export { useWeather, useWeatherAlert };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { useAtomValue } from "jotai";
|
|
5
|
+
import { useQueryWeatherSummary } from "../apis/client";
|
|
6
|
+
import { weatherCoordinate, weatherFarmIdx } from "../jotai";
|
|
7
|
+
import type {
|
|
8
|
+
API_Req_Weather,
|
|
9
|
+
UseWeatherOptions,
|
|
10
|
+
UseWeatherReturn,
|
|
11
|
+
} from "../types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Weather Hook; 통합 weather summary API를 조회한다.
|
|
15
|
+
* @hook
|
|
16
|
+
* @desc weatherCoordinate/weatherFarmIdx atom 값을 통합 summary route 요청 파라미터로 변환해 조회한다.
|
|
17
|
+
* @param {UseWeatherOptions} [options] weather 조회 옵션
|
|
18
|
+
* @param {string} [options.locale] 요청 언어 코드
|
|
19
|
+
* @param {boolean} [options.enabled] query 실행 여부
|
|
20
|
+
* @return {UseWeatherReturn} 통합 weather query 결과
|
|
21
|
+
*/
|
|
22
|
+
export default function useWeather({
|
|
23
|
+
locale,
|
|
24
|
+
enabled = true,
|
|
25
|
+
}: UseWeatherOptions = {}): UseWeatherReturn {
|
|
26
|
+
const coordinate = useAtomValue(weatherCoordinate);
|
|
27
|
+
const farm_idx = useAtomValue(weatherFarmIdx);
|
|
28
|
+
const params = useMemo<API_Req_Weather>(
|
|
29
|
+
() => ({
|
|
30
|
+
lat: coordinate.lat,
|
|
31
|
+
lng: coordinate.lng,
|
|
32
|
+
...(farm_idx ? { farm_idx } : {}),
|
|
33
|
+
...(locale ? { locale } : {}),
|
|
34
|
+
}),
|
|
35
|
+
[coordinate.lat, coordinate.lng, farm_idx, locale],
|
|
36
|
+
);
|
|
37
|
+
const { data, isFetching } = useQueryWeatherSummary(params, { enabled });
|
|
38
|
+
|
|
39
|
+
const alert = useMemo(() => data?.alert?.alerts || [], [data]);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
params,
|
|
43
|
+
coordinate,
|
|
44
|
+
farm_idx,
|
|
45
|
+
...(data ? { summary: data, now: data.now, forecast: data.forecast } : {}),
|
|
46
|
+
alert,
|
|
47
|
+
isFetching,
|
|
48
|
+
isFetchingNow: isFetching,
|
|
49
|
+
isFetchingForecast: isFetching,
|
|
50
|
+
isFetchingAlert: isFetching,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { useAtomValue } from "jotai";
|
|
5
|
+
import { useQueryWeatherAlert } from "../apis/client";
|
|
6
|
+
import { weatherFarmIdx } from "../jotai";
|
|
7
|
+
import type { UseWeatherAlertOptions, UseWeatherAlertReturn } from "../types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Weather Hook; 통합 weather alert API를 조회한다.
|
|
11
|
+
* @hook
|
|
12
|
+
* @desc weatherFarmIdx atom 값을 통합 alert route 요청 파라미터로 변환해 조회한다.
|
|
13
|
+
* @param {UseWeatherAlertOptions} [options] 특보 조회 옵션
|
|
14
|
+
* @param {boolean} [options.enabled] query 실행 여부
|
|
15
|
+
* @return {UseWeatherAlertReturn} 특보 query 결과
|
|
16
|
+
*/
|
|
17
|
+
export default function useWeatherAlert({
|
|
18
|
+
enabled = true,
|
|
19
|
+
}: UseWeatherAlertOptions = {}): UseWeatherAlertReturn {
|
|
20
|
+
const farm_idx = useAtomValue(weatherFarmIdx);
|
|
21
|
+
const { data, isFetching } = useQueryWeatherAlert(
|
|
22
|
+
{
|
|
23
|
+
farm_idx,
|
|
24
|
+
},
|
|
25
|
+
{ enabled },
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const alert = useMemo(() => data?.alerts || [], [data]);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
farm_idx,
|
|
32
|
+
alert,
|
|
33
|
+
isFetching,
|
|
34
|
+
};
|
|
35
|
+
}
|
package/src/weather/index.tsx
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import "./index.scss";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Weather; page-header 템플릿과
|
|
5
|
+
* Weather; page-header 템플릿과 normalized API/hook/jotai 도구를 제공하는 엔트리
|
|
6
6
|
*/
|
|
7
7
|
export * from "./utils";
|
|
8
8
|
export * from "./apis";
|
|
9
|
-
export * from "./jotai";
|
|
10
9
|
export * from "./hooks";
|
|
10
|
+
export * from "./jotai";
|
|
11
11
|
export * from "./types";
|
|
12
12
|
export { default as WeatherComponents } from "./components";
|
|
13
13
|
export { WeatherPageHeader } from "./components";
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
import type { WeatherCoordinate } from "../types";
|
|
4
4
|
import { atomWithReset } from "jotai/utils";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Weather Jotai; 현재 지역 좌표 atom.
|
|
8
|
+
* @desc service app에서 주입한 주소와 위경도를 weather template 전역 상태로 보관한다.
|
|
9
|
+
*/
|
|
6
10
|
export const weatherCoordinate = atomWithReset<WeatherCoordinate>({
|
|
7
11
|
address: null,
|
|
8
12
|
lat: null,
|