릴리즈 v0.3.2: 일반 사용자(User) 권한 메뉴 가시성 및 권한 제어 패치

This commit is contained in:
choibk 2026-01-24 23:12:52 +09:00
parent faf17d3fcc
commit c6ab665685
8 changed files with 28 additions and 9 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "smartims", "name": "smartims",
"private": true, "private": true,
"version": "0.3.1", "version": "0.3.2",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -1,6 +1,6 @@
{ {
"name": "server", "name": "server",
"version": "0.3.1", "version": "0.3.2",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -7,6 +7,7 @@ import { assetApi } from '../../../shared/api/assetApi';
import type { Asset } from '../../../shared/api/assetApi'; import type { Asset } from '../../../shared/api/assetApi';
import { SERVER_URL } from '../../../shared/api/client'; import { SERVER_URL } from '../../../shared/api/client';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { useAuth } from '../../../shared/auth/AuthContext';
interface AssetBasicInfoProps { interface AssetBasicInfoProps {
asset: Asset & { image?: string, consumables?: any[] }; asset: Asset & { image?: string, consumables?: any[] };
@ -14,6 +15,8 @@ interface AssetBasicInfoProps {
} }
export function AssetBasicInfo({ asset, onRefresh }: AssetBasicInfoProps) { export function AssetBasicInfo({ asset, onRefresh }: AssetBasicInfoProps) {
const { user } = useAuth();
const isAdmin = user?.role === 'admin' || user?.role === 'supervisor';
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const [editData, setEditData] = useState(asset); const [editData, setEditData] = useState(asset);
const [isZoomed, setIsZoomed] = useState(false); const [isZoomed, setIsZoomed] = useState(false);
@ -69,7 +72,7 @@ export function AssetBasicInfo({ asset, onRefresh }: AssetBasicInfoProps) {
<Button size="sm" onClick={handleSave} icon={<Save size={16} />}></Button> <Button size="sm" onClick={handleSave} icon={<Save size={16} />}></Button>
</> </>
) : ( ) : (
<Button size="sm" variant="secondary" onClick={() => setIsEditing(true)}></Button> isAdmin && <Button size="sm" variant="secondary" onClick={() => setIsEditing(true)}></Button>
)} )}
<Button variant="secondary" size="sm" icon={<Printer size={16} />}></Button> <Button variant="secondary" size="sm" icon={<Printer size={16} />}></Button>
</div> </div>
@ -341,7 +344,7 @@ export function AssetBasicInfo({ asset, onRefresh }: AssetBasicInfoProps) {
<div className="consumables-section"> <div className="consumables-section">
<div className="flex justify-between items-center mb-2"> <div className="flex justify-between items-center mb-2">
<h3 className="section-title text-lg font-bold"> </h3> <h3 className="section-title text-lg font-bold"> </h3>
<Button size="sm" variant="secondary" icon={<Plus size={14} />}> </Button> {isAdmin && <Button size="sm" variant="secondary" icon={<Plus size={14} />}> </Button>}
</div> </div>
<table className="doc-table w-full text-center border-collapse border border-slate-300"> <table className="doc-table w-full text-center border-collapse border border-slate-300">
<thead> <thead>

View File

@ -22,5 +22,5 @@ export const assetModule: IModuleDefinition = {
{ path: '/instruments', element: <AssetListPage />, label: '계측기 관리', position: 'top' }, { path: '/instruments', element: <AssetListPage />, label: '계측기 관리', position: 'top' },
{ path: '/vehicles', element: <AssetListPage />, label: '차량 관리', position: 'top' }, { path: '/vehicles', element: <AssetListPage />, label: '차량 관리', position: 'top' },
], ],
requiredRoles: ['admin', 'manager'] requiredRoles: ['admin', 'manager', 'user']
}; };

View File

@ -4,6 +4,7 @@ import { Card } from '../../../shared/ui/Card';
import { Button } from '../../../shared/ui/Button'; import { Button } from '../../../shared/ui/Button';
import { Input } from '../../../shared/ui/Input'; import { Input } from '../../../shared/ui/Input';
import { Search, Plus, Filter, Download, ChevronsLeft, ChevronsRight, ChevronLeft, ChevronRight } from 'lucide-react'; import { Search, Plus, Filter, Download, ChevronsLeft, ChevronsRight, ChevronLeft, ChevronRight } from 'lucide-react';
import { useAuth } from '../../../shared/auth/AuthContext';
import './AssetListPage.css'; import './AssetListPage.css';
import { getCategories } from './AssetSettingsPage'; import { getCategories } from './AssetSettingsPage';
@ -19,6 +20,8 @@ import { saveAs } from 'file-saver';
export function AssetListPage() { export function AssetListPage() {
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const { user } = useAuth();
const isAdmin = user?.role === 'admin' || user?.role === 'supervisor';
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [assets, setAssets] = useState<Asset[]>([]); const [assets, setAssets] = useState<Asset[]>([]);
@ -277,7 +280,9 @@ export function AssetListPage() {
</Button> </Button>
<Button variant="secondary" icon={<Download size={16} />} onClick={handleExcelDownload}> </Button> <Button variant="secondary" icon={<Download size={16} />} onClick={handleExcelDownload}> </Button>
{isAdmin && (
<Button onClick={() => navigate('/asset/register')} icon={<Plus size={16} />}> </Button> <Button onClick={() => navigate('/asset/register')} icon={<Plus size={16} />}> </Button>
)}
{/* Filter Popup */} {/* Filter Popup */}
{isFilterOpen && ( {isFilterOpen && (

View File

@ -7,5 +7,5 @@ export const cctvModule: IModuleDefinition = {
routes: [ routes: [
{ path: '/live', element: <MonitoringPage />, label: '실시간 관제' }, { path: '/live', element: <MonitoringPage />, label: '실시간 관제' },
], ],
requiredRoles: ['admin', 'operator'] requiredRoles: ['admin', 'operator', 'user']
}; };

View File

@ -7,5 +7,5 @@ export const productionModule: IModuleDefinition = {
routes: [ routes: [
{ path: '/dashboard', element: <ProductionPage />, label: '생산 현황' }, { path: '/dashboard', element: <ProductionPage />, label: '생산 현황' },
], ],
requiredRoles: ['admin', 'manager'] requiredRoles: ['admin', 'manager', 'user']
}; };

View File

@ -97,7 +97,7 @@ export function VersionPage() {
}; };
// Client/Frontend version fixed at build time // Client/Frontend version fixed at build time
const frontendVersion = '0.3.1'; const frontendVersion = '0.3.2';
const buildDate = '2026-01-24'; const buildDate = '2026-01-24';
// Check if update is needed based on frontend version vs remote tag // Check if update is needed based on frontend version vs remote tag
@ -249,6 +249,17 @@ export function VersionPage() {
<div className="space-y-4"> <div className="space-y-4">
{[ {[
{
version: '0.3.2',
date: '2026-01-24',
title: '일반 사용자(User) 권한 가시성 패치',
changes: [
'일반 사용자 계정 접속 시 사이드바 메뉴가 보이지 않던 권한 필터 오류 수정',
'자산 관리 상세 정보에서 일반 사용자의 수정/삭제 버튼 노출 제한 (View-only)',
'CCTV 모듈 내 일반 사용자의 제어 권한 제한 재검토'
],
type: 'fix'
},
{ {
version: '0.3.1', version: '0.3.1',
date: '2026-01-24', date: '2026-01-24',