feat: enhance category value retrieval with company code filtering
- Updated the `getCategoryValues` function to allow filtering based on a specified company code when requested by a super admin. - Modified the service layer to ensure that super admins can retrieve common category values while preventing data mixing from different companies. - Adjusted the frontend component to include the filter parameter in API requests, ensuring that the correct company-specific categories are displayed. Made-with: Cursor
This commit is contained in:
parent
d890155354
commit
62a5ae5f4b
|
|
@ -62,24 +62,31 @@ export const getAllCategoryColumns = async (req: AuthenticatedRequest, res: Resp
|
|||
*/
|
||||
export const getCategoryValues = async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const userCompanyCode = req.user!.companyCode;
|
||||
const { tableName, columnName } = req.params;
|
||||
const includeInactive = req.query.includeInactive === "true";
|
||||
const menuObjid = req.query.menuObjid ? Number(req.query.menuObjid) : undefined;
|
||||
const filterCompanyCode = req.query.filterCompanyCode as string | undefined;
|
||||
|
||||
// 최고관리자가 특정 회사 기준 필터링을 요청한 경우 해당 회사 코드 사용
|
||||
const effectiveCompanyCode = (userCompanyCode === "*" && filterCompanyCode)
|
||||
? filterCompanyCode
|
||||
: userCompanyCode;
|
||||
|
||||
logger.info("카테고리 값 조회 요청", {
|
||||
tableName,
|
||||
columnName,
|
||||
menuObjid,
|
||||
companyCode,
|
||||
companyCode: effectiveCompanyCode,
|
||||
filterCompanyCode,
|
||||
});
|
||||
|
||||
const values = await tableCategoryValueService.getCategoryValues(
|
||||
tableName,
|
||||
columnName,
|
||||
companyCode,
|
||||
effectiveCompanyCode,
|
||||
includeInactive,
|
||||
menuObjid // ← menuObjid 전달
|
||||
menuObjid
|
||||
);
|
||||
|
||||
return res.json({
|
||||
|
|
|
|||
|
|
@ -217,12 +217,12 @@ class TableCategoryValueService {
|
|||
AND column_name = $2
|
||||
`;
|
||||
|
||||
// category_values 테이블 사용 (menu_objid 없음)
|
||||
// company_code 기반 필터링
|
||||
if (companyCode === "*") {
|
||||
// 최고 관리자: 모든 값 조회
|
||||
query = baseSelect;
|
||||
// 최고 관리자: 공통(*) 카테고리만 조회 (모든 회사 카테고리 혼합 방지)
|
||||
query = baseSelect + ` AND company_code = '*'`;
|
||||
params = [tableName, columnName];
|
||||
logger.info("최고 관리자 전체 카테고리 값 조회 (category_values)");
|
||||
logger.info("최고 관리자: 공통 카테고리만 조회 (category_values)");
|
||||
} else {
|
||||
// 일반 회사: 자신의 회사 또는 공통(*) 카테고리 조회
|
||||
query = baseSelect + ` AND (company_code = $3 OR company_code = '*')`;
|
||||
|
|
|
|||
|
|
@ -713,7 +713,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const [categoryMappings, setCategoryMappings] = useState<
|
||||
Record<string, Record<string, { label: string; color?: string }>>
|
||||
>({});
|
||||
const [categoryMappingsKey, setCategoryMappingsKey] = useState(0); // 강제 리렌더링용
|
||||
const [categoryMappingsKey, setCategoryMappingsKey] = useState(0);
|
||||
const [searchValues, setSearchValues] = useState<Record<string, any>>({});
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
||||
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
||||
|
|
@ -1047,9 +1047,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const getColumnUniqueValues = async (columnName: string) => {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
|
||||
// 최고관리자가 특정 회사 프리뷰 시 해당 회사 카테고리만 필터링
|
||||
const filterParam = companyCode && companyCode !== "*"
|
||||
? `?filterCompanyCode=${encodeURIComponent(companyCode)}`
|
||||
: "";
|
||||
|
||||
// 1단계: 카테고리 API 시도 (columnMeta 무관하게 항상 시도)
|
||||
try {
|
||||
const response = await apiClient.get(`/table-categories/${tableConfig.selectedTable}/${columnName}/values`);
|
||||
const response = await apiClient.get(`/table-categories/${tableConfig.selectedTable}/${columnName}/values${filterParam}`);
|
||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
return response.data.data.map((item: any) => ({
|
||||
value: item.valueCode,
|
||||
|
|
@ -1154,15 +1159,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
tableConfig.selectedTable,
|
||||
tableConfig.columns,
|
||||
columnLabels,
|
||||
columnMeta, // columnMeta가 변경되면 재등록 (inputType 정보 필요)
|
||||
categoryMappings, // 카테고리 매핑 변경 시 재등록 (필터 라벨 변환용)
|
||||
columnMeta,
|
||||
categoryMappings,
|
||||
columnWidths,
|
||||
tableLabel,
|
||||
data, // 데이터 자체가 변경되면 재등록 (고유 값 조회용)
|
||||
totalItems, // 전체 항목 수가 변경되면 재등록
|
||||
data,
|
||||
totalItems,
|
||||
registerTable,
|
||||
// unregisterTable은 의존성에서 제외 - 무한 루프 방지
|
||||
// unregisterTable 함수는 의존성이 없어 안정적임
|
||||
]);
|
||||
|
||||
// 🎯 초기 로드 시 localStorage에서 정렬 상태 불러오기 (없으면 defaultSort 적용)
|
||||
|
|
@ -1406,7 +1409,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
||||
const apiClient = (await import("@/lib/api/client")).apiClient;
|
||||
|
||||
// 최고관리자가 특정 회사 프리뷰 시 해당 회사 카테고리만 필터링
|
||||
const filterCompanyParam = companyCode && companyCode !== "*"
|
||||
? `&filterCompanyCode=${encodeURIComponent(companyCode)}`
|
||||
: "";
|
||||
|
||||
// 트리 구조를 평탄화하는 헬퍼 함수 (메인 테이블 + 엔티티 조인 공통 사용)
|
||||
// valueCode만 키로 사용 (valueId까지 넣으면 같은 라벨이 2번 나옴)
|
||||
const flattenTree = (items: any[], mapping: Record<string, { label: string; color?: string }>) => {
|
||||
items.forEach((item: any) => {
|
||||
if (item.valueCode) {
|
||||
|
|
@ -1415,12 +1424,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
color: item.color,
|
||||
};
|
||||
}
|
||||
if (item.valueId !== undefined && item.valueId !== null) {
|
||||
mapping[String(item.valueId)] = {
|
||||
label: item.valueLabel,
|
||||
color: item.color,
|
||||
};
|
||||
}
|
||||
if (item.children && Array.isArray(item.children) && item.children.length > 0) {
|
||||
flattenTree(item.children, mapping);
|
||||
}
|
||||
|
|
@ -1448,7 +1451,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
}
|
||||
|
||||
// 비활성화된 카테고리도 라벨로 표시하기 위해 includeInactive=true
|
||||
const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values?includeInactive=true`);
|
||||
const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values?includeInactive=true${filterCompanyParam}`);
|
||||
|
||||
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||
|
|
@ -1531,7 +1534,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// inputType이 category인 경우 카테고리 매핑 로드
|
||||
if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) {
|
||||
try {
|
||||
const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values?includeInactive=true`);
|
||||
const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values?includeInactive=true${filterCompanyParam}`);
|
||||
|
||||
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||
|
|
@ -1601,6 +1604,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
JSON.stringify(categoryColumns),
|
||||
JSON.stringify(tableConfig.columns),
|
||||
columnMeta,
|
||||
companyCode,
|
||||
]);
|
||||
|
||||
// ========================================
|
||||
|
|
|
|||
Loading…
Reference in New Issue