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) => {
|
export const getCategoryValues = async (req: AuthenticatedRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const companyCode = req.user!.companyCode;
|
const userCompanyCode = req.user!.companyCode;
|
||||||
const { tableName, columnName } = req.params;
|
const { tableName, columnName } = req.params;
|
||||||
const includeInactive = req.query.includeInactive === "true";
|
const includeInactive = req.query.includeInactive === "true";
|
||||||
const menuObjid = req.query.menuObjid ? Number(req.query.menuObjid) : undefined;
|
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("카테고리 값 조회 요청", {
|
logger.info("카테고리 값 조회 요청", {
|
||||||
tableName,
|
tableName,
|
||||||
columnName,
|
columnName,
|
||||||
menuObjid,
|
menuObjid,
|
||||||
companyCode,
|
companyCode: effectiveCompanyCode,
|
||||||
|
filterCompanyCode,
|
||||||
});
|
});
|
||||||
|
|
||||||
const values = await tableCategoryValueService.getCategoryValues(
|
const values = await tableCategoryValueService.getCategoryValues(
|
||||||
tableName,
|
tableName,
|
||||||
columnName,
|
columnName,
|
||||||
companyCode,
|
effectiveCompanyCode,
|
||||||
includeInactive,
|
includeInactive,
|
||||||
menuObjid // ← menuObjid 전달
|
menuObjid
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
|
|
|
||||||
|
|
@ -217,12 +217,12 @@ class TableCategoryValueService {
|
||||||
AND column_name = $2
|
AND column_name = $2
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// category_values 테이블 사용 (menu_objid 없음)
|
// company_code 기반 필터링
|
||||||
if (companyCode === "*") {
|
if (companyCode === "*") {
|
||||||
// 최고 관리자: 모든 값 조회
|
// 최고 관리자: 공통(*) 카테고리만 조회 (모든 회사 카테고리 혼합 방지)
|
||||||
query = baseSelect;
|
query = baseSelect + ` AND company_code = '*'`;
|
||||||
params = [tableName, columnName];
|
params = [tableName, columnName];
|
||||||
logger.info("최고 관리자 전체 카테고리 값 조회 (category_values)");
|
logger.info("최고 관리자: 공통 카테고리만 조회 (category_values)");
|
||||||
} else {
|
} else {
|
||||||
// 일반 회사: 자신의 회사 또는 공통(*) 카테고리 조회
|
// 일반 회사: 자신의 회사 또는 공통(*) 카테고리 조회
|
||||||
query = baseSelect + ` AND (company_code = $3 OR company_code = '*')`;
|
query = baseSelect + ` AND (company_code = $3 OR company_code = '*')`;
|
||||||
|
|
|
||||||
|
|
@ -713,7 +713,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
const [categoryMappings, setCategoryMappings] = useState<
|
const [categoryMappings, setCategoryMappings] = useState<
|
||||||
Record<string, Record<string, { label: string; color?: string }>>
|
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 [searchValues, setSearchValues] = useState<Record<string, any>>({});
|
||||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
||||||
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
||||||
|
|
@ -1047,9 +1047,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
const getColumnUniqueValues = async (columnName: string) => {
|
const getColumnUniqueValues = async (columnName: string) => {
|
||||||
const { apiClient } = await import("@/lib/api/client");
|
const { apiClient } = await import("@/lib/api/client");
|
||||||
|
|
||||||
|
// 최고관리자가 특정 회사 프리뷰 시 해당 회사 카테고리만 필터링
|
||||||
|
const filterParam = companyCode && companyCode !== "*"
|
||||||
|
? `?filterCompanyCode=${encodeURIComponent(companyCode)}`
|
||||||
|
: "";
|
||||||
|
|
||||||
// 1단계: 카테고리 API 시도 (columnMeta 무관하게 항상 시도)
|
// 1단계: 카테고리 API 시도 (columnMeta 무관하게 항상 시도)
|
||||||
try {
|
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) {
|
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||||
return response.data.data.map((item: any) => ({
|
return response.data.data.map((item: any) => ({
|
||||||
value: item.valueCode,
|
value: item.valueCode,
|
||||||
|
|
@ -1154,15 +1159,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
tableConfig.selectedTable,
|
tableConfig.selectedTable,
|
||||||
tableConfig.columns,
|
tableConfig.columns,
|
||||||
columnLabels,
|
columnLabels,
|
||||||
columnMeta, // columnMeta가 변경되면 재등록 (inputType 정보 필요)
|
columnMeta,
|
||||||
categoryMappings, // 카테고리 매핑 변경 시 재등록 (필터 라벨 변환용)
|
categoryMappings,
|
||||||
columnWidths,
|
columnWidths,
|
||||||
tableLabel,
|
tableLabel,
|
||||||
data, // 데이터 자체가 변경되면 재등록 (고유 값 조회용)
|
data,
|
||||||
totalItems, // 전체 항목 수가 변경되면 재등록
|
totalItems,
|
||||||
registerTable,
|
registerTable,
|
||||||
// unregisterTable은 의존성에서 제외 - 무한 루프 방지
|
|
||||||
// unregisterTable 함수는 의존성이 없어 안정적임
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 🎯 초기 로드 시 localStorage에서 정렬 상태 불러오기 (없으면 defaultSort 적용)
|
// 🎯 초기 로드 시 localStorage에서 정렬 상태 불러오기 (없으면 defaultSort 적용)
|
||||||
|
|
@ -1406,7 +1409,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
||||||
const apiClient = (await import("@/lib/api/client")).apiClient;
|
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 }>) => {
|
const flattenTree = (items: any[], mapping: Record<string, { label: string; color?: string }>) => {
|
||||||
items.forEach((item: any) => {
|
items.forEach((item: any) => {
|
||||||
if (item.valueCode) {
|
if (item.valueCode) {
|
||||||
|
|
@ -1415,12 +1424,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
color: item.color,
|
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) {
|
if (item.children && Array.isArray(item.children) && item.children.length > 0) {
|
||||||
flattenTree(item.children, mapping);
|
flattenTree(item.children, mapping);
|
||||||
}
|
}
|
||||||
|
|
@ -1448,7 +1451,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// 비활성화된 카테고리도 라벨로 표시하기 위해 includeInactive=true
|
// 비활성화된 카테고리도 라벨로 표시하기 위해 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)) {
|
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||||
|
|
@ -1531,7 +1534,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
// inputType이 category인 경우 카테고리 매핑 로드
|
// inputType이 category인 경우 카테고리 매핑 로드
|
||||||
if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) {
|
if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) {
|
||||||
try {
|
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)) {
|
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||||
|
|
@ -1601,6 +1604,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
JSON.stringify(categoryColumns),
|
JSON.stringify(categoryColumns),
|
||||||
JSON.stringify(tableConfig.columns),
|
JSON.stringify(tableConfig.columns),
|
||||||
columnMeta,
|
columnMeta,
|
||||||
|
companyCode,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue