Merge commit '16541d6d54b044ec5f6bb60db6ef101401cb81ae' into iipbs/sn/oi
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
} from "./lib/chart";
|
||||
import type { ContentType } from "recharts/types/component/DefaultLegendContent";
|
||||
|
||||
// 툴팁 커스텀 css
|
||||
// 툴크 커스텀 css
|
||||
/** 차트 기본 스타일 */
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
@@ -52,7 +52,7 @@ const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?:
|
||||
<div key={index} style={{ marginTop: "10px", display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
{/* 색상 박스 추가 (범례 색상과 동일) */}
|
||||
<div style={{ width: "10px", height: "10px", backgroundColor: item.color, borderRadius: "50%" }}></div>
|
||||
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value}`}</span>
|
||||
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -95,6 +95,18 @@ export default function LineBarchart({
|
||||
const barConfig = chartConfig?.barVal;
|
||||
const lineConfig = chartConfig?.lineVal;
|
||||
|
||||
// 데이터의 최대값 계산
|
||||
const getMaxValue = (data: any[], keyPrefix: string) => {
|
||||
return Math.max(...data.map(item =>
|
||||
Object.keys(item)
|
||||
.filter(key => key.includes(keyPrefix))
|
||||
.map(key => item[key])
|
||||
).flat());
|
||||
};
|
||||
|
||||
const lineMaxValue = getMaxValue(chartData, 'lineVal');
|
||||
const barMaxValue = getMaxValue(chartData, 'barVal');
|
||||
|
||||
return (
|
||||
<div className={chartClassName}>
|
||||
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
|
||||
@@ -113,29 +125,62 @@ export default function LineBarchart({
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="label" fontSize="8px" fontWeight="bold"/>
|
||||
{/* 왼쪽 Y축: lineVal */}
|
||||
{yFieldText && <YAxis yAxisId="left" className="text-[12px] font-bold" orientation="left" label={{ value: yFieldText, angle: -90, position: 'insideLeft' }} />}
|
||||
{yFieldText && <YAxis
|
||||
yAxisId="left"
|
||||
className="text-[12px] font-bold"
|
||||
tickCount={6}
|
||||
orientation="left"
|
||||
domain={[0, Math.ceil(lineMaxValue * 1.1)]}
|
||||
label={{ value: yFieldText, angle: -90, position: 'insideLeft' }}
|
||||
/>}
|
||||
{/* 오른쪽 Y축: barVal */}
|
||||
{yFieldText2 && <YAxis yAxisId="right" className="text-[12px] font-bold" fontWeight="bold" orientation="right" label={{ value: yFieldText2, angle: -90, position: 'insideRight' }} />}
|
||||
{yFieldText2 && <YAxis
|
||||
yAxisId="right"
|
||||
className="text-[12px] font-bold"
|
||||
tickCount={6}
|
||||
orientation="right"
|
||||
domain={[0, Math.ceil(barMaxValue * 1.1)]}
|
||||
label={{ value: yFieldText2, angle: -90, position: 'insideRight' }}
|
||||
reversed={true}
|
||||
/>}
|
||||
<Tooltip content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />} />
|
||||
{showLegend && <Legend content={CustomLegend as ContentType}/>}
|
||||
{/* 바 차트 (오른쪽 Y축 사용) */}
|
||||
<Bar
|
||||
yAxisId="right"
|
||||
dataKey="barVal"
|
||||
fill={barConfig?.color || "black"}
|
||||
name={String(barConfig?.label || "Y축")}
|
||||
maxBarSize={80}
|
||||
/>
|
||||
{Object.keys(chartData[0]).map((key, index) => {
|
||||
const config = chartConfig?.[key];
|
||||
if(key.includes("barVal")) {
|
||||
return (
|
||||
<Bar
|
||||
key={`bar-${key}-${index}`}
|
||||
yAxisId="right"
|
||||
dataKey={key}
|
||||
fill={config?.color || "black"}
|
||||
name={String(config?.label || "Y축")}
|
||||
maxBarSize={80}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{/* 라인 차트 (왼쪽 Y축 사용) */}
|
||||
<Line
|
||||
yAxisId="left"
|
||||
type="monotone"
|
||||
dataKey="lineVal"
|
||||
stroke={lineConfig?.color || "black"}
|
||||
strokeWidth={2}
|
||||
dot={{r: 0}}
|
||||
name={String(lineConfig?.label || "X축")}
|
||||
/>
|
||||
{Object.keys(chartData[0]).map((key, index) => {
|
||||
const config = chartConfig?.[key];
|
||||
if(key.includes("lineVal")) {
|
||||
return (
|
||||
<Line
|
||||
key={`line-${key}-${index}`}
|
||||
yAxisId="left"
|
||||
type="linear"
|
||||
dataKey={key}
|
||||
stroke={config?.color || "black"}
|
||||
strokeWidth={2}
|
||||
dot={{r: 0}}
|
||||
name={String(config?.label || "X축")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</ComposedChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
|
||||
@@ -91,10 +91,16 @@ export default function Linechart({
|
||||
yFieldText,
|
||||
showLegend = true
|
||||
}: LineChartProps) {
|
||||
if(!chartData || Object.keys(chartData).length === 0){
|
||||
return <div>데이터가 없습니다.</div>
|
||||
if (!chartData || Object.keys(chartData).length === 0) {
|
||||
return <div>데이터가 없습니다.</div>;
|
||||
}
|
||||
|
||||
// 모든 데이터셋이 비어있는지 확인
|
||||
const hasNoData = Object.values(chartData).every(dataset => !dataset || dataset.length === 0);
|
||||
if (hasNoData) {
|
||||
return <div>데이터가 없습니다.</div>;
|
||||
}
|
||||
|
||||
// 데이터 병합 및 변환
|
||||
const mergedData = Object.entries(chartData).reduce<Array<{[key: string]: string | number}>>((acc, [key, dataset]) => {
|
||||
dataset.forEach((entry, index) => {
|
||||
@@ -106,6 +112,17 @@ export default function Linechart({
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// 데이터의 최대값 계산
|
||||
const getMaxValue = (data: ChartDataType, keyPrefix: string) => {
|
||||
return Math.max(...Object.values(data).map(item =>
|
||||
Object.keys(item)
|
||||
.filter(key => key.includes(keyPrefix))
|
||||
.map(key => item[key] as unknown as number)
|
||||
).flat());
|
||||
};
|
||||
|
||||
const lineMaxValue = getMaxValue(chartData, 'lineVal');
|
||||
|
||||
return (
|
||||
<div className={chartClassName}>
|
||||
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
|
||||
@@ -122,8 +139,8 @@ export default function Linechart({
|
||||
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis dataKey="label" fontSize="8px" fontWeight="bold"/>
|
||||
<YAxis yAxisId="left" className="text-[12px] font-bold" orientation="left" label={{ value: yFieldText, className:"text-[12px] foont-bold", angle: -90, position: 'insideLeft' }} />
|
||||
<XAxis dataKey="label" fontSize="8px" fontWeight="bold" />
|
||||
<YAxis yAxisId="left" className="text-[12px] font-bold" domain={[0, Math.ceil(lineMaxValue * 1.1)]} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} />
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
{showLegend && <Legend content={CustomLegend as ContentType}/>}
|
||||
{Object.keys(chartData).map((key) => {
|
||||
|
||||
@@ -28,8 +28,8 @@ export const FileUpload =
|
||||
id="fileInput"
|
||||
className="outline-dashed outline-1 outline-slate-500"
|
||||
>
|
||||
<div className="flex items-center justify-center flex-col p-8 w-full ">
|
||||
<CloudUpload className='text-gray-500 w-10 h-10' />
|
||||
<div className="flex items-center justify-center flex-col p-1 w-full ">
|
||||
<CloudUpload className='text-gray-500 w-5 h-4' />
|
||||
<p className="mb-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
<span className="font-semibold">클릭 후 업로드 </span>
|
||||
혹은 파일을 드래그 해주세요
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
export default function MainDiv(props: {children: React.ReactNode}) {
|
||||
return (
|
||||
<div className="min-h-screen bg-container">
|
||||
<div className="h-auto bg-container">
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -10,14 +10,20 @@ interface UITabProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
defaultValue?: string;
|
||||
tabList: TabItem[];
|
||||
children: React.ReactNode;
|
||||
onValueChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const UITab = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
UITabProps
|
||||
>(({defaultValue, tabList, children}, ref)=>{
|
||||
>(({defaultValue, tabList, children, onValueChange, ...props}, ref)=>{
|
||||
return (
|
||||
<Tabs defaultValue={defaultValue || tabList[0]?.value} className={"w-full space-x-1 border-b border-gray-200"}>
|
||||
<Tabs
|
||||
defaultValue={defaultValue || tabList[0]?.value}
|
||||
className={"w-full space-x-1 border-b border-gray-200"}
|
||||
onValueChange={onValueChange}
|
||||
{...props}
|
||||
>
|
||||
<TabsList className="flex space-x-1">
|
||||
{tabList.map((tab) => (
|
||||
<TabsTrigger
|
||||
|
||||
@@ -72,7 +72,11 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
onClick={() => handleCellClick(index, head.id)}
|
||||
key={`cell-${head.id}-${index}`}
|
||||
>
|
||||
{typeof el[head.id] === 'number' ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") : String(el[head.id] ?? '')}
|
||||
{String(el[head.id]).includes('.')
|
||||
? String(el[head.id]).split('.').map((part, index) =>
|
||||
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
|
||||
).join('.')
|
||||
: String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
|
||||
</TableCell>:
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
|
||||
@@ -82,7 +86,11 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
onClick={() => handleCellClick(index, head.id)}
|
||||
key={`cell-${head.id}-${index}`}
|
||||
>
|
||||
{typeof el[head.id] === 'number' ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") : String(el[head.id] ?? '')}
|
||||
{String(el[head.id]).includes('.')
|
||||
? String(el[head.id]).split('.').map((part, index) =>
|
||||
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
|
||||
).join('.')
|
||||
: String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
|
||||
</TableCell>
|
||||
)
|
||||
))}
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"./index.css": "./dist/index.css"
|
||||
},
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
@@ -26,6 +27,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@common/assets": "workspace:*",
|
||||
"@common/config": "workspace:*",
|
||||
"@frontend-opensource/use-react-hooks": "^1.1.4",
|
||||
"@minoru/react-dnd-treeview": "^3.5.0",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { Client } from "@common/network";
|
||||
import { BASE_URL, SITE_ID } from "../config";
|
||||
import { getConfig } from "@common/config";
|
||||
|
||||
export const client = Client.getInstance({
|
||||
baseURL: BASE_URL,
|
||||
siteId: SITE_ID,
|
||||
});
|
||||
const getClientInstance = () => {
|
||||
const config = getConfig();
|
||||
const { PROXY_HOST, SITE_ID } = config;
|
||||
const BASE_URL = `${PROXY_HOST}/server`;
|
||||
|
||||
return Client.getInstance({
|
||||
baseURL: BASE_URL,
|
||||
siteId: SITE_ID ?? "",
|
||||
});
|
||||
};
|
||||
|
||||
export const getApiClient = () => getClientInstance();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { client } from "./client";
|
||||
import { getApiClient } from "./client";
|
||||
import { ENDPOINT } from "../config";
|
||||
|
||||
export interface MenuItem {
|
||||
@@ -21,6 +21,8 @@ export interface MenuItem {
|
||||
requiredUrlPath: boolean;
|
||||
}
|
||||
|
||||
const client = getApiClient();
|
||||
|
||||
export const getMenuList = async (siteId: string) => {
|
||||
const response = await client.get<MenuItem[]>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENU_ROLES}/${siteId}`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { client } from "./client";
|
||||
import { getApiClient } from "./client";
|
||||
import { ENDPOINT } from "../config";
|
||||
|
||||
export interface SiteInfo {
|
||||
@@ -23,6 +23,8 @@ export interface SiteInfo {
|
||||
mdfrNm: string;
|
||||
}
|
||||
|
||||
const client = getApiClient();
|
||||
|
||||
export const getSiteInfo = async (siteId: string) => {
|
||||
const response = await client.get<SiteInfo>(
|
||||
`${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { client } from "./client";
|
||||
import { getApiClient } from "./client";
|
||||
import { ENDPOINT } from "../config";
|
||||
|
||||
export interface IUser {
|
||||
@@ -35,6 +35,8 @@ export interface IUser {
|
||||
hasPassword: boolean;
|
||||
}
|
||||
|
||||
const client = getApiClient();
|
||||
|
||||
export const getUserInfo = async (userUniqId: string) => {
|
||||
const response = await client.get<IUser>(
|
||||
`${ENDPOINT.USER_SERVICE.USERS}/${userUniqId}`,
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { PROXY_HOST } from "./env";
|
||||
|
||||
export const BASE_URL = `${PROXY_HOST}/server`;
|
||||
|
||||
export const ENDPOINT = {
|
||||
CMS_SERVICE: {
|
||||
MENU_ROLES: "/cms-service/api/v1/menu-roles",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export const PORT = process.env.PORT || "3000";
|
||||
export const PROXY_HOST = process.env.PROXY_HOST || `http://localhost:${PORT}`;
|
||||
|
||||
export const SERVER_API_URL = process.env.SERVER_API_URL;
|
||||
export const SITE_ID = process.env.SITE_ID || "CS";
|
||||
|
||||
export const CLAIM_NAME = process.env.CLAIM_NAME || "Authorization";
|
||||
export const ACCESS_TOKEN = process.env.ACCESS_TOKEN || "access-token";
|
||||
export const REFRESH_TOKEN = process.env.REFRESH_TOKEN || "refresh-token";
|
||||
export const AUTH_USER_ID = process.env.AUTH_USER_ID || "token-id";
|
||||
@@ -1,2 +1 @@
|
||||
export * from "./api";
|
||||
export * from "./env";
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
@layer components {
|
||||
button[role="checkbox"] {
|
||||
@apply size-[1.5rem] shrink-0 overflow-hidden rounded-[0.5rem] border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground;
|
||||
@apply size-[1.5rem] shrink-0 overflow-hidden rounded-[0.5rem] border border-primary bg-background ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground;
|
||||
}
|
||||
button[role="checkbox"] > span {
|
||||
@apply flex size-full items-center justify-center text-current;
|
||||
}
|
||||
button[role="checkbox"] + label {
|
||||
@apply text-label text-[1.3rem] font-medium;
|
||||
@apply text-[1.3rem] font-medium text-label;
|
||||
}
|
||||
button[role="checkbox"]:disabled + label {
|
||||
@apply cursor-not-allowed opacity-70;
|
||||
|
||||
@@ -13,11 +13,11 @@ import { cn } from "../../lib";
|
||||
import { Button } from "../button";
|
||||
import { Table, TableCell, TableHead, TableHeader, TableRow } from "../table";
|
||||
import type { HTMLAttributes } from "react";
|
||||
|
||||
declare module "@tanstack/react-table" {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface ColumnMeta<TData extends RowData, TValue> {
|
||||
align: "left" | "center" | "right";
|
||||
align?: "left" | "center" | "right";
|
||||
rowSpan?: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,15 +70,35 @@ function THead<T>({ table }: { table: TableInstance<T> }) {
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
const columnRelativeDepth = header.depth - header.column.depth;
|
||||
if (
|
||||
!header.isPlaceholder &&
|
||||
columnRelativeDepth > 1 &&
|
||||
header.id === header.column.id
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let rowSpan = 1;
|
||||
if (header.isPlaceholder) {
|
||||
const leafs = header.getLeafHeaders();
|
||||
rowSpan = (leafs?.[leafs.length - 1]?.depth ?? 0) - header.depth;
|
||||
}
|
||||
|
||||
const { meta } = header.column.columnDef;
|
||||
|
||||
return (
|
||||
<TableHead key={header.id} className="sticky top-0" {...meta}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
<TableHead
|
||||
key={header.id}
|
||||
className="sticky top-0"
|
||||
colSpan={header.colSpan}
|
||||
rowSpan={rowSpan}
|
||||
{...meta}
|
||||
>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { images } from "@common/assets";
|
||||
import { getConfig } from "@common/config";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { getCookie, setCookie } from "../../lib";
|
||||
import { getSiteInfo, getUserInfo } from "../../api";
|
||||
import { AUTH_USER_ID, ENDPOINT, SITE_ID } from "../../config";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
@@ -11,8 +12,10 @@ import {
|
||||
} from "../dropdown-menu";
|
||||
import { Button } from "../button";
|
||||
|
||||
const { AUTH_USER_ID, SITE_ID } = getConfig();
|
||||
|
||||
export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
const userUniqId = getCookie(AUTH_USER_ID ?? "") ?? "";
|
||||
|
||||
const { data: userInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS],
|
||||
@@ -22,7 +25,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
|
||||
const { data: siteInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.SITES, SITE_ID],
|
||||
queryFn: () => getSiteInfo(SITE_ID),
|
||||
queryFn: () => getSiteInfo(SITE_ID ?? ""),
|
||||
select: (data) => {
|
||||
if (data.siteSkn) {
|
||||
const siteSkin = data.siteSkn.toLowerCase();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { getConfig } from "@common/config";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { cn, setCookie } from "../../lib";
|
||||
import { ENDPOINT, SITE_ID } from "../../config";
|
||||
import { getMenuList, type MenuItem } from "../../api";
|
||||
import {
|
||||
Sidebar,
|
||||
@@ -19,6 +20,8 @@ import {
|
||||
} from "../collapsible";
|
||||
import { getParentIds } from "./lib";
|
||||
|
||||
const { SITE_ID } = getConfig();
|
||||
|
||||
export function LNB({
|
||||
viewOnly,
|
||||
currentPath,
|
||||
@@ -30,7 +33,7 @@ export function LNB({
|
||||
}) {
|
||||
const { data: menuList } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => getMenuList(SITE_ID),
|
||||
queryFn: () => getMenuList(SITE_ID ?? ""),
|
||||
enabled: !viewOnly,
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@common/network/*": ["../network/*"],
|
||||
"@common/config": ["../config/dist/"],
|
||||
"@common/network": ["../network/"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ export default defineConfig((options) => ({
|
||||
format: ["esm"],
|
||||
external: ["react", "react-dom"],
|
||||
platform: "browser",
|
||||
target: ["es2020", "chrome70", "edge18", "firefox70", "node18"],
|
||||
target: ["es2020"],
|
||||
tsconfig: path.resolve(__dirname, "tsconfig.build.json"),
|
||||
sourcemap: !options.watch,
|
||||
minify: !options.watch,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@common/config",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"clean": "rm -rf dist .turbo node_modules"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"eslint": "^9.8.0",
|
||||
"tsup": "^8.2.3",
|
||||
"typescript": "^5.5.3",
|
||||
"vite": "^5.3.5"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./initialize-config";
|
||||
export * from "./initializer";
|
||||
@@ -0,0 +1,54 @@
|
||||
export interface EnvConfig {
|
||||
PORT?: string;
|
||||
PROXY_HOST?: string;
|
||||
SERVER_API_URL?: string;
|
||||
SITE_ID?: string;
|
||||
CLAIM_NAME?: string;
|
||||
ACCESS_TOKEN?: string;
|
||||
REFRESH_TOKEN?: string;
|
||||
AUTH_USER_ID?: string;
|
||||
}
|
||||
|
||||
const defaultConfig: EnvConfig = {
|
||||
PORT: "3000",
|
||||
PROXY_HOST: "",
|
||||
SERVER_API_URL: "",
|
||||
SITE_ID: "CS",
|
||||
CLAIM_NAME: "Authorization",
|
||||
ACCESS_TOKEN: "access-token",
|
||||
REFRESH_TOKEN: "refresh-token",
|
||||
AUTH_USER_ID: "token-id",
|
||||
};
|
||||
|
||||
let isInitialized = false;
|
||||
let envConfig: EnvConfig = { ...defaultConfig };
|
||||
|
||||
export function initializeConfig(values: Partial<EnvConfig>): void {
|
||||
if (isInitialized) {
|
||||
console.warn("Configuration already initialized. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredValues = Object.entries(values).reduce((acc, [key, value]) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
acc[key as keyof EnvConfig] = value;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Partial<EnvConfig>);
|
||||
|
||||
if (filteredValues.PORT && !filteredValues.PROXY_HOST) {
|
||||
filteredValues.PROXY_HOST = `http://localhost:${filteredValues.PORT}`;
|
||||
}
|
||||
|
||||
envConfig = { ...defaultConfig, ...filteredValues };
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
export function getConfig(): EnvConfig {
|
||||
if (!isInitialized) {
|
||||
console.warn(
|
||||
"Attempted to access config before initialization. Returning default values."
|
||||
);
|
||||
}
|
||||
return envConfig;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { initializeConfig, type EnvConfig } from "./initialize-config";
|
||||
|
||||
export function initializeNextClientConfig(): void {
|
||||
if (typeof window === "undefined") {
|
||||
console.warn("initializeNextClientConfig called on the server. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const env: Partial<EnvConfig> = {
|
||||
PORT: process.env.NEXT_PUBLIC_PORT,
|
||||
PROXY_HOST: process.env.NEXT_PUBLIC_PROXY_HOST,
|
||||
SERVER_API_URL: process.env.NEXT_PUBLIC_SERVER_API_URL,
|
||||
SITE_ID: process.env.NEXT_PUBLIC_SITE_ID,
|
||||
CLAIM_NAME: process.env.NEXT_PUBLIC_CLAIM_NAME,
|
||||
ACCESS_TOKEN: process.env.NEXT_PUBLIC_ACCESS_TOKEN,
|
||||
REFRESH_TOKEN: process.env.NEXT_PUBLIC_REFRESH_TOKEN,
|
||||
AUTH_USER_ID: process.env.NEXT_PUBLIC_AUTH_USER_ID,
|
||||
};
|
||||
initializeConfig(env);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node", "vite/client"],
|
||||
"incremental": false
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
export default defineConfig({
|
||||
entry: ["src/index.ts"],
|
||||
format: ["esm"],
|
||||
dts: true,
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
external: [],
|
||||
});
|
||||
Reference in New Issue
Block a user