diff --git a/packages/common/business_components/.gitignore b/packages/common/business_components/.gitignore new file mode 100644 index 000000000..8efffcff1 --- /dev/null +++ b/packages/common/business_components/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*storybook.log +storybook-static +.vercel + +.env.production \ No newline at end of file diff --git a/packages/common/business_components/.prettierrc b/packages/common/business_components/.prettierrc new file mode 100644 index 000000000..b4bfed357 --- /dev/null +++ b/packages/common/business_components/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/packages/common/business_components/components.json b/packages/common/business_components/components.json new file mode 100644 index 000000000..b8c9e23f0 --- /dev/null +++ b/packages/common/business_components/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/packages/common/business_components/eslint.config.js b/packages/common/business_components/eslint.config.js new file mode 100644 index 000000000..0bbf074e3 --- /dev/null +++ b/packages/common/business_components/eslint.config.js @@ -0,0 +1,28 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + } +); diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json new file mode 100644 index 000000000..d9ea7fac0 --- /dev/null +++ b/packages/common/business_components/package.json @@ -0,0 +1,67 @@ +{ + "name": "@common/business_components", + "version": "0.0.0", + "private": true, + "sideEffects": false, + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist/**" + ], + "scripts": { + "build": "tsup", + "storybook": "storybook dev -p 6006", + "build:storybook": "storybook build", + "lint": "eslint .", + "clean": "rm -rf dist" + }, + "dependencies": { + "@common/components": "workspace:*", + "recharts": "^2.15.1" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@storybook/addon-essentials": "^8.5.3", + "@storybook/addon-interactions": "^8.5.3", + "@storybook/addon-onboarding": "^8.5.3", + "@storybook/addon-themes": "^8.5.4", + "@storybook/blocks": "^8.5.3", + "@storybook/manager-api": "^8.5.5", + "@storybook/react": "^8.5.3", + "@storybook/react-vite": "^8.5.3", + "@storybook/test": "^8.5.3", + "@storybook/theming": "^8.5.5", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.20", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.16", + "eslint-plugin-storybook": "^0.11.2", + "globals": "^15.14.0", + "postcss": "^8.5.1", + "prettier": "^3.4.2", + "prettier-plugin-tailwindcss": "^0.6.11", + "storybook": "^8.5.3", + "tailwindcss": "^3.4.17", + "tsup": "8.3.6", + "typescript": "~5.6.2", + "typescript-eslint": "^8.18.2", + "vite": "^6.0.5", + "vite-tsconfig-paths": "5.1.4" + }, + "eslintConfig": { + "extends": [ + "plugin:storybook/recommended" + ] + }, + "packageManager": "pnpm@8.15.5+sha1.a58c038faac410c947dbdb93eb30994037d0fce2" +} \ No newline at end of file diff --git a/packages/common/business_components/src/env.ts b/packages/common/business_components/src/env.ts new file mode 100644 index 000000000..85dcfa2e2 --- /dev/null +++ b/packages/common/business_components/src/env.ts @@ -0,0 +1 @@ +export const BASE_ASSETS_PATH = process.env.BASE_ASSETS_PATH ?? "/assets"; diff --git a/packages/common/business_components/src/index.ts b/packages/common/business_components/src/index.ts new file mode 100644 index 000000000..4aedf5936 --- /dev/null +++ b/packages/common/business_components/src/index.ts @@ -0,0 +1 @@ +export * from "./ui"; diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx new file mode 100644 index 000000000..98d759f09 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -0,0 +1,36 @@ +"use client" +import type { CSSProperties } from "react"; +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig +} from "./lib/chart"; +import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; + +export default function Chart(props: { chartStyle: CSSProperties | undefined; chartConfig: ChartConfig; chartData: any[] | undefined; }) { + return ( + + + + + + value.slice(0, 3)} + /> + }/> + + + + + + ) +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx new file mode 100644 index 000000000..8caa35e47 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx @@ -0,0 +1,43 @@ +"use client" + +import { Pie, PieChart } from "recharts"; + +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig +} from "./lib/chart"; +import type { CSSProperties } from "react"; + +// eslint-disable-next-line react-refresh/only-export-components +export default (props: { chartConfig: ChartConfig; chartStyle: CSSProperties | undefined; chartData: never[] | undefined; }) =>{ + return ( + + + + + } + /> + + + + + + ) +} diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx new file mode 100644 index 000000000..7cddf66d1 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -0,0 +1,51 @@ +"use client" +import type { CSSProperties } from "react"; +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig +} from "./lib/chart"; +import { CartesianGrid, Line, LineChart, XAxis } from "recharts"; + +export default function Linechart(props: { chartStyle: CSSProperties | undefined; chartConfig: ChartConfig; chartData: any[] | undefined; }) { + return ( + + + + + + value.slice(0, 3)} + /> + } + /> + + + + + + ) +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx new file mode 100644 index 000000000..d57b6e1ec --- /dev/null +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -0,0 +1,54 @@ + +import React from 'react' +import BarChart from './BarChart'; +import LineChart from './LineChart'; +import DoughnutChart from './DoughnutChart'; +import {type ChartConfig } from './lib/chart'; + +/** 차트 기본 스타일 */ +const styles = { + divHeight : { + height: '400px', + width : '500px' + } as React.CSSProperties +} + +/**차트 FORM */ +const CHART_FORM = { + /**바 차트 형태*/ + BAR : 'bar', + /**라인 차트 형태*/ + LINE:'line', + /**도넛 차트 형태*/ + DN :'daughnut' +}; + +function MainChart(props: { chartForm: string; chartConfig: any; chartData: any; }) { + if( props.chartForm == CHART_FORM.BAR ){ + return ( +
+ + +
+ ); + } else if( props.chartForm == CHART_FORM.LINE ){ + return ( +
+ + +
+ ); + } else if( props.chartForm == CHART_FORM.DN ){ + return ( +
+ + +
+ ); + } +} + +export {MainChart}; +// eslint-disable-next-line react-refresh/only-export-components +export {CHART_FORM}; +export type {ChartConfig}; \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/lib/card.tsx b/packages/common/business_components/src/ui/chart/lib/card.tsx new file mode 100644 index 000000000..cabfbfc59 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/lib/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/packages/common/business_components/src/ui/chart/lib/chart.tsx b/packages/common/business_components/src/ui/chart/lib/chart.tsx new file mode 100644 index 000000000..bd48953ee --- /dev/null +++ b/packages/common/business_components/src/ui/chart/lib/chart.tsx @@ -0,0 +1,365 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +