- 방제선/유회수기/이송펌프/방제차량/살포장치 장비 필터 드롭다운 추가 - 페이지네이션 위 합계 행에 필터된 기관의 장비별 총합 표시 - 장비 필터 선택 시 해당 컬럼 헤더/셀/합계 항목 cyan 하이라이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
438 lines
23 KiB
TypeScript
438 lines
23 KiB
TypeScript
import { useState, useEffect } from 'react'
|
||
import { typeTagCls } from './assetTypes'
|
||
import { fetchOrganizations, fetchOrganizationDetail } from '../services/assetsApi'
|
||
import type { AssetOrgCompat } from '../services/assetsApi'
|
||
import AssetMap from './AssetMap'
|
||
|
||
function AssetManagement() {
|
||
const [viewMode, setViewMode] = useState<'list' | 'map'>('list')
|
||
const [organizations, setOrganizations] = useState<AssetOrgCompat[]>([])
|
||
const [selectedOrg, setSelectedOrg] = useState<AssetOrgCompat | null>(null)
|
||
const [detailTab, setDetailTab] = useState<'equip' | 'material' | 'contact'>('equip')
|
||
const [regionFilter, setRegionFilter] = useState('all')
|
||
const [searchTerm, setSearchTerm] = useState('')
|
||
const [typeFilterVal, setTypeFilterVal] = useState('all')
|
||
const [equipFilter, setEquipFilter] = useState('all')
|
||
const [currentPage, setCurrentPage] = useState(1)
|
||
const [loading, setLoading] = useState(true)
|
||
const pageSize = 15
|
||
|
||
// API에서 기관 목록 로드
|
||
useEffect(() => {
|
||
let cancelled = false
|
||
fetchOrganizations()
|
||
.then(data => {
|
||
if (cancelled) return
|
||
setOrganizations(data)
|
||
if (data.length > 0) setSelectedOrg(data[0])
|
||
})
|
||
.catch(err => console.error('[assets] 기관 목록 로드 실패:', err))
|
||
.finally(() => { if (!cancelled) setLoading(false) })
|
||
return () => { cancelled = true }
|
||
}, [])
|
||
|
||
// 기관 선택 시 상세 조회 (장비/담당자)
|
||
const handleSelectOrg = async (org: AssetOrgCompat) => {
|
||
setSelectedOrg(org)
|
||
try {
|
||
const detail = await fetchOrganizationDetail(org.id)
|
||
setSelectedOrg(detail)
|
||
} catch {
|
||
// 상세 조회 실패 시 기본 정보 유지
|
||
}
|
||
}
|
||
|
||
const filtered = organizations.filter(o => {
|
||
if (regionFilter !== 'all' && !o.jurisdiction.includes(regionFilter)) return false
|
||
if (typeFilterVal !== 'all' && o.type !== typeFilterVal) return false
|
||
if (equipFilter !== 'all') {
|
||
const equipMap: Record<string, (org: AssetOrgCompat) => boolean> = {
|
||
vessel: org => org.vessel > 0,
|
||
skimmer: org => org.skimmer > 0,
|
||
pump: org => org.pump > 0,
|
||
vehicle: org => org.vehicle > 0,
|
||
sprayer: org => org.sprayer > 0,
|
||
}
|
||
if (equipMap[equipFilter] && !equipMap[equipFilter](o)) return false
|
||
}
|
||
if (searchTerm && !o.name.includes(searchTerm) && !o.address.includes(searchTerm)) return false
|
||
return true
|
||
})
|
||
|
||
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize))
|
||
const safePage = Math.min(currentPage, totalPages)
|
||
const paged = filtered.slice((safePage - 1) * pageSize, safePage * pageSize)
|
||
|
||
// 필터 변경 시 첫 페이지로
|
||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||
useEffect(() => { setCurrentPage(1) }, [regionFilter, typeFilterVal, equipFilter, searchTerm])
|
||
|
||
const regionShort = (j: string) => {
|
||
if (j.includes('중부')) return '중부청'
|
||
if (j.includes('서해')) return '서해청'
|
||
if (j.includes('남해')) return '남해청'
|
||
if (j.includes('동해')) return '동해청'
|
||
if (j.includes('중앙')) return '중특단'
|
||
return '제주청'
|
||
}
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="flex items-center justify-center h-full">
|
||
<div className="text-text-3 text-sm font-korean">방제자산 데이터를 불러오는 중...</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="flex flex-col h-full">
|
||
{/* View Switcher & Filters */}
|
||
<div className="flex items-center justify-between mb-3 pb-3 border-b border-border">
|
||
<div className="flex gap-1">
|
||
<button
|
||
onClick={() => setViewMode('list')}
|
||
className={`px-3 py-1.5 text-[11px] font-semibold rounded-sm font-korean transition-colors ${
|
||
viewMode === 'list'
|
||
? 'bg-[rgba(6,182,212,0.15)] text-primary-cyan border border-primary-cyan/30'
|
||
: 'bg-bg-3 border border-border text-text-2 hover:bg-bg-hover'
|
||
}`}
|
||
>
|
||
📋 방제자산리스트
|
||
</button>
|
||
<button
|
||
onClick={() => setViewMode('map')}
|
||
className={`px-3 py-1.5 text-[11px] font-semibold rounded-sm font-korean transition-colors ${
|
||
viewMode === 'map'
|
||
? 'bg-[rgba(6,182,212,0.15)] text-primary-cyan border border-primary-cyan/30'
|
||
: 'bg-bg-3 border border-border text-text-2 hover:bg-bg-hover'
|
||
}`}
|
||
>
|
||
🗺 지도 보기
|
||
</button>
|
||
</div>
|
||
<div className="flex gap-1.5 items-center">
|
||
<input
|
||
type="text"
|
||
placeholder="기관명 검색..."
|
||
value={searchTerm}
|
||
onChange={e => setSearchTerm(e.target.value)}
|
||
className="prd-i w-40 py-1.5 px-2.5"
|
||
/>
|
||
<select value={regionFilter} onChange={e => setRegionFilter(e.target.value)} className="prd-i w-[100px] py-1.5 px-2">
|
||
<option value="all">전체 관할</option>
|
||
<option value="남해">남해청</option>
|
||
<option value="서해">서해청</option>
|
||
<option value="중부">중부청</option>
|
||
<option value="동해">동해청</option>
|
||
<option value="제주">제주청</option>
|
||
</select>
|
||
<select value={typeFilterVal} onChange={e => setTypeFilterVal(e.target.value)} className="prd-i w-[100px] py-1.5 px-2">
|
||
<option value="all">전체 유형</option>
|
||
<option value="해경관할">해경관할</option>
|
||
<option value="해경경찰서">해경경찰서</option>
|
||
<option value="파출소">파출소</option>
|
||
<option value="관련기관">관련기관</option>
|
||
<option value="해양환경공단">해양환경공단</option>
|
||
<option value="업체">업체</option>
|
||
<option value="지자체">지자체</option>
|
||
<option value="기름저장시설">기름저장시설</option>
|
||
<option value="정유사">정유사</option>
|
||
<option value="해군">해군</option>
|
||
<option value="기타">기타</option>
|
||
</select>
|
||
<select value={equipFilter} onChange={e => setEquipFilter(e.target.value)} className="prd-i w-[100px] py-1.5 px-2">
|
||
<option value="all">전체 장비</option>
|
||
<option value="vessel">방제선</option>
|
||
<option value="skimmer">유회수기</option>
|
||
<option value="pump">이송펌프</option>
|
||
<option value="vehicle">방제차량</option>
|
||
<option value="sprayer">살포장치</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{viewMode === 'list' ? (
|
||
/* ── LIST VIEW ── */
|
||
<div className="flex-1 bg-bg-3 border border-border rounded-md overflow-hidden flex flex-col">
|
||
<div className="flex-1">
|
||
<table className="w-full text-left" style={{ tableLayout: 'fixed' }}>
|
||
<colgroup>
|
||
<col style={{ width: '3.5%' }} />
|
||
<col style={{ width: '7%' }} />
|
||
<col style={{ width: '7%' }} />
|
||
<col style={{ width: '12%' }} />
|
||
<col />
|
||
<col style={{ width: '8%' }} />
|
||
<col style={{ width: '7%' }} />
|
||
<col style={{ width: '7%' }} />
|
||
<col style={{ width: '5%' }} />
|
||
<col style={{ width: '5%' }} />
|
||
<col style={{ width: '5%' }} />
|
||
</colgroup>
|
||
<thead>
|
||
<tr className="border-b border-border bg-bg-0">
|
||
{['번호', '유형', '관할청', '기관명', '주소', '방제선', '유회수기', '이송펌프', '방제차량', '살포장치', '총자산'].map((h, i) => {
|
||
const equipColMap: Record<string, number> = { vessel: 5, skimmer: 6, pump: 7, vehicle: 8, sprayer: 9 }
|
||
const isHighlight = equipFilter !== 'all' && equipColMap[equipFilter] === i
|
||
return (
|
||
<th key={i} className={`px-2.5 py-2.5 text-[10px] font-bold font-korean border-b border-border ${[0,5,6,7,8,9,10].includes(i) ? 'text-center' : ''} ${isHighlight ? 'text-primary-cyan bg-primary-cyan/5' : 'text-text-2'}`}>
|
||
{h}
|
||
</th>
|
||
)
|
||
})}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{paged.map((org, idx) => (
|
||
<tr
|
||
key={org.id}
|
||
className={`border-b border-border/50 hover:bg-[rgba(255,255,255,0.02)] cursor-pointer transition-colors ${
|
||
selectedOrg?.id === org.id ? 'bg-[rgba(6,182,212,0.03)]' : ''
|
||
}`}
|
||
onClick={() => { handleSelectOrg(org); setViewMode('map') }}
|
||
>
|
||
<td className="px-2.5 py-2 text-center font-mono text-[10px]">{(safePage - 1) * pageSize + idx + 1}</td>
|
||
<td className="px-2.5 py-2">
|
||
<span className={`text-[9px] px-1.5 py-0.5 rounded font-bold font-korean ${typeTagCls(org.type)}`}>{org.type}</span>
|
||
</td>
|
||
<td className="px-2.5 py-2 text-[10px] font-semibold font-korean">{regionShort(org.jurisdiction)}</td>
|
||
<td className="px-2.5 py-2 text-[10px] font-semibold text-primary-cyan font-korean cursor-pointer truncate">{org.name}</td>
|
||
<td className="px-2.5 py-2 text-[10px] text-text-3 font-korean truncate">{org.address}</td>
|
||
<td className={`px-2.5 py-2 text-center font-mono text-[10px] font-semibold ${equipFilter === 'vessel' ? 'text-primary-cyan bg-primary-cyan/5' : ''}`}>{org.vessel}척</td>
|
||
<td className={`px-2.5 py-2 text-center font-mono text-[10px] ${equipFilter === 'skimmer' ? 'text-primary-cyan font-semibold bg-primary-cyan/5' : ''}`}>{org.skimmer}대</td>
|
||
<td className={`px-2.5 py-2 text-center font-mono text-[10px] ${equipFilter === 'pump' ? 'text-primary-cyan font-semibold bg-primary-cyan/5' : ''}`}>{org.pump}대</td>
|
||
<td className={`px-2.5 py-2 text-center font-mono text-[10px] ${equipFilter === 'vehicle' ? 'text-primary-cyan font-semibold bg-primary-cyan/5' : ''}`}>{org.vehicle}대</td>
|
||
<td className={`px-2.5 py-2 text-center font-mono text-[10px] ${equipFilter === 'sprayer' ? 'text-primary-cyan font-semibold bg-primary-cyan/5' : ''}`}>{org.sprayer}대</td>
|
||
<td className="px-2.5 py-2 text-center font-bold text-primary-cyan font-mono text-[10px]">{org.totalAssets}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
{/* Totals Summary */}
|
||
<div className="flex items-center justify-end gap-4 px-4 py-2 border-t border-border bg-bg-0/80">
|
||
<span className="text-[10px] text-text-3 font-korean font-semibold mr-auto">
|
||
합계 ({filtered.length}개 기관)
|
||
</span>
|
||
{[
|
||
{ key: 'vessel', label: '방제선', value: filtered.reduce((s, o) => s + o.vessel, 0), unit: '척' },
|
||
{ key: 'skimmer', label: '유회수기', value: filtered.reduce((s, o) => s + o.skimmer, 0), unit: '대' },
|
||
{ key: 'pump', label: '이송펌프', value: filtered.reduce((s, o) => s + o.pump, 0), unit: '대' },
|
||
{ key: 'vehicle', label: '방제차량', value: filtered.reduce((s, o) => s + o.vehicle, 0), unit: '대' },
|
||
{ key: 'sprayer', label: '살포장치', value: filtered.reduce((s, o) => s + o.sprayer, 0), unit: '대' },
|
||
{ key: 'total', label: '총자산', value: filtered.reduce((s, o) => s + o.totalAssets, 0), unit: '' },
|
||
].map((t) => {
|
||
const isActive = equipFilter === t.key || t.key === 'total'
|
||
return (
|
||
<div key={t.key} className={`flex items-center gap-1 px-1.5 py-0.5 rounded ${equipFilter === t.key ? 'bg-primary-cyan/10' : ''}`}>
|
||
<span className={`text-[9px] font-korean ${isActive ? 'text-primary-cyan' : 'text-text-3'}`}>{t.label}</span>
|
||
<span className={`text-[10px] font-mono font-bold ${isActive ? 'text-primary-cyan' : 'text-text-1'}`}>{t.value.toLocaleString()}{t.unit}</span>
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
|
||
{/* Pagination */}
|
||
<div className="flex items-center justify-center gap-4 px-4 py-2.5 border-t border-border bg-bg-0">
|
||
<span className="text-[10px] text-text-3 font-korean">
|
||
전체 <span className="font-semibold text-text-2">{filtered.length}</span>건 중{' '}
|
||
<span className="font-semibold text-text-2">{(safePage - 1) * pageSize + 1}-{Math.min(safePage * pageSize, filtered.length)}</span>
|
||
</span>
|
||
<div className="flex items-center gap-1">
|
||
<button
|
||
onClick={() => setCurrentPage(1)}
|
||
disabled={safePage <= 1}
|
||
className="px-1.5 py-1 text-[10px] rounded border border-border bg-bg-3 text-text-2 disabled:opacity-30 hover:bg-bg-hover transition-colors cursor-pointer disabled:cursor-default"
|
||
>«</button>
|
||
<button
|
||
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
|
||
disabled={safePage <= 1}
|
||
className="px-1.5 py-1 text-[10px] rounded border border-border bg-bg-3 text-text-2 disabled:opacity-30 hover:bg-bg-hover transition-colors cursor-pointer disabled:cursor-default"
|
||
>‹</button>
|
||
{Array.from({ length: totalPages }, (_, i) => i + 1).map(p => (
|
||
<button
|
||
key={p}
|
||
onClick={() => setCurrentPage(p)}
|
||
className={`w-6 h-6 text-[10px] font-bold rounded transition-colors cursor-pointer ${
|
||
p === safePage
|
||
? 'bg-primary-cyan/20 text-primary-cyan border border-primary-cyan/40'
|
||
: 'border border-border bg-bg-3 text-text-3 hover:bg-bg-hover'
|
||
}`}
|
||
>{p}</button>
|
||
))}
|
||
<button
|
||
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
|
||
disabled={safePage >= totalPages}
|
||
className="px-1.5 py-1 text-[10px] rounded border border-border bg-bg-3 text-text-2 disabled:opacity-30 hover:bg-bg-hover transition-colors cursor-pointer disabled:cursor-default"
|
||
>›</button>
|
||
<button
|
||
onClick={() => setCurrentPage(totalPages)}
|
||
disabled={safePage >= totalPages}
|
||
className="px-1.5 py-1 text-[10px] rounded border border-border bg-bg-3 text-text-2 disabled:opacity-30 hover:bg-bg-hover transition-colors cursor-pointer disabled:cursor-default"
|
||
>»</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
/* ── MAP VIEW ── */
|
||
<div className="flex-1 flex overflow-hidden rounded-md border border-border">
|
||
{/* Map */}
|
||
<div className="flex-1 relative overflow-hidden">
|
||
<AssetMap
|
||
organizations={filtered}
|
||
selectedOrg={selectedOrg!}
|
||
onSelectOrg={handleSelectOrg}
|
||
regionFilter={regionFilter}
|
||
onRegionFilterChange={setRegionFilter}
|
||
/>
|
||
</div>
|
||
|
||
{/* Right Detail Panel */}
|
||
{selectedOrg && (
|
||
<aside className="w-[340px] min-w-[340px] bg-bg-1 border-l border-border flex flex-col">
|
||
{/* Header */}
|
||
<div className="p-4 border-b border-border">
|
||
<div className="text-sm font-bold mb-1 font-korean">{selectedOrg.name}</div>
|
||
<div className="text-[11px] text-text-2 font-semibold font-korean mb-1">{selectedOrg.type} · {regionShort(selectedOrg.jurisdiction)} · {selectedOrg.area}</div>
|
||
<div className="text-[11px] text-text-3 font-korean">{selectedOrg.address}</div>
|
||
</div>
|
||
|
||
{/* Sub-tabs */}
|
||
<div className="flex border-b border-border">
|
||
{(['equip', 'material', 'contact'] as const).map(t => (
|
||
<button
|
||
key={t}
|
||
onClick={() => setDetailTab(t)}
|
||
className={`flex-1 py-2.5 text-center text-[11px] font-semibold font-korean border-b-2 transition-colors cursor-pointer ${
|
||
detailTab === t
|
||
? 'text-primary-cyan border-primary-cyan'
|
||
: 'text-text-3 border-transparent hover:text-text-2'
|
||
}`}
|
||
>
|
||
{t === 'equip' ? '장비' : t === 'material' ? '자재' : '연락처'}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="flex-1 overflow-y-auto p-3.5 scrollbar-thin">
|
||
{/* Summary */}
|
||
<div className="grid grid-cols-3 gap-1.5 mb-3">
|
||
{[
|
||
{ value: `${selectedOrg.vessel}척`, label: '방제선' },
|
||
{ value: `${selectedOrg.skimmer}대`, label: '유회수기' },
|
||
{ value: String(selectedOrg.totalAssets), label: '총 자산' },
|
||
].map((s, i) => (
|
||
<div key={i} className="bg-bg-3 border border-border rounded-sm p-2.5 text-center">
|
||
<div className="text-lg font-bold text-primary-cyan font-mono">{s.value}</div>
|
||
<div className="text-[9px] text-text-3 mt-0.5 font-korean">{s.label}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{detailTab === 'equip' && (
|
||
<div className="flex flex-col gap-1">
|
||
{selectedOrg.equipment.length > 0 ? selectedOrg.equipment.map((cat, ci) => {
|
||
const unitMap: Record<string, string> = {
|
||
'방제선': '척', '유회수기': '대', '비치크리너': '대', '이송펌프': '대', '방제차량': '대',
|
||
'해안운반차': '대', '고압세척기': '대', '저압세척기': '대', '동력분무기': '대', '유량계측기': '대',
|
||
'방제창고': '개소', '발전기': '대', '현장지휘소': '개', '지원장비': '대', '장비부품': '개',
|
||
'경비함정방제': '대', '살포장치': '대',
|
||
}
|
||
const unit = unitMap[cat.category] || '개'
|
||
return (
|
||
<div key={ci} className="flex items-center justify-between px-2.5 py-2 bg-bg-3 border border-border rounded-sm hover:bg-bg-hover transition-colors">
|
||
<span className="text-[11px] font-semibold flex items-center gap-1.5 font-korean">
|
||
{cat.icon} {cat.category}
|
||
</span>
|
||
<span className="text-[11px] font-bold font-mono"><span className="text-primary-cyan">{cat.count}</span><span className="text-text-3 font-normal ml-0.5">{unit}</span></span>
|
||
</div>
|
||
)
|
||
}) : (
|
||
<div className="text-center text-text-3 text-xs py-8 font-korean">상세 장비 데이터가 없습니다.</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{detailTab === 'material' && (
|
||
<div className="flex flex-col gap-1.5">
|
||
{[
|
||
['방제선', `${selectedOrg.vessel}척`],
|
||
['유회수기', `${selectedOrg.skimmer}대`],
|
||
['이송펌프', `${selectedOrg.pump}대`],
|
||
['방제차량', `${selectedOrg.vehicle}대`],
|
||
['살포장치', `${selectedOrg.sprayer}대`],
|
||
['총 자산', `${selectedOrg.totalAssets}건`],
|
||
].map(([k, v], i) => (
|
||
<div key={i} className="flex justify-between px-2.5 py-2 bg-bg-0 rounded text-[11px]">
|
||
<span className="text-text-3 font-korean">{k}</span>
|
||
<span className="font-mono font-semibold text-text-1">{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{detailTab === 'contact' && (
|
||
<div className="flex flex-col gap-2">
|
||
{/* 기관 기본 정보 */}
|
||
<div className="bg-bg-3 border border-border rounded-sm p-3">
|
||
<div className="text-[10px] font-bold text-text-3 mb-2 font-korean">기관 정보</div>
|
||
{[
|
||
['기관명', selectedOrg.name],
|
||
['유형', selectedOrg.type],
|
||
['관할청', selectedOrg.jurisdiction],
|
||
['주소', selectedOrg.address],
|
||
...(selectedOrg.phone ? [['대표 연락처', selectedOrg.phone]] : []),
|
||
].map(([k, v], j) => (
|
||
<div key={j} className="flex justify-between py-1.5 text-[11px] border-b border-border/30 last:border-b-0">
|
||
<span className="text-text-3 font-korean shrink-0 mr-2">{k}</span>
|
||
<span className={`text-text-1 text-right ${k === '대표 연락처' ? 'font-mono font-semibold text-primary-cyan' : 'font-korean'}`}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 담당자 목록 */}
|
||
{selectedOrg.contacts.length > 0 && (
|
||
<div className="bg-bg-3 border border-border rounded-sm p-3">
|
||
<div className="text-[10px] font-bold text-text-3 mb-2 font-korean">담당자</div>
|
||
{selectedOrg.contacts.map((c, i) => (
|
||
<div key={i} className="mb-2.5 last:mb-0">
|
||
{[
|
||
['직책', c.role],
|
||
['담당자', c.name],
|
||
['연락처', c.phone],
|
||
].filter(([, v]) => v).map(([k, v], j) => (
|
||
<div key={j} className="flex justify-between py-1.5 text-[11px] border-b border-border/30 last:border-b-0">
|
||
<span className="text-text-3 font-korean">{k}</span>
|
||
<span className={`text-text-1 ${k === '연락처' ? 'font-mono font-semibold text-primary-cyan' : 'font-korean'}`}>{v}</span>
|
||
</div>
|
||
))}
|
||
{i < selectedOrg.contacts.length - 1 && <div className="border-t border-border mt-1" />}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Bottom Actions */}
|
||
<div className="p-3.5 border-t border-border flex gap-2">
|
||
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean text-white border-none cursor-pointer" style={{ background: 'linear-gradient(135deg, var(--cyan), var(--blue))' }} >
|
||
📥 다운로드
|
||
</button>
|
||
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean bg-bg-3 border border-border text-text-2 cursor-pointer hover:bg-bg-hover transition-colors">
|
||
✏ 수정
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default AssetManagement
|