diff --git a/.env b/.env new file mode 100644 index 0000000..196445d --- /dev/null +++ b/.env @@ -0,0 +1 @@ +NEXT_PUBLIC_BASE_API_URL="http://13.209.39.139:32171" \ No newline at end of file diff --git a/next.config.js b/next.config.js index 7b09dd4..17a24ae 100644 --- a/next.config.js +++ b/next.config.js @@ -1,5 +1,17 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + reactStrictMode: true, + images: { + domains: ['13.209.39.139'], + // remotePatterns: [ + // { + // protocol: 'http', + // hostname: '13.209.39.139', + // port: '31192', + // pathname: '/api/v1/buckets/gseps-test-a/objects/**', + // }, + // ], + }, compiler: { styledComponents: true, }, diff --git a/package-lock.json b/package-lock.json index ef131e2..2c9d73b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,19 @@ "name": "front-boilerplate", "version": "0.0.1", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", + "@nivo/bar": "^0.83.0", + "@nivo/core": "^0.83.0", + "@nivo/line": "^0.83.0", "@sdt/sdt-ui-kit": "^0.1.20", "@tanstack/react-query": "^4.33.0", "autoprefixer": "10.4.15", "axios": "^1.4.0", "cookies-next": "^3.0.0", + "date-fns": "^2.30.0", "eslint": "8.47.0", "eslint-config-next": "13.4.19", "lodash": "^4.17.21", @@ -2817,6 +2825,29 @@ "react": ">= 16.14.0 < 19.0.0" } }, + "node_modules/@nivo/bar": { + "version": "0.83.0", + "resolved": "https://registry.npmjs.org/@nivo/bar/-/bar-0.83.0.tgz", + "integrity": "sha512-QXN6BcT1PiT/YViyoDU4G5mytbOUP1jYbuQmJhDDxKPMLNcZ/pHfThedRGVfDoD1poHBRJtV6mbgeCpAVmlTtw==", + "dependencies": { + "@nivo/annotations": "0.83.0", + "@nivo/axes": "0.83.0", + "@nivo/colors": "0.83.0", + "@nivo/core": "0.83.0", + "@nivo/legends": "0.83.0", + "@nivo/scales": "0.83.0", + "@nivo/tooltip": "0.83.0", + "@react-spring/web": "9.4.5 || ^9.7.2", + "@types/d3-scale": "^3.2.3", + "@types/d3-shape": "^2.0.0", + "d3-scale": "^3.2.3", + "d3-shape": "^1.3.5", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">= 16.14.0 < 19.0.0" + } + }, "node_modules/@nivo/colors": { "version": "0.83.0", "resolved": "https://registry.npmjs.org/@nivo/colors/-/colors-0.83.0.tgz", @@ -4503,6 +4534,21 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -10500,6 +10546,26 @@ "prop-types": "^15.7.2" } }, + "@nivo/bar": { + "version": "0.83.0", + "resolved": "https://registry.npmjs.org/@nivo/bar/-/bar-0.83.0.tgz", + "integrity": "sha512-QXN6BcT1PiT/YViyoDU4G5mytbOUP1jYbuQmJhDDxKPMLNcZ/pHfThedRGVfDoD1poHBRJtV6mbgeCpAVmlTtw==", + "requires": { + "@nivo/annotations": "0.83.0", + "@nivo/axes": "0.83.0", + "@nivo/colors": "0.83.0", + "@nivo/core": "0.83.0", + "@nivo/legends": "0.83.0", + "@nivo/scales": "0.83.0", + "@nivo/tooltip": "0.83.0", + "@react-spring/web": "9.4.5 || ^9.7.2", + "@types/d3-scale": "^3.2.3", + "@types/d3-shape": "^2.0.0", + "d3-scale": "^3.2.3", + "d3-shape": "^1.3.5", + "lodash": "^4.17.21" + } + }, "@nivo/colors": { "version": "0.83.0", "resolved": "https://registry.npmjs.org/@nivo/colors/-/colors-0.83.0.tgz", @@ -11761,6 +11827,14 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 26558ae..79bd884 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,19 @@ "lint": "next lint" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", + "@nivo/bar": "^0.83.0", + "@nivo/core": "^0.83.0", + "@nivo/line": "^0.83.0", "@sdt/sdt-ui-kit": "^0.1.20", "@tanstack/react-query": "^4.33.0", "autoprefixer": "10.4.15", "axios": "^1.4.0", "cookies-next": "^3.0.0", + "date-fns": "^2.30.0", "eslint": "8.47.0", "eslint-config-next": "13.4.19", "lodash": "^4.17.21", diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..f559c92 Binary files /dev/null and b/public/logo.png differ diff --git a/src/api/dashboard/resultApi.ts b/src/api/dashboard/resultApi.ts new file mode 100644 index 0000000..85517e6 --- /dev/null +++ b/src/api/dashboard/resultApi.ts @@ -0,0 +1,12 @@ +import { ResultDataType } from '@/app/dashboard/types'; +import { afterAxios, fetchApi } from '../config'; + +export interface GetResultApiRequestType {} + +export type GetResultApiResponseType = ResultDataType; + +export const getResultApi = (): Promise => + fetchApi({ + url: 'blokworks/v1/transport/inference/results', + method: 'GET', + }).then(afterAxios); diff --git a/src/app/dashboard/components/BarChart.tsx b/src/app/dashboard/components/BarChart.tsx new file mode 100644 index 0000000..703f269 --- /dev/null +++ b/src/app/dashboard/components/BarChart.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { ResponsiveBar } from '@nivo/bar'; + +function BarChart({ data }: any) { + return ( +
+ + e.id + ': ' + e.formattedValue + ' in country: ' + e.indexValue + } + /> +
+ ); +} + +export default BarChart; diff --git a/src/app/dashboard/components/Dashboard.tsx b/src/app/dashboard/components/Dashboard.tsx new file mode 100644 index 0000000..5b0b224 --- /dev/null +++ b/src/app/dashboard/components/Dashboard.tsx @@ -0,0 +1,80 @@ +'use client'; + +import React from 'react'; +import BarChart from './BarChart'; +import { format } from 'date-fns'; +import Image from 'next/image'; +import useDashboard from '../hooks/useDashboard'; +import { Button } from '@sdt/sdt-ui-kit'; +import { useQueryClient } from '@tanstack/react-query'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowsRotate } from '@fortawesome/free-solid-svg-icons'; + +function Dashboard() { + const { getResult, convertChartData } = useDashboard(); + const queryClient = useQueryClient(); + + return ( +
+
+ logo +
+
+ {getResult.data && ( + <> +
+

Input Image

+
+ origin image +
+

+ Inference Result Image +

+
+ inference image +
+
+
+
+ 촬영일시 :{' '} + {format( + getResult.data?.latest.timestamp, + 'yyyy-MM-dd HH:mm:ss', + )} + +
+ + +
+ + )} +
+
+ Copyright 2023 SDT Inc. All rights reserved. +
+
+ ); +} + +export default Dashboard; diff --git a/src/app/dashboard/components/LineChart.tsx b/src/app/dashboard/components/LineChart.tsx new file mode 100644 index 0000000..8f397f1 --- /dev/null +++ b/src/app/dashboard/components/LineChart.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { ResponsiveLine } from '@nivo/line'; + +function LineChart({ data }: any) { + return ( +
+
+ + + {/* */} +
+
+ ); +} + +export default LineChart; diff --git a/src/app/dashboard/components/RecentResult.tsx b/src/app/dashboard/components/RecentResult.tsx new file mode 100644 index 0000000..90618a3 --- /dev/null +++ b/src/app/dashboard/components/RecentResult.tsx @@ -0,0 +1,26 @@ +'use client'; + +import React from 'react'; +import BarChart from './BarChart'; +import { DUMMY_DATA } from '../constants'; +import { format } from 'date-fns'; + +function RecentResult() { + return ( +
+
image
+ + +
+
+
촬영일시
+
+ {format(DUMMY_DATA.latest.timestamp, 'yyyy-MM-dd HH:mm:ss')} +
+
+
+
+ ); +} + +export default RecentResult; diff --git a/src/app/dashboard/components/Result.tsx b/src/app/dashboard/components/Result.tsx new file mode 100644 index 0000000..631e62c --- /dev/null +++ b/src/app/dashboard/components/Result.tsx @@ -0,0 +1,41 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import LineChart from './LineChart'; +import { Button, TextInput } from '@sdt/sdt-ui-kit'; + +function Result() { + const [line, setLine] = useState([]); + + const chartData = [...Array(1000)].map((v, i) => ({ + x: i, + y: Math.floor(Math.random() * 100), + })); + const data = [ + { + id: 'korea', + color: 'rgb(217, 38, 107)', + data: chartData, + }, + ]; + + // useEffect(() => { + // setTimeout(() => { + // setLine(chartData); + // }, 3000); + // }, [chartData]); + + return ( +
+
+ 입자의 크기 + + +
+ + +
+ ); +} + +export default Result; diff --git a/src/app/dashboard/constants/index.ts b/src/app/dashboard/constants/index.ts new file mode 100644 index 0000000..23095a4 --- /dev/null +++ b/src/app/dashboard/constants/index.ts @@ -0,0 +1,29 @@ +export const DUMMY_DATA = { + latest: { + imageUrl: + 'http://13.209.39.139:31192/api/v1/buckets/gseps-test-a/objects/download?prefix=MjAyMzA5MDctMTEyNzU2LmpwZw==&version_id=null', + timestamp: 1694073245526, + particleSizeRatio: [ + { + range: 186, + count: 1, + }, + { + range: 102.5, + count: 1, + }, + { + range: 79.5, + count: 8, + }, + { + range: 313.5, + count: 1, + }, + { + range: 146.5, + count: 1, + }, + ], + }, +}; diff --git a/src/app/dashboard/hooks/useDashboard.1.tsx b/src/app/dashboard/hooks/useDashboard.1.tsx new file mode 100644 index 0000000..95aa2fd --- /dev/null +++ b/src/app/dashboard/hooks/useDashboard.1.tsx @@ -0,0 +1,31 @@ +import { useMemo } from 'react'; +import { getResultApi } from '@/api/dashboard/resultApi'; +import { useQuery } from '@tanstack/react-query'; + +export function useDashboard() { + const getResult = useQuery(['result'], () => getResultApi()); + + const [resultData, setPieData] = useState([]); + + useEffect(() => { + setTimeout(() => { + setPieData(PIECHART_DUMMY); + }, 10); + }, []); + + /** + * bar chart convert data + */ + const convertChartData = useMemo(() => { + const data = getResult.data?.latest.particleSizeRatio?.map((result, i) => { + return { + range: Number(result.range).toFixed(2), + count: result.count, + }; + }); + + return data; + }, [getResult.data]); + + return { getResult, convertChartData }; +} diff --git a/src/app/dashboard/hooks/useDashboard.tsx b/src/app/dashboard/hooks/useDashboard.tsx new file mode 100644 index 0000000..73e4da9 --- /dev/null +++ b/src/app/dashboard/hooks/useDashboard.tsx @@ -0,0 +1,27 @@ +import React, { useMemo } from 'react'; +import { getResultApi } from '@/api/dashboard/resultApi'; +import { useQuery } from '@tanstack/react-query'; + +function useDashboard() { + const getResult = useQuery(['result'], () => getResultApi(), { + refetchOnWindowFocus: false, + }); + + /** + * bar chart convert data + */ + const convertChartData = useMemo(() => { + const data = getResult.data?.latest.particleSizeRatio?.map((result, i) => { + return { + range: Number(result.range).toFixed(2), + count: result.count, + }; + }); + + return data; + }, [getResult.data]); + + return { getResult, convertChartData }; +} + +export default useDashboard; diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx new file mode 100644 index 0000000..673bf76 --- /dev/null +++ b/src/app/dashboard/layout.tsx @@ -0,0 +1,7 @@ +export default function LoginLayout({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx new file mode 100644 index 0000000..1de0357 --- /dev/null +++ b/src/app/dashboard/page.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import Dashboard from './components/Dashboard'; + +function LoginPage() { + return ; +} + +export default LoginPage; diff --git a/src/app/dashboard/types/index.ts b/src/app/dashboard/types/index.ts new file mode 100644 index 0000000..8aaf8d5 --- /dev/null +++ b/src/app/dashboard/types/index.ts @@ -0,0 +1,12 @@ +import { StaticImageData } from 'next/image'; + +export interface ResultDataType { + origin: { + imageUrl: StaticImageData; + }; + latest: { + imageUrl: StaticImageData; + timestamp: number; + particleSizeRatio: { range: string; count: number }[]; + }; +} diff --git a/src/app/page.tsx b/src/app/page.tsx index aa942ce..ffc24d1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,113 +1,15 @@ -import Image from 'next/image'; +'use client'; + +import React, { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; export default function Home() { - return ( -
-
-

- Get started by editing  - src/app/page.tsx -

- -
+ const router = useRouter(); + const targetRoutePath = '/dashboard'; -
- Next.js Logo -
+ useEffect(() => { + router.push(targetRoutePath); + }, [router]); -
- -

- Docs{' '} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{' '} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
- - -

- Templates{' '} - - -> - -

-

- Explore the Next.js 13 playground. -

-
- - -

- Deploy{' '} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
-
- ); + return
; } diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 65bc123..7fe42e6 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -9,9 +9,9 @@ export default function Layout({ children }: { children: React.ReactNode }) { useSilentAuth(); return ( -
-
-
{children}
+
+
+
{children}