coreui 소스를 ts 로 전환

This commit is contained in:
2025-12-30 19:15:56 +09:00
parent a99165e4d7
commit 7fb0cf6fba
35 changed files with 113 additions and 40 deletions

View File

@@ -1,4 +1,5 @@
{ {
"type": "module",
"name": "@coreui/coreui-free-react-admin-template", "name": "@coreui/coreui-free-react-admin-template",
"version": "5.5.0", "version": "5.5.0",
"description": "CoreUI Free React Admin Template", "description": "CoreUI Free React Admin Template",
@@ -27,6 +28,11 @@
"@coreui/react-chartjs": "^3.0.0", "@coreui/react-chartjs": "^3.0.0",
"@coreui/utils": "^2.0.2", "@coreui/utils": "^2.0.2",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@types/prop-types": "^15.7.15",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"core-js": "^3.47.0", "core-js": "^3.47.0",
@@ -36,7 +42,8 @@
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.11.0", "react-router-dom": "^7.11.0",
"redux": "5.0.1", "redux": "5.0.1",
"simplebar-react": "^3.3.2" "simplebar-react": "^3.3.2",
"typescript": "^5.9.3"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",

View File

@@ -1,6 +1,7 @@
import React, { Suspense, useEffect } from 'react' import React, { Suspense, useEffect } from 'react'
import { HashRouter, Route, Routes } from 'react-router-dom' import { HashRouter, Route, Routes } from 'react-router-dom'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { RootState } from './store'
import { CSpinner, useColorModes } from '@coreui/react' import { CSpinner, useColorModes } from '@coreui/react'
import './scss/style.scss' import './scss/style.scss'
@@ -19,11 +20,11 @@ const Page500 = React.lazy(() => import('./views/pages/page500/Page500'))
const App = () => { const App = () => {
const { isColorModeSet, setColorMode } = useColorModes('coreui-free-react-admin-template-theme') const { isColorModeSet, setColorMode } = useColorModes('coreui-free-react-admin-template-theme')
const storedTheme = useSelector((state) => state.theme) const storedTheme = useSelector((state: RootState) => state.theme)
useEffect(() => { useEffect(() => {
const urlParams = new URLSearchParams(window.location.href.split('?')[1]) const urlParams = new URLSearchParams(window.location.href.split('?')[1])
const theme = urlParams.get('theme') && urlParams.get('theme').match(/^[A-Za-z0-9\s]+/)[0] const theme = urlParams.get('theme')?.match(/^[A-Za-z0-9\s]+/)?.[0]
if (theme) { if (theme) {
setColorMode(theme) setColorMode(theme)
} }
@@ -45,11 +46,11 @@ const App = () => {
} }
> >
<Routes> <Routes>
<Route exact path="/login" name="Login Page" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route exact path="/register" name="Register Page" element={<Register />} /> <Route path="/register" element={<Register />} />
<Route exact path="/404" name="Page 404" element={<Page404 />} /> <Route path="/404" element={<Page404 />} />
<Route exact path="/500" name="Page 500" element={<Page500 />} /> <Route path="/500" element={<Page500 />} />
<Route path="*" name="Home" element={<DefaultLayout />} /> <Route path="*" element={<DefaultLayout />} />
</Routes> </Routes>
</Suspense> </Suspense>
</HashRouter> </HashRouter>

View File

@@ -16,8 +16,6 @@ const AppContent = () => {
<Route <Route
key={idx} key={idx}
path={route.path} path={route.path}
exact={route.exact}
name={route.name}
element={<route.element />} element={<route.element />}
/> />
) )

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { RootState } from '../store'
import { import {
CContainer, CContainer,
CDropdown, CDropdown,
@@ -29,11 +30,11 @@ import { AppBreadcrumb } from './index'
import { AppHeaderDropdown } from './header/index' import { AppHeaderDropdown } from './header/index'
const AppHeader = () => { const AppHeader = () => {
const headerRef = useRef() const headerRef = useRef<HTMLDivElement>(null)
const { colorMode, setColorMode } = useColorModes('coreui-free-react-admin-template-theme') const { colorMode, setColorMode } = useColorModes('coreui-free-react-admin-template-theme')
const dispatch = useDispatch() const dispatch = useDispatch()
const sidebarShow = useSelector((state) => state.sidebarShow) const sidebarShow = useSelector((state: RootState) => state.sidebarShow)
useEffect(() => { useEffect(() => {
const handleScroll = () => { const handleScroll = () => {

View File

@@ -1,5 +1,7 @@
import React from 'react' import React from 'react'
import { NavLink } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { RootState } from '../store'
import { import {
CCloseButton, CCloseButton,
@@ -21,8 +23,8 @@ import navigation from '../_nav'
const AppSidebar = () => { const AppSidebar = () => {
const dispatch = useDispatch() const dispatch = useDispatch()
const unfoldable = useSelector((state) => state.sidebarUnfoldable) const unfoldable = useSelector((state: RootState) => state.sidebarUnfoldable)
const sidebarShow = useSelector((state) => state.sidebarShow) const sidebarShow = useSelector((state: RootState) => state.sidebarShow)
return ( return (
<CSidebar <CSidebar
@@ -36,7 +38,7 @@ const AppSidebar = () => {
}} }}
> >
<CSidebarHeader className="border-bottom"> <CSidebarHeader className="border-bottom">
<CSidebarBrand to="/"> <CSidebarBrand as={NavLink} to="/">
<CIcon customClassName="sidebar-brand-full" icon={logo} height={32} /> <CIcon customClassName="sidebar-brand-full" icon={logo} height={32} />
<CIcon customClassName="sidebar-brand-narrow" icon={sygnet} height={32} /> <CIcon customClassName="sidebar-brand-narrow" icon={sygnet} height={32} />
</CSidebarBrand> </CSidebarBrand>

19
src/images.d.ts vendored Normal file
View File

@@ -0,0 +1,19 @@
declare module '*.webp' {
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.svg' {
const src: string;
export default src;
}

View File

@@ -6,7 +6,7 @@ import 'core-js'
import App from './App' import App from './App'
import store from './store' import store from './store'
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')!).render(
<Provider store={store}> <Provider store={store}>
<App /> <App />
</Provider>, </Provider>,

View File

@@ -1,18 +0,0 @@
import { legacy_createStore as createStore } from 'redux'
const initialState = {
sidebarShow: true,
theme: 'light',
}
const changeState = (state = initialState, { type, ...rest }) => {
switch (type) {
case 'set':
return { ...state, ...rest }
default:
return state
}
}
const store = createStore(changeState)
export default store

33
src/store.tsx Normal file
View File

@@ -0,0 +1,33 @@
import { legacy_createStore as createStore } from 'redux'
// 상태(State) 타입 정의
interface AppState {
sidebarShow: boolean
sidebarUnfoldable: boolean
theme: string
}
const initialState: AppState = {
sidebarShow: true,
sidebarUnfoldable: false,
theme: 'light',
}
// 액션(Action) 타입 정의
interface Action {
type: string
[key: string]: any
}
const changeState = (state = initialState, { type, ...rest }: Action): AppState => {
switch (type) {
case 'set':
return { ...state, ...rest }
default:
return state
}
}
const store = createStore(changeState)
export type RootState = ReturnType<typeof store.getState>
export default store

4
src/styles.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.scss' {
const content: { [className: string]: string };
export default content;
}

31
tsconfig.json Normal file
View File

@@ -0,0 +1,31 @@
{
"compilerOptions": {
"baseUrl": ".",
"target": "ES5",
"strict": true,
"allowJs": true,
"esModuleInterop": true,
"jsx": "react-jsx",
// Stricter Typechecking Options
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// Style Options
// "noImplicitReturns": true,
// "noImplicitOverride": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "noPropertyAccessFromIndexSignature": true,
// Recommended Options
"verbatimModuleSyntax": false,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
},
"include": [
"src",
"src/images.d.ts",
"src/styles.d.ts"
]
}

View File

@@ -16,11 +16,6 @@ export default defineConfig(() => {
], ],
}, },
}, },
esbuild: {
loader: 'jsx',
include: /src\/.*\.jsx?$/,
exclude: [],
},
optimizeDeps: { optimizeDeps: {
force: true, force: true,
esbuildOptions: { esbuildOptions: {
@@ -33,7 +28,7 @@ export default defineConfig(() => {
resolve: { resolve: {
alias: [ alias: [
{ {
find: 'src/', find: 'src',
replacement: `${path.resolve(__dirname, 'src')}/`, replacement: `${path.resolve(__dirname, 'src')}/`,
}, },
], ],