개인정보 관련 업데이트 진행

ot 정보는 타인이 못봄
휴가신청 관련건도 타인이 못봄
This commit is contained in:
backuppc
2025-11-11 11:43:29 +09:00
parent e6a39d52e9
commit 709ca7c85a
57 changed files with 1869 additions and 8491 deletions

View File

@@ -50,7 +50,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<OutputPath>..\..\..\..\amkor\GroupWare\</OutputPath>
<OutputPath>..\..\..\..\Amkor\GroupWare\</OutputPath>
<DefineConstants>TRACE;DEBUG;WEB1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
@@ -671,21 +671,6 @@
<Content Include="Web\wwwroot\Project\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Web\wwwroot\react\LoginApp.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\TestApp.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\DashboardApp.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\CommonNavigation.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\CommonCode.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\css\common.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -722,18 +707,6 @@
<None Include="Web\wwwroot\login.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\JobReport.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\Kuntae.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\Todo.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\Project.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\lib\js\react.development.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -749,9 +722,6 @@
<None Include="Web\wwwroot\lib\js\tailwind-config.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Web\wwwroot\react\DevWarning.jsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0">

View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
// 지정되도록 할 수 있습니다.
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("25.11.03.1350")]
[assembly: AssemblyFileVersion("25.11.03.1350")]
[assembly: AssemblyVersion("25.11.11.1030")]
[assembly: AssemblyFileVersion("25.11.11.1030")]

View File

@@ -18,7 +18,34 @@ namespace Project.OWIN
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// 정적 파일 서빙을 가장 먼저 설정 (다른 모든 미들웨어보다 우선)
// 캐시 방지 미들웨어를 가장 먼저 설정 (정적 파일 서빙보다 우선)
app.Use(async (context, next) =>
{
var path = context.Request.Path.Value;
// 모든 정적 리소스에 대해 캐시 방지 헤더 설정
if (path.EndsWith(".js") ||
path.EndsWith(".css") ||
path.EndsWith(".jsx") ||
path.EndsWith(".tsx") ||
path.EndsWith(".html") ||
path.EndsWith(".htm"))
{
context.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
context.Response.Headers["Pragma"] = "no-cache";
context.Response.Headers["Expires"] = "0";
}
// JSX/TSX 파일을 JavaScript로 처리
if (path.EndsWith(".jsx") || path.EndsWith(".tsx"))
{
context.Response.ContentType = "application/javascript; charset=utf-8";
}
await next();
});
// 정적 파일 서빙 설정
var staticFileOptions = new FileServerOptions
{
EnableDefaultFiles = true,
@@ -28,31 +55,6 @@ namespace Project.OWIN
};
app.UseFileServer(staticFileOptions);
// 캐시 방지 미들웨어 (정적 파일이 처리되지 않은 경우에만 적용)
app.Use(async (context, next) =>
{
var path = context.Request.Path.Value;
if (path.EndsWith(".js") ||
path.EndsWith(".css") ||
path.EndsWith(".jsx") ||
path.EndsWith(".tsx") ||
path.EndsWith(".html"))
{
context.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
context.Response.Headers["Pragma"] = "no-cache";
context.Response.Headers["Expires"] = "0";
}
// JSX/TSX 파일을 JavaScript로 처리
if (path.EndsWith(".jsx") || path.EndsWith(".tsx"))
{
context.Response.ContentType = "application/javascript";
}
await next();
});
// Configure Web API for Self-Host (정적 파일 후에 설정)
HttpConfiguration config = new HttpConfiguration();

View File

@@ -7,7 +7,7 @@
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="version" content="v2.0-20250127">
<title>근태현황 대시보드</title>
<title>근태현황 대시보드*</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
@@ -218,105 +218,6 @@
</div>
</div>
<!-- 출근 대상자 모달 -->
<div id="presentUserModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="glass-effect rounded-2xl w-full max-w-4xl max-h-[80vh] overflow-hidden animate-slide-up">
<!-- 모달 헤더 -->
<div class="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 class="text-xl font-semibold text-white flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
금일 출근 대상자 목록
</h2>
<button onclick="hidePresentUserModal()" class="text-white/70 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<!-- 모달 내용 -->
<div class="overflow-x-auto max-h-[60vh] custom-scrollbar">
<table class="w-full">
<thead class="bg-white/10 sticky top-0">
<tr>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">사번</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">이름</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">공정</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">직급</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">상태</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">이메일</th>
</tr>
</thead>
<tbody id="presentUserTable" class="divide-y divide-white/10">
<!-- 데이터가 여기에 표시됩니다 -->
</tbody>
</table>
</div>
<!-- 모달 푸터 -->
<div class="px-6 py-4 border-t border-white/10 flex justify-between items-center">
<p class="text-white/70 text-sm"><span id="presentUserCount">0</span></p>
<button onclick="hidePresentUserModal()" class="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors">
닫기
</button>
</div>
</div>
</div>
</div>
<!-- 휴가요청 모달 -->
<div id="holidayRequestModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="glass-effect rounded-2xl w-full max-w-6xl max-h-[80vh] overflow-hidden animate-slide-up">
<!-- 모달 헤더 -->
<div class="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 class="text-xl font-semibold text-white flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
휴가 신청 목록
</h2>
<button onclick="hideHolidayRequestModal()" class="text-white/70 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<!-- 모달 내용 -->
<div class="overflow-x-auto max-h-[60vh] custom-scrollbar">
<table class="w-full">
<thead class="bg-white/10 sticky top-0">
<tr>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">사번</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">이름</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">항목</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">일자</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">요청일</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">요청시간</th>
<th class="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">비고</th>
</tr>
</thead>
<tbody id="holidayRequestTable" class="divide-y divide-white/10">
<!-- 데이터가 여기에 표시됩니다 -->
</tbody>
</table>
</div>
<!-- 모달 푸터 -->
<div class="px-6 py-4 border-t border-white/10 flex justify-between items-center">
<p class="text-white/70 text-sm"><span id="holidayRequestCount">0</span></p>
<button onclick="hideHolidayRequestModal()" class="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors">
닫기
</button>
</div>
</div>
</div>
</div>
<!-- 구매NR 모달 -->
<div id="purchaseNRModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">

View File

@@ -1,559 +0,0 @@
// CommonApp.jsx - React Common Code Management Component for GroupWare
const { useState, useEffect, useRef } = React;
const CommonApp = () => {
// 상태 관리
const [groupData, setGroupData] = useState([]);
const [currentData, setCurrentData] = useState([]);
const [selectedGroupCode, setSelectedGroupCode] = useState(null);
const [selectedGroupName, setSelectedGroupName] = useState('');
const [loading, setLoading] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [deleteTargetIdx, setDeleteTargetIdx] = useState(null);
const [editMode, setEditMode] = useState('add');
// 편집 폼 데이터
const [editData, setEditData] = useState({
idx: '',
grp: '',
code: '',
svalue: '',
ivalue: '',
fvalue: '',
svalue2: '',
memo: ''
});
// 페이지 로드시 초기 데이터 로드
useEffect(() => {
loadGroups();
}, []);
// API 호출 함수들
const loadGroups = async () => {
setLoading(true);
try {
const response = await fetch('http://127.0.0.1:7979/Common/GetGroups');
const data = await response.json();
setGroupData(data || []);
} catch (error) {
console.error('그룹 데이터 로드 중 오류 발생:', error);
showNotification('그룹 데이터 로드 중 오류가 발생했습니다.', 'error');
} finally {
setLoading(false);
}
};
const loadDataByGroup = async (grp) => {
setLoading(true);
try {
let url = 'http://127.0.0.1:7979/Common/GetList';
if (grp) {
url += '?grp=' + encodeURIComponent(grp);
}
const response = await fetch(url);
const data = await response.json();
setCurrentData(data || []);
} catch (error) {
console.error('데이터 로드 중 오류 발생:', error);
showNotification('데이터 로드 중 오류가 발생했습니다.', 'error');
} finally {
setLoading(false);
}
};
const saveData = async () => {
try {
const data = {
idx: parseInt(editData.idx) || 0,
grp: editData.grp,
code: editData.code,
svalue: editData.svalue,
ivalue: parseInt(editData.ivalue) || 0,
fvalue: parseFloat(editData.fvalue) || 0.0,
svalue2: editData.svalue2,
memo: editData.memo
};
setLoading(true);
const response = await fetch('http://127.0.0.1:7979/Common/Save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.Success) {
showNotification(result.Message, 'success');
setShowEditModal(false);
if (selectedGroupCode) {
loadDataByGroup(selectedGroupCode);
}
} else {
showNotification(result.Message, 'error');
}
} catch (error) {
console.error('저장 중 오류 발생:', error);
showNotification('저장 중 오류가 발생했습니다.', 'error');
} finally {
setLoading(false);
}
};
const deleteItem = async () => {
if (!deleteTargetIdx) return;
try {
setLoading(true);
const response = await fetch('http://127.0.0.1:7979/Common/Delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ idx: deleteTargetIdx })
});
const data = await response.json();
if (data.Success) {
showNotification(data.Message, 'success');
setShowDeleteModal(false);
if (selectedGroupCode) {
loadDataByGroup(selectedGroupCode);
}
} else {
showNotification(data.Message, 'error');
}
} catch (error) {
console.error('삭제 중 오류 발생:', error);
showNotification('삭제 중 오류가 발생했습니다.', 'error');
} finally {
setLoading(false);
setDeleteTargetIdx(null);
}
};
// 이벤트 핸들러들
const selectGroup = (code, name) => {
setSelectedGroupCode(code);
setSelectedGroupName(name);
loadDataByGroup(code);
};
const openAddModal = () => {
if (!selectedGroupCode) {
showNotification('먼저 코드그룹을 선택하세요.', 'warning');
return;
}
setEditMode('add');
setEditData({
idx: '',
grp: selectedGroupCode,
code: '',
svalue: '',
ivalue: '',
fvalue: '',
svalue2: '',
memo: ''
});
setShowEditModal(true);
};
const openEditModal = (item) => {
setEditMode('edit');
setEditData({
idx: item.idx,
grp: item.grp || '',
code: item.code || '',
svalue: item.svalue || '',
ivalue: item.ivalue || '',
fvalue: item.fvalue || '',
svalue2: item.svalue2 || '',
memo: item.memo || ''
});
setShowEditModal(true);
};
const openDeleteModal = (idx) => {
setDeleteTargetIdx(idx);
setShowDeleteModal(true);
setShowEditModal(false);
};
const closeModals = () => {
setShowEditModal(false);
setShowDeleteModal(false);
setDeleteTargetIdx(null);
};
const handleInputChange = (field, value) => {
setEditData(prev => ({ ...prev, [field]: value }));
};
const showNotification = (message, type = 'info') => {
// 기존 알림 제거
const existing = document.querySelectorAll('.notification-toast');
existing.forEach(el => el.remove());
const colors = {
info: 'bg-blue-500/90',
success: 'bg-green-500/90',
warning: 'bg-yellow-500/90',
error: 'bg-red-500/90'
};
const icons = {
info: '🔵',
success: '✅',
warning: '⚠️',
error: '❌'
};
const notification = document.createElement('div');
notification.className = `notification-toast fixed top-4 right-4 ${colors[type]} backdrop-blur-sm text-white px-4 py-3 rounded-lg z-50 shadow-lg border border-white/20`;
notification.innerHTML = `<div class="flex items-center"><span class="mr-2">${icons[type]}</span>${message}</div>`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
// 키보드 이벤트
useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
closeModals();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, []);
return (
<div className="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
{/* Navigation Component */}
<CommonNavigation currentPage="common" />
<div className="container mx-auto px-4 py-8">
{/* 2열 구조 메인 컨테이너 */}
<div className="flex gap-6 h-[calc(100vh-200px)]">
{/* 좌측: 코드그룹 리스트 */}
<div className="w-80">
<div className="glass-effect rounded-2xl h-full card-hover animate-slide-up flex flex-col">
<div className="p-4 border-b border-white/10">
<h3 className="text-lg font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 11H5m14-7l-7 7-7-7M19 21l-7-7-7 7"></path>
</svg>
코드그룹 목록
</h3>
</div>
<div className="flex-1 overflow-y-auto custom-scrollbar p-2">
<div className="space-y-1">
{groupData.length === 0 ? (
<div className="text-white/70 text-center py-4">그룹 데이터가 없습니다.</div>
) : (
groupData.map(group => (
<div
key={group.code}
className={`cursor-pointer p-3 rounded-lg border border-white/20 hover:bg-white/10 transition-all ${
selectedGroupCode === group.code ? 'bg-white/20' : ''
}`}
onClick={() => selectGroup(group.code, group.memo)}
>
<div className="flex items-center">
<div className="w-8 h-8 bg-white/20 rounded-full flex items-center justify-center mr-3">
<span className="text-white text-sm font-medium">{group.code}</span>
</div>
<div className="flex-1">
<div className="text-white font-medium">{group.memo}</div>
</div>
</div>
</div>
))
)}
</div>
</div>
</div>
</div>
{/* 우측: 상세 데이터 */}
<div className="flex-1">
<div className="glass-effect rounded-2xl h-full card-hover animate-slide-up flex flex-col">
{/* 상단 헤더 */}
<div className="p-4 border-b border-white/10 flex items-center justify-between">
<div>
<h3 className="text-lg font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<span>
{selectedGroupCode ? `${selectedGroupCode} - ${selectedGroupName}` : '코드그룹을 선택하세요'}
</span>
</h3>
<p className="text-white/70 text-sm mt-1">
<span className="text-white font-medium">{currentData.length}</span>
</p>
</div>
<button
onClick={openAddModal}
className="bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white px-4 py-2 rounded-lg transition-all border border-white/30 flex items-center text-sm"
>
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
추가
</button>
</div>
{/* 데이터 테이블 */}
<div className="flex-1 overflow-x-auto overflow-y-auto custom-scrollbar">
<table className="w-full">
<thead className="bg-white/10 sticky top-0">
<tr>
<th className="w-24 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">코드</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">비고</th>
<th className="w-32 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">(문자열)</th>
<th className="w-20 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">(숫자)</th>
<th className="w-20 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">(실수)</th>
<th className="w-24 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">값2</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{currentData.length === 0 ? (
<tr>
<td colSpan="6" className="px-4 py-8 text-center text-white/70">
<svg className="w-12 h-12 mx-auto mb-2 text-white/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
{selectedGroupCode ? '데이터가 없습니다.' : '좌측에서 코드그룹을 선택하세요'}
</td>
</tr>
) : (
currentData.map(item => (
<tr
key={item.idx}
className="hover:bg-white/5 transition-colors cursor-pointer"
onClick={() => openEditModal(item)}
>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.code || '-'}</td>
<td className="px-4 py-4 text-sm text-white">{item.memo || '-'}</td>
<td className="px-4 py-4 text-sm text-white max-w-32 truncate" title={item.svalue || '-'}>
{item.svalue || '-'}
</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.ivalue || '0'}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.fvalue || '0.0'}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.svalue2 || '-'}</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
</div>
{/* 로딩 인디케이터 */}
{loading && (
<div className="fixed top-4 right-4 bg-white/20 backdrop-blur-sm rounded-full px-4 py-2 text-white text-sm z-40">
<div className="flex items-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
데이터 로딩 ...
</div>
</div>
)}
</div>
{/* 추가/편집 모달 */}
{showEditModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-2xl animate-slide-up">
{/* 모달 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white">
{editMode === 'add' ? '공용코드 추가' : '공용코드 편집'}
</h2>
<button
onClick={closeModals}
className="text-white/70 hover:text-white transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
{/* 모달 내용 */}
<div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-white/70 mb-2">코드그룹 *</label>
<select
value={editData.grp}
onChange={(e) => handleInputChange('grp', e.target.value)}
required
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
>
<option value="" className="bg-gray-800 text-white">선택하세요</option>
{groupData.map(group => (
<option key={group.code} value={group.code} className="bg-gray-800 text-white">
{group.code}-{group.memo}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">코드 *</label>
<input
type="text"
value={editData.code}
onChange={(e) => handleInputChange('code', e.target.value)}
required
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="코드를 입력하세요"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">(문자열)</label>
<input
type="text"
value={editData.svalue}
onChange={(e) => handleInputChange('svalue', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="문자열 값"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">(숫자)</label>
<input
type="number"
value={editData.ivalue}
onChange={(e) => handleInputChange('ivalue', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="숫자 값"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">(실수)</label>
<input
type="number"
step="0.01"
value={editData.fvalue}
onChange={(e) => handleInputChange('fvalue', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="실수 값"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">값2</label>
<input
type="text"
value={editData.svalue2}
onChange={(e) => handleInputChange('svalue2', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="추가 문자열 값"
/>
</div>
</div>
<div className="mt-4">
<label className="block text-sm font-medium text-white/70 mb-2">비고</label>
<textarea
value={editData.memo}
onChange={(e) => handleInputChange('memo', e.target.value)}
rows="3"
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="비고사항을 입력하세요"
></textarea>
</div>
</div>
{/* 모달 푸터 */}
<div className="px-6 py-4 border-t border-white/10 flex justify-between">
{editMode === 'edit' && (
<button
onClick={() => openDeleteModal(editData.idx)}
className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg transition-colors flex items-center"
>
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
삭제
</button>
)}
<div className="flex gap-2 ml-auto">
<button
onClick={closeModals}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
취소
</button>
<button
onClick={saveData}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
저장
</button>
</div>
</div>
</div>
</div>
</div>
)}
{/* 삭제 확인 모달 */}
{showDeleteModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-md animate-slide-up">
<div className="p-6">
<div className="flex items-center mb-4">
<div className="w-12 h-12 bg-red-100/20 rounded-full flex items-center justify-center mr-4">
<svg className="w-6 h-6 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
</div>
<div>
<h3 className="text-lg font-medium text-white">삭제 확인</h3>
<p className="text-sm text-white/70"> 작업은 되돌릴 없습니다.</p>
</div>
</div>
<p className="text-white/80 mb-6">
선택한 공용코드를 삭제하시겠습니까?<br/>
<span className="text-sm text-white/60"> 작업은 되돌릴 없습니다.</span>
</p>
<div className="flex justify-end gap-2">
<button
onClick={closeModals}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
취소
</button>
<button
onClick={deleteItem}
className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg transition-colors"
>
삭제
</button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
);
};

View File

@@ -1,590 +0,0 @@
const { useState, useEffect } = React;
function CommonCode() {
// 상태 관리
const [currentData, setCurrentData] = useState([]);
const [groupData, setGroupData] = useState([]);
const [selectedGroupCode, setSelectedGroupCode] = useState(null);
const [selectedGroupName, setSelectedGroupName] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [deleteTargetIdx, setDeleteTargetIdx] = useState(null);
const [editData, setEditData] = useState({
idx: '',
grp: '',
code: '',
svalue: '',
ivalue: '',
fvalue: '',
svalue2: '',
memo: ''
});
const [isEditMode, setIsEditMode] = useState(false);
// 컴포넌트 마운트 시 초기 데이터 로드
useEffect(() => {
loadGroups();
}, []);
// 코드그룹 목록 로드
const loadGroups = async () => {
setIsLoading(true);
try {
const response = await fetch('/Common/GetGroups');
const data = await response.json();
setGroupData(data || []);
} catch (error) {
console.error('그룹 데이터 로드 중 오류 발생:', error);
showNotification('그룹 데이터 로드 중 오류가 발생했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 그룹 선택 처리
const selectGroup = async (code, name) => {
setSelectedGroupCode(code);
setSelectedGroupName(name);
await loadDataByGroup(code);
};
// 특정 그룹의 데이터 로드
const loadDataByGroup = async (grp) => {
setIsLoading(true);
try {
let url = '/Common/GetList';
if (grp) {
url += '?grp=' + encodeURIComponent(grp);
}
const response = await fetch(url);
const data = await response.json();
setCurrentData(data || []);
} catch (error) {
console.error('데이터 로드 중 오류 발생:', error);
showNotification('데이터 로드 중 오류가 발생했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 추가 모달 표시
const showAddModal = () => {
if (!selectedGroupCode) {
showNotification('먼저 코드그룹을 선택하세요.', 'warning');
return;
}
setIsEditMode(false);
setEditData({
idx: '',
grp: selectedGroupCode,
code: '',
svalue: '',
ivalue: '',
fvalue: '',
svalue2: '',
memo: ''
});
setShowEditModal(true);
};
// 편집 모달 표시
const editItem = (idx) => {
const item = currentData.find(x => x.idx === idx);
if (!item) return;
setIsEditMode(true);
setEditData({
idx: item.idx,
grp: item.grp || '',
code: item.code || '',
svalue: item.svalue || '',
ivalue: item.ivalue || '',
fvalue: item.fvalue || '',
svalue2: item.svalue2 || '',
memo: item.memo || ''
});
setShowEditModal(true);
};
// 데이터 저장
const saveData = async () => {
const form = document.getElementById('editForm');
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const data = {
idx: parseInt(editData.idx) || 0,
grp: editData.grp,
code: editData.code,
svalue: editData.svalue,
ivalue: parseInt(editData.ivalue) || 0,
fvalue: parseFloat(editData.fvalue) || 0.0,
svalue2: editData.svalue2,
memo: editData.memo
};
setIsLoading(true);
try {
const response = await fetch('/Common/Save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.Success) {
showNotification(result.Message, 'success');
setShowEditModal(false);
if (selectedGroupCode) {
await loadDataByGroup(selectedGroupCode);
}
} else {
showNotification(result.Message, 'error');
}
} catch (error) {
console.error('저장 중 오류 발생:', error);
showNotification('저장 중 오류가 발생했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 삭제 확인
const confirmDelete = async () => {
if (!deleteTargetIdx) return;
setIsLoading(true);
try {
const response = await fetch('/Common/Delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ idx: deleteTargetIdx })
});
const data = await response.json();
if (data.Success) {
showNotification(data.Message, 'success');
setShowDeleteModal(false);
setDeleteTargetIdx(null);
if (selectedGroupCode) {
await loadDataByGroup(selectedGroupCode);
}
} else {
showNotification(data.Message, 'error');
}
} catch (error) {
console.error('삭제 중 오류 발생:', error);
showNotification('삭제 중 오류가 발생했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 삭제 요청
const deleteCurrentItem = () => {
if (!editData.idx) {
showNotification('삭제할 항목이 없습니다.', 'warning');
return;
}
setDeleteTargetIdx(parseInt(editData.idx));
setShowEditModal(false);
setShowDeleteModal(true);
};
// 알림 표시 함수
const showNotification = (message, type = 'info') => {
const colors = {
info: 'bg-blue-500/90 backdrop-blur-sm',
success: 'bg-green-500/90 backdrop-blur-sm',
warning: 'bg-yellow-500/90 backdrop-blur-sm',
error: 'bg-red-500/90 backdrop-blur-sm'
};
const icons = {
info: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
),
success: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
),
warning: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
),
error: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
)
};
// React에서는 실제 DOM 조작 대신 Toast 라이브러리나 상태로 관리하는 것이 좋습니다
// 여기서는 기존 방식을 유지하되 React 컴포넌트 스타일로 구현
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-4 py-3 rounded-lg z-50 transition-all duration-300 transform translate-x-0 opacity-100 shadow-lg border border-white/20`;
notification.innerHTML = `
<div class="flex items-center">
${type === 'info' ? '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>' : ''}
${type === 'success' ? '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>' : ''}
${type === 'warning' ? '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z"></path></svg>' : ''}
${type === 'error' ? '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>' : ''}
<span class="ml-2">${message}</span>
</div>
`;
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(0)';
notification.style.opacity = '1';
}, 10);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
// 키보드 이벤트 핸들러
useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
setShowEditModal(false);
setShowDeleteModal(false);
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, []);
// 입력 필드 변경 핸들러
const handleInputChange = (field, value) => {
setEditData(prev => ({ ...prev, [field]: value }));
};
return (
<div className="container mx-auto px-4 py-8">
{/* 2열 구조 메인 컨테이너 */}
<div className="flex gap-6 h-[calc(100vh-200px)]">
{/* 좌측: 코드그룹 리스트 */}
<div className="w-80">
<div className="glass-effect rounded-2xl h-full card-hover animate-slide-up flex flex-col">
<div className="p-4 border-b border-white/10">
<h3 className="text-lg font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 11H5m14-7l-7 7-7-7M19 21l-7-7-7 7"></path>
</svg>
코드그룹 목록
</h3>
</div>
<div className="flex-1 overflow-y-auto custom-scrollbar p-2">
<div className="space-y-1">
{groupData.length === 0 ? (
<div className="text-white/70 text-center py-4">그룹 데이터가 없습니다.</div>
) : (
groupData.map(group => (
<div
key={group.code}
className={`group-item cursor-pointer p-3 rounded-lg border border-white/20 hover:bg-white/10 transition-all ${
selectedGroupCode === group.code ? 'bg-white/20' : ''
}`}
onClick={() => selectGroup(group.code, group.memo)}
>
<div className="flex items-center">
<div className="w-8 h-8 bg-white/20 rounded-full flex items-center justify-center mr-3">
<span className="text-white text-sm font-medium">{group.code}</span>
</div>
<div className="flex-1">
<div className="text-white font-medium">{group.memo}</div>
</div>
</div>
</div>
))
)}
</div>
</div>
</div>
</div>
{/* 우측: 상세 데이터 */}
<div className="flex-1">
<div className="glass-effect rounded-2xl h-full card-hover animate-slide-up flex flex-col">
{/* 상단 헤더 */}
<div className="p-4 border-b border-white/10 flex items-center justify-between">
<div>
<h3 className="text-lg font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<span>{selectedGroupCode ? `${selectedGroupCode} - ${selectedGroupName}` : '코드그룹을 선택하세요'}</span>
</h3>
<p className="text-white/70 text-sm mt-1"> <span className="text-white font-medium">{currentData.length}</span></p>
</div>
<button
onClick={showAddModal}
className="bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white px-4 py-2 rounded-lg transition-all border border-white/30 flex items-center text-sm"
>
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
추가
</button>
</div>
{/* 데이터 테이블 */}
<div className="flex-1 overflow-x-auto overflow-y-auto custom-scrollbar">
<table className="w-full">
<thead className="bg-white/10 sticky top-0">
<tr>
<th className="w-24 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">코드</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">비고</th>
<th className="w-32 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">(문자열)</th>
<th className="w-20 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">(숫자)</th>
<th className="w-20 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">(실수)</th>
<th className="w-24 px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">값2</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{currentData.length === 0 ? (
<tr>
<td colSpan="6" className="px-4 py-8 text-center text-white/70">
<svg className="w-12 h-12 mx-auto mb-2 text-white/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
{selectedGroupCode ? '데이터가 없습니다.' : '좌측에서 코드그룹을 선택하세요'}
</td>
</tr>
) : (
currentData.map(item => (
<tr
key={item.idx}
className="hover:bg-white/5 transition-colors cursor-pointer"
onClick={() => editItem(item.idx)}
>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.code || '-'}</td>
<td className="px-4 py-4 text-sm text-white">{item.memo || '-'}</td>
<td className="px-4 py-4 text-sm text-white svalue-cell" title={item.svalue || '-'}>{item.svalue || '-'}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.ivalue || '0'}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.fvalue || '0.0'}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-white">{item.svalue2 || '-'}</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
</div>
{/* 로딩 인디케이터 */}
{isLoading && (
<div className="fixed top-4 right-4 bg-white/20 backdrop-blur-sm rounded-full px-4 py-2 text-white text-sm">
<div className="flex items-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
데이터 로딩 ...
</div>
</div>
)}
{/* 추가/편집 모달 */}
{showEditModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-2xl animate-slide-up">
{/* 모달 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white">
{isEditMode ? '공용코드 편집' : '공용코드 추가'}
</h2>
<button
onClick={() => setShowEditModal(false)}
className="text-white/70 hover:text-white transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
{/* 모달 내용 */}
<form id="editForm" className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-white/70 mb-2">코드그룹 *</label>
<select
value={editData.grp}
onChange={(e) => handleInputChange('grp', e.target.value)}
required
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
>
<option value="" className="bg-gray-800 text-white">선택하세요</option>
{groupData.map(group => (
<option key={group.code} value={group.code} className="bg-gray-800 text-white">
{group.code}-{group.memo}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">코드 *</label>
<input
type="text"
value={editData.code}
onChange={(e) => handleInputChange('code', e.target.value)}
required
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="코드를 입력하세요"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">(문자열)</label>
<input
type="text"
value={editData.svalue}
onChange={(e) => handleInputChange('svalue', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="문자열 값"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">(숫자)</label>
<input
type="number"
value={editData.ivalue}
onChange={(e) => handleInputChange('ivalue', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="숫자 값"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">(실수)</label>
<input
type="number"
step="0.01"
value={editData.fvalue}
onChange={(e) => handleInputChange('fvalue', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="실수 값"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/70 mb-2">값2</label>
<input
type="text"
value={editData.svalue2}
onChange={(e) => handleInputChange('svalue2', e.target.value)}
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="추가 문자열 값"
/>
</div>
</div>
<div className="mt-4">
<label className="block text-sm font-medium text-white/70 mb-2">비고</label>
<textarea
value={editData.memo}
onChange={(e) => handleInputChange('memo', e.target.value)}
rows="3"
className="w-full px-3 py-2 bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="비고사항을 입력하세요"
></textarea>
</div>
</form>
{/* 모달 푸터 */}
<div className="px-6 py-4 border-t border-white/10 flex justify-between">
<button
onClick={deleteCurrentItem}
disabled={!isEditMode}
className={`bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg transition-colors flex items-center ${
!isEditMode ? 'opacity-50 cursor-not-allowed' : ''
}`}
>
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
삭제
</button>
<div className="flex gap-2">
<button
onClick={() => setShowEditModal(false)}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
취소
</button>
<button
onClick={saveData}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
저장
</button>
</div>
</div>
</div>
</div>
</div>
)}
{/* 삭제 확인 모달 */}
{showDeleteModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-md animate-slide-up">
<div className="p-6">
<div className="flex items-center mb-4">
<div className="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center mr-4">
<svg className="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
</div>
<div>
<h3 className="text-lg font-medium text-gray-900">삭제 확인</h3>
<p className="text-sm text-gray-500"> 작업은 되돌릴 없습니다.</p>
</div>
</div>
<p className="text-gray-700 mb-6">
선택한 공용코드를 삭제하시겠습니까?<br />
<span className="text-sm text-gray-500"> 작업은 되돌릴 없습니다.</span>
</p>
<div className="flex justify-end gap-2">
<button
onClick={() => { setShowDeleteModal(false); setDeleteTargetIdx(null); }}
className="bg-gray-300 hover:bg-gray-400 text-gray-700 px-4 py-2 rounded-lg transition-colors"
>
취소
</button>
<button
onClick={confirmDelete}
className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg transition-colors"
>
삭제
</button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
);
}

View File

@@ -1,166 +0,0 @@
// CommonNavigation.jsx - React Navigation Component for GroupWare
const CommonNavigation = ({ currentPage = 'dashboard' }) => {
const [menuItems, setMenuItems] = useState([]);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [currentUser, setCurrentUser] = useState('사용자');
// 기본 메뉴 아이템 - React 경로 사용
const defaultMenuItems = [
{
key: 'dashboard',
title: '대시보드',
url: '/react/dashboard',
icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z',
isVisible: true,
sortOrder: 1
},
{
key: 'common',
title: '공용코드',
url: '/react/common',
icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z',
isVisible: true,
sortOrder: 2
},
{
key: 'jobreport',
title: '업무일지',
url: '/react/jobreport',
icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2',
isVisible: true,
sortOrder: 3
},
{
key: 'kuntae',
title: '근태관리',
url: '/react/kuntae',
icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
isVisible: true,
sortOrder: 4
},
{
key: 'todo',
title: '할일관리',
url: '/react/todo',
icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4',
isVisible: true,
sortOrder: 5
},
{
key: 'project',
title: '프로젝트',
url: '/react/project',
icon: 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10',
isVisible: true,
sortOrder: 6
},
{
key: 'purchase',
title: '구매관리',
url: '/Purchase/',
icon: 'M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z',
isVisible: true,
sortOrder: 7
},
{
key: 'customer',
title: '고객관리',
url: '/Customer/',
icon: 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z',
isVisible: true,
sortOrder: 8
}
];
// 메뉴 아이템 로드 - defaultMenuItems 사용 (React 경로)
useEffect(() => {
setMenuItems(defaultMenuItems);
}, []);
// 보이는 메뉴 아이템만 정렬해서 반환
const visibleItems = menuItems
.filter(item => item.isVisible)
.sort((a, b) => a.sortOrder - b.sortOrder);
return (
<nav className="glass-effect border-b border-white/10 relative z-40">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
{/* 로고 및 브랜드 */}
<div className="flex items-center space-x-8">
<div className="flex items-center space-x-2">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z"></path>
</svg>
<span className="text-xl font-bold text-white">GroupWare</span>
</div>
{/* 데스크톱 메뉴 */}
<nav className="hidden md:flex space-x-1">
{visibleItems.map(item => (
<a
key={item.key}
href={item.url}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
currentPage === item.key
? 'bg-white/20 text-white'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}
>
<svg className="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={item.icon}></path>
</svg>
{item.title}
</a>
))}
</nav>
</div>
{/* 우측 메뉴 */}
<div className="flex items-center space-x-4">
<div className="text-sm text-white/60">
<span>{currentUser}</span>
</div>
{/* 모바일 메뉴 버튼 */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden text-white/60 hover:text-white focus:outline-none focus:text-white"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{mobileMenuOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16"></path>
)}
</svg>
</button>
</div>
</div>
{/* 모바일 메뉴 */}
{mobileMenuOpen && (
<div className="md:hidden border-t border-white/10 py-2">
{visibleItems.map(item => (
<a
key={item.key}
href={item.url}
className={`block px-3 py-2 text-sm font-medium transition-colors ${
currentPage === item.key
? 'bg-white/20 text-white'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}
onClick={() => setMobileMenuOpen(false)}
>
<svg className="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={item.icon}></path>
</svg>
{item.title}
</a>
))}
</div>
)}
</div>
</nav>
);
};

View File

@@ -1,669 +0,0 @@
// DashboardApp.jsx - React Dashboard Component for GroupWare
const { useState, useEffect, useRef } = React;
function DashboardApp() {
// 상태 관리
const [dashboardData, setDashboardData] = useState({
presentCount: 0,
leaveCount: 0,
leaveRequestCount: 0,
purchaseCountNR: 0,
purchaseCountCR: 0
});
const [isLoading, setIsLoading] = useState(true);
const [lastUpdated, setLastUpdated] = useState('');
const [modals, setModals] = useState({
presentUsers: false,
holidayUsers: false,
holidayRequest: false,
purchaseNR: false,
purchaseCR: false
});
const [modalData, setModalData] = useState({
presentUsers: [],
holidayUsers: [],
holidayRequests: [],
purchaseNR: [],
purchaseCR: []
});
const [todoList, setTodoList] = useState([]);
// 모달 제어 함수
const showModal = (modalName) => {
setModals(prev => ({ ...prev, [modalName]: true }));
loadModalData(modalName);
};
const hideModal = (modalName) => {
setModals(prev => ({ ...prev, [modalName]: false }));
};
// Dashboard 데이터 로드
const loadDashboardData = async () => {
setIsLoading(true);
try {
// 실제 DashBoardController API 호출
const [
currentUserResponse,
leaveCountResponse,
holyRequestResponse,
purchaseWaitResponse
] = await Promise.all([
fetch('http://127.0.0.1:7979/DashBoard/GetCurrentUserCount'),
fetch('http://127.0.0.1:7979/DashBoard/TodayCountH'),
fetch('http://127.0.0.1:7979/DashBoard/GetHolydayRequestCount'),
fetch('http://127.0.0.1:7979/DashBoard/GetPurchaseWaitCount')
]);
// 현재 근무자 수 (JSON 응답)
const currentUserData = await currentUserResponse.json();
const presentCount = currentUserData.Count || 0;
// 휴가자 수 (텍스트 응답)
const leaveCountText = await leaveCountResponse.text();
const leaveCount = parseInt(leaveCountText.replace(/"/g, ''), 10) || 0;
// 휴가 요청 수 (JSON 응답)
const holyRequestData = await holyRequestResponse.json();
const leaveRequestCount = holyRequestData.HOLY || 0;
// 구매 대기 수 (JSON 응답)
const purchaseWaitData = await purchaseWaitResponse.json();
const purchaseCountNR = purchaseWaitData.NR || 0;
const purchaseCountCR = purchaseWaitData.CR || 0;
setDashboardData({
presentCount,
leaveCount,
leaveRequestCount,
purchaseCountNR,
purchaseCountCR
});
setLastUpdated(new Date().toLocaleString('ko-KR'));
} catch (error) {
console.error('대시보드 데이터 로드 실패:', error);
} finally {
setIsLoading(false);
}
};
// 모달 데이터 로드
const loadModalData = async (modalName) => {
try {
let endpoint = '';
switch (modalName) {
case 'presentUsers':
endpoint = 'http://127.0.0.1:7979/DashBoard/GetPresentUserList';
break;
case 'holidayUsers':
endpoint = 'http://127.0.0.1:7979/DashBoard/GetholyUser';
break;
case 'holidayRequest':
endpoint = 'http://127.0.0.1:7979/DashBoard/GetholyRequestUser';
break;
case 'purchaseNR':
endpoint = 'http://127.0.0.1:7979/DashBoard/GetPurchaseNRList';
break;
case 'purchaseCR':
endpoint = 'http://127.0.0.1:7979/DashBoard/GetPurchaseCRList';
break;
default:
return;
}
const response = await fetch(endpoint);
const data = await response.json();
setModalData(prev => ({
...prev,
[modalName]: Array.isArray(data) ? data : []
}));
} catch (error) {
console.error(`모달 데이터 로드 실패 (${modalName}):`, error);
setModalData(prev => ({
...prev,
[modalName]: []
}));
}
};
// Todo 목록 로드
const loadTodoList = async () => {
try {
const response = await fetch('http://127.0.0.1:7979/Todo/GetUrgentTodos');
const data = await response.json();
if (data.Success && data.Data) {
setTodoList(data.Data);
} else {
setTodoList([]);
}
} catch (error) {
console.error('Todo 목록 로드 실패:', error);
setTodoList([]);
}
};
// 자동 새로고침 (30초마다)
useEffect(() => {
loadDashboardData();
loadModalData('holidayUsers'); // 휴가자 목록 자동 로드
loadTodoList(); // Todo 목록 자동 로드
const interval = setInterval(() => {
loadDashboardData();
loadModalData('holidayUsers'); // 30초마다 휴가자 목록도 새로고침
loadTodoList(); // 30초마다 Todo 목록도 새로고침
}, 30000); // 30초
return () => clearInterval(interval);
}, []);
// 통계 카드 컴포넌트
const StatCard = ({ title, count, icon, color, onClick, isClickable = false }) => {
return (
<div
className={`glass-effect rounded-2xl p-6 card-hover animate-slide-up ${isClickable ? 'cursor-pointer' : ''}`}
onClick={onClick}
>
<div className="flex items-center justify-between">
<div>
<p className="text-white/70 text-sm font-medium">{title}</p>
<p className="text-3xl font-bold text-white">
{isLoading ? (
<div className="animate-pulse bg-white/20 h-8 w-16 rounded"></div>
) : count}
</p>
</div>
<div className={`w-12 h-12 ${color}/20 rounded-full flex items-center justify-center`}>
{icon}
</div>
</div>
</div>
);
};
// 테이블 모달 컴포넌트
const TableModal = ({ isOpen, onClose, title, headers, data, renderRow, maxWidth = 'max-w-4xl' }) => {
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className={`glass-effect rounded-2xl w-full ${maxWidth} max-h-[80vh] overflow-hidden animate-slide-up`}>
{/* 모달 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
{title}
</h2>
<button
onClick={onClose}
className="text-white/70 hover:text-white transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
{/* 모달 내용 */}
<div className="overflow-x-auto max-h-[60vh] custom-scrollbar">
<table className="w-full">
<thead className="bg-white/10 sticky top-0">
<tr>
{headers.map((header, index) => (
<th key={index} className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">
{header}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{data.length > 0 ? (
data.map((item, index) => renderRow(item, index))
) : (
<tr>
<td colSpan={headers.length} className="px-6 py-8 text-center text-white/50">
데이터가 없습니다.
</td>
</tr>
)}
</tbody>
</table>
</div>
{/* 모달 푸터 */}
<div className="px-6 py-4 border-t border-white/10 flex justify-between items-center">
<p className="text-white/70 text-sm">
<span className="font-medium">{data.length}</span>
</p>
<button
onClick={onClose}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
닫기
</button>
</div>
</div>
</div>
</div>
);
};
return (
<div className="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<div className="container mx-auto px-4 py-8">
{/* 헤더 */}
<div className="text-center mb-8 animate-fade-in">
<h1 className="text-4xl font-bold mb-4">근태현황 대시보드</h1>
<p className="text-white/70">실시간 근태 업무 현황을 확인하세요</p>
{lastUpdated && (
<p className="text-white/50 text-sm mt-2">마지막 업데이트: {lastUpdated}</p>
)}
</div>
{/* 새로고침 버튼 */}
<div className="flex justify-end mb-6">
<button
onClick={loadDashboardData}
disabled={isLoading}
className="glass-effect rounded-lg px-4 py-2 text-white hover:bg-white/10 transition-all duration-300 disabled:opacity-50"
>
{isLoading ? (
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2 inline-block"></div>
) : (
<svg className="w-4 h-4 mr-2 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
)}
새로고침
</button>
</div>
{/* 통계 카드 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8">
<StatCard
title="출근(대상)"
count={dashboardData.presentCount}
isClickable={true}
onClick={() => showModal('presentUsers')}
color="bg-success-500"
icon={
<svg className="w-6 h-6 text-success-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
}
/>
<StatCard
title="휴가"
count={dashboardData.leaveCount}
isClickable={true}
onClick={() => showModal('holidayUsers')}
color="bg-warning-500"
icon={
<svg className="w-6 h-6 text-warning-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
}
/>
<StatCard
title="휴가요청"
count={dashboardData.leaveRequestCount}
isClickable={true}
onClick={() => showModal('holidayRequest')}
color="bg-primary-500"
icon={
<svg className="w-6 h-6 text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
}
/>
<StatCard
title="구매요청(NR)"
count={dashboardData.purchaseCountNR}
isClickable={true}
onClick={() => showModal('purchaseNR')}
color="bg-danger-500"
icon={
<svg className="w-6 h-6 text-danger-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"></path>
</svg>
}
/>
<StatCard
title="구매요청(CR)"
count={dashboardData.purchaseCountCR}
isClickable={true}
onClick={() => showModal('purchaseCR')}
color="bg-purple-500"
icon={
<svg className="w-6 h-6 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"></path>
</svg>
}
/>
</div>
{/* 2칸 레이아웃: 좌측 휴가현황, 우측 할일 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 animate-slide-up">
{/* 좌측: 휴가/기타 현황 */}
<div className="glass-effect rounded-2xl overflow-hidden">
<div className="px-6 py-4 border-b border-white/10">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
휴가/기타 현황
</h2>
</div>
<div className="overflow-x-auto max-h-[460px] custom-scrollbar">
<table className="w-full">
<thead className="bg-white/10 sticky top-0">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">이름</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">형태</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">종류</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">기간</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase tracking-wider">사유</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{modalData.holidayUsers.map((user, index) => {
// 형태에 따른 색상 결정
const typeColorClass = (user.type === '휴가') ? 'bg-green-500/20 text-green-300' : 'bg-warning-500/20 text-warning-300';
// 종류에 따른 색상 결정
let cateColorClass = 'bg-warning-500/20 text-warning-300'; // 기본값
if (user.cate === '휴가') {
cateColorClass = 'bg-warning-500/20 text-warning-300'; // 노란색 계열
} else if (user.cate === '파견') {
cateColorClass = 'bg-purple-500/20 text-purple-300'; // 보라색 계열
} else {
cateColorClass = 'bg-warning-500/20 text-warning-300'; // 기타는 주황색 계열
}
// 기간 표시 형식 개선
let periodText = '';
if (user.sdate && user.edate) {
if (user.sdate === user.edate) {
periodText = user.sdate;
} else {
periodText = `${user.sdate}~${user.edate}`;
}
} else {
periodText = '-';
}
return (
<tr key={index} className="hover:bg-white/5">
<td className="px-4 py-3 text-white text-sm font-medium">{user.name || '이름 없음'}</td>
<td className="px-4 py-3">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${typeColorClass}`}>
{user.type || 'N/A'}
</span>
</td>
<td className="px-4 py-3">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${cateColorClass}`}>
{user.cate || '종류 없음'}
</span>
</td>
<td className="px-4 py-3 text-white text-sm">{periodText}</td>
<td className="px-4 py-3 text-white/70 text-sm">{user.title || '사유 없음'}</td>
</tr>
);
})}
{modalData.holidayUsers.length === 0 && (
<tr>
<td colSpan="5" className="px-4 py-8 text-center text-white/50">
현재 휴가자가 없습니다
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
{/* 우측: 할일 */}
<div className="glass-effect rounded-2xl overflow-hidden">
<div className="px-6 py-4 border-b border-white/10">
<h2 className="text-xl font-semibold text-white flex items-center justify-between">
<span className="flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
</svg>
할일
</span>
<div className="flex items-center space-x-2">
<button
className="text-xs bg-primary-500/20 hover:bg-primary-500/30 text-primary-300 hover:text-primary-200 px-3 py-1 rounded-full transition-colors flex items-center"
onClick={() => alert('할일 추가 기능은 준비 중입니다.')}
>
<svg className="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
할일추가
</button>
<button
className="text-xs bg-white/20 hover:bg-white/30 px-3 py-1 rounded-full transition-colors"
onClick={() => window.location.href = '/react/todo'}
>
전체보기
</button>
</div>
</h2>
</div>
<div className="p-4">
<div className="space-y-3 max-h-[384px] overflow-y-auto custom-scrollbar">
{todoList.length > 0 ? (
todoList.map((todo, index) => {
const flagIcon = todo.flag ? '📌 ' : '';
// 상태별 클래스
const getTodoStatusClass = (status) => {
switch(status) {
case '0': return 'bg-gray-500/20 text-gray-300';
case '1': return 'bg-primary-500/20 text-primary-300';
case '2': return 'bg-danger-500/20 text-danger-300';
case '3': return 'bg-warning-500/20 text-warning-300';
case '5': return 'bg-success-500/20 text-success-300';
default: return 'bg-white/10 text-white/50';
}
};
const getTodoStatusText = (status) => {
switch(status) {
case '0': return '대기';
case '1': return '진행';
case '2': return '취소';
case '3': return '보류';
case '5': return '완료';
default: return '대기';
}
};
const getTodoSeqnoClass = (seqno) => {
switch(seqno) {
case '0': return 'bg-gray-500/20 text-gray-300';
case '1': return 'bg-success-500/20 text-success-300';
case '2': return 'bg-warning-500/20 text-warning-300';
case '3': return 'bg-danger-500/20 text-danger-300';
default: return 'bg-white/10 text-white/50';
}
};
const getTodoSeqnoText = (seqno) => {
switch(seqno) {
case '0': return '낮음';
case '1': return '보통';
case '2': return '높음';
case '3': return '긴급';
default: return '보통';
}
};
const statusClass = getTodoStatusClass(todo.status);
const statusText = getTodoStatusText(todo.status);
const seqnoClass = getTodoSeqnoClass(todo.seqno);
const seqnoText = getTodoSeqnoText(todo.seqno);
const expireText = todo.expire ? new Date(todo.expire).toLocaleDateString('ko-KR') : '';
const isExpired = todo.expire && new Date(todo.expire) < new Date();
const expireClass = isExpired ? 'text-danger-400' : 'text-white/60';
// 만료일이 지난 경우 배경을 적색계통으로 강조
const expiredBgClass = isExpired ? 'bg-danger-600/30 border-danger-400/40 hover:bg-danger-600/40' : 'bg-white/10 hover:bg-white/15 border-white/20';
return (
<div key={index} className={`${expiredBgClass} backdrop-blur-sm rounded-lg p-3 transition-colors cursor-pointer border`}
onClick={() => alert('Todo 상세보기 기능은 준비 중입니다.')}>
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-1">
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${statusClass}`}>
{statusText}
</span>
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${seqnoClass}`}>
{seqnoText}
</span>
</div>
{expireText && (
<span className={`text-xs ${expireClass}`}>
{expireText}
</span>
)}
</div>
<p className="text-white text-sm font-medium mb-1">
{flagIcon}{todo.title || '제목 없음'}
</p>
{todo.description && (
<p className="text-white/70 text-xs mb-2 line-clamp-2">
{todo.description}
</p>
)}
{todo.request && (
<p className="text-white/50 text-xs mt-2">요청자: {todo.request}</p>
)}
</div>
);
})
) : (
<div className="text-center text-white/50 py-8">
<svg className="w-8 h-8 mx-auto mb-2 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
</svg>
급한 할일이 없습니다
</div>
)}
</div>
</div>
</div>
</div>
{/* 출근 대상자 모달 */}
<TableModal
isOpen={modals.presentUsers}
onClose={() => hideModal('presentUsers')}
title="금일 출근 대상자 목록"
headers={['사번', '이름', '공정', '직급', '상태', '이메일']}
data={modalData.presentUsers}
renderRow={(user, index) => (
<tr key={index} className="hover:bg-white/5">
<td className="px-6 py-4 text-white text-sm">{user.id || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{user.name || '이름 없음'}</td>
<td className="px-6 py-4 text-white text-sm">{user.gname || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{user.level || 'N/A'}</td>
<td className="px-6 py-4 text-success-400 text-sm">출근</td>
<td className="px-6 py-4 text-white/70 text-sm">{user.email || 'N/A'}</td>
</tr>
)}
/>
{/* 휴가 요청 모달 */}
<TableModal
isOpen={modals.holidayRequest}
onClose={() => hideModal('holidayRequest')}
title="휴가 신청 목록"
maxWidth="max-w-6xl"
headers={['사번', '이름', '항목', '일자', '요청일', '요청시간', '비고']}
data={modalData.holidayRequests}
renderRow={(request, index) => (
<tr key={index} className="hover:bg-white/5">
<td className="px-6 py-4 text-white text-sm">{request.uid || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{request.name || '이름 없음'}</td>
<td className="px-6 py-4 text-white text-sm">{request.cate || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">
{request.sdate && request.edate ? `${request.sdate} ~ ${request.edate}` : 'N/A'}
</td>
<td className="px-6 py-4 text-white text-sm">{request.holydays || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{request.holytimes || 'N/A'}</td>
<td className="px-6 py-4 text-white/70 text-sm">{request.HolyReason || request.remark || 'N/A'}</td>
</tr>
)}
/>
{/* 구매요청 NR 모달 */}
<TableModal
isOpen={modals.purchaseNR}
onClose={() => hideModal('purchaseNR')}
title="구매요청(NR) 목록"
maxWidth="max-w-7xl"
headers={['요청일', '공정', '품목', '규격', '단위', '수량', '단가', '금액']}
data={modalData.purchaseNR}
renderRow={(item, index) => (
<tr key={index} className="hover:bg-white/5">
<td className="px-6 py-4 text-white text-sm">{item.pdate || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.process || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumname || '품목 없음'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumscale || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumunit || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumqtyreq || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">
{item.pumprice ? `${Number(item.pumprice).toLocaleString()}` : 'N/A'}
</td>
<td className="px-6 py-4 text-danger-400 text-sm font-medium">
{item.pumamt ? `${Number(item.pumamt).toLocaleString()}` : 'N/A'}
</td>
</tr>
)}
/>
{/* 구매요청 CR 모달 */}
<TableModal
isOpen={modals.purchaseCR}
onClose={() => hideModal('purchaseCR')}
title="구매요청(CR) 목록"
maxWidth="max-w-7xl"
headers={['요청일', '공정', '품목', '규격', '단위', '수량', '단가', '금액']}
data={modalData.purchaseCR}
renderRow={(item, index) => (
<tr key={index} className="hover:bg-white/5">
<td className="px-6 py-4 text-white text-sm">{item.pdate || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.process || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumname || '품목 없음'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumscale || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumunit || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">{item.pumqtyreq || 'N/A'}</td>
<td className="px-6 py-4 text-white text-sm">
{item.pumprice ? `${Number(item.pumprice).toLocaleString()}` : 'N/A'}
</td>
<td className="px-6 py-4 text-purple-400 text-sm font-medium">
{item.pumamt ? `${Number(item.pumamt).toLocaleString()}` : 'N/A'}
</td>
</tr>
)}
/>
</div>
</div>
);
}

View File

@@ -1,21 +0,0 @@
// 개발중 경고 메시지 컴포넌트
function DevWarning({ show = false }) {
// show props가 false이거나 없으면 아무것도 렌더링하지 않음
if (!show) {
return null;
}
return (
<div className="dev-warning animate-slide-up">
<div className="flex items-center">
<svg className="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
<div>
<p className="title">🚧 개발중인 기능입니다</p>
<p className="description">일부 기능이 정상적으로 동작하지 않을 있습니다.</p>
</div>
</div>
</div>
);
}

View File

@@ -1,988 +0,0 @@
const { useState, useEffect } = React;
function JobReport() {
// 상태 관리
const [jobData, setJobData] = useState([]);
const [filteredData, setFilteredData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(25);
const [sortColumn, setSortColumn] = useState('pdate');
const [sortDirection, setSortDirection] = useState('desc');
const [isLoading, setIsLoading] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [editData, setEditData] = useState({});
const [isEditMode, setIsEditMode] = useState(false);
// 필터 상태
const [filters, setFilters] = useState({
startDate: '',
endDate: '',
status: '',
type: '',
user: '',
project: '',
search: ''
});
// 통계 데이터
const [statistics, setStatistics] = useState({
totalDays: 0,
todayHours: 0,
todayProgress: 0,
totalOT: 0,
activeProjects: 0
});
// 컴포넌트 마운트시 초기화
useEffect(() => {
initializeFilters();
loadJobData();
loadUserList();
}, []);
// 필터 변경 시 데이터 필터링
useEffect(() => {
filterData();
}, [jobData, filters]);
// 초기 필터 설정 (오늘부터 -2주)
const initializeFilters = () => {
const now = new Date();
const today = now.toISOString().split('T')[0];
const twoWeeksAgo = new Date(now.getTime() - (14 * 24 * 60 * 60 * 1000)).toISOString().split('T')[0];
setFilters(prev => ({
...prev,
startDate: twoWeeksAgo,
endDate: today
}));
};
// 업무일지 데이터 로드
const loadJobData = async () => {
setIsLoading(true);
try {
let url = '/Jobreport/GetJobData';
const params = new URLSearchParams();
if (filters.startDate) params.append('startDate', filters.startDate);
if (filters.endDate) params.append('endDate', filters.endDate);
if (filters.user) params.append('user', filters.user);
if (params.toString()) {
url += '?' + params.toString();
}
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: 데이터를 불러오는데 실패했습니다.`);
}
const responseText = await response.text();
const responseData = JSON.parse(responseText);
if (Array.isArray(responseData)) {
setJobData(responseData);
} else if (responseData.error) {
throw new Error(responseData.error);
} else {
setJobData([]);
}
} catch (error) {
console.error('Error loading job data:', error);
setJobData([]);
showNotification('데이터를 불러오는데 실패했습니다: ' + error.message, 'error');
} finally {
setIsLoading(false);
}
};
// 사용자 목록 로드
const loadUserList = async () => {
try {
const response = await fetch('/Jobreport/GetUsers');
if (response.ok) {
const data = await response.json();
// 사용자 데이터를 상태로 저장 (필요시)
}
} catch (error) {
console.error('사용자 목록 로드 중 오류:', error);
}
};
// 통계 업데이트
useEffect(() => {
updateStatistics();
}, [jobData]);
const updateStatistics = () => {
const totalDays = new Set(jobData.map(item => item.pdate)).size;
const totalOT = jobData.reduce((sum, item) => sum + (parseFloat(item.ot) || 0), 0);
const activeProjects = new Set(jobData.filter(item => item.status === '진행중').map(item => item.projectName)).size;
const now = new Date();
const today = now.toISOString().split('T')[0];
const todayData = jobData.filter(item => {
if (!item.pdate) return false;
const itemDate = item.pdate.toString();
if (itemDate.length >= 10) {
return itemDate.substring(0, 10) === today;
}
return false;
});
let todayHours = 0;
if (todayData.length > 0) {
todayHours = todayData.reduce((sum, item) => sum + (parseFloat(item.hrs) || 0), 0);
}
const todayProgress = (todayHours / 8) * 100;
setStatistics({
totalDays,
todayHours,
todayProgress,
totalOT,
activeProjects
});
};
// 데이터 필터링
const filterData = () => {
let filtered = jobData.filter(item => {
const statusMatch = !filters.status || item.status === filters.status;
const typeMatch = !filters.type || item.type === filters.type;
const projectMatch = !filters.project || item.projectName === filters.project;
const searchMatch = !filters.search ||
(item.description && item.description.toLowerCase().includes(filters.search.toLowerCase())) ||
(item.projectName && item.projectName.toLowerCase().includes(filters.search.toLowerCase())) ||
(item.requestpart && item.requestpart.toLowerCase().includes(filters.search.toLowerCase()));
return statusMatch && typeMatch && projectMatch && searchMatch;
});
// 정렬
filtered.sort((a, b) => {
let aVal = a[sortColumn];
let bVal = b[sortColumn];
if (sortColumn === 'pdate') {
aVal = new Date(aVal);
bVal = new Date(bVal);
} else if (['hrs', 'ot'].includes(sortColumn)) {
aVal = parseFloat(aVal) || 0;
bVal = parseFloat(bVal) || 0;
} else {
aVal = (aVal || '').toString().toLowerCase();
bVal = (bVal || '').toString().toLowerCase();
}
if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;
if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;
return 0;
});
setFilteredData(filtered);
setCurrentPage(1);
};
// 필터 변경 핸들러
const handleFilterChange = (field, value) => {
setFilters(prev => ({ ...prev, [field]: value }));
// 날짜 필터 변경시 데이터 다시 로드
if (field === 'startDate' || field === 'endDate' || field === 'user') {
setTimeout(() => loadJobData(), 100);
}
};
// 필터 초기화
const clearFilters = () => {
const now = new Date();
const today = now.toISOString().split('T')[0];
const twoWeeksAgo = new Date(now.getTime() - (14 * 24 * 60 * 60 * 1000)).toISOString().split('T')[0];
setFilters({
startDate: twoWeeksAgo,
endDate: today,
status: '',
type: '',
user: '',
project: '',
search: ''
});
setTimeout(() => loadJobData(), 100);
};
// 정렬 처리
const handleSort = (column) => {
if (sortColumn === column) {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortColumn(column);
setSortDirection('asc');
}
};
// 추가 모달 표시
const showAddJobModal = () => {
const today = new Date().toISOString().split('T')[0];
setIsEditMode(false);
setEditData({
idx: '',
pdate: today,
status: '진행 중',
projectName: '',
requestpart: '',
type: '',
hrs: '8',
ot: '',
otStart: '',
otEnd: '',
description: ''
});
setShowEditModal(true);
};
// 편집 모달 표시
const showEditJobModal = async (item) => {
try {
// 상세 정보 로드
const response = await fetch(`/Jobreport/GetJobDetail?id=${item.idx}`);
if (response.ok) {
const fullItem = await response.json();
item = fullItem.error ? item : fullItem;
}
} catch (error) {
console.warn('Failed to load full details, using truncated data:', error);
}
setIsEditMode(true);
setEditData({
idx: item.idx || '',
pdate: item.pdate || '',
status: item.status || '',
projectName: item.projectName || '',
requestpart: item.requestpart || '',
type: item.type || '',
hrs: item.hrs || '',
ot: item.ot || '',
otStart: item.otStart || '',
otEnd: item.otEnd || '',
description: item.description || ''
});
setShowEditModal(true);
};
// 저장 처리
const handleSave = async (event) => {
event.preventDefault();
const formData = new URLSearchParams();
Object.keys(editData).forEach(key => {
if (key !== 'idx' || editData.idx) {
formData.append(key, editData[key]);
}
});
try {
const url = isEditMode ? '/Jobreport/Edit' : '/Jobreport/Add';
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formData
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${isEditMode ? '수정' : '추가'}에 실패했습니다.`);
}
setShowEditModal(false);
await loadJobData();
showNotification(`업무일지가 성공적으로 ${isEditMode ? '수정' : '추가'}되었습니다.`, 'success');
} catch (error) {
console.error('Error saving job:', error);
showNotification(`업무일지 ${isEditMode ? '수정' : '추가'} 중 오류가 발생했습니다: ` + error.message, 'error');
}
};
// 삭제 처리
const handleDelete = async () => {
if (!editData.idx) {
showNotification('삭제할 수 없는 항목입니다.', 'warning');
return;
}
if (!window.confirm('정말로 이 업무일지를 삭제하시겠습니까?')) {
return;
}
try {
const response = await fetch(`/Jobreport/Delete/${editData.idx}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('삭제에 실패했습니다.');
}
const result = await response.json();
if (result.success) {
setShowEditModal(false);
await loadJobData();
showNotification('업무일지가 성공적으로 삭제되었습니다.', 'success');
} else {
throw new Error(result.message || '삭제에 실패했습니다.');
}
} catch (error) {
console.error('Error deleting job:', error);
showNotification('업무일지 삭제 중 오류가 발생했습니다: ' + error.message, 'error');
}
};
// 엑셀 내보내기
const exportToExcel = () => {
if (filteredData.length === 0) {
showNotification('내보낼 데이터가 없습니다.', 'warning');
return;
}
const periodText = filters.startDate && filters.endDate ? `_${filters.startDate}_${filters.endDate}` : '';
const headers = ['날짜', '상태', '프로젝트명', '요청부서', '타입', '업무내용', '근무시간', '초과근무'];
const csvContent = [
headers.join(','),
...filteredData.map(item => [
formatDate(item.pdate),
item.status || '',
item.projectName || '',
item.requestpart || '',
item.type || '',
`"${(item.description || '').replace(/"/g, '""')}"`,
item.hrs || '',
item.ot || ''
].join(','))
].join('\n');
const blob = new Blob(['\ufeff' + csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `업무일지${periodText}_${new Date().toISOString().split('T')[0]}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 유틸리티 함수들
const formatDate = (dateString) => {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleDateString('ko-KR');
};
const getStatusColor = (status) => {
switch (status) {
case '진행 중': return 'bg-blue-100 text-blue-800';
case '진행 완료': return 'bg-green-100 text-green-800';
case '대기': return 'bg-yellow-100 text-yellow-800';
default: return 'bg-gray-100 text-gray-800';
}
};
// 알림 표시 함수
const showNotification = (message, type = 'info') => {
const colors = {
info: 'bg-blue-500/90 backdrop-blur-sm',
success: 'bg-green-500/90 backdrop-blur-sm',
warning: 'bg-yellow-500/90 backdrop-blur-sm',
error: 'bg-red-500/90 backdrop-blur-sm'
};
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-4 py-3 rounded-lg z-50 transition-all duration-300 transform translate-x-0 opacity-100 shadow-lg border border-white/20`;
notification.innerHTML = `
<div class="flex items-center">
<span class="ml-2">${message}</span>
</div>
`;
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(0)';
notification.style.opacity = '1';
}, 10);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
// 페이지네이션
const maxPage = Math.ceil(filteredData.length / pageSize);
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const pageData = filteredData.slice(startIndex, endIndex);
// 프로젝트 목록 업데이트
const uniqueProjects = [...new Set(jobData.map(item => item.projectName).filter(Boolean))];
return (
<div className="container mx-auto px-4 py-8">
{/* 개발중 경고 메시지 */}
<div className="bg-orange-500 rounded-lg p-4 mb-6 border-l-4 border-orange-700 animate-slide-up shadow-lg">
<div className="flex items-center">
<svg className="w-5 h-5 text-orange-900 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
<div>
<p className="text-white font-bold text-base">🚧 개발중인 기능입니다</p>
<p className="text-orange-100 text-sm font-medium">일부 기능이 정상적으로 동작하지 않을 있습니다.</p>
</div>
</div>
</div>
{/* 통계 카드 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8 animate-slide-up">
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-primary-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80"> 업무일수</p>
<p className="text-2xl font-bold text-white">{statistics.totalDays}</p>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-success-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80">오늘 근무시간</p>
<p className={`text-2xl font-bold ${statistics.todayHours < 8 ? 'text-red-300' : 'text-green-300'}`}>
{statistics.todayHours.toFixed(1)}h
</p>
<p className="text-sm text-white/60">(목표 8시간의 {statistics.todayProgress.toFixed(0)}%)</p>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-warning-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80"> 초과근무</p>
<p className="text-2xl font-bold text-white">{statistics.totalOT.toFixed(1)}h</p>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-purple-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80">진행중 프로젝트</p>
<p className="text-2xl font-bold text-white">{statistics.activeProjects}</p>
</div>
</div>
</div>
</div>
{/* 필터 및 검색 */}
<div className="glass-effect rounded-lg mb-6 animate-slide-up">
<div className="p-4">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
{/* 좌측: 필터 컨트롤 */}
<div className="lg:col-span-2 space-y-3">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div>
<label className="block text-xs font-medium text-white/80 mb-1">조회기간</label>
<div className="flex space-x-1">
<input
type="date"
value={filters.startDate}
onChange={(e) => handleFilterChange('startDate', e.target.value)}
className="flex-1 bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
/>
<span className="flex items-center text-white/60 text-xs">~</span>
<input
type="date"
value={filters.endDate}
onChange={(e) => handleFilterChange('endDate', e.target.value)}
className="flex-1 bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
/>
</div>
</div>
<div>
<label className="block text-xs font-medium text-white/80 mb-1">상태</label>
<select
value={filters.status}
onChange={(e) => handleFilterChange('status', e.target.value)}
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
>
<option value="">전체</option>
<option value="진행중">진행중</option>
<option value="완료">완료</option>
<option value="대기">대기</option>
</select>
</div>
<div>
<label className="block text-xs font-medium text-white/80 mb-1">타입</label>
<select
value={filters.type}
onChange={(e) => handleFilterChange('type', e.target.value)}
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
>
<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>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-white/80 mb-1">프로젝트</label>
<select
value={filters.project}
onChange={(e) => handleFilterChange('project', e.target.value)}
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
>
<option value="">전체</option>
{uniqueProjects.map(project => (
<option key={project} value={project}>{project}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-medium text-white/80 mb-1">검색</label>
<input
type="text"
value={filters.search}
onChange={(e) => handleFilterChange('search', e.target.value)}
placeholder="업무 내용 검색..."
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
/>
</div>
</div>
</div>
{/* 우측: 액션 버튼들 */}
<div className="flex flex-col space-y-2 justify-center">
<button
onClick={showAddJobModal}
className="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-md flex items-center justify-center transition-colors"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
업무일지 추가
</button>
<button
onClick={exportToExcel}
className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-md flex items-center justify-center transition-colors"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
엑셀 다운로드
</button>
<button
onClick={clearFilters}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-md flex items-center justify-center transition-colors"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
필터 초기화
</button>
</div>
</div>
</div>
</div>
{/* 데이터 테이블 */}
<div className="glass-effect rounded-lg overflow-hidden animate-slide-up custom-scrollbar">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-white/20">
<thead className="bg-white/10">
<tr>
<th
className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => handleSort('pdate')}
>
날짜 <svg className="w-4 h-4 inline ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => handleSort('status')}
>
상태 <svg className="w-4 h-4 inline ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => handleSort('hrs')}
>
근무시간 <svg className="w-4 h-4 inline ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => handleSort('projectName')}
>
프로젝트명 <svg className="w-4 h-4 inline ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">업무내용</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => handleSort('requestpart')}
>
요청부서 <svg className="w-4 h-4 inline ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => handleSort('type')}
>
타입 <svg className="w-4 h-4 inline ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{isLoading ? (
<tr>
<td colSpan="7" className="p-8 text-center">
<div className="inline-flex items-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span className="text-white/80">데이터를 불러오는 ...</span>
</div>
</td>
</tr>
) : pageData.length === 0 ? (
<tr>
<td colSpan="7" className="p-8 text-center">
<svg className="w-12 h-12 text-white/60 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
</svg>
<p className="text-white/70">업무일지 데이터가 없습니다.</p>
</td>
</tr>
) : (
pageData.map((item, index) => {
const hrs = parseFloat(item.hrs) || 0;
const ot = parseFloat(item.ot) || 0;
let workTimeDisplay = '';
if (hrs > 0) {
workTimeDisplay = hrs.toFixed(1);
if (ot > 0) {
workTimeDisplay += '+' + ot.toFixed(1);
}
} else {
workTimeDisplay = '-';
}
return (
<tr
key={item.idx || index}
className="hover:bg-white/10 cursor-pointer transition-colors"
onClick={() => showEditJobModal(item)}
>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{formatDate(item.pdate)}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getStatusColor(item.status)}`}>
{item.status || '-'}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{workTimeDisplay}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
<div className="flex items-center">
<span>{item.projectName || '-'}</span>
</div>
</td>
<td className="px-6 py-4 text-sm text-white">
<div className="max-w-xs truncate" title={item.description || ''}>
{item.description || '-'}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{item.requestpart || '-'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{item.type || '-'}
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
</div>
{/* 페이지네이션 */}
<div className="mt-6 flex items-center justify-between glass-effect rounded-lg p-4">
<div className="flex items-center space-x-2">
<span className="text-sm text-white/80">페이지당 :</span>
<select
value={pageSize}
onChange={(e) => {
setPageSize(parseInt(e.target.value));
setCurrentPage(1);
}}
className="bg-white/20 border border-white/30 rounded-md px-2 py-1 text-sm text-white"
>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
<div className="flex items-center space-x-2">
<button
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
disabled={currentPage <= 1}
className="px-3 py-1 border border-white/30 rounded-md text-sm text-white hover:bg-white/20 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
이전
</button>
<span className="text-sm text-white/80">{currentPage} / {maxPage}</span>
<button
onClick={() => setCurrentPage(Math.min(maxPage, currentPage + 1))}
disabled={currentPage >= maxPage}
className="px-3 py-1 border border-white/30 rounded-md text-sm text-white hover:bg-white/20 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
다음
</button>
</div>
</div>
{/* 편집 모달 */}
{showEditModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-6xl animate-slide-up">
{/* 모달 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
업무일지 {isEditMode ? '편집' : '추가'}
</h2>
<button
onClick={() => setShowEditModal(false)}
className="text-white/70 hover:text-white transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
{/* 모달 내용 */}
<form onSubmit={handleSave} className="p-6">
<div className="grid grid-cols-1 lg:grid-cols-5 gap-8">
{/* 좌측: 기본 정보 */}
<div className="lg:col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-2">날짜 *</label>
<input
type="date"
value={editData.pdate || ''}
onChange={(e) => setEditData(prev => ({...prev, pdate: e.target.value}))}
required
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">상태 *</label>
<select
value={editData.status || ''}
onChange={(e) => setEditData(prev => ({...prev, status: e.target.value}))}
required
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
>
<option value="">선택하세요</option>
<option value="진행 중">진행 </option>
<option value="완료">완료</option>
<option value="대기">대기</option>
<option value="보류">보류</option>
</select>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">요청부서</label>
<input
type="text"
value={editData.requestpart || ''}
onChange={(e) => setEditData(prev => ({...prev, requestpart: e.target.value}))}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">타입</label>
<select
value={editData.type || ''}
onChange={(e) => setEditData(prev => ({...prev, type: e.target.value}))}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
>
<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>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">근무시간 (시간) *</label>
<input
type="number"
step="any"
value={editData.hrs || ''}
onChange={(e) => setEditData(prev => ({...prev, hrs: e.target.value}))}
required
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">초과근무 (시간)</label>
<input
type="number"
step="any"
value={editData.ot || ''}
onChange={(e) => setEditData(prev => ({...prev, ot: e.target.value}))}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">초과근무 시작시간</label>
<input
type="time"
value={editData.otStart || ''}
onChange={(e) => setEditData(prev => ({...prev, otStart: e.target.value}))}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">초과근무 종료시간</label>
<input
type="time"
value={editData.otEnd || ''}
onChange={(e) => setEditData(prev => ({...prev, otEnd: e.target.value}))}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
</div>
</div>
{/* 우측: 프로젝트명과 업무내용 */}
<div className="lg:col-span-3 space-y-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-2">프로젝트명 *</label>
<input
type="text"
value={editData.projectName || ''}
onChange={(e) => setEditData(prev => ({...prev, projectName: e.target.value}))}
required
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
<div className="flex-grow">
<label className="block text-white/70 text-sm font-medium mb-2">업무내용 *</label>
<textarea
value={editData.description || ''}
onChange={(e) => setEditData(prev => ({...prev, description: e.target.value}))}
rows="15"
required
className="w-full h-full min-h-[360px] bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all resize-vertical"
placeholder="상세한 업무 내용을 입력하세요..."
/>
</div>
</div>
</div>
</form>
{/* 모달 푸터 */}
<div className="px-6 py-4 border-t border-white/10 flex justify-between items-center bg-black/10 rounded-b-2xl">
{isEditMode && (
<button
type="button"
onClick={handleDelete}
className="bg-danger-500 hover:bg-danger-600 text-white px-4 py-2 rounded-lg transition-colors"
>
삭제
</button>
)}
<div className="flex space-x-3 ml-auto">
<button
type="button"
onClick={() => setShowEditModal(false)}
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
>
취소
</button>
<button
type="submit"
onClick={handleSave}
className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg transition-colors"
>
저장
</button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
);
}

View File

@@ -1,339 +0,0 @@
const { useState, useEffect } = React;
function Kuntae() {
// 상태 관리
const [kuntaeData, setKuntaeData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [filters, setFilters] = useState({
startDate: '',
endDate: ''
});
// 통계 데이터
const [statistics, setStatistics] = useState({
totalDays: 0,
approvedDays: 0,
pendingDays: 0,
rejectedDays: 0
});
// 컴포넌트 마운트시 초기화
useEffect(() => {
initializeFilters();
}, []);
// 초기 필터 설정 (이번 달)
const initializeFilters = () => {
const now = new Date();
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
setFilters({
startDate: firstDay.toISOString().split('T')[0],
endDate: lastDay.toISOString().split('T')[0]
});
// 초기 데이터 로드
loadKuntaeData({
startDate: firstDay.toISOString().split('T')[0],
endDate: lastDay.toISOString().split('T')[0]
});
};
// 근태 데이터 로드
const loadKuntaeData = async (searchFilters = filters) => {
setIsLoading(true);
try {
let url = '/Kuntae/GetData';
const params = new URLSearchParams();
if (searchFilters.startDate) params.append('startDate', searchFilters.startDate);
if (searchFilters.endDate) params.append('endDate', searchFilters.endDate);
if (params.toString()) {
url += '?' + params.toString();
}
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: 데이터를 불러오는데 실패했습니다.`);
}
const responseData = await response.json();
if (Array.isArray(responseData)) {
setKuntaeData(responseData);
updateStatistics(responseData);
} else if (responseData.error) {
throw new Error(responseData.error);
} else {
setKuntaeData([]);
updateStatistics([]);
}
} catch (error) {
console.error('Error loading kuntae data:', error);
setKuntaeData([]);
updateStatistics([]);
showNotification('데이터를 불러오는데 실패했습니다: ' + error.message, 'error');
} finally {
setIsLoading(false);
}
};
// 통계 업데이트
const updateStatistics = (data) => {
const totalDays = data.length;
const approvedDays = data.filter(item => item.status === '승인').length;
const pendingDays = data.filter(item => item.status === '대기').length;
const rejectedDays = data.filter(item => item.status === '반려').length;
setStatistics({
totalDays,
approvedDays,
pendingDays,
rejectedDays
});
};
// 필터 변경 핸들러
const handleFilterChange = (field, value) => {
setFilters(prev => ({ ...prev, [field]: value }));
};
// 검색 실행
const handleSearch = () => {
loadKuntaeData();
};
// 유틸리티 함수들
const formatDate = (dateString) => {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleDateString('ko-KR');
};
const getStatusColor = (status) => {
switch (status) {
case '승인': return 'bg-green-100 text-green-800';
case '대기': return 'bg-yellow-100 text-yellow-800';
case '반려': return 'bg-red-100 text-red-800';
default: return 'bg-gray-100 text-gray-800';
}
};
// 알림 표시 함수
const showNotification = (message, type = 'info') => {
const colors = {
info: 'bg-blue-500/90 backdrop-blur-sm',
success: 'bg-green-500/90 backdrop-blur-sm',
warning: 'bg-yellow-500/90 backdrop-blur-sm',
error: 'bg-red-500/90 backdrop-blur-sm'
};
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-4 py-3 rounded-lg z-50 transition-all duration-300 transform translate-x-0 opacity-100 shadow-lg border border-white/20`;
notification.innerHTML = `
<div class="flex items-center">
<span class="ml-2">${message}</span>
</div>
`;
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(0)';
notification.style.opacity = '1';
}, 10);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
return (
<div className="container mx-auto px-4 py-8">
{/* 개발중 경고 메시지 */}
<div className="bg-orange-500 rounded-lg p-4 mb-6 border-l-4 border-orange-700 animate-slide-up shadow-lg">
<div className="flex items-center">
<svg className="w-5 h-5 text-orange-900 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
<div>
<p className="text-white font-bold text-base">🚧 개발중인 기능입니다</p>
<p className="text-orange-100 text-sm font-medium">일부 기능이 정상적으로 동작하지 않을 있습니다.</p>
</div>
</div>
</div>
{/* 검색 및 필터 섹션 */}
<div className="glass-effect rounded-lg p-6 mb-6 animate-slide-up">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-white/80 mb-2">시작일</label>
<input
type="date"
value={filters.startDate}
onChange={(e) => handleFilterChange('startDate', e.target.value)}
className="w-full px-3 py-2 bg-white/20 border border-white/30 rounded-md text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/80 mb-2">종료일</label>
<input
type="date"
value={filters.endDate}
onChange={(e) => handleFilterChange('endDate', e.target.value)}
className="w-full px-3 py-2 bg-white/20 border border-white/30 rounded-md text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-transparent"
/>
</div>
<div className="flex items-end">
<button
onClick={handleSearch}
disabled={isLoading}
className="w-full glass-effect text-white px-4 py-2 rounded-md hover:bg-white/30 transition-colors duration-200 flex items-center justify-center disabled:opacity-50"
>
{isLoading ? (
<>
<div className="loading inline-block w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"></div>
로딩중...
</>
) : (
'조회'
)}
</button>
</div>
</div>
</div>
{/* 통계 카드 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6 animate-slide-up">
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-primary-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80"> 신청건</p>
<p className="text-2xl font-bold text-white">{statistics.totalDays}</p>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-success-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80">승인</p>
<p className="text-2xl font-bold text-green-300">{statistics.approvedDays}</p>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-warning-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80">대기</p>
<p className="text-2xl font-bold text-yellow-300">{statistics.pendingDays}</p>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-red-500/20 rounded-lg">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-white/80">반려</p>
<p className="text-2xl font-bold text-red-300">{statistics.rejectedDays}</p>
</div>
</div>
</div>
</div>
{/* 데이터 테이블 */}
<div className="glass-effect rounded-lg overflow-hidden animate-slide-up">
<div className="table-container custom-scrollbar" style={{ maxHeight: '600px', overflowY: 'auto' }}>
<table className="min-w-full divide-y divide-white/20">
<thead className="bg-white/10 sticky top-0">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">신청일</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">시작일</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">종료일</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">휴가종류</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">일수</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">상태</th>
<th className="px-6 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider">사유</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{isLoading ? (
<tr>
<td colSpan="7" className="p-8 text-center">
<div className="inline-flex items-center">
<div className="loading inline-block w-5 h-5 border-3 border-white/30 border-t-white rounded-full animate-spin mr-3"></div>
<span className="text-white/80">데이터를 불러오는 ...</span>
</div>
</td>
</tr>
) : kuntaeData.length === 0 ? (
<tr>
<td colSpan="7" className="p-8 text-center">
<svg className="w-12 h-12 text-white/60 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<p className="text-white/70">근태 데이터가 없습니다.</p>
</td>
</tr>
) : (
kuntaeData.map((item, index) => (
<tr key={item.idx || index} className="hover:bg-white/10 transition-colors">
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{formatDate(item.requestDate)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{formatDate(item.startDate)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{formatDate(item.endDate)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{item.type || '-'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">
{item.days || '0'}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getStatusColor(item.status)}`}>
{item.status || '-'}
</span>
</td>
<td className="px-6 py-4 text-sm text-white">
<div className="max-w-xs truncate" title={item.reason || ''}>
{item.reason || '-'}
</div>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
);
}

View File

@@ -1,308 +0,0 @@
// LoginApp.jsx - React Login Component for GroupWare
const { useState, useEffect, useRef } = React;
function LoginApp() {
const [formData, setFormData] = useState({
gcode: '',
userId: '',
password: '',
rememberMe: false
});
const [userGroups, setUserGroups] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [message, setMessage] = useState({ type: '', text: '', show: false });
const [isFormReady, setIsFormReady] = useState(false);
const gcodeRef = useRef(null);
const userIdRef = useRef(null);
const passwordRef = useRef(null);
// 메시지 표시 함수
const showMessage = (type, text) => {
setMessage({ type, text, show: true });
setTimeout(() => {
setMessage(prev => ({ ...prev, show: false }));
}, 3000);
};
// 폼 데이터 업데이트
const handleInputChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
// 사용자 그룹 목록 로드
const loadUserGroups = async () => {
try {
const response = await fetch('/DashBoard/GetUserGroups');
const data = await response.json();
// 유효한 그룹만 필터링
const validGroups = data.filter(group => group.gcode && group.name);
setUserGroups(validGroups);
// 이전 로그인 정보 로드
await loadPreviousLoginInfo();
} catch (error) {
console.error('그룹 목록 로드 중 오류 발생:', error);
showMessage('error', '부서 목록을 불러오는 중 오류가 발생했습니다.');
}
};
// 이전 로그인 정보 로드
const loadPreviousLoginInfo = async () => {
try {
const response = await fetch('/Home/GetPreviousLoginInfo');
const result = await response.json();
if (result.Success && result.Data) {
const { Gcode, UserId } = result.Data;
setFormData(prev => ({
...prev,
gcode: Gcode || '',
userId: UserId ? UserId.split(';')[0] : ''
}));
// 포커스 설정
setTimeout(() => {
if (Gcode && UserId) {
passwordRef.current?.focus();
} else {
gcodeRef.current?.focus();
}
}, 100);
}
setIsFormReady(true);
} catch (error) {
console.error('이전 로그인 정보 로드 중 오류 발생:', error);
setIsFormReady(true);
setTimeout(() => {
gcodeRef.current?.focus();
}, 100);
}
};
// 로그인 처리
const handleLogin = async (e) => {
e.preventDefault();
const { gcode, userId, password, rememberMe } = formData;
// 유효성 검사
if (!gcode || !userId || !password) {
showMessage('error', '그룹코드/사용자ID/비밀번호를 입력해주세요.');
return;
}
setIsLoading(true);
try {
const response = await fetch('/Home/Login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
Gcode: gcode,
UserId: userId,
Password: password,
RememberMe: rememberMe
})
});
const data = await response.json();
if (data.Success) {
// 로그인 성공
showMessage('success', data.Message);
// WebView2에 로그인 성공 메시지 전송
if (window.chrome && window.chrome.webview) {
window.chrome.webview.postMessage('LOGIN_SUCCESS');
}
// 리다이렉트 URL이 있으면 이동
if (data.RedirectUrl) {
setTimeout(() => {
window.location.href = data.RedirectUrl;
}, 1000);
}
} else {
// 로그인 실패
showMessage('error', data.Message || '로그인에 실패했습니다.');
}
} catch (error) {
console.error('로그인 요청 중 오류 발생:', error);
showMessage('error', '서버 연결에 실패했습니다. 다시 시도해주세요.');
} finally {
setIsLoading(false);
}
};
// 컴포넌트 마운트 시 실행
useEffect(() => {
loadUserGroups();
}, []);
return (
<div className="gradient-bg min-h-screen flex items-center justify-center p-4">
<div className="w-full max-w-md">
{/* 로그인 카드 */}
<div className="glass-effect rounded-3xl p-8 card-hover animate-bounce-in">
{/* 로고 및 제목 */}
<div className="text-center mb-8 animate-fade-in">
<div className="w-16 h-16 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
<h1 className="text-2xl font-bold text-white mb-2">GroupWare</h1>
<p className="text-white/70 text-sm">로그인하여 시스템에 접속하세요</p>
</div>
{/* 로그인 폼 */}
<form onSubmit={handleLogin} className="space-y-6 animate-slide-up">
{/* Gcode 드롭다운 */}
<div className="relative">
<select
ref={gcodeRef}
name="gcode"
value={formData.gcode}
onChange={handleInputChange}
className="input-field w-full px-4 py-3 bg-white/10 border border-white/20 rounded-xl text-white focus:outline-none focus:border-primary-400 input-focus appearance-none"
required
disabled={!isFormReady}
>
<option value="" className="text-gray-800">부서를 선택하세요</option>
{userGroups.map(group => (
<option key={group.gcode} value={group.gcode} className="text-gray-800">
{group.name}
</option>
))}
</select>
<div className="absolute right-3 top-3 pointer-events-none">
<svg className="w-5 h-5 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
</div>
{/* 사용자 ID 입력 */}
<div className="relative">
<input
ref={userIdRef}
type="text"
name="userId"
value={formData.userId}
onChange={handleInputChange}
className="input-field w-full px-4 py-3 bg-white/10 border border-white/20 rounded-xl text-white placeholder-white/60 focus:outline-none focus:border-primary-400 input-focus"
placeholder="사원번호"
required
disabled={!isFormReady}
/>
<div className="absolute right-3 top-3">
<svg className="w-5 h-5 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
</div>
</div>
{/* 비밀번호 입력 */}
<div className="relative">
<input
ref={passwordRef}
type="password"
name="password"
value={formData.password}
onChange={handleInputChange}
className="input-field w-full px-4 py-3 bg-white/10 border border-white/20 rounded-xl text-white placeholder-white/60 focus:outline-none focus:border-primary-400 input-focus"
placeholder="비밀번호"
required
disabled={!isFormReady}
/>
<div className="absolute right-3 top-3">
<svg className="w-5 h-5 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
</div>
{/* 로그인 버튼 */}
<button
type="submit"
disabled={isLoading || !isFormReady}
className="w-full bg-primary-500 hover:bg-primary-600 disabled:opacity-50 disabled:cursor-not-allowed text-white font-semibold py-3 px-4 rounded-xl transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2 focus:ring-offset-transparent"
>
<span className="flex items-center justify-center">
{isLoading ? (
<>
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"></div>
로그인 ...
</>
) : (
<>
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
</svg>
로그인
</>
)}
</span>
</button>
</form>
{/* 추가 옵션 */}
<div className="mt-6 text-center">
<div className="flex items-center justify-center space-x-4 text-sm">
<label className="flex items-center text-white/70 hover:text-white cursor-pointer transition-colors">
<input
type="checkbox"
name="rememberMe"
checked={formData.rememberMe}
onChange={handleInputChange}
className="mr-2 w-4 h-4 text-primary-500 bg-white/10 border-white/20 rounded focus:ring-primary-400 focus:ring-2"
/>
로그인 정보 저장
</label>
<a href="#" className="text-primary-300 hover:text-primary-200 transition-colors">비밀번호 찾기</a>
</div>
</div>
</div>
{/* 푸터 */}
<div className="text-center mt-6 animate-fade-in">
<p className="text-white/50 text-sm">
© 2024 GroupWare System. All rights reserved.
</p>
</div>
{/* 메시지 표시 */}
{message.show && (
<div className={`fixed top-4 left-1/2 transform -translate-x-1/2 px-6 py-3 rounded-lg shadow-lg animate-slide-up ${
message.type === 'error' ? 'bg-red-500' : 'bg-green-500'
} text-white`}>
<div className="flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{message.type === 'error' ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
)}
</svg>
<span>{message.text}</span>
</div>
</div>
)}
</div>
</div>
);
}

View File

@@ -1,884 +0,0 @@
const { useState, useEffect } = React;
function Project() {
// 상태 관리
const [projects, setProjects] = useState([]);
const [filteredProjects, setFilteredProjects] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [currentUser, setCurrentUser] = useState('사용자');
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage] = useState(10);
// 모달 상태
const [showProjectModal, setShowProjectModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [currentProjectIdx, setCurrentProjectIdx] = useState(null);
// 필터 상태
const [filters, setFilters] = useState({
status: '진행',
search: '',
manager: 'my'
});
// UI 상태
const [showFilters, setShowFilters] = useState(false);
const [showColumnDropdown, setShowColumnDropdown] = useState(false);
const [visibleColumns, setVisibleColumns] = useState({
serial: false,
plant: false,
line: false,
package: false,
staff: false,
process: false,
expire: false,
delivery: false,
design: false,
electric: false,
program: false,
budgetDue: false,
budget: false,
jasmin: false
});
// 프로젝트 폼 상태
const [projectForm, setProjectForm] = useState({
idx: 0,
name: '',
process: '',
sdate: '',
edate: '',
ddate: '',
odate: '',
userManager: '',
status: '진행',
memo: ''
});
// 통계 상태
const [statusCounts, setStatusCounts] = useState({
진행: 0,
완료: 0,
대기: 0,
중단: 0
});
// 컴포넌트 마운트시 초기화
useEffect(() => {
getCurrentUser();
loadProjects();
}, []);
// 필터 변경시 프로젝트 목록 새로 로드
useEffect(() => {
loadProjects();
}, [filters.status, filters.manager]);
// 검색어 변경시 필터링
useEffect(() => {
filterData();
}, [filters.search, projects]);
// 현재 사용자 정보 가져오기
const getCurrentUser = async () => {
try {
const response = await fetch('/Common/GetCurrentUser');
const data = await response.json();
if (data.Success && data.Data) {
const userName = data.Data.userName || data.Data.name || '사용자';
setCurrentUser(userName);
}
} catch (error) {
console.error('Error getting current user:', error);
}
};
// 프로젝트 목록 로드
const loadProjects = async () => {
setIsLoading(true);
try {
const response = await fetch(`/Project/GetProjects?status=${filters.status}&userFilter=${filters.manager}`);
const data = await response.json();
if (data.Success) {
const projectData = data.Data || [];
setProjects(projectData);
setFilteredProjects(projectData);
updateStatusCounts(projectData);
if (data.CurrentUser) {
setCurrentUser(data.CurrentUser);
}
} else {
console.error('Error:', data.Message);
showNotification('데이터를 불러오는데 실패했습니다.', 'error');
}
} catch (error) {
console.error('Error:', error);
showNotification('데이터를 불러오는데 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 상태별 카운트 업데이트
const updateStatusCounts = (projectData) => {
const counts = { 진행: 0, 완료: 0, 대기: 0, 중단: 0 };
projectData.forEach(project => {
const status = project.상태 || '진행';
counts[status] = (counts[status] || 0) + 1;
});
setStatusCounts(counts);
};
// 데이터 필터링
const filterData = () => {
if (!filters.search.trim()) {
setFilteredProjects(projects);
setCurrentPage(1);
return;
}
const searchTerm = filters.search.toLowerCase();
const filtered = projects.filter(project => {
return (project.프로젝트명 && project.프로젝트명.toLowerCase().includes(searchTerm)) ||
(project.프로젝트공정 && project.프로젝트공정.toLowerCase().includes(searchTerm)) ||
(project.자산번호 && project.자산번호.toLowerCase().includes(searchTerm)) ||
(project.장비모델 && project.장비모델.toLowerCase().includes(searchTerm)) ||
(project.시리얼번호 && project.시리얼번호.toLowerCase().includes(searchTerm)) ||
(project.프로젝트관리자 && project.프로젝트관리자.toLowerCase().includes(searchTerm)) ||
(project.설계담당 && project.설계담당.toLowerCase().includes(searchTerm)) ||
(project.전장담당 && project.전장담당.toLowerCase().includes(searchTerm)) ||
(project.프로그램담당 && project.프로그램담당.toLowerCase().includes(searchTerm));
});
setFilteredProjects(filtered);
setCurrentPage(1);
};
// 프로젝트 추가 모달 표시
const showAddProjectModal = () => {
setCurrentProjectIdx(null);
setProjectForm({
idx: 0,
name: '',
process: '',
sdate: '',
edate: '',
ddate: '',
odate: '',
userManager: '',
status: '진행',
memo: ''
});
setShowProjectModal(true);
};
// 프로젝트 편집
const editProject = async (idx) => {
setCurrentProjectIdx(idx);
setIsLoading(true);
try {
const response = await fetch(`/Project/GetProject?id=${idx}`);
const data = await response.json();
if (data.Success && data.Data) {
const project = data.Data;
setProjectForm({
idx: project.idx,
name: project.프로젝트명 || '',
process: project.프로젝트공정 || '',
sdate: project.시작일 || '',
edate: project.완료일 || '',
ddate: project.만료일 || '',
odate: project.출고일 || '',
userManager: project.프로젝트관리자 || '',
status: project.상태 || '진행',
memo: project.memo || ''
});
setShowProjectModal(true);
} else {
showNotification('프로젝트 정보를 불러오는데 실패했습니다: ' + data.Message, 'error');
}
} catch (error) {
console.error('Error:', error);
showNotification('프로젝트 정보를 불러오는데 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 프로젝트 저장 (추가/수정)
const saveProject = async (e) => {
e.preventDefault();
setIsLoading(true);
const projectData = {
...projectForm,
idx: currentProjectIdx ? parseInt(projectForm.idx) : 0,
sdate: projectForm.sdate || null,
edate: projectForm.edate || null,
ddate: projectForm.ddate || null,
odate: projectForm.odate || null
};
const url = currentProjectIdx ? '/Project/UpdateProject' : '/Project/CreateProject';
const method = currentProjectIdx ? 'PUT' : 'POST';
try {
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(projectData)
});
const data = await response.json();
if (data.Success) {
showNotification(data.Message || (currentProjectIdx ? '프로젝트가 수정되었습니다.' : '프로젝트가 추가되었습니다.'), 'success');
setShowProjectModal(false);
loadProjects();
} else {
showNotification('오류: ' + data.Message, 'error');
}
} catch (error) {
console.error('Error:', error);
showNotification('저장 중 오류가 발생했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 프로젝트 삭제
const deleteProject = async () => {
if (!currentProjectIdx) return;
setIsLoading(true);
try {
const response = await fetch(`/Project/DeleteProject?id=${currentProjectIdx}`, {
method: 'DELETE'
});
const data = await response.json();
if (data.Success) {
showNotification(data.Message || '프로젝트가 삭제되었습니다.', 'success');
setShowDeleteModal(false);
setShowProjectModal(false);
loadProjects();
} else {
showNotification('오류: ' + data.Message, 'error');
}
} catch (error) {
console.error('Error:', error);
showNotification('삭제 중 오류가 발생했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 필터 초기화
const clearFilters = () => {
setFilters({
status: '진행',
search: '',
manager: 'my'
});
};
// 엑셀 다운로드
const exportToExcel = () => {
if (filteredProjects.length === 0) {
showNotification('내보낼 데이터가 없습니다.', 'warning');
return;
}
const headers = ['상태', '자산번호', '장비모델', '시리얼번호', '우선순위', '요청국가', '요청공장', '요청라인', '요청부서패키지', '요청자', '프로젝트공정', '시작일', '완료일', '만료일', '출고일', '프로젝트명', '프로젝트관리자', '설계담당', '전장담당', '프로그램담당', '예산만기일', '예산', '웹관리번호'];
const csvContent = [
headers.join(','),
...filteredProjects.map(project => [
project.상태 || '',
project.자산번호 || '',
project.장비모델 || '',
project.시리얼번호 || '',
project.우선순위 || '',
project.요청국가 || '',
project.요청공장 || '',
project.요청라인 || '',
project.요청부서패키지 || '',
project.요청자 || '',
project.프로젝트공정 || '',
project.시작일 || '',
project.완료일 || '',
project.만료일 || '',
project.출고일 || '',
project.프로젝트명 || '',
project.프로젝트관리자 || '',
project.설계담당 || '',
project.전장담당 || '',
project.프로그램담당 || '',
project.예산만기일 || '',
project.예산 || '',
project.웹관리번호 || ''
].join(','))
].join('\n');
const blob = new Blob(['\ufeff' + csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `프로젝트목록_${new Date().toISOString().split('T')[0]}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 유틸리티 함수들
const getStatusClass = (status) => {
switch(status) {
case '진행': return 'bg-blue-500/20 text-blue-300';
case '완료': return 'bg-green-500/20 text-green-300';
case '대기': return 'bg-yellow-500/20 text-yellow-300';
case '중단': return 'bg-red-500/20 text-red-300';
default: return 'bg-gray-500/20 text-gray-300';
}
};
const formatDateToMonthDay = (dateString) => {
if (!dateString) return '';
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) return dateString;
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${month}-${day}`;
} catch (error) {
return dateString;
}
};
// 알림 표시 함수
const showNotification = (message, type = 'info') => {
const colors = {
info: 'bg-blue-500/90 backdrop-blur-sm',
success: 'bg-green-500/90 backdrop-blur-sm',
warning: 'bg-yellow-500/90 backdrop-blur-sm',
error: 'bg-red-500/90 backdrop-blur-sm'
};
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-4 py-3 rounded-lg z-50 transition-all duration-300 transform translate-x-0 opacity-100 shadow-lg border border-white/20`;
notification.innerHTML = `
<div class="flex items-center">
<span class="ml-2">${message}</span>
</div>
`;
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(0)';
notification.style.opacity = '1';
}, 10);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
// 페이지네이션 계산
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const paginatedProjects = filteredProjects.slice(startIndex, endIndex);
const changePage = (page) => {
if (page >= 1 && page <= totalPages) {
setCurrentPage(page);
}
};
return (
<div className="container mx-auto px-4 py-8">
{/* 프로젝트 목록 */}
<div className="glass-effect rounded-lg overflow-hidden animate-slide-up">
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
</svg>
프로젝트 목록
</h2>
<div className="flex space-x-3">
<button onClick={showAddProjectModal} className="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center text-sm" title="프로젝트 추가">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
프로젝트 추가
</button>
<button onClick={() => setShowFilters(!showFilters)} className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors flex items-center text-sm" title="필터 표시/숨김">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707L13 14v6a1 1 0 01-.707.293l-4-1A1 1 0 018 19v-5L0.293 7.293A1 1 0 010 6.586V4z"></path>
</svg>
필터
<svg className={`w-4 h-4 ml-2 transition-transform ${showFilters ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
</div>
</div>
{/* 상태별 카드 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 p-4 border-b border-white/10">
<div className="bg-white/10 rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-blue-300 mb-1">{statusCounts.진행}</div>
<div className="text-sm text-white/60">진행</div>
</div>
<div className="bg-white/10 rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-green-300 mb-1">{statusCounts.완료}</div>
<div className="text-sm text-white/60">완료</div>
</div>
<div className="bg-white/10 rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-yellow-300 mb-1">{statusCounts.대기}</div>
<div className="text-sm text-white/60">대기</div>
</div>
<div className="bg-white/10 rounded-lg p-4 text-center">
<div className="text-2xl font-bold text-red-300 mb-1">{statusCounts.중단}</div>
<div className="text-sm text-white/60">중단</div>
</div>
</div>
{/* 필터 영역 */}
{showFilters && (
<div className="p-4 border-b border-white/10 animate-slide-up">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
{/* 좌측: 필터 컨트롤 */}
<div className="lg:col-span-2 space-y-3">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div>
<label className="block text-xs font-medium text-white/80 mb-1">상태</label>
<select
value={filters.status}
onChange={(e) => setFilters({...filters, status: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
>
<option value="진행">진행</option>
<option value="완료">완료</option>
<option value="대기">대기</option>
<option value="중단">중단</option>
</select>
</div>
<div>
<label className="block text-xs font-medium text-white/80 mb-1">검색</label>
<input
type="text"
value={filters.search}
onChange={(e) => setFilters({...filters, search: e.target.value})}
placeholder="프로젝트명 검색..."
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
/>
</div>
<div>
<label className="block text-xs font-medium text-white/80 mb-1">담당자</label>
<select
value={filters.manager}
onChange={(e) => setFilters({...filters, manager: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-2 py-1 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent text-xs"
>
<option value="my"> 프로젝트</option>
<option value="all">전체 프로젝트</option>
</select>
</div>
</div>
</div>
{/* 우측: 액션 버튼들 */}
<div className="flex flex-wrap gap-2 justify-end">
<div className="relative">
<button onClick={() => setShowColumnDropdown(!showColumnDropdown)} className="bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg flex items-center transition-colors text-sm" title="컬럼 표시/숨김">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2H9z"></path>
</svg>
컬럼 설정
</button>
{showColumnDropdown && (
<div className="absolute top-full right-0 bg-gray-800/95 backdrop-blur-sm border border-white/20 rounded-lg p-3 min-w-48 z-50 mt-1">
<div className="text-sm font-medium text-white/80 mb-2">표시할 컬럼 선택</div>
<div className="space-y-1 text-sm max-h-40 overflow-y-auto">
{Object.entries({
serial: '시리얼번호',
plant: '요청공장',
line: '요청라인',
package: '요청부서패키지',
staff: '요청자',
process: '프로젝트공정',
expire: '만료일',
delivery: '출고일',
design: '설계담당',
electric: '전장담당',
program: '프로그램담당',
budgetDue: '예산만기일',
budget: '예산',
jasmin: '웹관리번호'
}).map(([key, label]) => (
<label key={key} className="flex items-center text-white cursor-pointer hover:text-blue-300">
<input
type="checkbox"
checked={visibleColumns[key]}
onChange={(e) => setVisibleColumns({...visibleColumns, [key]: e.target.checked})}
className="mr-2"
/>
{label}
</label>
))}
</div>
</div>
)}
</div>
<button onClick={exportToExcel} className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg flex items-center transition-colors text-sm" title="엑셀 다운로드">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
엑셀 다운로드
</button>
<button onClick={clearFilters} className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg flex items-center transition-colors text-sm" title="필터 초기화">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
필터 초기화
</button>
</div>
</div>
</div>
)}
{/* 데이터 테이블 */}
<div className="px-4 py-3 bg-white/5">
<div className="text-sm text-white/70 font-medium">프로젝트 현황</div>
</div>
<div className="overflow-x-auto custom-scrollbar">
<table className="w-full divide-y divide-white/20">
<thead className="bg-white/10">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-20">상태</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 min-w-32">프로젝트명</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">자산번호</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-28">장비모델</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">우선순위</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-20">요청국가</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-28">프로젝트관리자</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">시작일</th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">완료일</th>
{visibleColumns.serial && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">시리얼번호</th>}
{visibleColumns.plant && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-20">요청공장</th>}
{visibleColumns.line && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-20">요청라인</th>}
{visibleColumns.package && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-28">요청부서패키지</th>}
{visibleColumns.staff && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-20">요청자</th>}
{visibleColumns.process && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">프로젝트공정</th>}
{visibleColumns.expire && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">만료일</th>}
{visibleColumns.delivery && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">출고일</th>}
{visibleColumns.design && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">설계담당</th>}
{visibleColumns.electric && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">전장담당</th>}
{visibleColumns.program && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">프로그램담당</th>}
{visibleColumns.budgetDue && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-24">예산만기일</th>}
{visibleColumns.budget && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider border-r border-white/20 w-20">예산</th>}
{visibleColumns.jasmin && <th className="px-3 py-3 text-left text-xs font-medium text-white/80 uppercase tracking-wider w-24">웹관리번호</th>}
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{isLoading ? (
<tr>
<td colSpan="23" className="px-6 py-4 text-center">
<div className="inline-flex items-center">
<div className="loading inline-block w-5 h-5 border-3 border-white/30 border-t-white rounded-full animate-spin mr-3"></div>
<span className="text-white/80">데이터를 불러오는 ...</span>
</div>
</td>
</tr>
) : paginatedProjects.length === 0 ? (
<tr>
<td colSpan="23" className="px-6 py-4 text-center text-white/60">데이터가 없습니다.</td>
</tr>
) : (
paginatedProjects.map(project => (
<tr key={project.idx} onClick={() => editProject(project.idx)} className="hover:bg-white/10 cursor-pointer transition-colors">
<td className="px-4 py-4 text-sm border-r border-white/20">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusClass(project.상태)}`}>
{project.상태 || '진행'}
</span>
</td>
<td className="px-4 py-4 text-sm border-r border-white/20 font-medium">{project.프로젝트명 || ''}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{project.자산번호 || ''}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{project.장비모델 || ''}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{project.우선순위 || ''}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{project.요청국가 || ''}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{project.프로젝트관리자 || ''}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{formatDateToMonthDay(project.시작일)}</td>
<td className="px-4 py-4 text-sm border-r border-white/20">{formatDateToMonthDay(project.완료일)}</td>
{visibleColumns.serial && <td className="px-3 py-4 text-sm border-r border-white/20">{project.시리얼번호 || ''}</td>}
{visibleColumns.plant && <td className="px-3 py-4 text-sm border-r border-white/20">{project.요청공장 || ''}</td>}
{visibleColumns.line && <td className="px-3 py-4 text-sm border-r border-white/20">{project.요청라인 || ''}</td>}
{visibleColumns.package && <td className="px-3 py-4 text-sm border-r border-white/20">{project.요청부서패키지 || ''}</td>}
{visibleColumns.staff && <td className="px-3 py-4 text-sm border-r border-white/20">{project.요청자 || ''}</td>}
{visibleColumns.process && <td className="px-3 py-4 text-sm border-r border-white/20">{project.프로젝트공정 || ''}</td>}
{visibleColumns.expire && <td className="px-3 py-4 text-sm border-r border-white/20">{formatDateToMonthDay(project.만료일)}</td>}
{visibleColumns.delivery && <td className="px-3 py-4 text-sm border-r border-white/20">{formatDateToMonthDay(project.출고일)}</td>}
{visibleColumns.design && <td className="px-3 py-4 text-sm border-r border-white/20">{project.설계담당 || ''}</td>}
{visibleColumns.electric && <td className="px-3 py-4 text-sm border-r border-white/20">{project.전장담당 || ''}</td>}
{visibleColumns.program && <td className="px-3 py-4 text-sm border-r border-white/20">{project.프로그램담당 || ''}</td>}
{visibleColumns.budgetDue && <td className="px-3 py-4 text-sm border-r border-white/20">{formatDateToMonthDay(project.예산만기일)}</td>}
{visibleColumns.budget && <td className="px-3 py-4 text-sm border-r border-white/20">{project.예산 || ''}</td>}
{visibleColumns.jasmin && (
<td className="px-3 py-4 text-sm">
{project.웹관리번호 ? (
<a href={`https://scwa.amkor.co.kr/jasmine/view/${project.웹관리번호}`} target="_blank" rel="noopener noreferrer" className="text-blue-300 hover:text-blue-200 underline">
{project.웹관리번호}
</a>
) : ''}
</td>
)}
</tr>
))
)}
</tbody>
</table>
</div>
{/* 페이징 */}
<div className="px-4 py-4 border-t border-white/10">
<div className="flex items-center justify-between">
<div className="text-sm text-white/60">
{filteredProjects.length} {startIndex + 1}-{Math.min(endIndex, filteredProjects.length)} 표시
</div>
<div className="flex items-center space-x-2">
<button onClick={() => changePage(currentPage - 1)} disabled={currentPage <= 1} className="px-3 py-1 bg-white/20 hover:bg-white/30 text-white rounded-md text-sm disabled:opacity-50 disabled:cursor-not-allowed">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 19l-7-7 7-7"></path>
</svg>
</button>
<div className="flex space-x-1">
{Array.from({length: Math.min(5, totalPages)}, (_, i) => {
const pageNum = Math.max(1, currentPage - 2) + i;
if (pageNum > totalPages) return null;
return (
<button
key={pageNum}
onClick={() => changePage(pageNum)}
className={`px-3 py-1 rounded-md text-sm transition-colors ${
pageNum === currentPage
? 'bg-primary-500 text-white'
: 'bg-white/20 hover:bg-white/30 text-white'
}`}
>
{pageNum}
</button>
);
})}
</div>
<button onClick={() => changePage(currentPage + 1)} disabled={currentPage >= totalPages} className="px-3 py-1 bg-white/20 hover:bg-white/30 text-white rounded-md text-sm disabled:opacity-50 disabled:cursor-not-allowed">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
</div>
</div>
</div>
</div>
{/* 프로젝트 추가/편집 모달 */}
{showProjectModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-lg w-full max-w-2xl animate-slide-up">
<div className="p-6">
<div className="flex justify-between items-center mb-6">
<h3 className="text-xl font-semibold">{currentProjectIdx ? '프로젝트 편집' : '프로젝트 추가'}</h3>
<button onClick={() => setShowProjectModal(false)} className="text-white/60 hover:text-white">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<form onSubmit={saveProject}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-white/80 mb-2">프로젝트명 *</label>
<input
type="text"
value={projectForm.name}
onChange={(e) => setProjectForm({...projectForm, name: e.target.value})}
required
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/80 mb-2">부서</label>
<input
type="text"
value={projectForm.process}
onChange={(e) => setProjectForm({...projectForm, process: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-white/80 mb-2">시작일</label>
<input
type="date"
value={projectForm.sdate}
onChange={(e) => setProjectForm({...projectForm, sdate: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/80 mb-2">종료일</label>
<input
type="date"
value={projectForm.edate}
onChange={(e) => setProjectForm({...projectForm, edate: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-white/80 mb-2">개발완료일</label>
<input
type="date"
value={projectForm.ddate}
onChange={(e) => setProjectForm({...projectForm, ddate: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/80 mb-2">운영개시일</label>
<input
type="date"
value={projectForm.odate}
onChange={(e) => setProjectForm({...projectForm, odate: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-white/80 mb-2">담당자</label>
<input
type="text"
value={projectForm.userManager}
onChange={(e) => setProjectForm({...projectForm, userManager: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-white/80 mb-2">상태</label>
<select
value={projectForm.status}
onChange={(e) => setProjectForm({...projectForm, status: e.target.value})}
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
>
<option value="진행">진행</option>
<option value="완료">완료</option>
<option value="대기">대기</option>
<option value="중단">중단</option>
</select>
</div>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-white/80 mb-2">메모</label>
<textarea
value={projectForm.memo}
onChange={(e) => setProjectForm({...projectForm, memo: e.target.value})}
rows="3"
className="w-full bg-white/20 border border-white/30 rounded-md px-3 py-2 text-white placeholder-white/60 focus:outline-none focus:ring-1 focus:ring-white/50 focus:border-transparent"
></textarea>
</div>
<div className="flex justify-between items-center">
{currentProjectIdx && (
<button type="button" onClick={() => setShowDeleteModal(true)} className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md transition-colors">
삭제
</button>
)}
<div className="flex space-x-2 ml-auto">
<button type="button" onClick={() => setShowProjectModal(false)} className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-md transition-colors">
취소
</button>
<button type="submit" className="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-md transition-colors">
저장
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
)}
{/* 삭제 확인 모달 */}
{showDeleteModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-lg w-full max-w-md animate-slide-up">
<div className="p-6">
<div className="flex items-center mb-4">
<svg className="w-8 h-8 text-red-400 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
<h3 className="text-lg font-semibold">삭제 확인</h3>
</div>
<p className="text-white/80 mb-6">선택한 프로젝트를 삭제하시겠습니까?<br><span className="text-sm text-gray-500"> 작업은 되돌릴 없습니다.</span></p>
<div className="flex justify-end space-x-2">
<button onClick={() => setShowDeleteModal(false)} className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-md transition-colors">
취소
</button>
<button onClick={deleteProject} className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md transition-colors">
삭제
</button>
</div>
</div>
</div>
</div>
</div>
)}
{/* 로딩 인디케이터 */}
{isLoading && (
<div className="fixed top-4 right-4 bg-white/20 backdrop-blur-sm rounded-full px-4 py-2 text-white text-sm z-40">
<div className="flex items-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
처리 ...
</div>
</div>
)}
{/* 외부 클릭시 드롭다운 닫기 */}
{showColumnDropdown && (
<div
className="fixed inset-0 z-10"
onClick={() => setShowColumnDropdown(false)}
></div>
)}
</div>
);
}

View File

@@ -1,147 +0,0 @@
// TestApp.jsx - React Test Component for GroupWare
const { useState, useEffect } = React;
function TestApp() {
const [status, setStatus] = useState('loading');
const [counter, setCounter] = useState(0);
const [serverTime, setServerTime] = useState('');
const [apiTest, setApiTest] = useState({ status: 'pending', message: '' });
// 컴포넌트가 마운트될 때 실행
useEffect(() => {
// React가 정상적으로 로드되었음을 표시
setTimeout(() => {
setStatus('success');
setServerTime(new Date().toLocaleString('ko-KR'));
}, 1000);
// API 테스트
testAPI();
}, []);
// GroupWare API 테스트 함수
const testAPI = async () => {
try {
// Home 컨트롤러 테스트
const response = await fetch('/Home');
if (response.ok) {
setApiTest({ status: 'success', message: 'API 연결 성공' });
} else {
setApiTest({ status: 'warning', message: `API 응답: ${response.status}` });
}
} catch (error) {
setApiTest({ status: 'error', message: `API 오류: ${error.message}` });
}
};
const buttonStyle = {
padding: '10px 20px',
margin: '5px',
border: 'none',
borderRadius: '5px',
cursor: 'pointer',
fontSize: '14px'
};
return (
<div>
<div className={`status ${status === 'success' ? 'success' : 'loading'}`}>
{status === 'success' ? (
<div>
<h3> React 컴포넌트가 성공적으로 로드되었습니다!</h3>
<p><strong>현재 시간:</strong> {serverTime}</p>
<p><strong>파일 위치:</strong> /react/TestApp.jsx</p>
</div>
) : (
<h3>React 컴포넌트를 로딩 중입니다...</h3>
)}
</div>
{status === 'success' && (
<div>
<div className="status">
<h3>📊 상태 관리 테스트</h3>
<p><strong>카운터:</strong> {counter}</p>
<button
onClick={() => setCounter(counter + 1)}
style={{
...buttonStyle,
backgroundColor: '#007bff',
color: 'white'
}}
>
증가 (+1)
</button>
<button
onClick={() => setCounter(counter - 1)}
style={{
...buttonStyle,
backgroundColor: '#dc3545',
color: 'white'
}}
>
감소 (-1)
</button>
<button
onClick={() => setCounter(0)}
style={{
...buttonStyle,
backgroundColor: '#6c757d',
color: 'white'
}}
>
리셋
</button>
</div>
<div className={`status ${
apiTest.status === 'success' ? 'success' :
apiTest.status === 'error' ? 'error' : 'loading'
}`}>
<h3>🌐 GroupWare API 연결 테스트</h3>
<p><strong>상태:</strong> {apiTest.message}</p>
<p><strong>테스트 엔드포인트:</strong> /Home</p>
<button
onClick={testAPI}
style={{
...buttonStyle,
backgroundColor: '#28a745',
color: 'white'
}}
>
API 다시 테스트
</button>
</div>
<div className="status">
<h3>📋 React + OWIN 통합 테스트 체크리스트</h3>
<ul style={{ textAlign: 'left' }}>
<li> OWIN 정적 파일 서빙</li>
<li> React 라이브러리 로딩 (CDN)</li>
<li> JSX 파일 분리 로딩</li>
<li> JSX 컴파일 (Babel)</li>
<li> React Hooks (useState, useEffect)</li>
<li> 이벤트 핸들링</li>
<li> API 호출 (fetch)</li>
<li> 반응형 UI 업데이트</li>
<li> 컴포넌트 모듈화</li>
</ul>
</div>
<div className="status success">
<h3>🎉 통합 테스트 결과</h3>
<p>GroupWare + OWIN + React 환경이 성공적으로 구성되었습니다!</p>
<p><strong>다음 단계:</strong></p>
<ul style={{ textAlign: 'left' }}>
<li>React Router 추가 (SPA 라우팅)</li>
<li>상태 관리 라이브러리 추가 (Redux/Zustand)</li>
<li>UI 컴포넌트 라이브러리 추가 (Material-UI/Ant Design)</li>
<li>번들링 도구 설정 (Webpack/Vite)</li>
<li>TypeScript 지원 추가</li>
</ul>
</div>
</div>
)}
</div>
);
}

View File

@@ -1,811 +0,0 @@
const { useState, useEffect } = React;
function Todo() {
// 상태 관리
const [todos, setTodos] = useState([]);
const [activeTodos, setActiveTodos] = useState([]);
const [completedTodos, setCompletedTodos] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [currentTab, setCurrentTab] = useState('active');
const [currentEditId, setCurrentEditId] = useState(null);
// 모달 상태
const [showAddModal, setShowAddModal] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
// 폼 상태
const [todoForm, setTodoForm] = useState({
title: '',
remark: '',
expire: '',
seqno: 0,
flag: false,
request: '',
status: '0'
});
const [editForm, setEditForm] = useState({
idx: 0,
title: '',
remark: '',
expire: '',
seqno: 0,
flag: false,
request: '',
status: '0'
});
// 컴포넌트 마운트시 할일 목록 로드
useEffect(() => {
loadTodos();
}, []);
// 할일 목록을 활성/완료로 분리
useEffect(() => {
const active = todos.filter(todo => (todo.status || '0') !== '5');
const completed = todos.filter(todo => (todo.status || '0') === '5');
setActiveTodos(active);
setCompletedTodos(completed);
}, [todos]);
// 할일 목록 로드
const loadTodos = async () => {
setIsLoading(true);
try {
const response = await fetch('/Todo/GetTodos');
const data = await response.json();
if (data.Success) {
setTodos(data.Data || []);
} else {
showNotification(data.Message || '할일 목록을 불러올 수 없습니다.', 'error');
}
} catch (error) {
console.error('할일 목록 로드 중 오류:', error);
showNotification('서버 연결에 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 새 할일 추가
const addTodo = async (e) => {
e.preventDefault();
if (!todoForm.remark.trim()) {
showNotification('할일 내용을 입력해주세요.', 'error');
return;
}
setIsLoading(true);
try {
const response = await fetch('/Todo/CreateTodo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...todoForm,
seqno: parseInt(todoForm.seqno),
expire: todoForm.expire || null,
request: todoForm.request || null
})
});
const data = await response.json();
if (data.Success) {
setShowAddModal(false);
setTodoForm({
title: '',
remark: '',
expire: '',
seqno: 0,
flag: false,
request: '',
status: '0'
});
loadTodos();
showNotification(data.Message || '할일이 추가되었습니다.', 'success');
} else {
showNotification(data.Message || '할일 추가에 실패했습니다.', 'error');
}
} catch (error) {
console.error('할일 추가 중 오류:', error);
showNotification('서버 연결에 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 할일 수정
const updateTodo = async () => {
if (!editForm.remark.trim()) {
showNotification('할일 내용을 입력해주세요.', 'error');
return;
}
setIsLoading(true);
try {
const response = await fetch('/Todo/UpdateTodo', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...editForm,
seqno: parseInt(editForm.seqno),
expire: editForm.expire || null,
request: editForm.request || null
})
});
const data = await response.json();
if (data.Success) {
setShowEditModal(false);
setCurrentEditId(null);
loadTodos();
showNotification(data.Message || '할일이 수정되었습니다.', 'success');
} else {
showNotification(data.Message || '할일 수정에 실패했습니다.', 'error');
}
} catch (error) {
console.error('할일 수정 중 오류:', error);
showNotification('서버 연결에 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 상태 업데이트 (편집 모달에서 바로 서버에 반영)
const updateTodoStatus = async (status) => {
if (!currentEditId) return;
const formData = {
...editForm,
status: status,
seqno: parseInt(editForm.seqno),
expire: editForm.expire || null,
request: editForm.request || null
};
if (!formData.remark.trim()) {
showNotification('할일 내용을 입력해주세요.', 'error');
return;
}
setIsLoading(true);
try {
const response = await fetch('/Todo/UpdateTodo', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
});
const data = await response.json();
if (data.Success) {
setShowEditModal(false);
setCurrentEditId(null);
loadTodos();
showNotification(`상태가 '${getStatusText(status)}'(으)로 변경되었습니다.`, 'success');
} else {
showNotification(data.Message || '상태 변경에 실패했습니다.', 'error');
}
} catch (error) {
console.error('상태 변경 중 오류:', error);
showNotification('서버 연결에 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 할일 편집 모달 열기
const editTodo = async (id) => {
setCurrentEditId(id);
setIsLoading(true);
try {
const response = await fetch(`/Todo/GetTodo?id=${id}`);
const data = await response.json();
if (data.Success && data.Data) {
const todo = data.Data;
setEditForm({
idx: todo.idx,
title: todo.title || '',
remark: todo.remark || '',
expire: todo.expire ? new Date(todo.expire).toISOString().split('T')[0] : '',
seqno: todo.seqno || 0,
flag: todo.flag || false,
request: todo.request || '',
status: todo.status || '0'
});
setShowEditModal(true);
} else {
showNotification(data.Message || '할일 정보를 불러올 수 없습니다.', 'error');
}
} catch (error) {
console.error('할일 조회 중 오류:', error);
showNotification('서버 연결에 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 할일 삭제
const deleteTodo = async (id) => {
if (!confirm('정말로 이 할일을 삭제하시겠습니까?')) {
return;
}
setIsLoading(true);
try {
const response = await fetch(`/Todo/DeleteTodo?id=${id}`, {
method: 'DELETE'
});
const data = await response.json();
if (data.Success) {
loadTodos();
showNotification(data.Message || '할일이 삭제되었습니다.', 'success');
} else {
showNotification(data.Message || '할일 삭제에 실패했습니다.', 'error');
}
} catch (error) {
console.error('할일 삭제 중 오류:', error);
showNotification('서버 연결에 실패했습니다.', 'error');
} finally {
setIsLoading(false);
}
};
// 유틸리티 함수들
const getStatusClass = (status) => {
switch(status) {
case '0': return 'bg-gray-500/20 text-gray-300';
case '1': return 'bg-primary-500/20 text-primary-300';
case '2': return 'bg-danger-500/20 text-danger-300';
case '3': return 'bg-warning-500/20 text-warning-300';
case '5': return 'bg-success-500/20 text-success-300';
default: return 'bg-white/10 text-white/50';
}
};
const getStatusText = (status) => {
switch(status) {
case '0': return '대기';
case '1': return '진행';
case '2': return '취소';
case '3': return '보류';
case '5': return '완료';
default: return '대기';
}
};
const getSeqnoClass = (seqno) => {
switch(seqno) {
case 1: return 'bg-primary-500/20 text-primary-300';
case 2: return 'bg-warning-500/20 text-warning-300';
case 3: return 'bg-danger-500/20 text-danger-300';
default: return 'bg-white/10 text-white/50';
}
};
const getSeqnoText = (seqno) => {
switch(seqno) {
case 1: return '중요';
case 2: return '매우 중요';
case 3: return '긴급';
default: return '보통';
}
};
const formatDate = (dateString) => {
if (!dateString) return '-';
return new Date(dateString).toLocaleDateString('ko-KR');
};
// 알림 표시 함수
const showNotification = (message, type = 'info') => {
const colors = {
info: 'bg-blue-500/90 backdrop-blur-sm',
success: 'bg-green-500/90 backdrop-blur-sm',
warning: 'bg-yellow-500/90 backdrop-blur-sm',
error: 'bg-red-500/90 backdrop-blur-sm'
};
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-4 py-3 rounded-lg z-50 transition-all duration-300 transform translate-x-0 opacity-100 shadow-lg border border-white/20`;
notification.innerHTML = `
<div class="flex items-center">
<span class="ml-2">${message}</span>
</div>
`;
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(0)';
notification.style.opacity = '1';
}, 10);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
// 할일 행 렌더링
const renderTodoRow = (todo, includeOkdate = false) => {
const statusClass = getStatusClass(todo.status);
const statusText = getStatusText(todo.status);
const flagClass = todo.flag ? 'bg-warning-500/20 text-warning-300' : 'bg-white/10 text-white/50';
const flagText = todo.flag ? '고정' : '일반';
const seqnoClass = getSeqnoClass(todo.seqno);
const seqnoText = getSeqnoText(todo.seqno);
const expireText = formatDate(todo.expire);
const isExpired = todo.expire && new Date(todo.expire) < new Date();
const expireClass = isExpired ? 'text-danger-400' : 'text-white/80';
const okdateText = formatDate(todo.okdate);
const okdateClass = todo.okdate ? 'text-success-400' : 'text-white/80';
return (
<tr key={todo.idx} className="hover:bg-white/5 transition-colors cursor-pointer" onClick={() => editTodo(todo.idx)}>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${statusClass}`}>
{statusText}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${flagClass}`}>
{flagText}
</span>
</td>
<td className="px-6 py-4 text-white">{todo.title || '제목 없음'}</td>
<td className="px-6 py-4 text-white/80 max-w-xs truncate">{todo.remark || ''}</td>
<td className="px-6 py-4 text-white/80">{todo.request || '-'}</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${seqnoClass}`}>
{seqnoText}
</span>
</td>
<td className={`px-6 py-4 whitespace-nowrap ${expireClass}`}>{expireText}</td>
{includeOkdate && <td className={`px-6 py-4 whitespace-nowrap ${okdateClass}`}>{okdateText}</td>}
<td className="px-6 py-4 whitespace-nowrap text-sm" onClick={(e) => e.stopPropagation()}>
<button onClick={() => editTodo(todo.idx)} className="text-primary-400 hover:text-primary-300 mr-3 transition-colors">
수정
</button>
<button onClick={() => deleteTodo(todo.idx)} className="text-danger-400 hover:text-danger-300 transition-colors">
삭제
</button>
</td>
</tr>
);
};
return (
<div className="container mx-auto px-4 py-8">
{/* 할일 목록 */}
<div className="glass-effect rounded-2xl overflow-hidden animate-slide-up">
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
</svg>
할일 목록
</h2>
<button onClick={() => setShowAddModal(true)} className="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center text-sm">
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
할일 추가
</button>
</div>
{/* 탭 메뉴 */}
<div className="px-6 py-2 border-b border-white/10">
<div className="flex space-x-1 bg-white/5 rounded-lg p-1">
<button onClick={() => setCurrentTab('active')} className={`flex-1 px-4 py-2 text-sm font-medium rounded-md transition-all duration-200 ${
currentTab === 'active'
? 'text-white bg-white/20 shadow-sm'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}>
<div className="flex items-center justify-center space-x-2">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
<span>진행중인 할일</span>
<span className="px-2 py-0.5 text-xs bg-primary-500/30 text-primary-200 rounded-full">{activeTodos.length}</span>
</div>
</button>
<button onClick={() => setCurrentTab('completed')} className={`flex-1 px-4 py-2 text-sm font-medium rounded-md transition-all duration-200 ${
currentTab === 'completed'
? 'text-white bg-white/20 shadow-sm'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}>
<div className="flex items-center justify-center space-x-2">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>완료된 할일</span>
<span className="px-2 py-0.5 text-xs bg-success-500/30 text-success-200 rounded-full">{completedTodos.length}</span>
</div>
</button>
</div>
</div>
{/* 진행중인 할일 테이블 */}
{currentTab === 'active' && (
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-white/10">
<tr>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">진행상태</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">플래그</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">제목</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">내용</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">요청자</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">중요도</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">만료일</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">작업</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{isLoading ? (
<tr>
<td colSpan="8" className="p-8 text-center">
<div className="inline-flex items-center">
<div className="loading inline-block w-5 h-5 border-3 border-white/30 border-t-white rounded-full animate-spin mr-3"></div>
<span className="text-white/80">데이터를 불러오는 ...</span>
</div>
</td>
</tr>
) : activeTodos.length === 0 ? (
<tr>
<td colSpan="8" className="px-6 py-8 text-center text-white/50">
진행중인 할일이 없습니다
</td>
</tr>
) : (
activeTodos.map(todo => renderTodoRow(todo, false))
)}
</tbody>
</table>
</div>
)}
{/* 완료된 할일 테이블 */}
{currentTab === 'completed' && (
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-white/10">
<tr>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">진행상태</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">플래그</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">제목</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">내용</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">요청자</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">중요도</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">만료일</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">완료일</th>
<th className="px-6 py-4 text-left text-xs font-medium text-white/70 uppercase tracking-wider">작업</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{isLoading ? (
<tr>
<td colSpan="9" className="p-8 text-center">
<div className="inline-flex items-center">
<div className="loading inline-block w-5 h-5 border-3 border-white/30 border-t-white rounded-full animate-spin mr-3"></div>
<span className="text-white/80">데이터를 불러오는 ...</span>
</div>
</td>
</tr>
) : completedTodos.length === 0 ? (
<tr>
<td colSpan="9" className="px-6 py-8 text-center text-white/50">
완료된 할일이 없습니다
</td>
</tr>
) : (
completedTodos.map(todo => renderTodoRow(todo, true))
)}
</tbody>
</table>
</div>
)}
</div>
{/* 로딩 인디케이터 */}
{isLoading && (
<div className="fixed top-4 right-4 bg-white/20 backdrop-blur-sm rounded-full px-4 py-2 text-white text-sm">
<div className="flex items-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
처리 ...
</div>
</div>
)}
{/* 새 할일 추가 모달 */}
{showAddModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-2xl animate-slide-up">
{/* 모달 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
할일 추가
</h2>
<button onClick={() => setShowAddModal(false)} className="text-white/70 hover:text-white transition-colors">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
{/* 모달 내용 */}
<div className="p-6">
<form onSubmit={addTodo} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-2">제목 (선택사항)</label>
<input
type="text"
value={todoForm.title}
onChange={(e) => setTodoForm({...todoForm, title: e.target.value})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="할일 제목을 입력하세요"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">만료일 (선택사항)</label>
<input
type="date"
value={todoForm.expire}
onChange={(e) => setTodoForm({...todoForm, expire: e.target.value})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">내용 *</label>
<textarea
value={todoForm.remark}
onChange={(e) => setTodoForm({...todoForm, remark: e.target.value})}
rows="3"
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="할일 내용을 입력하세요 (필수)"
required
></textarea>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">요청자</label>
<input
type="text"
value={todoForm.request}
onChange={(e) => setTodoForm({...todoForm, request: e.target.value})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="업무 요청자를 입력하세요"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-2">진행상태</label>
<div className="flex flex-wrap gap-2">
{[
{value: '0', label: '대기', class: 'bg-gray-500/20 text-gray-300'},
{value: '1', label: '진행', class: 'bg-primary-500/20 text-primary-300'},
{value: '3', label: '보류', class: 'bg-warning-500/20 text-warning-300'},
{value: '2', label: '취소', class: 'bg-danger-500/20 text-danger-300'},
{value: '5', label: '완료', class: 'bg-success-500/20 text-success-300'}
].map(status => (
<button
key={status.value}
type="button"
onClick={() => setTodoForm({...todoForm, status: status.value})}
className={`px-3 py-1 rounded-lg text-xs font-medium transition-all ${
todoForm.status === status.value
? status.class + ' border-' + status.class.split(' ')[0].replace('bg-', '').replace('/20', '/30')
: 'bg-white/10 text-white/50 border border-white/20 hover:bg-white/20'
}`}
>
{status.label}
</button>
))}
</div>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">중요도</label>
<select
value={todoForm.seqno}
onChange={(e) => setTodoForm({...todoForm, seqno: parseInt(e.target.value)})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
>
<option value="0">보통</option>
<option value="1">중요</option>
<option value="2">매우 중요</option>
<option value="3">긴급</option>
</select>
</div>
<div className="flex items-end">
<label className="flex items-center text-white/70 text-sm font-medium">
<input
type="checkbox"
checked={todoForm.flag}
onChange={(e) => setTodoForm({...todoForm, flag: e.target.checked})}
className="mr-2 text-primary-500 focus:ring-primary-400 focus:ring-offset-0 rounded"
/>
플래그 (상단 고정)
</label>
</div>
</div>
</form>
</div>
{/* 모달 푸터 */}
<div className="px-6 py-4 border-t border-white/10 flex justify-end space-x-3">
<button type="button" onClick={() => setShowAddModal(false)} className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors">
취소
</button>
<button type="submit" form="todoForm" onClick={addTodo} className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg transition-colors flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
추가
</button>
</div>
</div>
</div>
</div>
)}
{/* 수정 모달 */}
{showEditModal && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50">
<div className="flex items-center justify-center min-h-screen p-4">
<div className="glass-effect rounded-2xl w-full max-w-2xl animate-slide-up">
{/* 모달 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<h2 className="text-xl font-semibold text-white flex items-center">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
할일 수정
</h2>
<button onClick={() => {setShowEditModal(false); setCurrentEditId(null);}} className="text-white/70 hover:text-white transition-colors">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
{/* 모달 내용 */}
<div className="p-6">
<div className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-2">제목 (선택사항)</label>
<input
type="text"
value={editForm.title}
onChange={(e) => setEditForm({...editForm, title: e.target.value})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="할일 제목을 입력하세요"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">만료일 (선택사항)</label>
<input
type="date"
value={editForm.expire}
onChange={(e) => setEditForm({...editForm, expire: e.target.value})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
/>
</div>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">내용 *</label>
<textarea
value={editForm.remark}
onChange={(e) => setEditForm({...editForm, remark: e.target.value})}
rows="3"
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="할일 내용을 입력하세요 (필수)"
required
></textarea>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">요청자</label>
<input
type="text"
value={editForm.request}
onChange={(e) => setEditForm({...editForm, request: e.target.value})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
placeholder="업무 요청자를 입력하세요"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-2">진행상태</label>
<div className="flex flex-wrap gap-2">
{[
{value: '0', label: '대기', class: 'bg-gray-500/20 text-gray-300'},
{value: '1', label: '진행', class: 'bg-primary-500/20 text-primary-300'},
{value: '3', label: '보류', class: 'bg-warning-500/20 text-warning-300'},
{value: '2', label: '취소', class: 'bg-danger-500/20 text-danger-300'},
{value: '5', label: '완료', class: 'bg-success-500/20 text-success-300'}
].map(status => (
<button
key={status.value}
type="button"
onClick={() => updateTodoStatus(status.value)}
className={`px-3 py-1 rounded-lg text-xs font-medium transition-all ${
editForm.status === status.value
? status.class + ' border-' + status.class.split(' ')[0].replace('bg-', '').replace('/20', '/30')
: 'bg-white/10 text-white/50 border border-white/20 hover:bg-white/20'
}`}
>
{status.label}
</button>
))}
</div>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-2">중요도</label>
<select
value={editForm.seqno}
onChange={(e) => setEditForm({...editForm, seqno: parseInt(e.target.value)})}
className="w-full bg-white/20 backdrop-blur-sm border border-white/30 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 transition-all"
>
<option value="0">보통</option>
<option value="1">중요</option>
<option value="2">매우 중요</option>
<option value="3">긴급</option>
</select>
</div>
<div>
<label className="flex items-center text-white/70 text-sm font-medium mt-6">
<input
type="checkbox"
checked={editForm.flag}
onChange={(e) => setEditForm({...editForm, flag: e.target.checked})}
className="mr-2 text-primary-500 focus:ring-primary-400 focus:ring-offset-0 rounded"
/>
플래그 (상단 고정)
</label>
</div>
</div>
</div>
</div>
{/* 모달 푸터 */}
<div className="px-6 py-4 border-t border-white/10 flex justify-end space-x-3">
<button onClick={() => {setShowEditModal(false); setCurrentEditId(null);}} className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors">
취소
</button>
<button onClick={updateTodo} className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg transition-colors">
수정
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
}

View File

@@ -1,270 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>공용코드관리 (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<!-- Tailwind 설정 -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
},
warning: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f',
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
}
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
}
}
}
}
}
</script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* 스크롤바 스타일링 */
.custom-scrollbar::-webkit-scrollbar {
width: 16px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* 애니메이션 */
.animate-fade-in {
animation: fadeIn 0.5s ease-in-out;
}
.animate-slide-up {
animation: slideUp 0.3s ease-out;
}
/* 셀렉트 박스 옵션 스타일링 */
select option {
background-color: #374151 !important;
color: white !important;
}
select option:hover {
background-color: #4B5563 !important;
}
select option:checked {
background-color: #6366F1 !important;
}
/* 테이블 셀 텍스트 오버플로우 처리 */
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 값(문자열) 열 최대 너비 제한 */
.svalue-cell {
max-width: 128px; /* w-32 = 128px */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 개발중 경고 스타일 */
.dev-warning {
position: fixed;
bottom: 20px;
right: 20px;
background: linear-gradient(135deg, #fbbf24, #f59e0b);
color: white;
padding: 12px 20px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(245, 158, 11, 0.3);
z-index: 1000;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.dev-warning .icon {
width: 20px;
height: 20px;
margin-right: 8px;
flex-shrink: 0;
}
.dev-warning .title {
font-weight: 600;
margin: 0;
font-size: 14px;
}
.dev-warning .description {
font-size: 12px;
opacity: 0.9;
margin: 2px 0 0 0;
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<!-- 로딩 스켈레톤 UI -->
<div id="react-common-app" class="animate-fade-in">
<div class="container mx-auto px-4 py-8">
<div class="flex gap-6 h-[calc(100vh-200px)]">
<!-- 좌측 스켈레톤 -->
<div class="w-80">
<div class="glass-effect rounded-2xl h-full animate-pulse">
<div class="p-4 border-b border-white/10">
<div class="h-6 bg-white/20 rounded w-3/4"></div>
</div>
<div class="p-4 space-y-3">
<div class="h-16 bg-white/10 rounded-lg"></div>
<div class="h-16 bg-white/10 rounded-lg"></div>
<div class="h-16 bg-white/10 rounded-lg"></div>
</div>
</div>
</div>
<!-- 우측 스켈레톤 -->
<div class="flex-1">
<div class="glass-effect rounded-2xl h-full animate-pulse">
<div class="p-4 border-b border-white/10 flex justify-between items-center">
<div class="h-6 bg-white/20 rounded w-1/3"></div>
<div class="h-8 bg-white/20 rounded w-20"></div>
</div>
<div class="p-4">
<div class="space-y-3">
<div class="h-10 bg-white/10 rounded"></div>
<div class="h-10 bg-white/10 rounded"></div>
<div class="h-10 bg-white/10 rounded"></div>
<div class="h-10 bg-white/10 rounded"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 네비게이션 컴포넌트 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<!-- 개발중 경고 컴포넌트 -->
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- App 컴포넌트 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
return (
<div>
<CommonNavigation currentPage="common" />
<CommonCode />
</div>
);
}
</script>
<!-- 공용코드 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonCode"></script>
<!-- 앱 초기화 -->
<script type="text/babel">
const root = ReactDOM.createRoot(document.getElementById('react-common-app'));
root.render(<App />);
</script>
</body>
</html>

View File

@@ -1,297 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="version" content="v2.0-20250905-react">
<title>근태현황 대시보드 (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
},
warning: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f',
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
}
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
}
}
}
}
}
</script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* 스크롤바 스타일링 */
.custom-scrollbar::-webkit-scrollbar {
width: 16px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* React 컴포넌트 로딩 스타일 */
.react-dashboard-container {
min-height: 100vh;
}
.loading-skeleton {
background: linear-gradient(90deg, rgba(255,255,255,0.1) 25%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.1) 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
</style>
</head>
<body>
<div id="react-dashboard-app" class="react-dashboard-container">
<!-- 로딩 화면 -->
<div class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<div class="container mx-auto px-4 py-8">
<!-- 로딩 헤더 -->
<div class="text-center mb-8">
<div class="loading-skeleton h-10 w-80 mx-auto rounded-lg mb-4"></div>
<div class="loading-skeleton h-6 w-60 mx-auto rounded-lg mb-2"></div>
<div class="loading-skeleton h-4 w-40 mx-auto rounded-lg"></div>
</div>
<!-- 로딩 통계 카드들 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8">
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-16 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-success-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-16 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-warning-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-16 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-primary-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-20 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-danger-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-20 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-purple-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
</div>
<!-- 로딩 추가 정보 -->
<div class="glass-effect rounded-2xl p-6">
<div class="loading-skeleton h-8 w-32 rounded mb-4"></div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="text-center">
<div class="w-16 h-16 bg-success-500/20 rounded-full mx-auto mb-2">
<div class="loading-skeleton w-full h-full rounded-full"></div>
</div>
<div class="loading-skeleton h-4 w-16 rounded mx-auto mb-1"></div>
<div class="loading-skeleton h-3 w-24 rounded mx-auto"></div>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-warning-500/20 rounded-full mx-auto mb-2">
<div class="loading-skeleton w-full h-full rounded-full"></div>
</div>
<div class="loading-skeleton h-4 w-16 rounded mx-auto mb-1"></div>
<div class="loading-skeleton h-3 w-24 rounded mx-auto"></div>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-primary-500/20 rounded-full mx-auto mb-2">
<div class="loading-skeleton w-full h-full rounded-full"></div>
</div>
<div class="loading-skeleton h-4 w-16 rounded mx-auto mb-1"></div>
<div class="loading-skeleton h-3 w-24 rounded mx-auto"></div>
</div>
</div>
</div>
<!-- 로딩 텍스트 -->
<div class="text-center mt-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-white mx-auto mb-4"></div>
<p class="text-white/70">React Dashboard 컴포넌트를 로딩 중입니다...</p>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- Dashboard 컴포넌트 -->
<script type="text/babel" src="/react/component/DashboardApp"></script>
<!-- 앱 초기화 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
console.log('✅ App 컴포넌트 렌더링 시작');
console.log('📊 CommonNavigation 사용 가능:', typeof CommonNavigation);
console.log('📊 DashboardApp 사용 가능:', typeof DashboardApp);
console.log('📊 DevWarning 사용 가능:', typeof DevWarning);
return (
<div>
<CommonNavigation currentPage="dashboard" />
<DashboardApp />
<DevWarning show={false} />
</div>
);
}
// 루트 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-dashboard-app'));
root.render(<App />);
console.log('✅ React Dashboard 앱이 마운트되었습니다.');
</script>
</body>
</html>

View File

@@ -1,261 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>업무일지 (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* 스크롤바 스타일링 */
.custom-scrollbar::-webkit-scrollbar {
width: 16px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* 애니메이션 */
.animate-fade-in {
animation: fadeIn 0.5s ease-in-out;
}
.animate-slide-up {
animation: slideUp 0.3s ease-out;
}
/* 셀렉트 박스 옵션 스타일링 */
select option {
background-color: #374151 !important;
color: white !important;
}
select option:hover {
background-color: #4B5563 !important;
}
select option:checked {
background-color: #6366F1 !important;
}
/* 개발중 경고 스타일 */
.dev-warning {
position: fixed;
bottom: 20px;
right: 20px;
background: linear-gradient(135deg, #fbbf24, #f59e0b);
color: white;
padding: 12px 20px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(245, 158, 11, 0.3);
z-index: 1000;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.dev-warning .icon {
width: 20px;
height: 20px;
margin-right: 8px;
flex-shrink: 0;
}
.dev-warning .title {
font-weight: 600;
margin: 0;
font-size: 14px;
}
.dev-warning .description {
font-size: 12px;
opacity: 0.9;
margin: 2px 0 0 0;
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<!-- 로딩 스켈레톤 UI -->
<div id="react-jobreport-app" class="animate-fade-in">
<div class="container mx-auto px-4 py-8">
{/* 스켈레톤 UI */}
<div className="space-y-6">
{/* 경고 메시지 스켈레톤 */}
<div className="bg-white/10 rounded-lg p-4 animate-pulse">
<div className="h-6 bg-white/20 rounded w-3/4"></div>
</div>
{/* 통계 카드 스켈레톤 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="glass-effect rounded-lg p-6 animate-pulse">
<div className="flex items-center">
<div className="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div className="h-4 bg-white/20 rounded w-20 mb-2"></div>
<div className="h-6 bg-white/20 rounded w-12"></div>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 animate-pulse">
<div className="flex items-center">
<div className="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div className="h-4 bg-white/20 rounded w-24 mb-2"></div>
<div className="h-6 bg-white/20 rounded w-16"></div>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 animate-pulse">
<div className="flex items-center">
<div className="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div className="h-4 bg-white/20 rounded w-20 mb-2"></div>
<div className="h-6 bg-white/20 rounded w-14"></div>
</div>
</div>
</div>
<div className="glass-effect rounded-lg p-6 animate-pulse">
<div className="flex items-center">
<div className="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div className="h-4 bg-white/20 rounded w-28 mb-2"></div>
<div className="h-6 bg-white/20 rounded w-8"></div>
</div>
</div>
</div>
</div>
{/* 필터 스켈레톤 */}
<div className="glass-effect rounded-lg p-4 animate-pulse">
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2 space-y-3">
<div className="grid grid-cols-3 gap-3">
<div className="h-8 bg-white/10 rounded"></div>
<div className="h-8 bg-white/10 rounded"></div>
<div className="h-8 bg-white/10 rounded"></div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="h-8 bg-white/10 rounded"></div>
<div className="h-8 bg-white/10 rounded"></div>
</div>
</div>
<div className="space-y-2">
<div className="h-10 bg-white/10 rounded"></div>
<div className="h-10 bg-white/10 rounded"></div>
<div className="h-10 bg-white/10 rounded"></div>
</div>
</div>
</div>
{/* 테이블 스켈레톤 */}
<div className="glass-effect rounded-lg p-4 animate-pulse">
<div className="space-y-3">
<div className="grid grid-cols-7 gap-4">
<div className="h-6 bg-white/20 rounded"></div>
<div className="h-6 bg-white/20 rounded"></div>
<div className="h-6 bg-white/20 rounded"></div>
<div className="h-6 bg-white/20 rounded"></div>
<div className="h-6 bg-white/20 rounded"></div>
<div className="h-6 bg-white/20 rounded"></div>
<div className="h-6 bg-white/20 rounded"></div>
</div>
<div className="space-y-2">
<div className="grid grid-cols-7 gap-4">
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
</div>
<div className="grid grid-cols-7 gap-4">
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
</div>
<div className="grid grid-cols-7 gap-4">
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
<div className="h-4 bg-white/10 rounded"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- 앱 컴포넌트 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
return (
<div>
<CommonNavigation currentPage="jobreport" />
<JobReport />
<DevWarning />
</div>
);
}
</script>
<!-- 업무일지 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/JobReport"></script>
<!-- 앱 초기화 -->
<script type="text/babel">
const root = ReactDOM.createRoot(document.getElementById('react-jobreport-app'));
root.render(<App />);
</script>
</body>
</html>

View File

@@ -1,120 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React JSX Test - GroupWare</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 900px;
margin: 0 auto;
background-color: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.header h1 {
color: #333;
margin: 0;
font-size: 2.5em;
}
.header p {
color: #666;
font-size: 1.2em;
margin: 10px 0;
}
.status {
padding: 20px;
margin: 20px 0;
border-radius: 8px;
}
.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.loading {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
}
.error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
.tech-info {
background-color: #e3f2fd;
border: 1px solid #90caf9;
color: #0d47a1;
margin: 20px 0;
padding: 15px;
border-radius: 8px;
}
.version-info {
font-size: 0.9em;
color: #666;
text-align: center;
margin-top: 30px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 8px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 React JSX Integration</h1>
<p>GroupWare + OWIN + React + JSX 모듈화 테스트</p>
<div class="tech-info">
<strong>기술 스택:</strong> C# WinForms + OWIN + React 18 + Babel JSX + 모듈화된 컴포넌트
</div>
</div>
<div id="react-app">
<div class="status loading">
React JSX 컴포넌트를 로딩 중입니다...
</div>
</div>
<div class="version-info">
<p><strong>환경:</strong> .NET Framework 4.6 + OWIN + React 18.2.0</p>
<p><strong>포트:</strong> 7979 | <strong>파일 위치:</strong> /react-jsx-test.html</p>
</div>
</div>
<!-- React & ReactDOM CDN -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel standalone for JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- JSX Component Import -->
<script type="text/babel" src="/react/component/TestApp"></script>
<!-- App Initialization -->
<script type="text/babel">
// JSX 컴포넌트를 DOM에 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-app'));
root.render(<TestApp />);
console.log('✅ React JSX 컴포넌트가 성공적으로 마운트되었습니다.');
console.log('📁 컴포넌트 파일: /react/TestApp.jsx');
console.log('🌐 테스트 페이지: /react-jsx-test.html');
</script>
</body>
</html>

View File

@@ -1,241 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>근태관리 (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid #ffffff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.table-container {
max-height: 600px;
overflow-y: auto;
}
/* 스크롤바 스타일링 */
.custom-scrollbar::-webkit-scrollbar {
width: 16px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* 애니메이션 */
.animate-fade-in {
animation: fadeIn 0.5s ease-in-out;
}
.animate-slide-up {
animation: slideUp 0.3s ease-out;
}
/* 개발중 경고 스타일 */
.dev-warning {
position: fixed;
bottom: 20px;
right: 20px;
background: linear-gradient(135deg, #fbbf24, #f59e0b);
color: white;
padding: 12px 20px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(245, 158, 11, 0.3);
z-index: 1000;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.dev-warning .icon {
width: 20px;
height: 20px;
margin-right: 8px;
flex-shrink: 0;
}
.dev-warning .title {
font-weight: 600;
margin: 0;
font-size: 14px;
}
.dev-warning .description {
font-size: 12px;
opacity: 0.9;
margin: 2px 0 0 0;
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<!-- 로딩 스켈레톤 UI -->
<div id="react-kuntae-app" class="animate-fade-in">
<div class="container mx-auto px-4 py-8">
<div class="space-y-6">
{/* 경고 메시지 스켈레톤 */}
<div class="bg-white/10 rounded-lg p-4 animate-pulse">
<div class="h-6 bg-white/20 rounded w-3/4"></div>
</div>
{/* 필터 스켈레톤 */}
<div class="glass-effect rounded-lg p-6 animate-pulse">
<div class="grid grid-cols-3 gap-4">
<div class="h-16 bg-white/10 rounded"></div>
<div class="h-16 bg-white/10 rounded"></div>
<div class="h-16 bg-white/10 rounded"></div>
</div>
</div>
{/* 통계 카드 스켈레톤 */}
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="glass-effect rounded-lg p-6 animate-pulse">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div class="h-4 bg-white/20 rounded w-20 mb-2"></div>
<div class="h-6 bg-white/20 rounded w-12"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-lg p-6 animate-pulse">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div class="h-4 bg-white/20 rounded w-16 mb-2"></div>
<div class="h-6 bg-white/20 rounded w-8"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-lg p-6 animate-pulse">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div class="h-4 bg-white/20 rounded w-16 mb-2"></div>
<div class="h-6 bg-white/20 rounded w-8"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-lg p-6 animate-pulse">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-lg mr-4"></div>
<div>
<div class="h-4 bg-white/20 rounded w-16 mb-2"></div>
<div class="h-6 bg-white/20 rounded w-8"></div>
</div>
</div>
</div>
</div>
{/* 테이블 스켈레톤 */}
<div class="glass-effect rounded-lg p-4 animate-pulse">
<div class="space-y-3">
<div class="grid grid-cols-7 gap-4">
<div class="h-6 bg-white/20 rounded"></div>
<div class="h-6 bg-white/20 rounded"></div>
<div class="h-6 bg-white/20 rounded"></div>
<div class="h-6 bg-white/20 rounded"></div>
<div class="h-6 bg-white/20 rounded"></div>
<div class="h-6 bg-white/20 rounded"></div>
<div class="h-6 bg-white/20 rounded"></div>
</div>
<div class="space-y-2">
<div class="grid grid-cols-7 gap-4">
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
</div>
<div class="grid grid-cols-7 gap-4">
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
<div class="h-4 bg-white/10 rounded"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- 근태관리 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/Kuntae"></script>
<!-- 앱 컴포넌트 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
return (
<div>
<CommonNavigation currentPage="kuntae" />
<Kuntae />
<DevWarning />
</div>
);
}
// 루트 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-kuntae-app'));
root.render(<App />);
</script>
</body>
</html>

View File

@@ -1,177 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로그인 - GroupWare (React)</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
},
warning: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f',
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
}
},
animation: {
'fade-in': 'fadeIn 0.6s ease-in-out',
'slide-up': 'slideUp 0.4s ease-out',
'bounce-in': 'bounceIn 0.6s ease-out',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(20px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
bounceIn: {
'0%': { transform: 'scale(0.3)', opacity: '0' },
'50%': { transform: 'scale(1.05)' },
'70%': { transform: 'scale(0.9)' },
'100%': { transform: 'scale(1)', opacity: '1' },
}
}
}
}
}
</script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-2px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.input-focus {
transition: all 0.3s ease;
}
.input-focus:focus {
transform: scale(1.02);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.floating-label {
transition: all 0.3s ease;
}
.input-field:focus + .floating-label,
.input-field:not(:placeholder-shown) + .floating-label {
transform: translateY(-1.5rem) scale(0.85);
color: #3b82f6;
}
/* 드롭다운 스타일 */
select.input-field option {
background-color: #1f2937;
color: white;
}
select.input-field:focus option:checked {
background-color: #3b82f6;
}
select.input-field option:hover {
background-color: #374151;
}
/* React 컴포넌트 전용 스타일 */
.react-login-container {
min-height: 100vh;
}
</style>
</head>
<body>
<div id="react-login-app" class="react-login-container">
<div class="gradient-bg min-h-screen flex items-center justify-center p-4">
<div class="w-full max-w-md">
<div class="glass-effect rounded-3xl p-8 card-hover">
<div class="text-center">
<div class="w-16 h-16 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-white animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
<h1 class="text-2xl font-bold text-white mb-2">GroupWare</h1>
<p class="text-white/70 text-sm">React 로그인 컴포넌트를 로딩 중입니다...</p>
<div class="mt-6">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-white mx-auto"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- React & ReactDOM CDN -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel standalone for JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- React Login Component -->
<script type="text/babel" src="/react/component/LoginApp"></script>
<!-- App Initialization -->
<script type="text/babel">
const root = ReactDOM.createRoot(document.getElementById('react-login-app'));
root.render(<LoginApp />);
console.log('✅ React 로그인 페이지가 성공적으로 마운트되었습니다.');
console.log('📁 컴포넌트 파일: /react/LoginApp.jsx');
console.log('🌐 페이지 URL: /react/login');
</script>
</body>
</html>

View File

@@ -1,191 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>프로젝트 관리 - GroupWare (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.animate-slide-up {
animation: slideUp 0.5s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.bg-primary-500 {
background-color: #3b82f6;
}
.bg-primary-600 {
background-color: #2563eb;
}
.hover\:bg-primary-600:hover {
background-color: #2563eb;
}
.bg-green-500 {
background-color: #10b981;
}
.bg-green-600 {
background-color: #059669;
}
.hover\:bg-green-600:hover {
background-color: #059669;
}
.bg-red-500 {
background-color: #ef4444;
}
.bg-red-600 {
background-color: #dc2626;
}
.hover\:bg-red-600:hover {
background-color: #dc2626;
}
select option {
background-color: #1f2937;
color: white;
padding: 8px;
}
select option:hover {
background-color: #374151;
}
select option:checked {
background-color: #3b82f6;
}
select option:focus {
background-color: #374151;
}
.loading {
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top: 3px solid #fff;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<div id="react-project">
<!-- 스켈레톤 로딩 UI -->
<div class="container mx-auto px-4 py-8">
<div class="glass-effect rounded-lg overflow-hidden animate-pulse">
<div class="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<div class="h-6 bg-white/20 rounded w-40"></div>
<div class="flex space-x-3">
<div class="h-10 bg-white/20 rounded w-32"></div>
<div class="h-10 bg-white/20 rounded w-24"></div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 p-4">
<div class="bg-white/10 rounded-lg p-4">
<div class="h-8 bg-white/20 rounded mb-2"></div>
<div class="h-4 bg-white/20 rounded w-16"></div>
</div>
<div class="bg-white/10 rounded-lg p-4">
<div class="h-8 bg-white/20 rounded mb-2"></div>
<div class="h-4 bg-white/20 rounded w-16"></div>
</div>
<div class="bg-white/10 rounded-lg p-4">
<div class="h-8 bg-white/20 rounded mb-2"></div>
<div class="h-4 bg-white/20 rounded w-16"></div>
</div>
<div class="bg-white/10 rounded-lg p-4">
<div class="h-8 bg-white/20 rounded mb-2"></div>
<div class="h-4 bg-white/20 rounded w-16"></div>
</div>
</div>
<div class="p-6">
<div class="space-y-4">
<div class="h-4 bg-white/20 rounded w-full"></div>
<div class="h-4 bg-white/20 rounded w-3/4"></div>
<div class="h-4 bg-white/20 rounded w-1/2"></div>
</div>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- Project 컴포넌트 -->
<script type="text/babel" src="/react/component/Project"></script>
<!-- 앱 초기화 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
return (
<div>
<CommonNavigation currentPage="project" />
<Project />
<DevWarning />
</div>
);
}
// 루트 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-project'));
root.render(<App />);
</script>
</body>
</html>

View File

@@ -1,197 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Test Page - GroupWare</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header {
text-align: center;
margin-bottom: 30px;
}
.status {
padding: 20px;
margin: 20px 0;
border-radius: 5px;
}
.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.loading {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
}
.error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 React Integration Test</h1>
<p>GroupWare + OWIN + React 통합 테스트</p>
</div>
<div id="react-app">
<div class="status loading">
React 컴포넌트를 로딩 중입니다...
</div>
</div>
</div>
<!-- React & ReactDOM CDN -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel standalone for JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- React Component -->
<script type="text/babel">
const { useState, useEffect } = React;
// 메인 React 컴포넌트
function ReactTestApp() {
const [status, setStatus] = useState('loading');
const [counter, setCounter] = useState(0);
const [serverTime, setServerTime] = useState('');
const [apiTest, setApiTest] = useState({ status: 'pending', message: '' });
// 컴포넌트가 마운트될 때 실행
useEffect(() => {
// React가 정상적으로 로드되었음을 표시
setTimeout(() => {
setStatus('success');
setServerTime(new Date().toLocaleString('ko-KR'));
}, 1000);
// API 테스트 (GroupWare의 기존 컨트롤러 테스트)
testAPI();
}, []);
// GroupWare API 테스트 함수
const testAPI = async () => {
try {
// Home 컨트롤러 테스트 (기존에 있을 것으로 예상)
const response = await fetch('/Home');
if (response.ok) {
setApiTest({ status: 'success', message: 'API 연결 성공' });
} else {
setApiTest({ status: 'warning', message: `API 응답: ${response.status}` });
}
} catch (error) {
setApiTest({ status: 'error', message: `API 오류: ${error.message}` });
}
};
return (
<div>
<div className={`status ${status === 'success' ? 'success' : 'loading'}`}>
{status === 'success' ? (
<div>
<h3> React 컴포넌트가 성공적으로 로드되었습니다!</h3>
<p><strong>현재 시간:</strong> {serverTime}</p>
</div>
) : (
<h3>React 컴포넌트를 로딩 중입니다...</h3>
)}
</div>
{status === 'success' && (
<div>
<div className="status">
<h3>📊 상태 관리 테스트</h3>
<p><strong>카운터:</strong> {counter}</p>
<button
onClick={() => setCounter(counter + 1)}
style={{
padding: '10px 20px',
marginRight: '10px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
증가 (+1)
</button>
<button
onClick={() => setCounter(0)}
style={{
padding: '10px 20px',
backgroundColor: '#6c757d',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
리셋
</button>
</div>
<div className={`status ${
apiTest.status === 'success' ? 'success' :
apiTest.status === 'error' ? 'error' : 'loading'
}`}>
<h3>🌐 API 연결 테스트</h3>
<p><strong>상태:</strong> {apiTest.message}</p>
<button
onClick={testAPI}
style={{
padding: '10px 20px',
backgroundColor: '#28a745',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
API 다시 테스트
</button>
</div>
<div className="status">
<h3>📋 통합 테스트 체크리스트</h3>
<ul style={{ textAlign: 'left' }}>
<li> OWIN 정적 파일 서빙</li>
<li> React 라이브러리 로딩 (CDN)</li>
<li> JSX 컴파일 (Babel)</li>
<li> React Hooks (useState, useEffect)</li>
<li> 이벤트 핸들링</li>
<li> API 호출 (fetch)</li>
<li> 반응형 UI 업데이트</li>
</ul>
</div>
</div>
)}
</div>
);
}
// React 컴포넌트를 DOM에 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-app'));
root.render(<ReactTestApp />);
</script>
</body>
</html>

View File

@@ -1,115 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="version" content="v1.0-20250127">
<title>할일 관리 - GroupWare (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.loading {
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top: 3px solid #fff;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<div id="react-todo">
<!-- 스켈레톤 로딩 UI -->
<div class="container mx-auto px-4 py-8">
<div class="glass-effect rounded-2xl overflow-hidden animate-pulse">
<div class="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<div class="h-6 bg-white/20 rounded w-40"></div>
<div class="h-10 bg-white/20 rounded w-32"></div>
</div>
<div class="p-6">
<div class="space-y-4">
<div class="h-4 bg-white/20 rounded w-full"></div>
<div class="h-4 bg-white/20 rounded w-3/4"></div>
<div class="h-4 bg-white/20 rounded w-1/2"></div>
</div>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- Todo 컴포넌트 -->
<script type="text/babel" src="/react/component/Todo"></script>
<!-- 앱 초기화 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
return (
<div>
<CommonNavigation currentPage="todo" />
<Todo />
<DevWarning />
</div>
);
}
// 루트 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-todo'));
root.render(<App />);
</script>
</body>
</html>

View File

@@ -1092,6 +1092,12 @@ namespace Project
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.otconfirm));
//if(curLevel < 5)
//{
// Util.MsgE("이 기능을 사용할 권한이 없습니다");
// return;
//}
var f = new FPJ0000.JobReport_.rJobReportOT();
f.Show();
}

Submodule Sub/tcpservice updated: 1680e266da...d7fe2baa0e

View File

@@ -27,7 +27,7 @@ namespace FBS0000
tbGrp.Text = string.Empty;
//this.dv1.CellFormatting += dv1_CellFormatting;
}
private void __Load(object sender, EventArgs e)
{
@@ -95,7 +95,7 @@ namespace FBS0000
//var dtUser = JobReport.OrderBy(t => t.userProcess + t.name).GroupBy(t => t.id);// taUser.GetData(FCOMMON.info.Login.gcode, tbGrp.Text);
var dtUser = FCOMMON.DBM.getActiveUserTable(); //업무일지 미사용자로인해서 사용자 목록은 이것을 사용한다 220215
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
var users = new List<userinfo>();
var seq = 0;
foreach (System.Data.DataRow dr in dtUser.Rows)
@@ -126,9 +126,14 @@ namespace FBS0000
if (dr["outdate"] == null) newuser.outdate = string.Empty;
newuser.outdate = dr["outdate"].ToString();
if (curLevel >= 5 || newuser.empno == FCOMMON.info.Login.no)
{
users.Add(newuser);
seq += 1;
}
users.Add(newuser);
seq += 1;
}
//휴가테이블에서 데이터를 가져온다
@@ -154,8 +159,8 @@ namespace FBS0000
}
//이월잔액
var jand = qta.WorkUserJan_Yesterday_Day(FCOMMON.info.Login.gcode, item.empno, sd.ToString("yyyy-01-01"), sd.AddDays(-1).ToShortDateString(), "999999","%");
var jan = qta.WorkUserJan_Yesterday_Day(FCOMMON.info.Login.gcode, item.empno, sd.ToString("yyyy-01-01"), ed.ToShortDateString(), "999999","%");
var jand = qta.WorkUserJan_Yesterday_Day(FCOMMON.info.Login.gcode, item.empno, sd.ToString("yyyy-01-01"), sd.AddDays(-1).ToShortDateString(), "999999", "%");
var jan = qta.WorkUserJan_Yesterday_Day(FCOMMON.info.Login.gcode, item.empno, sd.ToString("yyyy-01-01"), ed.ToShortDateString(), "999999", "%");
var used = 0.0;
fpSpread1_Sheet1.Rows[rowindex].ResetBorder();
@@ -212,7 +217,7 @@ namespace FBS0000
//현재인원값
var bIndate = DateTime.TryParse(item.indate, out DateTime dtIn);
var bOutdate = DateTime.TryParse(item.outdate, out DateTime dtOut);
// Boolean usePerson = true;
// Boolean usePerson = true;
//
//if (bIndate == true && curDate.ToShortDateString().CompareTo(dtIn.ToShortDateString()) < 0)
@@ -258,7 +263,7 @@ namespace FBS0000
}
//근태 시작일자가 조회시작일보다 적다면, 데이터가 걸쳐진 것이므로 회색으로 처리하자 2308320
if(dr.sdate.ToShortDateString().CompareTo(sd.ToShortDateString()) < 0)
if (dr.sdate.ToShortDateString().CompareTo(sd.ToShortDateString()) < 0)
{
fpSpread1_Sheet1.Cells[rowindex, c].BackColor = Color.DimGray;
fpSpread1_Sheet1.Cells[rowindex, c].ForeColor = Color.White;
@@ -352,7 +357,7 @@ namespace FBS0000
fpSpread1_Sheet1.Columns[c].Tag = ps;
}
used = (float)jand-(float)jan ;
used = (float)jand - (float)jan;
//var jan = jand - used;
fpSpread1_Sheet1.Cells[rowindex, c++].Value = jand != 0 ? jand.ToString() : string.Empty;
fpSpread1_Sheet1.Cells[rowindex, c++].Value = used != 0 ? used.ToString() : string.Empty;

View File

@@ -33,7 +33,8 @@ namespace FBS0000
this.tbMon.Text = DateTime.Now.ToShortDateString();
cmbType.SelectedIndex = 0; //일기준으로한다
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyday));
if (curLevel < 5) cmbUser.Enabled = false;
}
private void rJobReport_Load(object sender, EventArgs e)

View File

@@ -78,8 +78,8 @@ namespace FBS0000
var ed = DateTime.Parse(this.tbMon.Text);
var sd = DateTime.Parse(ed.ToString("yyyy") + "-01-01");
//this.reportViewer1.LocalReport.DataSources.Clear();
var uid = "%";// GetUIDValue();
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyday));
var uid = curLevel > 5 ? "%" : FCOMMON.info.Login.no; // GetUIDValue();
if (cmbType.SelectedIndex == 0)
{
taDay.Fill(this.dsReport.Holydata_Day, FCOMMON.info.Login.gcode, uid, sd.ToShortDateString(), ed.ToShortDateString());

View File

@@ -79,14 +79,19 @@ namespace FBS0000
ToolStripMenuItem.Enabled = true;
toolStripButton2.Enabled = true;
toolStripButton3.Enabled = true;
}
cmbUser.Enabled = true;
}
else
{
toolStripButton3.Enabled = false;
cmbUser.Enabled = false;
toolStripButton3.Enabled = false;
toolStripButton2.Enabled = false;
btSave.Enabled = false;
btDel.Enabled = false;
btAdd.Enabled = false;
toolStripLabel3.Enabled = false;
RefreshData();
//this.cmbUser.Enabled = false; //사용자를 고칠수 없게 한다.
@@ -414,9 +419,13 @@ namespace FBS0000
private void toolStripButton5_Click(object sender, EventArgs e)
{
//var sd = DateTime.Now.ToString("yyyy-MM-01");
//var ed = DateTime.Parse(DateTime.Now.AddMonths(1).ToString("yyyy-MM-01")).AddDays(-1).ToShortDateString();
var f = new WorkTable();
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyday));
if(curLevel < 5)
{
AR.UTIL.MsgE("사용 권한이 없습니다");
return;
}
var f = new WorkTable();
f.Show();
}

View File

@@ -82,6 +82,8 @@ namespace FBS0000
}
else
{
cmbUser.Enabled = false;
toolStripLabel3.Enabled = false;
//toolStripButton3.Enabled = false;
//toolStripButton2.Enabled = false;
//btSave.Enabled = false;

View File

@@ -44,11 +44,17 @@ namespace FBS0000
this.dsReport.holydatasum.Clear();
var grp_user = dsReport.holydata.GroupBy(t => t.uid).ToList();
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyday));
foreach (var grp in grp_user)
{
//cate group
var first_user = grp.First();
if (curLevel < 5 && grp.Key.Equals(FCOMMON.info.Login.no) == false)
{
continue;
}
var grp_cate = grp.GroupBy(t => t.cate).ToList();
foreach (var cate in grp_cate)
{

File diff suppressed because it is too large Load Diff

View File

@@ -20,32 +20,32 @@ WHERE (idx = @Original_idx)</CommandText>
</DbCommand>
</DeleteCommand>
<InsertCommand>
<DbCommand CommandType="Text" ModifiedByUser="true">
<DbCommand CommandType="Text" ModifiedByUser="false">
<CommandText>INSERT INTO EETGW_HolydayRequest
(gcode, uid, cate, sdate, edate, conf, Remark, wuid, wdate, Response, HolyReason, HolyBackup, HolyLocation, HolyDays, HolyTimes, sendmail, stime, etime, conf_id, conf_time)
VALUES (@gcode,@uid,@cate,@sdate,@edate,@conf,@Remark,@wuid,@wdate,@Response,@HolyReason,@HolyBackup,@HolyLocation,@HolyDays,@HolyTimes,@sendmail,@stime,@etime,@conf_id,@conf_time);
SELECT idx, gcode, uid, cate, sdate, edate, conf, Remark, wuid, wdate FROM EETGW_HolydayRequest WHERE (idx = SCOPE_IDENTITY()) ORDER BY conf, sdate DESC</CommandText>
<Parameters>
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="uid" ColumnName="uid" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@uid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="uid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="cate" ColumnName="cate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@cate" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="cate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sdate" ColumnName="sdate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sdate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="edate" ColumnName="edate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@edate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="edate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf" ColumnName="conf" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@conf" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="conf" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Remark" ColumnName="Remark" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Remark" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Remark" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wuid" ColumnName="wuid" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@wuid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="wuid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wdate" ColumnName="wdate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Response" ColumnName="Response" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Response" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Response" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyReason" ColumnName="HolyReason" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyReason" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyReason" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyBackup" ColumnName="HolyBackup" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyBackup" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyBackup" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyLocation" ColumnName="HolyLocation" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyLocation" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyLocation" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyDays" ColumnName="HolyDays" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="int" DbType="Double" Direction="Input" ParameterName="@HolyDays" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyDays" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyTimes" ColumnName="HolyTimes" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="float" DbType="Double" Direction="Input" ParameterName="@HolyTimes" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyTimes" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sendmail" ColumnName="sendmail" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="bit" DbType="Boolean" Direction="Input" ParameterName="@sendmail" Precision="0" ProviderType="Bit" Scale="0" Size="1" SourceColumn="sendmail" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="stime" ColumnName="stime" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@stime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="stime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="etime" ColumnName="etime" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@etime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="etime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_id" ColumnName="conf_id" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@conf_id" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="conf_id" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_time" ColumnName="conf_time" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@conf_time" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="conf_time" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="uid" ColumnName="uid" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@uid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="uid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="cate" ColumnName="cate" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@cate" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="cate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sdate" ColumnName="sdate" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sdate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="edate" ColumnName="edate" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@edate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="edate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf" ColumnName="conf" DataSourceName="" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@conf" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="conf" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Remark" ColumnName="Remark" DataSourceName="" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Remark" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Remark" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wuid" ColumnName="wuid" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@wuid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="wuid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wdate" ColumnName="wdate" DataSourceName="" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Response" ColumnName="Response" DataSourceName="" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Response" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Response" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyReason" ColumnName="HolyReason" DataSourceName="" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyReason" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyReason" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyBackup" ColumnName="HolyBackup" DataSourceName="" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyBackup" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyBackup" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyLocation" ColumnName="HolyLocation" DataSourceName="" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyLocation" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyLocation" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyDays" ColumnName="HolyDays" DataSourceName="" DataTypeServer="int" DbType="Double" Direction="Input" ParameterName="@HolyDays" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyDays" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyTimes" ColumnName="HolyTimes" DataSourceName="" DataTypeServer="float" DbType="Double" Direction="Input" ParameterName="@HolyTimes" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyTimes" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sendmail" ColumnName="sendmail" DataSourceName="" DataTypeServer="bit" DbType="Boolean" Direction="Input" ParameterName="@sendmail" Precision="0" ProviderType="Bit" Scale="0" Size="1" SourceColumn="sendmail" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="stime" ColumnName="stime" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@stime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="stime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="etime" ColumnName="etime" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@etime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="etime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_id" ColumnName="conf_id" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@conf_id" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="conf_id" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_time" ColumnName="conf_time" DataSourceName="" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@conf_time" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="conf_time" SourceColumnNullMapping="false" SourceVersion="Current" />
</Parameters>
</DbCommand>
</InsertCommand>
@@ -58,17 +58,18 @@ SELECT idx, gcode, uid, cate, sdate, edate, conf, Remark, wuid, wdate FROM EETGW
EETGW_HolydayRequest.etime, EETGW_HolydayRequest.conf_id, EETGW_HolydayRequest.conf_time
FROM EETGW_HolydayRequest WITH (nolock) LEFT OUTER JOIN
vGroupUser ON EETGW_HolydayRequest.uid = vGroupUser.id AND EETGW_HolydayRequest.gcode = vGroupUser.gcode
WHERE (EETGW_HolydayRequest.gcode = @gcode) AND (EETGW_HolydayRequest.sdate &gt;= @sd) AND (EETGW_HolydayRequest.sdate &lt;= @ed)
WHERE (EETGW_HolydayRequest.gcode = @gcode) AND (EETGW_HolydayRequest.sdate &gt;= @sd) AND (EETGW_HolydayRequest.sdate &lt;= @ed) AND (EETGW_HolydayRequest.uid LIKE @uid)
ORDER BY EETGW_HolydayRequest.conf, EETGW_HolydayRequest.sdate DESC</CommandText>
<Parameters>
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sd" ColumnName="sdate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sd" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="ed" ColumnName="sdate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@ed" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="uid" ColumnName="uid" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@uid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="uid" SourceColumnNullMapping="false" SourceVersion="Current" />
</Parameters>
</DbCommand>
</SelectCommand>
<UpdateCommand>
<DbCommand CommandType="Text" ModifiedByUser="true">
<DbCommand CommandType="Text" ModifiedByUser="false">
<CommandText>UPDATE EETGW_HolydayRequest
SET gcode = @gcode, uid = @uid, cate = @cate, sdate = @sdate, edate = @edate, conf = @conf, Remark = @Remark, wuid = @wuid, wdate = @wdate, Response = @Response,
HolyReason = @HolyReason, HolyBackup = @HolyBackup, HolyLocation = @HolyLocation, HolyDays = @HolyDays, HolyTimes = @HolyTimes, sendmail = @sendmail, stime = @stime,
@@ -76,28 +77,28 @@ SET gcode = @gcode, uid = @uid, cate = @cate, sdate = @sdate, edate = @ed
WHERE (idx = @Original_idx);
SELECT idx, gcode, uid, cate, sdate, edate, conf, Remark, wuid, wdate FROM EETGW_HolydayRequest WHERE (idx = @idx) ORDER BY conf, sdate DESC</CommandText>
<Parameters>
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="uid" ColumnName="uid" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@uid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="uid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="cate" ColumnName="cate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@cate" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="cate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sdate" ColumnName="sdate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sdate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="edate" ColumnName="edate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@edate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="edate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf" ColumnName="conf" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@conf" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="conf" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Remark" ColumnName="Remark" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Remark" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Remark" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wuid" ColumnName="wuid" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@wuid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="wuid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wdate" ColumnName="wdate" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Response" ColumnName="Response" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Response" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Response" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyReason" ColumnName="HolyReason" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyReason" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyReason" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyBackup" ColumnName="HolyBackup" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyBackup" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyBackup" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyLocation" ColumnName="HolyLocation" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyLocation" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyLocation" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyDays" ColumnName="HolyDays" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="int" DbType="Double" Direction="Input" ParameterName="@HolyDays" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyDays" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyTimes" ColumnName="HolyTimes" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="float" DbType="Double" Direction="Input" ParameterName="@HolyTimes" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyTimes" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sendmail" ColumnName="sendmail" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="bit" DbType="Boolean" Direction="Input" ParameterName="@sendmail" Precision="0" ProviderType="Bit" Scale="0" Size="1" SourceColumn="sendmail" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="stime" ColumnName="stime" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@stime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="stime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="etime" ColumnName="etime" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@etime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="etime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_id" ColumnName="conf_id" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@conf_id" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="conf_id" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_time" ColumnName="conf_time" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@conf_time" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="conf_time" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="Original_idx" ColumnName="idx" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@Original_idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
<Parameter AllowDbNull="false" AutogeneratedName="idx" ColumnName="idx" DataSourceName="EE.dbo.EETGW_HolydayRequest" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="uid" ColumnName="uid" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@uid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="uid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="cate" ColumnName="cate" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@cate" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="cate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sdate" ColumnName="sdate" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sdate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="edate" ColumnName="edate" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@edate" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="edate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf" ColumnName="conf" DataSourceName="" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@conf" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="conf" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Remark" ColumnName="Remark" DataSourceName="" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Remark" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Remark" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wuid" ColumnName="wuid" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@wuid" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="wuid" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="wdate" ColumnName="wdate" DataSourceName="" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="Response" ColumnName="Response" DataSourceName="" DataTypeServer="varchar(255)" DbType="AnsiString" Direction="Input" ParameterName="@Response" Precision="0" ProviderType="VarChar" Scale="0" Size="255" SourceColumn="Response" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyReason" ColumnName="HolyReason" DataSourceName="" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyReason" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyReason" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyBackup" ColumnName="HolyBackup" DataSourceName="" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyBackup" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyBackup" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyLocation" ColumnName="HolyLocation" DataSourceName="" DataTypeServer="varchar(100)" DbType="AnsiString" Direction="Input" ParameterName="@HolyLocation" Precision="0" ProviderType="VarChar" Scale="0" Size="100" SourceColumn="HolyLocation" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyDays" ColumnName="HolyDays" DataSourceName="" DataTypeServer="int" DbType="Double" Direction="Input" ParameterName="@HolyDays" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyDays" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="HolyTimes" ColumnName="HolyTimes" DataSourceName="" DataTypeServer="float" DbType="Double" Direction="Input" ParameterName="@HolyTimes" Precision="0" ProviderType="Float" Scale="0" Size="8" SourceColumn="HolyTimes" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sendmail" ColumnName="sendmail" DataSourceName="" DataTypeServer="bit" DbType="Boolean" Direction="Input" ParameterName="@sendmail" Precision="0" ProviderType="Bit" Scale="0" Size="1" SourceColumn="sendmail" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="stime" ColumnName="stime" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@stime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="stime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="etime" ColumnName="etime" DataSourceName="" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@etime" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="etime" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_id" ColumnName="conf_id" DataSourceName="" DataTypeServer="varchar(20)" DbType="AnsiString" Direction="Input" ParameterName="@conf_id" Precision="0" ProviderType="VarChar" Scale="0" Size="20" SourceColumn="conf_id" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="conf_time" ColumnName="conf_time" DataSourceName="" DataTypeServer="smalldatetime" DbType="DateTime" Direction="Input" ParameterName="@conf_time" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="4" SourceColumn="conf_time" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="false" AutogeneratedName="Original_idx" ColumnName="idx" DataSourceName="" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@Original_idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
<Parameter AllowDbNull="false" AutogeneratedName="idx" ColumnName="idx" DataSourceName="" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
</Parameters>
</DbCommand>
</UpdateCommand>
@@ -138,152 +139,152 @@ SELECT idx, gcode, uid, cate, sdate, edate, conf, Remark, wuid, wdate FROM EETGW
</DataSource>
</xs:appinfo>
</xs:annotation>
<xs:element name="DSKuntae" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:Generator_UserDSName="DSKuntae" msprop:EnableTableAdapterManager="true" msprop:Generator_DataSetName="DSKuntae">
<xs:element name="DSKuntae" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:EnableTableAdapterManager="true" msprop:Generator_DataSetName="DSKuntae" msprop:Generator_UserDSName="DSKuntae">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="EETGW_HolydayRequest" msprop:Generator_RowEvHandlerName="EETGW_HolydayRequestRowChangeEventHandler" msprop:Generator_RowDeletedName="EETGW_HolydayRequestRowDeleted" msprop:Generator_RowDeletingName="EETGW_HolydayRequestRowDeleting" msprop:Generator_RowEvArgName="EETGW_HolydayRequestRowChangeEvent" msprop:Generator_TablePropName="EETGW_HolydayRequest" msprop:Generator_RowChangedName="EETGW_HolydayRequestRowChanged" msprop:Generator_UserTableName="EETGW_HolydayRequest" msprop:Generator_RowChangingName="EETGW_HolydayRequestRowChanging" msprop:Generator_RowClassName="EETGW_HolydayRequestRow" msprop:Generator_TableClassName="EETGW_HolydayRequestDataTable" msprop:Generator_TableVarName="tableEETGW_HolydayRequest">
<xs:element name="EETGW_HolydayRequest" msprop:Generator_UserTableName="EETGW_HolydayRequest" msprop:Generator_RowEvArgName="EETGW_HolydayRequestRowChangeEvent" msprop:Generator_TableVarName="tableEETGW_HolydayRequest" msprop:Generator_TablePropName="EETGW_HolydayRequest" msprop:Generator_RowDeletingName="EETGW_HolydayRequestRowDeleting" msprop:Generator_RowChangingName="EETGW_HolydayRequestRowChanging" msprop:Generator_RowDeletedName="EETGW_HolydayRequestRowDeleted" msprop:Generator_RowEvHandlerName="EETGW_HolydayRequestRowChangeEventHandler" msprop:Generator_TableClassName="EETGW_HolydayRequestDataTable" msprop:Generator_RowChangedName="EETGW_HolydayRequestRowChanged" msprop:Generator_RowClassName="EETGW_HolydayRequestRow">
<xs:complexType>
<xs:sequence>
<xs:element name="idx" msdata:ReadOnly="true" msdata:AutoIncrement="true" msdata:AutoIncrementSeed="-1" msdata:AutoIncrementStep="-1" msprop:Generator_ColumnPropNameInTable="idxColumn" msprop:Generator_ColumnPropNameInRow="idx" msprop:Generator_UserColumnName="idx" msprop:Generator_ColumnVarNameInTable="columnidx" type="xs:int" />
<xs:element name="gcode" msprop:Generator_ColumnPropNameInTable="gcodeColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="gcode" msprop:Generator_UserColumnName="gcode" msprop:Generator_ColumnVarNameInTable="columngcode">
<xs:element name="idx" msdata:ReadOnly="true" msdata:AutoIncrement="true" msdata:AutoIncrementSeed="-1" msdata:AutoIncrementStep="-1" msprop:Generator_ColumnVarNameInTable="columnidx" msprop:Generator_ColumnPropNameInRow="idx" msprop:Generator_ColumnPropNameInTable="idxColumn" msprop:Generator_UserColumnName="idx" type="xs:int" />
<xs:element name="gcode" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="gcode" msprop:Generator_ColumnVarNameInTable="columngcode" msprop:Generator_ColumnPropNameInTable="gcodeColumn" msprop:Generator_UserColumnName="gcode">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="uid" msprop:Generator_ColumnPropNameInTable="uidColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="uid" msprop:Generator_UserColumnName="uid" msprop:Generator_ColumnVarNameInTable="columnuid" minOccurs="0">
<xs:element name="uid" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="uid" msprop:Generator_ColumnVarNameInTable="columnuid" msprop:Generator_ColumnPropNameInTable="uidColumn" msprop:Generator_UserColumnName="uid" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="cate" msprop:Generator_ColumnPropNameInTable="cateColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="cate" msprop:Generator_UserColumnName="cate" msprop:Generator_ColumnVarNameInTable="columncate" minOccurs="0">
<xs:element name="cate" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="cate" msprop:Generator_ColumnVarNameInTable="columncate" msprop:Generator_ColumnPropNameInTable="cateColumn" msprop:Generator_UserColumnName="cate" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="sdate" msprop:Generator_ColumnPropNameInTable="sdateColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="sdate" msprop:Generator_UserColumnName="sdate" msprop:Generator_ColumnVarNameInTable="columnsdate" minOccurs="0">
<xs:element name="sdate" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="sdate" msprop:Generator_ColumnVarNameInTable="columnsdate" msprop:Generator_ColumnPropNameInTable="sdateColumn" msprop:Generator_UserColumnName="sdate" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="edate" msprop:Generator_ColumnPropNameInTable="edateColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="edate" msprop:Generator_UserColumnName="edate" msprop:Generator_ColumnVarNameInTable="columnedate" minOccurs="0">
<xs:element name="edate" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="edate" msprop:Generator_ColumnVarNameInTable="columnedate" msprop:Generator_ColumnPropNameInTable="edateColumn" msprop:Generator_UserColumnName="edate" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Remark" msprop:Generator_ColumnPropNameInTable="RemarkColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="Remark" msprop:Generator_UserColumnName="Remark" msprop:Generator_ColumnVarNameInTable="columnRemark" minOccurs="0">
<xs:element name="Remark" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="Remark" msprop:Generator_ColumnVarNameInTable="columnRemark" msprop:Generator_ColumnPropNameInTable="RemarkColumn" msprop:Generator_UserColumnName="Remark" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="wuid" msprop:Generator_ColumnPropNameInTable="wuidColumn" msprop:Generator_ColumnPropNameInRow="wuid" msprop:Generator_UserColumnName="wuid" msprop:Generator_ColumnVarNameInTable="columnwuid">
<xs:element name="wuid" msprop:Generator_ColumnVarNameInTable="columnwuid" msprop:Generator_ColumnPropNameInRow="wuid" msprop:Generator_ColumnPropNameInTable="wuidColumn" msprop:Generator_UserColumnName="wuid">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="wdate" msprop:Generator_ColumnPropNameInTable="wdateColumn" msprop:Generator_ColumnPropNameInRow="wdate" msprop:Generator_UserColumnName="wdate" msprop:Generator_ColumnVarNameInTable="columnwdate" type="xs:dateTime" />
<xs:element name="dept" msprop:Generator_ColumnPropNameInTable="deptColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="dept" msprop:Generator_UserColumnName="dept" msprop:Generator_ColumnVarNameInTable="columndept" minOccurs="0">
<xs:element name="wdate" msprop:Generator_ColumnVarNameInTable="columnwdate" msprop:Generator_ColumnPropNameInRow="wdate" msprop:Generator_ColumnPropNameInTable="wdateColumn" msprop:Generator_UserColumnName="wdate" type="xs:dateTime" />
<xs:element name="dept" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="dept" msprop:Generator_ColumnVarNameInTable="columndept" msprop:Generator_ColumnPropNameInTable="deptColumn" msprop:Generator_UserColumnName="dept" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="name" msprop:Generator_ColumnPropNameInTable="nameColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="name" msprop:Generator_UserColumnName="name" msprop:Generator_ColumnVarNameInTable="columnname" minOccurs="0">
<xs:element name="name" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="name" msprop:Generator_ColumnVarNameInTable="columnname" msprop:Generator_ColumnPropNameInTable="nameColumn" msprop:Generator_UserColumnName="name" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="grade" msprop:Generator_ColumnPropNameInTable="gradeColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="grade" msprop:Generator_UserColumnName="grade" msprop:Generator_ColumnVarNameInTable="columngrade" minOccurs="0">
<xs:element name="grade" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="grade" msprop:Generator_ColumnVarNameInTable="columngrade" msprop:Generator_ColumnPropNameInTable="gradeColumn" msprop:Generator_UserColumnName="grade" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="tel" msprop:Generator_ColumnPropNameInTable="telColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="tel" msprop:Generator_UserColumnName="tel" msprop:Generator_ColumnVarNameInTable="columntel" minOccurs="0">
<xs:element name="tel" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="tel" msprop:Generator_ColumnVarNameInTable="columntel" msprop:Generator_ColumnPropNameInTable="telColumn" msprop:Generator_UserColumnName="tel" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="processs" msdata:ReadOnly="true" msprop:Generator_ColumnPropNameInTable="processsColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="processs" msprop:Generator_UserColumnName="processs" msprop:Generator_ColumnVarNameInTable="columnprocesss" minOccurs="0">
<xs:element name="processs" msdata:ReadOnly="true" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="processs" msprop:Generator_ColumnVarNameInTable="columnprocesss" msprop:Generator_ColumnPropNameInTable="processsColumn" msprop:Generator_UserColumnName="processs" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="50" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Response" msprop:Generator_ColumnPropNameInTable="ResponseColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="Response" msprop:Generator_UserColumnName="Response" msprop:Generator_ColumnVarNameInTable="columnResponse" minOccurs="0">
<xs:element name="Response" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="Response" msprop:Generator_ColumnVarNameInTable="columnResponse" msprop:Generator_ColumnPropNameInTable="ResponseColumn" msprop:Generator_UserColumnName="Response" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="conf" msprop:Generator_ColumnPropNameInTable="confColumn" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="conf" msprop:Generator_UserColumnName="conf" msprop:Generator_ColumnVarNameInTable="columnconf" type="xs:int" minOccurs="0" />
<xs:element name="HolyReason" msprop:Generator_ColumnPropNameInTable="HolyReasonColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="HolyReason" msprop:Generator_UserColumnName="HolyReason" msprop:Generator_ColumnVarNameInTable="columnHolyReason" minOccurs="0">
<xs:element name="conf" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="conf" msprop:Generator_ColumnVarNameInTable="columnconf" msprop:Generator_ColumnPropNameInTable="confColumn" msprop:Generator_UserColumnName="conf" type="xs:int" minOccurs="0" />
<xs:element name="HolyReason" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="HolyReason" msprop:Generator_ColumnVarNameInTable="columnHolyReason" msprop:Generator_ColumnPropNameInTable="HolyReasonColumn" msprop:Generator_UserColumnName="HolyReason" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="HolyBackup" msprop:Generator_ColumnPropNameInTable="HolyBackupColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="HolyBackup" msprop:Generator_UserColumnName="HolyBackup" msprop:Generator_ColumnVarNameInTable="columnHolyBackup" minOccurs="0">
<xs:element name="HolyBackup" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="HolyBackup" msprop:Generator_ColumnVarNameInTable="columnHolyBackup" msprop:Generator_ColumnPropNameInTable="HolyBackupColumn" msprop:Generator_UserColumnName="HolyBackup" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="HolyLocation" msprop:Generator_ColumnPropNameInTable="HolyLocationColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="HolyLocation" msprop:Generator_UserColumnName="HolyLocation" msprop:Generator_ColumnVarNameInTable="columnHolyLocation" minOccurs="0">
<xs:element name="HolyLocation" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="HolyLocation" msprop:Generator_ColumnVarNameInTable="columnHolyLocation" msprop:Generator_ColumnPropNameInTable="HolyLocationColumn" msprop:Generator_UserColumnName="HolyLocation" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="HolyDays" msprop:Generator_ColumnPropNameInTable="HolyDaysColumn" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="HolyDays" msprop:Generator_UserColumnName="HolyDays" msprop:Generator_ColumnVarNameInTable="columnHolyDays" type="xs:double" minOccurs="0" />
<xs:element name="HolyTimes" msprop:Generator_ColumnPropNameInTable="HolyTimesColumn" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="HolyTimes" msprop:Generator_UserColumnName="HolyTimes" msprop:Generator_ColumnVarNameInTable="columnHolyTimes" type="xs:double" minOccurs="0" />
<xs:element name="sendmail" msprop:Generator_ColumnPropNameInTable="sendmailColumn" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="sendmail" msprop:Generator_UserColumnName="sendmail" msprop:Generator_ColumnVarNameInTable="columnsendmail" type="xs:boolean" minOccurs="0" />
<xs:element name="stime" msprop:Generator_ColumnPropNameInTable="stimeColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="stime" msprop:Generator_UserColumnName="stime" msprop:Generator_ColumnVarNameInTable="columnstime" minOccurs="0">
<xs:element name="HolyDays" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="HolyDays" msprop:Generator_ColumnVarNameInTable="columnHolyDays" msprop:Generator_ColumnPropNameInTable="HolyDaysColumn" msprop:Generator_UserColumnName="HolyDays" type="xs:double" minOccurs="0" />
<xs:element name="HolyTimes" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="HolyTimes" msprop:Generator_ColumnVarNameInTable="columnHolyTimes" msprop:Generator_ColumnPropNameInTable="HolyTimesColumn" msprop:Generator_UserColumnName="HolyTimes" type="xs:double" minOccurs="0" />
<xs:element name="sendmail" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="sendmail" msprop:Generator_ColumnVarNameInTable="columnsendmail" msprop:Generator_ColumnPropNameInTable="sendmailColumn" msprop:Generator_UserColumnName="sendmail" type="xs:boolean" minOccurs="0" />
<xs:element name="stime" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="stime" msprop:Generator_ColumnVarNameInTable="columnstime" msprop:Generator_ColumnPropNameInTable="stimeColumn" msprop:Generator_UserColumnName="stime" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="etime" msprop:Generator_ColumnPropNameInTable="etimeColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="etime" msprop:Generator_UserColumnName="etime" msprop:Generator_ColumnVarNameInTable="columnetime" minOccurs="0">
<xs:element name="etime" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="etime" msprop:Generator_ColumnVarNameInTable="columnetime" msprop:Generator_ColumnPropNameInTable="etimeColumn" msprop:Generator_UserColumnName="etime" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="conf_id" msprop:Generator_ColumnPropNameInTable="conf_idColumn" msprop:Generator_ColumnPropNameInRow="conf_id" msprop:Generator_UserColumnName="conf_id" msprop:Generator_ColumnVarNameInTable="columnconf_id" minOccurs="0">
<xs:element name="conf_id" msprop:Generator_ColumnVarNameInTable="columnconf_id" msprop:Generator_ColumnPropNameInRow="conf_id" msprop:Generator_ColumnPropNameInTable="conf_idColumn" msprop:Generator_UserColumnName="conf_id" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="conf_time" msprop:Generator_ColumnPropNameInTable="conf_timeColumn" msprop:Generator_ColumnPropNameInRow="conf_time" msprop:Generator_UserColumnName="conf_time" msprop:Generator_ColumnVarNameInTable="columnconf_time" type="xs:dateTime" minOccurs="0" />
<xs:element name="conf_time" msprop:Generator_ColumnVarNameInTable="columnconf_time" msprop:Generator_ColumnPropNameInRow="conf_time" msprop:Generator_ColumnPropNameInTable="conf_timeColumn" msprop:Generator_UserColumnName="conf_time" type="xs:dateTime" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>

View File

@@ -4,7 +4,7 @@
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
</autogenerated>-->
<DiagramLayout xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ex:showrelationlabel="False" ViewPortX="385" ViewPortY="0" xmlns:ex="urn:schemas-microsoft-com:xml-msdatasource-layout-extended" xmlns="urn:schemas-microsoft-com:xml-msdatasource-layout">
<DiagramLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ex:showrelationlabel="False" ViewPortX="385" ViewPortY="0" xmlns:ex="urn:schemas-microsoft-com:xml-msdatasource-layout-extended" xmlns="urn:schemas-microsoft-com:xml-msdatasource-layout">
<Shapes>
<Shape ID="DesignTable:EETGW_HolydayRequest" ZOrder="1" X="543" Y="76" Height="476" Width="287" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="425" />
</Shapes>

View File

@@ -38,7 +38,7 @@
this.dte = new System.Windows.Forms.DateTimePicker();
this.label2 = new System.Windows.Forms.Label();
this.dts = new System.Windows.Forms.DateTimePicker();
this.tbProcess = new System.Windows.Forms.ComboBox();
this.cmbProcess = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.btRefresh = new System.Windows.Forms.Button();
this.fpSpread1 = new FarPoint.Win.Spread.FpSpread();
@@ -61,7 +61,7 @@
this.panel1.Controls.Add(this.dte);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.dts);
this.panel1.Controls.Add(this.tbProcess);
this.panel1.Controls.Add(this.cmbProcess);
this.panel1.Controls.Add(this.label1);
this.panel1.Controls.Add(this.btRefresh);
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
@@ -157,13 +157,13 @@
//
// tbProcess
//
this.tbProcess.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.tbProcess.FormattingEnabled = true;
this.tbProcess.Location = new System.Drawing.Point(68, 32);
this.tbProcess.Name = "tbProcess";
this.tbProcess.Size = new System.Drawing.Size(180, 20);
this.tbProcess.TabIndex = 5;
this.tbProcess.SelectedIndexChanged += new System.EventHandler(this.tbProcess_SelectedIndexChanged);
this.cmbProcess.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbProcess.FormattingEnabled = true;
this.cmbProcess.Location = new System.Drawing.Point(68, 32);
this.cmbProcess.Name = "tbProcess";
this.cmbProcess.Size = new System.Drawing.Size(180, 20);
this.cmbProcess.TabIndex = 5;
this.cmbProcess.SelectedIndexChanged += new System.EventHandler(this.tbProcess_SelectedIndexChanged);
//
// label1
//
@@ -239,7 +239,7 @@
private System.Windows.Forms.DateTimePicker dte;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.DateTimePicker dts;
private System.Windows.Forms.ComboBox tbProcess;
private System.Windows.Forms.ComboBox cmbProcess;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button btRefresh;
private FarPoint.Win.Spread.FpSpread fpSpread1;

View File

@@ -14,7 +14,7 @@ namespace FPJ0000.JobReport_
public partial class fJobChartMenu : fBase
{
Boolean binit = false;
int curLevel = 0;// Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
public fJobChartMenu()
{
InitializeComponent();
@@ -22,7 +22,7 @@ namespace FPJ0000.JobReport_
Properties.Settings.Default["gwcs"] = FCOMMON.info.CS;
Properties.Settings.Default["EEEntities"] = FCOMMON.info.CS;
Properties.Settings.Default["EEEntitiesLayout"] = FCOMMON.info.CS;
curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
this.dts.Value = DateTime.Parse(DateTime.Now.AddMonths(-1).ToString("yyyy-MM-01"));
this.dte.Value = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01")).AddDays(-1);
@@ -30,19 +30,19 @@ namespace FPJ0000.JobReport_
private void fJobChartMenu_Load(object sender, EventArgs e)
{
EnsureVisibleAndUsableSize();
this.tbProcess.Items.Clear();
this.cmbProcess.Items.Clear();
tbProcess.Items.Add("--전체--");
cmbProcess.Items.Add("--전체--");
// var taProcess = new dsReportTableAdapters.ProcessListTableAdapter();
var dtProcessList = FCOMMON.DBM.GroupUserProcessList();// taProcess.GetData(FCOMMON.info.Login.gcode);
foreach (var dr in dtProcessList)
tbProcess.Items.Add(dr);
cmbProcess.Items.Add(dr);
//사용자의 공정명을 선택해준다
this.tbProcess.Text = FCOMMON.info.Login.process;
this.cmbProcess.Text = FCOMMON.info.Login.process;
//프로세스가 선택되지 않았다면 전체를 선택해준다.
if (tbProcess.SelectedIndex < 0) tbProcess.SelectedIndex = 0;
if (cmbProcess.SelectedIndex < 0) cmbProcess.SelectedIndex = 0;
//사용자 목록을 선택한다
UpdateUserList();
@@ -59,6 +59,12 @@ namespace FPJ0000.JobReport_
binit = true;
btSave.Enabled = true;
btRefresh.Enabled = true;
if(curLevel < 5)
{
this.cmbProcess.Enabled = false;
cmbUser.Enabled = false;
}
}
void UpdateUserList()
{
@@ -80,7 +86,7 @@ namespace FPJ0000.JobReport_
var id = item.Field<string>("id");
var dispname = item.Field<string>("dispname");
var process = item.Field<string>("process");
if (tbProcess.SelectedIndex > 0 && tbProcess.Text.Equals(process) == false) continue;
if (cmbProcess.SelectedIndex > 0 && cmbProcess.Text.Equals(process) == false) continue;
cmbUser.Items.Add(dispname);
}
@@ -96,7 +102,7 @@ namespace FPJ0000.JobReport_
////if (tbProcess.SelectedIndex != 0) userlist = userlist.Where(t => t.processs == tbProcess.Text); //해당 공정의 인원만 처리한다
if (tbProcess.SelectedIndex > 0)
if (cmbProcess.SelectedIndex > 0)
cmbUser.Text = string.Format("{1}({0})", FCOMMON.info.Login.no, FCOMMON.info.Login.nameK);
if (cmbUser.SelectedIndex < 0) cmbUser.SelectedIndex = 0; //기본전체로 선택해준다.
@@ -120,7 +126,7 @@ namespace FPJ0000.JobReport_
fpSpread1.Visible = false;
//조회공정
this.fpSpread1.Sheets[0].Cells[3, 2].Value = tbProcess.Text;
this.fpSpread1.Sheets[0].Cells[3, 2].Value = cmbProcess.Text;
//담당자
this.fpSpread1.Sheets[0].Cells[3, 3].Value = cmbUser.Text;
@@ -159,9 +165,9 @@ namespace FPJ0000.JobReport_
var UserNo = getUserID();// cmbUser.Text.Substring(1, cmbUser.Text.IndexOf(']') - 1);
userCount = 1;// db.vJobReportForUser.Where(t => t.gcode == FCOMMON.info.Login.gcode && t.id == UserNo).GroupBy(t=>t.id).Count();
}
else if (tbProcess.SelectedIndex > 0)
else if (cmbProcess.SelectedIndex > 0)
{
userCount = (int)taJobreportuser.GetProcessUserCount(FCOMMON.info.Login.gcode,tbProcess.Text);// db.vJobReportForUser.Where(t => t.gcode == FCOMMON.info.Login.gcode && t.userProcess == tbProcess.Text).GroupBy(t => t.id).Count();
userCount = (int)taJobreportuser.GetProcessUserCount(FCOMMON.info.Login.gcode,cmbProcess.Text);// db.vJobReportForUser.Where(t => t.gcode == FCOMMON.info.Login.gcode && t.userProcess == tbProcess.Text).GroupBy(t => t.id).Count();
}
else
{
@@ -188,9 +194,9 @@ namespace FPJ0000.JobReport_
//.OrderBy(t => t.pdate)
//.ToList();
}
else if (tbProcess.SelectedIndex > 0)
else if (cmbProcess.SelectedIndex > 0)
{
baseData = taJobreportuser.GetByProcess(FCOMMON.info.Login.gcode, tbProcess.Text, vSD, vED);// db.vJobReportForUser
baseData = taJobreportuser.GetByProcess(FCOMMON.info.Login.gcode, cmbProcess.Text, vSD, vED);// db.vJobReportForUser
//.Where(t => t.gcode == FCOMMON.info.Login.gcode && t.pdate.CompareTo(vSD) >= 0 && t.pdate.CompareTo(vED) <= 0 && t.userProcess == tbProcess.Text)
//.OrderBy(t => t.name)
//.OrderBy(t => t.pdate)

View File

@@ -30,27 +30,27 @@
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fJobReport));
FarPoint.Win.Spread.CellType.TextCellType textCellType1 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType2 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType3 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType4 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType5 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType6 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType7 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType8 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType9 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType10 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType1 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType2 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType3 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType11 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.DateTimeCellType dateTimeCellType1 = new FarPoint.Win.Spread.CellType.DateTimeCellType();
FarPoint.Win.Spread.CellType.DateTimeCellType dateTimeCellType2 = new FarPoint.Win.Spread.CellType.DateTimeCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType12 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType13 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType4 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType14 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType5 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType15 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType16 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType17 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType18 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType19 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType20 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType21 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType22 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType23 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType24 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType6 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType7 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType8 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType25 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.DateTimeCellType dateTimeCellType3 = new FarPoint.Win.Spread.CellType.DateTimeCellType();
FarPoint.Win.Spread.CellType.DateTimeCellType dateTimeCellType4 = new FarPoint.Win.Spread.CellType.DateTimeCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType26 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType27 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType9 = new FarPoint.Win.Spread.CellType.NumberCellType();
FarPoint.Win.Spread.CellType.TextCellType textCellType28 = new FarPoint.Win.Spread.CellType.TextCellType();
FarPoint.Win.Spread.CellType.NumberCellType numberCellType10 = new FarPoint.Win.Spread.CellType.NumberCellType();
this.bn = new System.Windows.Forms.BindingNavigator(this.components);
this.bs = new System.Windows.Forms.BindingSource(this.components);
this.dsMSSQL = new FPJ0000.dsPRJ();
@@ -105,7 +105,6 @@
this.tam = new FPJ0000.dsPRJTableAdapters.TableAdapterManager();
this.ta = new FPJ0000.dsPRJTableAdapters.JobReportTableAdapter();
this.fpSpread1 = new FarPoint.Win.Spread.FpSpread();
this.fpSpread1_Sheet1 = new FarPoint.Win.Spread.SheetView();
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.toolStripButton8 = new System.Windows.Forms.ToolStripButton();
this.lbStt = new System.Windows.Forms.ToolStripLabel();
@@ -128,18 +127,19 @@
this.toolStripButton6 = new System.Windows.Forms.ToolStripButton();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.fpSpread1_Sheet1 = new FarPoint.Win.Spread.SheetView();
((System.ComponentModel.ISupportInitialize)(this.bn)).BeginInit();
this.bn.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.bs)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dsMSSQL)).BeginInit();
this.cm.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1_Sheet1)).BeginInit();
this.toolStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1_Sheet1)).BeginInit();
this.SuspendLayout();
//
// bn
@@ -228,7 +228,6 @@
//
this.bindingNavigatorPositionItem.AccessibleName = "위치";
this.bindingNavigatorPositionItem.AutoSize = false;
this.bindingNavigatorPositionItem.Font = new System.Drawing.Font("맑은 고딕", 9F);
this.bindingNavigatorPositionItem.Name = "bindingNavigatorPositionItem";
this.bindingNavigatorPositionItem.Size = new System.Drawing.Size(50, 23);
this.bindingNavigatorPositionItem.Text = "0";
@@ -320,7 +319,6 @@
// tbFind
//
this.tbFind.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.tbFind.Font = new System.Drawing.Font("맑은 고딕", 9F);
this.tbFind.Name = "tbFind";
this.tbFind.Size = new System.Drawing.Size(100, 25);
this.tbFind.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tbFind_KeyDown);
@@ -642,193 +640,7 @@
this.fpSpread1.Size = new System.Drawing.Size(1364, 592);
this.fpSpread1.StatusBarVisible = true;
this.fpSpread1.TabIndex = 2;
//
// fpSpread1_Sheet1
//
this.fpSpread1_Sheet1.Reset();
this.fpSpread1_Sheet1.SheetName = "Sheet1";
// Formulas and custom names must be loaded with R1C1 reference style
this.fpSpread1_Sheet1.ReferenceStyle = FarPoint.Win.Spread.Model.ReferenceStyle.R1C1;
this.fpSpread1_Sheet1.ColumnCount = 21;
this.fpSpread1_Sheet1.ColumnHeader.RowCount = 2;
this.fpSpread1_Sheet1.ActiveColumnIndex = -1;
this.fpSpread1_Sheet1.ActiveRowIndex = -1;
this.fpSpread1_Sheet1.AutoGenerateColumns = false;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 0).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 0).Value = "날짜";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 1).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 1).Value = "WW";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 2).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 2).Value = "담당";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 3).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 3).Value = "요청부서";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 4).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 4).Value = "패키지";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 5).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 5).Value = "상태";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 6).ColumnSpan = 3;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 6).Value = "업무형태";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 7).Value = "(공정)업무분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 8).Value = "업무분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 9).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 9).Value = "프로젝트(아이템)";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 10).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 10).Value = "*";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 11).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 11).Value = "시간";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 12).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 12).Value = "초과";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 13).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 13).Value = "비고";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 14).ColumnSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 14).Value = "초과시간범위";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 15).Value = "초과종료";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 16).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 16).Value = "기술분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 17).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 17).Value = "기술레벨";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 18).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 18).Value = "기술료($K)";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 19).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 19).Value = "#";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 20).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 20).Value = "일련번호";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 6).Value = "형태";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 7).Value = "분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 8).Value = "공정";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 14).Value = "시작";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 15).Value = "종료";
this.fpSpread1_Sheet1.ColumnHeader.Rows.Get(0).Height = 30F;
this.fpSpread1_Sheet1.Columns.Get(0).CellType = textCellType1;
this.fpSpread1_Sheet1.Columns.Get(0).DataField = "pdate";
this.fpSpread1_Sheet1.Columns.Get(0).Width = 58F;
this.fpSpread1_Sheet1.Columns.Get(1).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.fpSpread1_Sheet1.Columns.Get(1).CellType = textCellType2;
this.fpSpread1_Sheet1.Columns.Get(1).DataField = "ww";
this.fpSpread1_Sheet1.Columns.Get(1).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(1).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(2).CellType = textCellType3;
this.fpSpread1_Sheet1.Columns.Get(2).DataField = "username";
this.fpSpread1_Sheet1.Columns.Get(2).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(2).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(3).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(3).CellType = textCellType4;
this.fpSpread1_Sheet1.Columns.Get(3).DataField = "requestpart";
this.fpSpread1_Sheet1.Columns.Get(3).Width = 78F;
this.fpSpread1_Sheet1.Columns.Get(4).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(4).CellType = textCellType5;
this.fpSpread1_Sheet1.Columns.Get(4).DataField = "package";
this.fpSpread1_Sheet1.Columns.Get(4).Width = 86F;
this.fpSpread1_Sheet1.Columns.Get(5).CellType = textCellType6;
this.fpSpread1_Sheet1.Columns.Get(5).DataField = "status";
this.fpSpread1_Sheet1.Columns.Get(5).Tag = "status";
this.fpSpread1_Sheet1.Columns.Get(6).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(6).CellType = textCellType7;
this.fpSpread1_Sheet1.Columns.Get(6).DataField = "type";
this.fpSpread1_Sheet1.Columns.Get(6).Label = "형태";
this.fpSpread1_Sheet1.Columns.Get(6).Width = 84F;
this.fpSpread1_Sheet1.Columns.Get(7).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(7).CellType = textCellType8;
this.fpSpread1_Sheet1.Columns.Get(7).DataField = "jobgrp";
this.fpSpread1_Sheet1.Columns.Get(7).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(7).Label = "분류";
this.fpSpread1_Sheet1.Columns.Get(7).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(7).Width = 90F;
this.fpSpread1_Sheet1.Columns.Get(8).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(8).CellType = textCellType9;
this.fpSpread1_Sheet1.Columns.Get(8).DataField = "process";
this.fpSpread1_Sheet1.Columns.Get(8).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Left;
this.fpSpread1_Sheet1.Columns.Get(8).Label = "공정";
this.fpSpread1_Sheet1.Columns.Get(8).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(8).Width = 80F;
this.fpSpread1_Sheet1.Columns.Get(9).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(9).CellType = textCellType10;
this.fpSpread1_Sheet1.Columns.Get(9).DataField = "projectName";
this.fpSpread1_Sheet1.Columns.Get(9).Width = 158F;
this.fpSpread1_Sheet1.Columns.Get(10).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
numberCellType1.DecimalPlaces = 0;
numberCellType1.LeadingZero = FarPoint.Win.Spread.CellType.LeadingZero.Yes;
numberCellType1.MaximumValue = 2147483647D;
numberCellType1.MinimumValue = -2147483648D;
this.fpSpread1_Sheet1.Columns.Get(10).CellType = numberCellType1;
this.fpSpread1_Sheet1.Columns.Get(10).DataField = "pidx";
this.fpSpread1_Sheet1.Columns.Get(10).Tag = "pidx";
this.fpSpread1_Sheet1.Columns.Get(10).Width = 39F;
numberCellType2.MaximumValue = 999999999999999D;
numberCellType2.MinimumValue = -999999999999999D;
this.fpSpread1_Sheet1.Columns.Get(11).CellType = numberCellType2;
this.fpSpread1_Sheet1.Columns.Get(11).DataField = "hrs";
this.fpSpread1_Sheet1.Columns.Get(11).Width = 52F;
numberCellType3.MaximumValue = 999999999999999D;
numberCellType3.MinimumValue = -999999999999999D;
numberCellType3.NullDisplay = "--";
this.fpSpread1_Sheet1.Columns.Get(12).CellType = numberCellType3;
this.fpSpread1_Sheet1.Columns.Get(12).DataField = "ot";
this.fpSpread1_Sheet1.Columns.Get(12).ForeColor = System.Drawing.Color.Red;
this.fpSpread1_Sheet1.Columns.Get(12).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(12).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(13).CellType = textCellType11;
this.fpSpread1_Sheet1.Columns.Get(13).DataField = "description";
this.fpSpread1_Sheet1.Columns.Get(13).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Left;
this.fpSpread1_Sheet1.Columns.Get(13).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(13).Width = 113F;
this.fpSpread1_Sheet1.Columns.Get(14).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(224)))), ((int)(((byte)(192)))));
dateTimeCellType1.Calendar = new System.Globalization.GregorianCalendar(System.Globalization.GregorianCalendarTypes.Localized);
dateTimeCellType1.CalendarSurroundingDaysColor = System.Drawing.SystemColors.GrayText;
dateTimeCellType1.DateTimeFormat = FarPoint.Win.Spread.CellType.DateTimeFormat.TimeOnly;
dateTimeCellType1.MaximumTime = System.TimeSpan.Parse("23:59:59.9999999");
dateTimeCellType1.TimeDefault = new System.DateTime(2025, 8, 28, 22, 53, 4, 0);
this.fpSpread1_Sheet1.Columns.Get(14).CellType = dateTimeCellType1;
this.fpSpread1_Sheet1.Columns.Get(14).DataField = "otStart";
this.fpSpread1_Sheet1.Columns.Get(14).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(14).Label = "시작";
this.fpSpread1_Sheet1.Columns.Get(14).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(15).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(224)))), ((int)(((byte)(192)))));
dateTimeCellType2.Calendar = new System.Globalization.GregorianCalendar(System.Globalization.GregorianCalendarTypes.Localized);
dateTimeCellType2.CalendarSurroundingDaysColor = System.Drawing.SystemColors.GrayText;
dateTimeCellType2.DateTimeFormat = FarPoint.Win.Spread.CellType.DateTimeFormat.TimeOnly;
dateTimeCellType2.MaximumTime = System.TimeSpan.Parse("23:59:59.9999999");
dateTimeCellType2.TimeDefault = new System.DateTime(2025, 8, 28, 22, 53, 4, 0);
this.fpSpread1_Sheet1.Columns.Get(15).CellType = dateTimeCellType2;
this.fpSpread1_Sheet1.Columns.Get(15).DataField = "otEnd";
this.fpSpread1_Sheet1.Columns.Get(15).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(15).Label = "종료";
this.fpSpread1_Sheet1.Columns.Get(15).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(16).CellType = textCellType12;
this.fpSpread1_Sheet1.Columns.Get(16).DataField = "kisuldiv";
this.fpSpread1_Sheet1.Columns.Get(16).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(16).Tag = "kisuldiv";
this.fpSpread1_Sheet1.Columns.Get(16).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(17).CellType = textCellType13;
this.fpSpread1_Sheet1.Columns.Get(17).DataField = "kisullv";
this.fpSpread1_Sheet1.Columns.Get(17).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(17).Tag = "kisullv";
this.fpSpread1_Sheet1.Columns.Get(17).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
numberCellType4.DecimalPlaces = 2;
numberCellType4.NegativeRed = true;
this.fpSpread1_Sheet1.Columns.Get(18).CellType = numberCellType4;
this.fpSpread1_Sheet1.Columns.Get(18).DataField = "kisulamt";
this.fpSpread1_Sheet1.Columns.Get(18).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(18).Tag = "kisulamt";
this.fpSpread1_Sheet1.Columns.Get(18).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(19).CellType = textCellType14;
this.fpSpread1_Sheet1.Columns.Get(19).DataField = "tag";
this.fpSpread1_Sheet1.Columns.Get(19).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
numberCellType5.DecimalPlaces = 0;
numberCellType5.MaximumValue = 10000000D;
numberCellType5.MinimumValue = -10000000D;
this.fpSpread1_Sheet1.Columns.Get(20).CellType = numberCellType5;
this.fpSpread1_Sheet1.Columns.Get(20).DataField = "idx";
this.fpSpread1_Sheet1.Columns.Get(20).Tag = "idx";
this.fpSpread1_Sheet1.DataAutoCellTypes = false;
this.fpSpread1_Sheet1.DataAutoSizeColumns = false;
this.fpSpread1_Sheet1.DataSource = this.bs;
this.fpSpread1_Sheet1.Protect = false;
this.fpSpread1_Sheet1.RowHeader.Columns.Default.Resizable = false;
this.fpSpread1_Sheet1.SelectionPolicy = FarPoint.Win.Spread.Model.SelectionPolicy.MultiRange;
this.fpSpread1_Sheet1.ShowEditingRowSelector = true;
this.fpSpread1_Sheet1.ShowRowSelector = true;
this.fpSpread1_Sheet1.ReferenceStyle = FarPoint.Win.Spread.Model.ReferenceStyle.A1;
this.fpSpread1.CellClick += new FarPoint.Win.Spread.CellClickEventHandler(this.fpSpread1_CellClick);
//
// toolStrip1
//
@@ -880,7 +692,6 @@
// dtSD
//
this.dtSD.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.dtSD.Font = new System.Drawing.Font("맑은 고딕", 9F);
this.dtSD.Name = "dtSD";
this.dtSD.Size = new System.Drawing.Size(90, 37);
this.dtSD.Text = "1982-11-23";
@@ -904,7 +715,6 @@
// dtED
//
this.dtED.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.dtED.Font = new System.Drawing.Font("맑은 고딕", 9F);
this.dtED.Name = "dtED";
this.dtED.Size = new System.Drawing.Size(90, 37);
this.dtED.Text = "1982-11-23";
@@ -940,7 +750,6 @@
// tbProcess
//
this.tbProcess.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))));
this.tbProcess.Font = new System.Drawing.Font("맑은 고딕", 9F);
this.tbProcess.Name = "tbProcess";
this.tbProcess.Size = new System.Drawing.Size(200, 37);
//
@@ -1043,6 +852,194 @@
this.splitContainer1.SplitterWidth = 10;
this.splitContainer1.TabIndex = 6;
//
// fpSpread1_Sheet1
//
this.fpSpread1_Sheet1.Reset();
this.fpSpread1_Sheet1.SheetName = "Sheet1";
// Formulas and custom names must be loaded with R1C1 reference style
this.fpSpread1_Sheet1.ReferenceStyle = FarPoint.Win.Spread.Model.ReferenceStyle.R1C1;
this.fpSpread1_Sheet1.ColumnCount = 21;
this.fpSpread1_Sheet1.ColumnHeader.RowCount = 2;
this.fpSpread1_Sheet1.ActiveColumnIndex = -1;
this.fpSpread1_Sheet1.ActiveRowIndex = -1;
this.fpSpread1_Sheet1.AutoGenerateColumns = false;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 0).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 0).Value = "날짜";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 1).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 1).Value = "WW";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 2).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 2).Value = "담당";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 3).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 3).Value = "요청부서";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 4).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 4).Value = "패키지";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 5).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 5).Value = "상태";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 6).ColumnSpan = 3;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 6).Value = "업무형태";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 7).Value = "(공정)업무분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 8).Value = "업무분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 9).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 9).Value = "프로젝트(아이템)";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 10).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 10).Value = "*";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 11).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 11).Value = "시간";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 12).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 12).Value = "초과";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 13).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 13).Value = "비고";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 14).ColumnSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 14).Value = "초과시간범위";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 15).Value = "초과종료";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 16).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 16).Value = "기술분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 17).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 17).Value = "기술레벨";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 18).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 18).Value = "기술료($K)";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 19).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 19).Value = "#";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 20).RowSpan = 2;
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(0, 20).Value = "일련번호";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 6).Value = "형태";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 7).Value = "분류";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 8).Value = "공정";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 14).Value = "시작";
this.fpSpread1_Sheet1.ColumnHeader.Cells.Get(1, 15).Value = "종료";
this.fpSpread1_Sheet1.ColumnHeader.Rows.Get(0).Height = 30F;
this.fpSpread1_Sheet1.Columns.Get(0).CellType = textCellType15;
this.fpSpread1_Sheet1.Columns.Get(0).DataField = "pdate";
this.fpSpread1_Sheet1.Columns.Get(0).Width = 58F;
this.fpSpread1_Sheet1.Columns.Get(1).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.fpSpread1_Sheet1.Columns.Get(1).CellType = textCellType16;
this.fpSpread1_Sheet1.Columns.Get(1).DataField = "ww";
this.fpSpread1_Sheet1.Columns.Get(1).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(1).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(2).CellType = textCellType17;
this.fpSpread1_Sheet1.Columns.Get(2).DataField = "username";
this.fpSpread1_Sheet1.Columns.Get(2).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(2).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(3).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(3).CellType = textCellType18;
this.fpSpread1_Sheet1.Columns.Get(3).DataField = "requestpart";
this.fpSpread1_Sheet1.Columns.Get(3).Width = 78F;
this.fpSpread1_Sheet1.Columns.Get(4).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(4).CellType = textCellType19;
this.fpSpread1_Sheet1.Columns.Get(4).DataField = "package";
this.fpSpread1_Sheet1.Columns.Get(4).Width = 86F;
this.fpSpread1_Sheet1.Columns.Get(5).CellType = textCellType20;
this.fpSpread1_Sheet1.Columns.Get(5).DataField = "status";
this.fpSpread1_Sheet1.Columns.Get(5).Tag = "status";
this.fpSpread1_Sheet1.Columns.Get(6).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(6).CellType = textCellType21;
this.fpSpread1_Sheet1.Columns.Get(6).DataField = "type";
this.fpSpread1_Sheet1.Columns.Get(6).Label = "형태";
this.fpSpread1_Sheet1.Columns.Get(6).Width = 84F;
this.fpSpread1_Sheet1.Columns.Get(7).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(7).CellType = textCellType22;
this.fpSpread1_Sheet1.Columns.Get(7).DataField = "jobgrp";
this.fpSpread1_Sheet1.Columns.Get(7).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(7).Label = "분류";
this.fpSpread1_Sheet1.Columns.Get(7).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(7).Width = 90F;
this.fpSpread1_Sheet1.Columns.Get(8).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(8).CellType = textCellType23;
this.fpSpread1_Sheet1.Columns.Get(8).DataField = "process";
this.fpSpread1_Sheet1.Columns.Get(8).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Left;
this.fpSpread1_Sheet1.Columns.Get(8).Label = "공정";
this.fpSpread1_Sheet1.Columns.Get(8).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(8).Width = 80F;
this.fpSpread1_Sheet1.Columns.Get(9).AllowAutoFilter = true;
this.fpSpread1_Sheet1.Columns.Get(9).CellType = textCellType24;
this.fpSpread1_Sheet1.Columns.Get(9).DataField = "projectName";
this.fpSpread1_Sheet1.Columns.Get(9).Width = 158F;
this.fpSpread1_Sheet1.Columns.Get(10).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
numberCellType6.DecimalPlaces = 0;
numberCellType6.LeadingZero = FarPoint.Win.Spread.CellType.LeadingZero.Yes;
numberCellType6.MaximumValue = 2147483647D;
numberCellType6.MinimumValue = -2147483648D;
this.fpSpread1_Sheet1.Columns.Get(10).CellType = numberCellType6;
this.fpSpread1_Sheet1.Columns.Get(10).DataField = "pidx";
this.fpSpread1_Sheet1.Columns.Get(10).Tag = "pidx";
this.fpSpread1_Sheet1.Columns.Get(10).Width = 39F;
numberCellType7.MaximumValue = 999999999999999D;
numberCellType7.MinimumValue = -999999999999999D;
this.fpSpread1_Sheet1.Columns.Get(11).CellType = numberCellType7;
this.fpSpread1_Sheet1.Columns.Get(11).DataField = "hrs";
this.fpSpread1_Sheet1.Columns.Get(11).Width = 52F;
numberCellType8.MaximumValue = 999999999999999D;
numberCellType8.MinimumValue = -999999999999999D;
numberCellType8.NullDisplay = "--";
this.fpSpread1_Sheet1.Columns.Get(12).CellType = numberCellType8;
this.fpSpread1_Sheet1.Columns.Get(12).DataField = "ot";
this.fpSpread1_Sheet1.Columns.Get(12).ForeColor = System.Drawing.Color.Red;
this.fpSpread1_Sheet1.Columns.Get(12).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(12).Tag = "ot";
this.fpSpread1_Sheet1.Columns.Get(12).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(13).CellType = textCellType25;
this.fpSpread1_Sheet1.Columns.Get(13).DataField = "description";
this.fpSpread1_Sheet1.Columns.Get(13).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Left;
this.fpSpread1_Sheet1.Columns.Get(13).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(13).Width = 113F;
this.fpSpread1_Sheet1.Columns.Get(14).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(224)))), ((int)(((byte)(192)))));
dateTimeCellType3.Calendar = new System.Globalization.GregorianCalendar(System.Globalization.GregorianCalendarTypes.Localized);
dateTimeCellType3.CalendarSurroundingDaysColor = System.Drawing.SystemColors.GrayText;
dateTimeCellType3.DateTimeFormat = FarPoint.Win.Spread.CellType.DateTimeFormat.TimeOnly;
dateTimeCellType3.MaximumTime = System.TimeSpan.Parse("23:59:59.9999999");
dateTimeCellType3.TimeDefault = new System.DateTime(2025, 11, 11, 22, 53, 4, 0);
this.fpSpread1_Sheet1.Columns.Get(14).CellType = dateTimeCellType3;
this.fpSpread1_Sheet1.Columns.Get(14).DataField = "otStart";
this.fpSpread1_Sheet1.Columns.Get(14).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(14).Label = "시작";
this.fpSpread1_Sheet1.Columns.Get(14).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(15).BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(224)))), ((int)(((byte)(192)))));
dateTimeCellType4.Calendar = new System.Globalization.GregorianCalendar(System.Globalization.GregorianCalendarTypes.Localized);
dateTimeCellType4.CalendarSurroundingDaysColor = System.Drawing.SystemColors.GrayText;
dateTimeCellType4.DateTimeFormat = FarPoint.Win.Spread.CellType.DateTimeFormat.TimeOnly;
dateTimeCellType4.MaximumTime = System.TimeSpan.Parse("23:59:59.9999999");
dateTimeCellType4.TimeDefault = new System.DateTime(2025, 11, 11, 22, 53, 4, 0);
this.fpSpread1_Sheet1.Columns.Get(15).CellType = dateTimeCellType4;
this.fpSpread1_Sheet1.Columns.Get(15).DataField = "otEnd";
this.fpSpread1_Sheet1.Columns.Get(15).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(15).Label = "종료";
this.fpSpread1_Sheet1.Columns.Get(15).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(16).CellType = textCellType26;
this.fpSpread1_Sheet1.Columns.Get(16).DataField = "kisuldiv";
this.fpSpread1_Sheet1.Columns.Get(16).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(16).Tag = "kisuldiv";
this.fpSpread1_Sheet1.Columns.Get(16).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(17).CellType = textCellType27;
this.fpSpread1_Sheet1.Columns.Get(17).DataField = "kisullv";
this.fpSpread1_Sheet1.Columns.Get(17).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(17).Tag = "kisullv";
this.fpSpread1_Sheet1.Columns.Get(17).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
numberCellType9.DecimalPlaces = 2;
numberCellType9.NegativeRed = true;
this.fpSpread1_Sheet1.Columns.Get(18).CellType = numberCellType9;
this.fpSpread1_Sheet1.Columns.Get(18).DataField = "kisulamt";
this.fpSpread1_Sheet1.Columns.Get(18).HorizontalAlignment = FarPoint.Win.Spread.CellHorizontalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(18).Tag = "kisulamt";
this.fpSpread1_Sheet1.Columns.Get(18).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
this.fpSpread1_Sheet1.Columns.Get(19).CellType = textCellType28;
this.fpSpread1_Sheet1.Columns.Get(19).DataField = "tag";
this.fpSpread1_Sheet1.Columns.Get(19).VerticalAlignment = FarPoint.Win.Spread.CellVerticalAlignment.Center;
numberCellType10.DecimalPlaces = 0;
numberCellType10.MaximumValue = 10000000D;
numberCellType10.MinimumValue = -10000000D;
this.fpSpread1_Sheet1.Columns.Get(20).CellType = numberCellType10;
this.fpSpread1_Sheet1.Columns.Get(20).DataField = "idx";
this.fpSpread1_Sheet1.Columns.Get(20).Tag = "idx";
this.fpSpread1_Sheet1.DataAutoCellTypes = false;
this.fpSpread1_Sheet1.DataAutoSizeColumns = false;
this.fpSpread1_Sheet1.DataSource = this.bs;
this.fpSpread1_Sheet1.Protect = false;
this.fpSpread1_Sheet1.RowHeader.Columns.Default.Resizable = false;
this.fpSpread1_Sheet1.SelectionPolicy = FarPoint.Win.Spread.Model.SelectionPolicy.MultiRange;
this.fpSpread1_Sheet1.ShowEditingRowSelector = true;
this.fpSpread1_Sheet1.ShowRowSelector = true;
this.fpSpread1_Sheet1.ReferenceStyle = FarPoint.Win.Spread.Model.ReferenceStyle.A1;
//
// fJobReport
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
@@ -1062,13 +1059,13 @@
((System.ComponentModel.ISupportInitialize)(this.dsMSSQL)).EndInit();
this.cm.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.fpSpread1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1_Sheet1)).EndInit();
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.fpSpread1_Sheet1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();

View File

@@ -13,6 +13,7 @@ namespace FPJ0000
{
public partial class fJobReport : FCOMMON.fBase
{
int curLevel = 0;// Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
string fn_fpcolsize = "";
public fJobReport()
{
@@ -23,7 +24,7 @@ namespace FPJ0000
dtSD.KeyDown += dtSD_KeyDown;
dtED.KeyDown += dtSD_KeyDown;
curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
}
void __Closed(object sender, FormClosedEventArgs e)
@@ -144,7 +145,7 @@ namespace FPJ0000
//일반사용자의경우에는 상태를 변경하지 못한다.
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if (curLevel >= 5)
{
btSave.Visible = true;
@@ -263,9 +264,16 @@ namespace FPJ0000
return;
}
string request = cmbUser.Text.Substring(cmbUser.Text.LastIndexOf('(') + 1);
request = request.Substring(0, request.Length - 1);
if (cmbUser.SelectedIndex == 0) request = "%"; //전체검색 201028 - chi(임현대)
if (cmbUser.SelectedIndex == 0) request = "%";
//사용자 목록다중선택으로 인해 처리
@@ -314,6 +322,21 @@ namespace FPJ0000
//else
// this.ta.FillByTagOnly(this.dsMSSQL.JobReport, dtSD.Text, dtED.Text, request, FCOMMON.info.Login.gcode);
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if ( request != FCOMMON.info.Login.no && curLevel < 5)
{
this.fpSpread1_Sheet1.Columns["ot"].Visible = false;
}
else
{
this.fpSpread1_Sheet1.Columns["ot"].Visible = true;
}
RefreshTime();
FPUtil.ColSizeLoad(ref this.fpSpread1, fn_fpcolsize);
FormattingData();
@@ -824,6 +847,11 @@ namespace FPJ0000
private void toolStripButton11_Click(object sender, EventArgs e)
{
if(curLevel < 5)
{
AR.UTIL.MsgE("사용 권한이 없습니다");// util.MsgE("");
return;
}
var f = new JobReport_.rJobReportUser();
f.Show();
}
@@ -983,5 +1011,10 @@ namespace FPJ0000
}
}
private void fpSpread1_CellClick(object sender, FarPoint.Win.Spread.CellClickEventArgs e)
{
}
}
}

View File

@@ -130,225 +130,227 @@
<data name="bindingNavigatorMoveFirstItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vQAADr0BR/uQrQAAASpJREFUOE9jGDygcNbz/00Lnv/PnPj4P1QIA4S3P8Apx5A789n/VUfe/8elKL77
wf/ghmu4DciY8vT/wn0fsCqK73n4f+n+///9qy/gNiCh58n/aVveYyiKaL8P1pw56/9/r9ITuA2I7Hr0
v3f1BxRFoa33wJpb1wFt7/z73yX/AG4DApsf/q+b/w6uKLjl7v9Fe///7wBqzpjz879d3c//9hnbcRvg
UXX/f/60NyiK7Ipv/0+f8/u/f9e3/zqF7/5bJKzHbYB96d3/2ZNfYyjSTzn/36ToxX+VrE//jSOX4TbA
Iu/O/9T+11gVGSSd+C+b9vW/bvA83AYYZt3+H9byEqci/dTL/zV8p+E2QCftxn+/6od4Fal4TMBtgFPu
lf8gBXgVDULAwAAA8HbAq6XlmnAAAAAASUVORK5CYII=
vQAADr0BR/uQrQAAATFJREFUOE9jYBg0oHDW8/9NC57/z5z4+D+6HAyEtz/AKceQO/PZ/1VH3v/HpSi+
+8H/4IZrWOXAIGPK0/8L933Aqii+5+H/pfv///evvoAhBwcJPU/+T9vyHkNRRPt9sObMWf//e5WewG1A
ZNej/72rP6AoCm29B9bcuu7/f//Ov/9d8g/gNiCw+eH/uvnv4IqCW+7+X7T3//+Odf//Z8z5+d+u7ud/
+4ztuA3wqLr/P3/aGxRFdsW3/6fP+f3fv+vbf53Cd/8tEtbjNsC+9O7/7MmvMRTpp5z/b1L04r9K1qf/
xpHLcBtgkXfnf2r/a6yKDJJO/JdN+/pfN3gehhwcGGbd/h/W8hKnIv3Uy/81fKdhlQMDnbQb//2qH+JV
pOIxAaccg1Pulf8gBXgVDUoAAPB2wKtYlLYeAAAAAElFTkSuQmCC
</value>
</data>
<data name="bindingNavigatorMovePreviousItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vQAADr0BR/uQrQAAALZJREFUOE9jGDogvP3BfyiTdBDf/eB/cMM18gyI73n4f+n+///9qy+QbkBE+32w
5sxZ//97lZ4gzYDQ1ntgza3rgLZ3/v3vkn+AeAOCW+7+X7T3//8OoOaMOT//29X9/G+fsZ00F9gV3/6f
Puf3f/+ub/91Ct/9t0hYT3oY6Kec/29S9OK/Stan/8aRy0g3AAQMkk78l037+l83eB55BoCAfurl/xq+
08g3AARUPCZQZsBgBQwMANAUYJgEulBVAAAAAElFTkSuQmCC
vQAADr0BR/uQrQAAALtJREFUOE9jYBgyILz9wX90MaJBfPeD/8EN18gzIL7n4f+l+///96++QLoBEe33
wZozZ/3/71V6gjQDQlvvgTW3rvv/37/z73+X/APEGxDccvf/or3//3es+/8/Y87P/3Z1P//bZ2wn3gAQ
sCu+/T99zu///l3f/usUvvtvkbCeNANAQD/l/H+Tohf/VbI+/TeOXEa6ASBgkHTiv2za1/+6wfPIMwAE
9FMv/9fwnUa+ASCg4jGBMgMGLwAA0BRgmCws/7cAAAAASUVORK5CYII=
</value>
</data>
<data name="bindingNavigatorMoveNextItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vQAADr0BR/uQrQAAAKNJREFUOE9jGHygcNbz/1AmeSB35rP/Cd33yDckY8rT//P2//6f0HWHPEMSep78
n73v1//OrX//u5VeJt2QyK5H/6ds+/W/ZOnf/wnT//63yT1LmiGBzQ//t659D9ZsXPLlv3T0tf/GkcuI
N8Sj6v7/krnv4JoVXXpIc4F96d3/gS3PyNMMAhZ5d/7bFFwhTzMIGGbdJl8zCOik3SBf81AEDAwAoH5f
oAc0QjgAAAAASUVORK5CYII=
vQAADr0BR/uQrQAAAKRJREFUOE9jYBh0oHDW8//oYiSB3JnP/id03yPfkIwpT//P2//7f0LXHfIMSeh5
8n/2vl//O7f+/e9Wepl0QyK7Hv2fsu3X/5Klf/8nTP/73yb3LGmGBDY//N+69j1Ys3HJl//S0df+G0cu
I94Qj6r7/0vmvoNrVnTpIV4zCNiX3v0f2PKMPM0gYJF3579NwRXyNIOAYdZt8jWDgE7aDfI1D00AAKB+
X6Bjq5qXAAAAAElFTkSuQmCC
</value>
</data>
<data name="bindingNavigatorMoveLastItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vQAADr0BR/uQrQAAASxJREFUOE9jGFygcNbz/1AmBgDJNS14/j9z4mOcahhyZz77n9B9D6sCkNyqI+//
h7c/wG1AxpSn/+ft//0/oesOhiKQ3MJ9H/4HN1zDbUBCz5P/s/f9+t+59e9/t9LLKApBctO2vP/vX30B
twGRXY/+T9n263/J0r//E6b//W+TexauGCTXu/rDf6/SE7gNCGx++L917XuwZuOSL/+lo6/9N45cBtYA
kqub/+6/S/4B3AZ4VN3/XzL3HVyzoksPXDFILn/am//2GdtxG2Bfevd/YMszDM0gAJLLnvz6v0XCetwG
WOTd+W9TcAVDMwiA5FL7X8O9hBUYZt3GqhkEQHJhLS//6wbPw22ATtoNnJIgOb/qh/81fKfhNgAfcMq9
8l/FYwIYQ4UGBWBgAAC+0b+zuQxOnAAAAABJRU5ErkJggg==
vQAADr0BR/uQrQAAAStJREFUOE9jYBhUoHDW8//oYjAAkmta8Px/5sTHONUw5M589j+h+x5WBSC5VUfe
/w9vf4BVHgwypjz9P2//7/8JXXcwFIHkFu778D+44RqGHBwk9Dz5P3vfr/+dW//+dyu9jKIQJDdty/v/
/tUXcBsQ2fXo/5Rtv/6XLP37P2H63/82uWfhikFyvas//PcqPYHbgMDmh/9b174HazYu+fJfOvraf+PI
ZWANILm6+e/+u+QfwG2AR9X9/yVz38E1K7r0wBWD5PKnvflvn7EdtwH2pXf/B7Y8w9AMk8ue/Pq/RcJ6
3AZY5N35b1NwBUMzTC61/zXcS1iBYdZtrJpBACQX1vLyv27wPKzyYKCTdgOnJEjOr/rhfw3faTjV4AVO
uVf+q3hMAGN0uYEFAL7Rv7NmXVYYAAAAAElFTkSuQmCC
</value>
</data>
<data name="btAdd.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vQAADr0BR/uQrQAAAUpJREFUOE9jGLzg7gL2/7fmcf6/Oofr/8UZvP+hwsSD60CNfx41/v/zsOH/yckC
pBtwfjov3ICDPSKkG3B8kiBQc93/Pw+q/u9oFydswKWZPP/PTuX7fxKo8Ui/0P993SJAzeX//94r+r++
Qeb/qhq5/0srFf/PL1X+P6tIFdPAU0B//nlYD9RUC8SV///cKwHivP9/72b+/3sn+f/f23H//92MAOKQ
/5NyNDENONQrDHbu3/ulQI0FQI3ZQI2pQI0J///digZqDPv/70bQ/3/X/f53peliGrCzXeL/lmap/+vA
zpX/v6RC8f/fWzFAjeH/p+Zp/J+QpfW/O0P3f3uq/v/mREPCYTIb6E+Qc//dCPjfk6FDWAM6APnz3w1/
IPb735qsT7oB3em6YP+CcH2cEekGtCQZ/G+IN/xfE2v8vzLahHQD6AQYGAAkI9iedfyIaQAAAABJRU5E
rkJggg==
vQAADr0BR/uQrQAAAVdJREFUOE/Nz0tLAmEUBmB3kWRoCUVEISFUJGb1OywiKrDsIpZdkJAkDUvDQkij
UKSbVIvatKhNi9oERRAGEQXhjJdp7Hd83/eGs2jhLGQ20QtndTgP71Gp/m0KZ1XInlTjM6XG+4EG5fuK
yaTUIN8bIMUQ0gmtcuBtX/MLPMT0yoHnuA6kuA4iruI20lAZ+DiswWuyFum4Dk+7dbiP6kHEFVDBg+tQ
My4DLbjwG3DqbcORxygHXxJakGIQRFwDEf0gwjKI4AYtzIHmHaA5Oxg/CsYPIb7YIQced+qluvTLCyIs
gRYWQPNO0NwkWNYGxg+DcYNgGSu2Z0xy4C7SiJtwE66kuq049xlAs2Ng/AiS7nbszXci6jIh4jQjPGWR
A+U59hiluowbQMzVVfmgPKU/GdcPxlmx5TArB6KzJunf0gTtPcqBzeluhCYsCIz3wm/rUw78WX4AJCPY
nlwVm9EAAAAASUVORK5CYII=
</value>
</data>
<data name="btEdit.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEvSURBVDhPpZI/S0JRGIed+i6Nbk0t0iCuSkuDi1MfIKfm
+gAOQktTQkMggYtRkwgidKmgyQwMCgqz0CivvvK8cK7neG4KdeHhHs45v+c9/xLyz08Ft713OQ+6SqXV
kfLlnXJw1lSK5VrERqGkMB4JCCLpvQ7lZfDlQJ+B4EnwI9nTkbYdAZMbjxOPq4eJPH1MvXC2sD8XsOzP
0bcX/C3MXEfAfmzBsnCnP10uWBWu3IS+gJOm0w5fHCZiw0aQzu3GC0xYgm2R+poTRnh8HeqNOALu920w
9MK0F8NGkMrs+ALewqrwUXss3ed+vKB6H+rh2OT3SjpO0IBgcyvnCjgDBGCq8mcMiQ3FHAGdLB/J4vMF
KhoI83LXk6m5gCpmufbyOWlgv0BVIMx4JPj7JzIDGHRUPz2nxiQAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEuSURBVDhPpZKxS0JBHMff1P/S2NbUIg7RqrQ0tDj5B9jU
nH+Ag9DSlOAQhNCi6BRBBD1SaDIDg4JCLVSqpz/5HJzdz3vp4MH3vcfdfT73e3cXyJot4NHs9qUSdkxK
t20p1lsmJxc3JkfFq3m2MwUTxucCQCTd96G8DcYq9NkAnoc/kiqPzLcSMPn6eeKl8TSRl8+pB6cyx38C
yv4afXvgfzBzlYD/cQXL4HZvulywCi49RL6AnabThWv5IBa2gt10Nl5gYQn3RaobCkZ4dh+ZE1ECzvdj
MPRgvhdhK0jsHfgC7sIq+PTuVzqvvXjB5WNkNsfNYa5gxgFtEOwk01rAHiAgdlXejCFxw2JKQCflI1m8
voQVbYC5uZtbCV2BLdctn50m/C9hVQKs7sE6bQYYdFQ/+SVRqQAAAABJRU5ErkJggg==
</value>
</data>
<data name="btDel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vQAADr0BR/uQrQAAAW9JREFUOE+1kE0ow2Ecx3dV3krt4oJaOSCTvIRkMqSxyITIzCQHDouEdnFwIOVC
DrhIDiQl5UTiNG/z2ppafy1S2gX/uDwfY6i1v7Hie3nqeb7fz+/7/FR/Ilwn0G0Exw4fV5GJlXlEZxXC
rIet9bAQvB5Ymgn2sLYAvSZEux7RUQFzE4qQt4bCXAYjPaHvnDoCkLpsRGMB2JqCTGLIijDlwqQ9bEMV
i9OIytR3EMNWcJ/BWH8A6j8/bOGFxwXNxYEvGbMQ9XnQ1/K78KfY3/VXzkMY0qFGG2H4RoLGQshJQNbG
86CNhdrsX9a/uQZTPhQl4rMY4OLofbl3aX7I8uwPC7y/g1YdjyVJuEvT8e1tfwUYteHUxCCfHChDeHmG
QQvokjlOU+PbWA0x3pZnILVVI3uvQyHsbiLnqnGmRCF1NYD8pDhpRxOH7HQoAKZGkFKjceszQbpSrumX
bO+G80MFwKUTxgfgcO/b8D9IpXoFiiMDHIQm0skAAAAASUVORK5CYII=
vQAADr0BR/uQrQAAAWtJREFUOE+1kE0ow2Ecx/9X5a2UiwtKOSCTmJBMhuQlMo3IvCUHDouEXHZwIOVC
DrhIDiQl5USy07zNa2tKf2laaRf84/J8xBCetab4XL/f76fn+SnKX4DrGLqrwbHDzywkWJlHdJYjLEbY
Wg8q4eYKlma+d1hbgF4TotWIaC+FuYmAktcXCksx2HrknBOHX1KbiTDngrXhW0kMdSBM2TA5Io+/wuI0
oiz5TcRwB7hPYazfLx3rDz7+gCsXNBb4v1SdgajTQ19TaOMP2NtFmPSIilSo0v1y7FHBnAdZMWi6aO51
kVCTGZoEzzWYciA/Dl9bBZwfvh3XmxIJy7PBJdx5odnAQ2E87qJUfPbtzwGjVpxJEWjH+4ElPD/BYBsY
EjhKicW3sSoVb0vSUFsq0W6upUxhdxMtOxZnYhhqVz1oj3JJUZSdpCg0p0POmLKhJofjNqaDeikX3tFG
uuHsQM65cML4ABzY5fA/eQGKIwMcVjm2bAAAAABJRU5ErkJggg==
</value>
</data>
<data name="btFind.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="btImport.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE8SURBVDhPnZI/S0JRGIf9Oo19ARdziDZBGmwRx5amFsG5
BolahJbWmkKwKYQ2BZGISohABIWCjJDsgmj3xHPivfc99yCCB56r98/vOed9z0mZNUZKDx48vU1N42Fg
uez0zfldz3J03bYc1hoOjoQbgkhG4x/zMQkceKY5vh2b3YPTWCICPm4NQ49m/zfiMwitwJFwYdnfwcwL
l06yngD4NlcsxwLqTQoIa8HNyyJipYDgWbcQSQT6dPU49wV0WgTF6pap1AtWoEEwnISRYDu/7wtYalLA
f9ACdsMRsLfsgtSLhDC/ugQEF/f/gszO3nIBjZIwIaA8wjB4/1oukE6zVAQ6WOssLAjS2bwrkB5ogZAU
MJkj4FQ9v46shJf66AIzCrzn5G5sZmIBK0AisMdAp4F6BWYm7JyD9Ycxf8jQQxybOGd5AAAAAElFTkSu
QmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE5SURBVDhPpZK9S0JRGIf9dxr7B1rMQdwCadBFGl2aXILm
GiRqEVxcc4rApgjcCiQi+gAJRFBQUBGpLkh2Tzwn3ut570mCOvBcvR+/55z3PSdh/jAS7uDB4+DNNO67
ltNWx1Sbz5aDsxtLqdJQKAk3BJH0x+9mNAsUPHM5vByb7d3jpUQEfHzdCz2uOp8RkyC0AiXhwrJfg7kX
3jlKeQLg263C3lJAvXEBYVdw0V5E/CogeHKbiyQCfao/fPgCOi2CQnnT7J/nrMAFQW8WRoJ0tugLWGpc
wH9wBeyGErC37ILUi4Qwv24JCGp334JkJr9aQKMkTAgojzB0h9PVAuk0S0XgBiuthQXBRiqrBdIDVyDE
BUymBJyqp5e+lfx0fJlR4D0nd209qVeARGCPgU4D9QrMTFidg/+ML8jQQxyg3treAAAAAElFTkSuQmCC
</value>
</data>
<data name="btOpenFolder.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton10.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAALwSURBVDhPhZJZTxNRGIZ7JT/BoBdekhiFKEaNXigIWMAE
NwSCyFYKAQEJ1GAi1hSMyGJAAiRqCETjhRpwgQKWrZTSNhQDMpZF9i1hqUCXGehMeT0zrYAJiV/y5Fyc
eZ/M986I+Al8qI67UqxXhZXofwfJ9TQ5GYFS/jQwYcU6JuSZljov05QfDX9/QAjtneAnPZplC7NKURRn
tm3CbN1l1cLDYHjRgrwvs4gqGbGL05Ue7qhrQgu1yxRl4nTTDhhmWBhmWeinWWintqD+tYkWkw2bLNA4
QiPv0wwSX1D0P5IgeQu9Yd9C/xyH73MsOVn0CZItdI9v4pvJTgRO9C9yMCw4UKCchf+DjndCOLLUb/KW
PHxbWnUDiZU3Udlah/z6UvjLveFHEBecQ6DiNMoay4RVrAyLpTU7AmO7WUEQUez/sWOoCU1jr1GpzxEk
sRUhyHkbg4y6KEiqryKhOgGNg0tooqz4+mMD66SnIEUPIwjE5WIPH3kmpx/twpshBYrU6YIkriqUhK8h
5WUyNOM2YZX2MRoq0sOazYGg/F6XgJ/gwm4mqyYexnEtqg338bQtG5GllxBXEQ7jtB1GUixfsGbC1YmZ
CAIVewQXHskdZ3KPIKZcjP4JHQq6UqFQZiHqeQDq1J8xMM/CSIrVTTnQS1i2OOD/2C04KfOU+soOI6M2
GrerghFddlmQZCljIKtPJm8SAP6+f5ZDH/+JCRZSpE+m1iXwlR2c95UdIg95wjfHE9eLApD2Kh4akwoF
qlykfoiHpCoCRiIwkH9DTwSMw0kE6t0VLmb2MIvrDAbnOYHu0QXcq0lBEglKq6OQXXt3547HvsURQcOu
gLfNmXcF/8PGr5C8R3A2q9Ou0g1u7/fwflDDo87jsY1Wd5wUmdI2ObNht3Tqh5yTKzQml2mMu1mxOgT4
5q0MhwFqxEnT9LrXneaf7rhIdCqlNc1H0tJ2Qqoy+0gbmB2SXHj/RdLAeEma144lNLd7xysTRSKR6A+3
VJ80gqHE3AAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton11.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton12.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton13.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton5.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton14.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton9.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="dsMSSQL.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="toolStripButton10.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPhdHrT1JxHMdxHtWf0KwHPXRrBStatXpQkhpq
m91MXZmiiK4buaTZltGIlqk0rQlbNaer9aCadkG8YBdABBZ0UVQqb4C6ZZHK5Rw859CnnYNhtrV+23vn
we/8Xtv39+PxeDxe2iVT0f46uzG73v4jXWknsuvtJJeG/TrI7DobmXnD6t6lsDRuyHm0ij2zYmVc67PM
BsnvbrebCYSjCISW+x5kIzEyE0T1cx/y6z0R8RnD6hVAVo111u0eYmyTFBxeGg4fDfskDevEIkxfouga
DiNKA3oPgeqnXpTcchMrkHRlF7EQWYTLz+Cdn4bLT+MthyzCPBpFz3AEUToG1wwDxzQFtcEH0cVXD7nD
eZqU8aPKnJ8y7WGUNB1BU3crrrZpIFLykaLkQ6zeiTTVNjToG7hRQiSNr3MRpBWaaQ7IrRM9eTXYgY7P
99Bkr+SQwtuZqHxQgLOt+ZDqDqBYVwz9x6/ocIfwYmAB8+Eo0lV9JAeIG8Wr+Uo5Y//0BvcHVag1neGQ
Im0WpLqDKL9TBstomBvl5WcCRg+BuTCF9Kv9cYBdGTVmsqJZAueoFTrHBVzvPY88zV4U3c6BczICp5cG
e8GWsfidBMIU0lR/ALsvK6ntVetR0CiGa8wG9ZuTUBkqkH8zFa2mZ/gwRcPpp2GboNA/QWE2SEF0ZQnY
okiSCRXrcLblGI5rM3CsYR+HVBgKoGgrQ54mFey+y8fgLfvEXhpBkoZAbo0DQsWaKaFiLYSKJAgrk3Co
NhWn7kpgGTZCbazCyccSSLW5cPoYOCZp2L00SCoGgdy0PMIeeR85M0/i4xTDZf40jXPN5SjV5kKmy8f5
ltOJPbbIIgOBvH0ZYDV/YBn4X2F2hLI/gB0VryM9/R9+/v3jvxoc9sQ2FepDCWBLee+4dyESfG0biI1/
IzA+S2B0qW8hiou9+RDJ4P3gSIwgiPnkE51DCWBrefcpgbSrd7PMGBDI2slEpfH4v5O2k8nSzrmNxZ0v
+RJDCXv2F6ujnydZP57oAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton11.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton12.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton13.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton5.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton14.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="cm.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@@ -400,9 +402,9 @@
<value>499, 17</value>
</metadata>
<metadata name="fpSpread1_Sheet1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>563, 17</value>
<value>561, 17</value>
</metadata>
<data name="dateTimeCellType1.Calendar" mimetype="application/x-microsoft.net.object.binary.base64">
<data name="dateTimeCellType3.Calendar" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAEAQAAACZTeXN0ZW0uR2xvYmFsaXphdGlvbi5HcmVnb3JpYW5DYWxlbmRh
cgYAAAAGbV90eXBlEW1fY3VycmVudEVyYVZhbHVlD3R3b0RpZ2l0WWVhck1heBpDYWxlbmRhcittX2N1
@@ -412,7 +414,7 @@
AAD/////AQgAAP////8AAQgAAAs=
</value>
</data>
<data name="dateTimeCellType2.Calendar" mimetype="application/x-microsoft.net.object.binary.base64">
<data name="dateTimeCellType4.Calendar" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAEAQAAACZTeXN0ZW0uR2xvYmFsaXphdGlvbi5HcmVnb3JpYW5DYWxlbmRh
cgYAAAAGbV90eXBlEW1fY3VycmVudEVyYVZhbHVlD3R3b0RpZ2l0WWVhck1heBpDYWxlbmRhcittX2N1
@@ -428,90 +430,89 @@
<data name="toolStripButton8.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANPSURBVFhH7dhZTxNRHAVwPoRx3yuyKWUvqyJu4BqNiYnx
C/giaowRQSiibC0Uyg6CyqKSuEaEQvdCK7tPqAmokWDUiFGgyCrHO+k0oT609w4JvvQk9/n+MjPn/ifX
wx133BGQh8dfX3hwzGy9f7QfDUfIOtyH+kN9qEvoRW18L+4d7MHdAz24s5+sfd2o2duN6rgu3N7TharY
TlTu7kTFrlcojyEr2oKyKAtKIy0oiTCjONyMIkkHlGFkhbajMKQdBcEma36QIZHf3nWWGQdFkAm5gaYJ
fnvXWW5cfqAJeQFG8Nu7zv/AycUswCXgyqLNUEbokSdpQ4FEh6LwDiqc3N9AD1zKkyuM1KL/8UfMTs2j
p/ED5CEaKpxsJwNQKI57cu2V77A4xfFaKlzuDj09UAiuONKER5e6sfBngacBP0cmIQtTUeFy/BiArLjS
qA5UnTJgemKWpwFzM/OoOWuEIthIhcv21dED2XCkFLFafH8/ztNseSHtR16wjhqX5cMApMVxR4kiXIO3
ms88y5auhiFy8KqZcJneLEBKXGG4HqYKx1IM94+S766VGZfppaUH0uCKIkgpLjqWYuzLbxTGqcl3Z2DG
3drOAHSFK4nkS2Gd42m2UlSfMYIMfUG4m54aeqAzHDe+FGE6DHV85Wm2PEvpgzxIKxiXsY0B6AzHzdb8
UB0G2x2BT5N7IQvUCsbdEKnpgc5w3OBXSkwoP6F3fMXT86g8TUBivSBc+lYGoDOcffArQvRoTOxyKMkv
UhJ5TCtk/jpmnHRLGz3QFc4+W2VBGuhL3/A8Wz71jSJT3MyMS9vMAqTA2c+5LLEKA20jPM8WS+0gMnxb
mHBpm1rpgbQ4rhDyAANyJCp8GxrjebY8SepBhncrNS51IwOQFmdvK/fNKRM0mBpf9LNASlN6kqC81FS4
6xsYgCw4e1uz/NSoP2d2KM2P4QlIfZqocCnrVfRAVpy9EBk+KmiUAzzPluyIFipc8joGoBCcvRBS75fo
JH8zM5NzsNQNIlXURIW7traFHigUZy+E1LMFKaLnBNeMdFEbFS5pDQtwCTjaQvyLS1rdTA/8H7irqxiA
BGddbtzllU30Vx/cRQ53V7KcuCsrms7z27vjjjv08fD4CwlIPe+HvHrCAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANNSURBVFhH7dhnT1NRHAZwPoRx74osZZTRUkARFzijMTEx
fgHfiBpjRBCKIKuFQtkgoEBVElwRoaWTLkahvkJNQI0Eg0aMsqc85ia24TYBzrkk+KbPJ/jl3Puc/8nf
w8Mdd9yhz9Ozb689OWMZf3zaDsUpOxQne1B/ogd1cd2oje3Go+M2PDxmQ81RG2qOdKH6cBeqYjrx4FAn
KqM7UHGwA+UH2lEW1Y6ySCtKI6woEVlRHG5BkdCCQoEZ8jAz5KEmFISYkB9sHM/jG+JdHUtmjXGQ8Y3I
CTKOuTqWzFrj8oKMyA1sg6tjyfwPnDSABrgKXGmkBfJwPXIFrcgX6FAoNBPhpP4GciBXHHNyBSIt7M8+
Y3ZqHraGT5CGaIhwkv0UQK445uRMFR+wOEWxWiJczj49OZALrkhkROONLiz8WXDifg1OQBKmJMJl+1EA
aXElEWZUXjBgemzWiZubmUf15TbIgtuIcFm+OnIgHc4CebQWPz6OOnFMXovtyA3WEeMyfSiApDjmKpEJ
NXiv+crCdSr6kROkpsJleNMACXEFQj2M5exSDNiHIQlTUeMyvLTkQBJcYbgRjdfZpRgZmkRBjBqyYAM1
7v5eCuBKuGLRv1KMzzlxTCmqLrUhj88Nl+6pIQcuh2PGlyxMh37zNyeOycukHkj5Ws64tD0UwOVwzGzN
C9Whz8QGvkjshiRIyxl3j6cmBy6HYwa/XGBE2Tk9+xNPz6PiogGSAD0nXOpuCuByOMfgl4Xo0RDfySrJ
76FJSKNUkPjrqHHiXa3kwJVwjtkq4WugL3nnBDL50jOMjIBmalzKThogAc5xz2UGKNHbOshCWmv7kObb
QoVL2aEiB5LimEJIAw3IFijxvX+EhXyeYEOat4oYl7ydAkiKc7SV+efkcRpMjS56LEzPo+S8FuleaiLc
3W0UQBqco62ZfmrUX7GwSvNzYAxinyYiXNJWJTmQFucoRJqPEhp5rxPIJCu8hQiXuIUCyAXnKITY+w06
FP2YmZiDta4PybwmItydzS3kQK44RyHEni1I4r1CMq8ZqbxWIlzCJhrgKnCkhXDFJWxsJgf+D9ztDRRA
Zley1rib65vIVx/MIofZlawl7ta6pquuDnfccYcgfwEJSD3vAJhh2AAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton7.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANSSURBVFhH7djZTxNRGAVw/gjjvq9tVWgBAbUguO+aGE18
8N0XFROj4oYitHbB0haL4i4qGhUTRew27bS1pRSEJ4kkKCZuiRhJpEQE5TjDTKPEpL13TPClJ7nP95e5
c+a7mZRkkklGQu5saSus3RyM3t7UilsbubXhOW6uf46adS24sbYF19c049rqZlxdxa2VEVxZEcHl5U24
VNCEi/lhVC8L40JeI87ncksdQtXSEGxLQji3OIjKnCCs2c9gyeLWogDMmQFUZPijZ9PZfeL2iTPKOJjS
/dCr/L3i9okz2rizKj/KlT6I2yfO/8AZ02iAhLiLBWHY8nwwq12oVLOoyg1JxhlTWXIg6ZOrzGXRer8L
A99+oK3uDSxLGck4w0IKIPGxbmXxZwLVL2HO8UrC6Rd4yYGk75w1n0HP+z6RBwz9HMK9/RGYs33UON18
CiBpIWzqIK7vCgwfcSzfo4Oo3s7CnOWnwp1ReMiBJLhYW61LfHh0vFXkCfn8phemPCcqMv3EOK2cAkiK
ixXClOVBpPaVyBPSwX6EPt1BjNPIaIAUuFghjNkOdEW6RZ4Qr60dOqWbCKeZx5ADaXF8IcxZ3LuW70TP
uz9KMwTcLQzjTKo7Ia5sLgWQFhdrqynDh0s7WQz2/y5Nf+8ArBvc0C/0xMWVznGTA6XgYm01qBg8PNIi
8oR0+D6iVGGPizs9mwIoFccXQq9kUFc0EvjS+4EDOuLiSma5yIFScUYliws7vH8dsXmdC1oFExd3aiYF
UAquXOWDQW3Hl7dRkSaUpHZPI0rljoS4kzOc5EBaHP+d0ygb8DrySaQJYSwvcFpuJ8IVT6cBUuK0qU40
3uwUWULamfcoUTwhxhVPc5ADaXC6NDceHGoWWUK6X3+FJrMBGjlDjDsxlQJIijOkeVG1zTvistDPXRYs
613UuONTKIAkOH58aVVPR5aCu27V7A6iVOakxh2bbCcHkuD42WoqcIo0IXwpSmR2SbijkyiAJDh+tpbN
t6Ppdie+9w0ifKsTp+RPJOOOTHxKDiTB8YNft8AzPCFOyh7/05PjcUUTaIAEuESDnxZXNL6BHPg/cIfH
UQA5XHS0cQfG1pP/+uB/5PD/SkYTd3BM/V5x+2SSSYY8KSm/AHvBPpKbf2MsAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANRSURBVFhH7dhZT5NBGAVgfoRx31daWVpAUCgI7uCaGE28
8N4bFROjgqKApaVspQXZXZBNo2LiAm3pR7+2tpSW5UqCCQKJookYSaRGFOGYIWm1GGDmM4Gbnl/wZCZn
3skbEOCPP/6w58HR7uSGIzZ3/eEu1B3qQt3BTtQmdaImsQP3D3Sger8L9/a5cHevC3f3OHFntxO3d7Wj
KqEdlfEOVOx0oDyuDWWxbSiT2VEaY0dJtB23dthQvN2GoqhX0Ea+gnabFZoIKwrDLe6CMP78dMeMmWcc
1GEW5Egto9MdM2a+cQVSC/IlZkx3zJiFwOWFsgApcZUJDpTEmaGRtaBYxqM01i4YlxfC0wNpcOTkimN5
dD0ewM/vv9DdOAhtDCcYlxvMAKTBTV3rMR5/x1rRC812kyBcTpCJHkiF2+lAUTyHkaFvXuDkxCQeXXBC
E2Vmxqm2MgBpcKQQJTIbqk9bp67Ykx/ucVSc4KGJtDDhssWt9EAanKetRdFmPEvr8gJJPg+OQh1nQGGE
hRqnFDEAaXGeQqgjW+FseOuDfMN/RE6YnhqnCGQBMuA8hciL0mPAOeyDNJX0QCUxUuEUWzh6ICuOFEIT
aUVhvAEj7/8qzSTwMNmB7BDjnLiszQxAVpynrepwM6pO8Rgf+1OasdGfKDpoRE5w66w4+SYjPVAIztPW
XCmHp6kdXiDJG/NHyMW6WXE3NzIAheKmfiUSDo0pvsBe0wfIxfpZcZkbWuiBQnF5Eh7lJ03/XLEmsQVK
MTcrLmM9A1AILl9qRq5Mhy/v3F4cKUnD2TbIRfo5cenrDPRAVhx55xSSJvQ7P3lxJJz2NW6KdFS4G2tZ
gIw4ZYgBbbV9PrgebgiZ4pfUuBtr9PRAFpwq1Ignl10+uOH+r1BENEEh4qhx11czAGlxuaEmlB43+XwW
xtzj0Ca1MOPSVjEAaXBkfCmlzb6lmJhEzRkb5IEGZty1lTp6IA2OzFZ1gsGLIyGlyAzUCcJdXcEApMGR
2Zq1VYf2+j78+DYOR10fMkQvBeNSlzfTA2lwZPCrglqnJkR64PP/OjmCS1nGAqTAzTX4WXEpS5vogQuB
u7KEAUh2JfONu7j4Bf3qgyxyyK5kPnGXFr04N93hjz/+UOQ3e8E+krxr0cIAAAAASUVORK5CYII=
</value>
</data>
<data name="toolStripButton4.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGJSURBVEhL7ZS9TsQwEITzeoBo4Dko6eiRDpAQ4i0oqWg5
aICXoKKjpTQMmgkTs+tLQuhupJHXPzuffYqu22qszta7BeZ0eUXhgsLnD3u/9lf3O3+/kENghPocYKyt
uKc52+fLIZF72FctaPQrTBJCIpjbYT7CjJkmNUcwt8Nmw3EwCm+5BUU9+hJReMsOw3jxuF9u347L9fPh
YJ3xuaLwlv1FgN69n5SXclnWH6fl6umg32d8rii8ZX+poDBqrP3rizMo9ka9OAre5NZLZcBxlpih/ICM
NYToY/E9OIWyV69VLmrifqTDHowGBeNjcXjrpehzqI/EDfUNJ1CHb16P+nDBM2gG00hMrKiphi/2Ulfd
pNrhGRRj3a+R8bnqJq8d7j+v93itfpxjfC5vwugfGwy4oJhHoKif8bFwsA5So4w1QTX3nqwfNTFt4aA3
Zq5BGmXGTRMaI5jbYfXImHmqA2sw1qJ9ts+XQhGGUbWstfqfj+3zFYUoHMbFuNwLF2C5vATmdKsN6rpP
fZjWUaDH7j0AAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFtSURBVEhL7ZQxTgMxEEVzPUA0cA5KOnqkABJC3IKSipZA
A1yCio42pdGs+Ku/b2ezdggVfGlkx/b857G9WSz+VamL1X6J4PjOlJkLGnH5dDCaXz7ujcaa5ZCIMCU4
xpbfc/pNn2YRzOhhtonsFJrE6rJwmLdbV61kghgZtBleA2IQ5tDoV2+CxnNB6NXzYbn/OC23r8dtd07j
ufCKAvrweVbeynVZrc/LzctRP0/OSDSeC69U0Ijox9ivVjwF9TsmZyCa1sQUVMevimMteZ18gUL3psdS
DU0enfzIXWixG0eCPxaHT0LxGbElt1MHx7d5937Smws+BSWELXkDcXEGz6BbVepikvoOn4JGy3y15IzE
JO87PPtk2Fd+rCNnJO6ULzngguoBEpTlkzNQtmOCVYH/3lSxryMvlXZPMIMgr7waRtWAM6ha+jWJhgTz
eDVOn2b5XWXH3x8r/vno06zMhBVz/sfHvEk7q+zP6At9mNZRIJ9c/gAAAABJRU5ErkJggg==
</value>
</data>
<data name="btSearch.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE4SURBVDhPtZPPasJAEMbzPn0FwWA92JtP4NGKB1/DP7ei
QSsovkChh7ZBrCfpyR4sikopUgq9StFzM/UbZ5asSS4FfzAkO7vft5udiZMEnSBpk5dhFJmncjdHxXaG
A+9K4SbFT1luEwQBbXavVO5d0nI3ovnW5yeiMriiu+kt5asXbABEdgRigAUQDr+aHLU3lxoLl/yPJhvF
GsiYJ/vPdX5qPK3bVJ25VFukafztGQNsKHJ791I3w+8KcpNth8XDz5YxACI/gsR1J8sTYcO4UIzwv1gG
cTshgJ5IT8hChTMHsHi+v+fvffmxywVwN2FDkdsGEOK2ceu4feQ0tDqKyKMGqDfqjvprLzyswuX7Tf4E
dBo6zn/3OB7XHovRyuhQ6+hhYKA9DpL+A1keRebNAhkaJH0OHOcP031C4EjYr6wAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEiSURBVDhPY2DAAf6jAXRxVNVIAKYga5bH/7TpTmAMYsNA
3EQb3Ab8+/fv/71PZ/5nzXX+f+3T7v+X320H0yCct8Dz/9rTM/8HtGvDDcPQDAIgBSCNOx9NBuOOiw7/
u644/N9+ZzLYIKwGwARAkvP2dIJpGN52Y/r/9gsO/zuu2P/f+3Qq3ACQhVhtz5jlBLcBJnb43Qyw5p0P
pmB3AcwV6TNcwBLIBmLDKAbAeWQAFAPQbUG2DeYimAsxnA4z4PLnDWD/Hv2AGl0gAAobZAOxGgDSCApt
UKiDQh/ZJbDYgQGcBoDiGxTvoPiHpYUt15Gj7y9uL4BSGijFbb81FYy33pgK1gxKyqAUiuJ0ZAAyAJbG
QQBXPkDXBwfoCuA60MRpAgDTfULg/+7qPQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADlSURBVEhL7dQxCsIwFMbxnMrZWatzUXRUCo5ewSs4ewZR
DyDeQfEETsUbRL7YYNSXNC/UOiQP/kvzyE9KUcQ7y/VJtlHFvQYPjzf50xKsJsFmq81WHq538swMO9il
zhALxkWd7kAOJwsnjjPsYNeGs2B14fR5YTYq5O5c1u7sL987iAUjF+6LIjaMKJyDoiAYmVAvn8lsXHij
KBhGwDWof4APihqHqQ+OKhg2X3U/n7+9ah88CKY+pM9ndTgbplDbmQtnwS7UtmPDWfDf/jIRLnKhOuzY
UMSGmyrBauKE26jiohshHicE2B3dbRrmAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADiSURBVEhL7dVBCsIwEAXQnsq1a62ui6JLpeDSK3gF155B
1AOId1A8gSvxBiNTCcTJJM3E2i6SwN9MhnklDW2WRbvW2wu0EepW8PkBf02CE2xks9vD6f4y6jTYg720
riKCcVCvP4LxbOXEcQ97sNeGi+Bq4PwzMJ+UcLg+a3uON7NHDHODdZzu2dAgmAMQpzUXGgxTfFAsIJ+W
3uhPMAZxBaoH8EExjcPcheMSDOtHPSyWX0ftgwfB3EWitTpcDFNAf6d0z4WLYDqYu0i0x4aL4M4+mZhO
fhJNJsERw22EuvGsNycE2B33w41tAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton6.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
</root>

View File

@@ -38,13 +38,13 @@
System.Windows.Forms.Label label5;
System.Windows.Forms.Label label3;
System.Windows.Forms.Label label6;
System.Windows.Forms.Label label2;
System.Windows.Forms.Label label7;
System.Windows.Forms.Label label8;
System.Windows.Forms.Label label9;
System.Windows.Forms.Label label10;
System.Windows.Forms.Label label11;
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fJobReport_Add));
this.label2 = new System.Windows.Forms.Label();
this.lbSummary = new System.Windows.Forms.Label();
this.tbHrs = new System.Windows.Forms.TextBox();
this.bs = new System.Windows.Forms.BindingSource(this.components);
@@ -93,7 +93,6 @@
label5 = new System.Windows.Forms.Label();
label3 = new System.Windows.Forms.Label();
label6 = new System.Windows.Forms.Label();
label2 = new System.Windows.Forms.Label();
label7 = new System.Windows.Forms.Label();
label8 = new System.Windows.Forms.Label();
label9 = new System.Windows.Forms.Label();
@@ -204,12 +203,12 @@
//
// label2
//
label2.AutoSize = true;
label2.Location = new System.Drawing.Point(160, 163);
label2.Name = "label2";
label2.Size = new System.Drawing.Size(53, 12);
label2.TabIndex = 21;
label2.Text = "초과시간";
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(160, 163);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(53, 12);
this.label2.TabIndex = 21;
this.label2.Text = "초과시간";
//
// label7
//
@@ -499,7 +498,7 @@
this.panel1.Controls.Add(this.tbProject);
this.panel1.Controls.Add(projectLabel);
this.panel1.Controls.Add(this.tbHrs);
this.panel1.Controls.Add(label2);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.lbTitleTip);
this.panel1.Controls.Add(hrsLabel);
this.panel1.Controls.Add(this.chkAutoAdd);
@@ -671,7 +670,9 @@
//
// tam
//
this.tam.AuthTableAdapter = null;
this.tam.BackupDataSetBeforeUpdate = false;
this.tam.EETGW_DocuFormTableAdapter = null;
this.tam.EETGW_JobReport_AutoInputTableAdapter = null;
this.tam.EETGW_JobReport_EBoardTableAdapter = null;
this.tam.EETGW_NoteTableAdapter = null;
@@ -758,5 +759,6 @@
private System.Windows.Forms.Button cmbTypeFull;
private System.Windows.Forms.ComboBox tbJobGrp;
private System.Windows.Forms.ComboBox tbJobType;
private System.Windows.Forms.Label label2;
}
}

View File

@@ -352,6 +352,11 @@ namespace FPJ0000.JobReport_
if (ReadMode)
{
//개인정보패치 251111
label2.Visible = false;
tbOt.Visible = false;
panel1.Enabled = false;
this.Text += "(읽기전용)";
}

View File

@@ -147,9 +147,6 @@
<metadata name="label6.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="label2.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="label7.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
@@ -175,9 +172,8 @@
<value>299, 17</value>
</metadata>
<data name="richTextBoxEx1.Rtf" xml:space="preserve">
<value>{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}{\f1\froman\fcharset129 \'b8\'bc\'c0\'ba \'b0\'ed\'b5\'f1;}}
{\*\generator Riched20 10.0.22621}\viewkind4\uc1
\pard\fs20\lang1042 3. \f1\'c7\'c1\'b7\'ce\'c1\'a7\'c6\'ae\f0\lang1033 \f1\'c1\'a6\'b8\'f1\'c0\'ba\f0 (\f1\'b0\'f8\'c0\'e5\'b6\'f3\'c0\'ce\f0 ,PKG\f1\'b0\'f8\'c1\'a4\f0 /\f1\'c0\'e5\'ba\'f1\'b8\'ed\f0 \f1\'c1\'a6\'c1\'b6\'bb\'e7\f0 /\f1\'b8\'f0\'b5\'a8\'b8\'ed\f0 \f1\'c0\'e5\'ba\'f1\'b9\'f8\'c8\'a3\f0 \f1\'c1\'f6\'bf\'f8\'b3\'bb\'bf\'eb\f0 ) \f1\'c7\'fc\'bd\'c4\'c0\'b8\'b7\'ce\f0 \f1\'c0\'db\'bc\'ba\'c7\'d8\f0 \f1\'c1\'d6\'bc\'bc\'bf\'e4\f0 .\lang1042\par
<value>{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}{\f1\froman\fcharset129 \'b8\'bc\'c0\'ba \'b0\'ed\'b5\'f1;}}
\viewkind4\uc1\pard\lang1042\fs20 3. \f1\'c7\'c1\'b7\'ce\'c1\'a7\'c6\'ae\lang1033\f0 \f1\'c1\'a6\'b8\'f1\'c0\'ba\f0 (\f1\'b0\'f8\'c0\'e5\'b6\'f3\'c0\'ce\f0 ,PKG\f1\'b0\'f8\'c1\'a4\f0 /\f1\'c0\'e5\'ba\'f1\'b8\'ed\f0 \f1\'c1\'a6\'c1\'b6\'bb\'e7\f0 /\f1\'b8\'f0\'b5\'a8\'b8\'ed\f0 \f1\'c0\'e5\'ba\'f1\'b9\'f8\'c8\'a3\f0 \f1\'c1\'f6\'bf\'f8\'b3\'bb\'bf\'eb\f0 ) \f1\'c7\'fc\'bd\'c4\'c0\'b8\'b7\'ce\f0 \f1\'c0\'db\'bc\'ba\'c7\'d8\f0 \f1\'c1\'d6\'bc\'bc\'bf\'e4\f0 .\lang1042\par
}
</value>
</data>

View File

@@ -32,7 +32,8 @@ namespace FPJ0000.JobReport_
if (tbProcess.SelectedIndex < 0) tbProcess.SelectedIndex = 0;
this.tbMon.Text = DateTime.Now.Year.ToString();
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if (curLevel < 5) tbProcess.Enabled = false;
refrehData();
}
@@ -70,12 +71,13 @@ namespace FPJ0000.JobReport_
this.dataGridView1.Columns.Add(drYm.yymm, drYm.yymm.Substring(0, 7) + "\r\n(" + basehr.ToString() + ")");
}
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
//이름으로 정렬해서 데이터를 가져온다
var namelist = this.dsReport.jobReport.OrderBy(t => t.uname).GroupBy(t => t.uname);
foreach (var uname in namelist)
{
var drName = uname.FirstOrDefault();
if (curLevel < 5 && drName.uid != FCOMMON.info.Login.no) continue; //개인정보 251111
List<string> rowdata = new List<string>();
rowdata.Add(drName.uname + "(" + drName.uid + ")");

View File

@@ -12,14 +12,14 @@ namespace FPJ0000.JobReport_
{
public partial class rJobReportDay : fBase
{
// Boolean binit = false;
public rJobReportDay(string baseday )
// Boolean binit = false;
public rJobReportDay(string baseday)
{
InitializeComponent();
this.WindowState = FormWindowState.Maximized;
if (baseday.Length > 7)
tbMon.Text = baseday.Substring(0, 7);
}
private void rJobReport_Load(object sender, EventArgs e)
@@ -35,9 +35,12 @@ namespace FPJ0000.JobReport_
this.tbProcess.Text = FCOMMON.info.Login.process;
if (tbProcess.SelectedIndex < 0) tbProcess.SelectedIndex = 0;
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if (curLevel < 5) tbProcess.Enabled = false;
//this.tbMon.Text = DateTime.Now.ToString("yyyy-MM");
refrehData();
// binit = true;
// binit = true;
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
@@ -66,15 +69,15 @@ namespace FPJ0000.JobReport_
var Process = tbProcess.Text.Trim();
if (Process == "%" || tbProcess.SelectedIndex == 0) Process = "";
var wekklist = new string[] { "일","월","화","수","목","금","토" };
var wekklist = new string[] { "일", "월", "화", "수", "목", "금", "토" };
//날짜에 해당하는 열을 먼저 생성한다
dataGridView1.Columns.Add("dvcu_damdang", "사원명");
// dataGridView1.Columns.Add("dvcu_process", "공정");
var daylist = dsReport.JobReportDay.OrderBy(t=>t.pdate).GroupBy(t => t.pdate);
// dataGridView1.Columns.Add("dvcu_process", "공정");
var daylist = dsReport.JobReportDay.OrderBy(t => t.pdate).GroupBy(t => t.pdate);
foreach (var dayitem in daylist)
{
var dtValue = DateTime.Parse(dayitem.Key);
var week = wekklist[(int)dtValue.DayOfWeek];
this.dataGridView1.Columns.Add("dvcu_pdate", dayitem.Key.Substring(8, 2) + "(" + week.ToString() + ")");
@@ -96,26 +99,29 @@ namespace FPJ0000.JobReport_
// this.dataGridView1.Columns.Add("dvcu_pdate", dayitem.Key.Substring(8, 2) );
//}
this.dataGridView1.Columns[this.dataGridView1.Columns.Count -1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.dataGridView1.Columns[this.dataGridView1.Columns.Count - 1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
dataGridView1.Columns.Add("dvcu_sum", "합계");
this.dataGridView1.Columns[this.dataGridView1.Columns.Count - 1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
foreach (var item in dsReport.JobReportDay.Where(t=>t.processs.Contains(Process)).OrderBy(t => t.uname + t.uid).GroupBy(t => t.uname))
foreach (var item in dsReport.JobReportDay.Where(t => t.processs.Contains(Process)).OrderBy(t => t.uname + t.uid).GroupBy(t => t.uname))
{
//사용자별로 데이터를 가져온다.
var username = item.Key;
var userid = item.FirstOrDefault().uid;
if (curLevel < 5 && userid != FCOMMON.info.Login.no) continue;
List<string> rowdata = new List<string>();
rowdata.Add(username);
// rowdata.Add(item.FirstOrDefault().processs);
// rowdata.Add(item.FirstOrDefault().processs);
//이 사용자의 데이터를 날짜별로 정렬해서 가져온다.
var sum = 0.0;
var sumOT = 0.0;
var sumFR = 0.0; //휴일근무시간 210310
for (int i = 1; i < this.dataGridView1.Columns.Count-1; i++)
for (int i = 1; i < this.dataGridView1.Columns.Count - 1; i++)
{
var col = this.dataGridView1.Columns[i];
var dayStr = col.HeaderText.Substring(0, 2);
@@ -133,15 +139,15 @@ namespace FPJ0000.JobReport_
{
//이날은 휴일이다
sumFR += daydata.ot;// + daydata.hrs;
//sumOT += daydata.ot;
rowdata.Add("*" + daydata.hrs.ToString() + "+" + daydata.ot.ToString());
//sumOT += daydata.ot;
rowdata.Add("*" + daydata.hrs.ToString() + "+" + daydata.ot.ToString());
}
else
{
//평일이다
sumOT += daydata.ot;
if(daydata.jobtype == "휴가")
if (daydata.jobtype == "휴가")
{
if (daydata.hrs + daydata.ot == 8.0) rowdata.Add("휴가");
else rowdata.Add((daydata.hrs.ToString() + "+" + daydata.ot.ToString()));
@@ -150,7 +156,7 @@ namespace FPJ0000.JobReport_
{
rowdata.Add((daydata.hrs.ToString() + "+" + daydata.ot.ToString()));
}
}
}
else rowdata.Add("--"); //업무일지에 자료가 없다.
@@ -158,13 +164,13 @@ namespace FPJ0000.JobReport_
rowdata.Add(sum.ToString() + "+" + sumOT.ToString() + "(*" + sumFR.ToString() + ")");
this.dataGridView1.Rows.Add(rowdata.ToArray());
}
foreach(DataGridViewRow dvrow in this.dataGridView1.Rows)
foreach (DataGridViewRow dvrow in this.dataGridView1.Rows)
{
for(int i = 1;i < this.dataGridView1.ColumnCount-1;i++)
for (int i = 1; i < this.dataGridView1.ColumnCount - 1; i++)
{
var col = this.dataGridView1.Columns[i ];
var col = this.dataGridView1.Columns[i];
var cellvalue = "--";
if(dvrow.Cells[i].Value != null) cellvalue= dvrow.Cells[i].Value.ToString();
if (dvrow.Cells[i].Value != null) cellvalue = dvrow.Cells[i].Value.ToString();
if (cellvalue == "--") dvrow.Cells[i].Style.ForeColor = Color.Gray;
else if (cellvalue == "휴가") dvrow.Cells[i].Style.ForeColor = Color.Tomato;
else
@@ -174,7 +180,7 @@ namespace FPJ0000.JobReport_
double ot;
if (double.TryParse(datasplbu[1], out ot) == false) ot = 0;
if(col.Tag.ToString() == "1")
if (col.Tag.ToString() == "1")
{
dvrow.Cells[i].Style.ForeColor = Color.Green;
}
@@ -198,10 +204,10 @@ namespace FPJ0000.JobReport_
dvrow.Cells[i].Style.ForeColor = Color.Red;
}
}
}
}
}
this.dataGridView1.AutoResizeColumns();
}
@@ -209,11 +215,11 @@ namespace FPJ0000.JobReport_
private void tbProcess_SelectedIndexChanged(object sender, EventArgs e)
{
}
}
private void button1_Click_1(object sender, EventArgs e)
private void button1_Click_1(object sender, EventArgs e)
{
}
private void lbStt_Click(object sender, EventArgs e)
@@ -249,7 +255,7 @@ namespace FPJ0000.JobReport_
var sdo = DateTime.Parse(this.tbMon.Text + "-01");
var sd = sdo.AddMonths(-1);
tbMon.Text = sd.ToString("yyyy-MM");
}
private void toolStripButton7_Click(object sender, EventArgs e)

View File

@@ -1,4 +1,5 @@
using FCOMMON;
using AR;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -12,10 +13,11 @@ namespace FPJ0000.JobReport_
{
public partial class rJobReportOT : fBase
{
int curLevel=0;
public rJobReportOT()
{
InitializeComponent();
//this.WindowState = FormWindowState.Maximized;
curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.otconfirm));
}
private void rJobReport_Load(object sender, EventArgs e)
@@ -38,14 +40,16 @@ namespace FPJ0000.JobReport_
cmbUserList.SelectedIndex = 0;
refrehData();
//refrehData();
}
void refrehData()
{
////휴일데이터가 들어가 있음.
//var taH = new dsReportTableAdapters.HolidayLIstTableAdapter();
//taH.Fill(this.dsReport.HolidayLIst, tbMon.Text + "%");
if(curLevel < 5)
{
UTIL.MsgE("사용 권한이 없습니다");
return;
}
var isReqData = cmbApploval.SelectedIndex > 0; //미승인선택
string prcname = tbProcess.SelectedIndex < 1 ? "%" : tbProcess.Text.Trim();
@@ -459,7 +463,7 @@ namespace FPJ0000.JobReport_
private void tbProcess_SelectedIndexChanged(object sender, EventArgs e)
{
if (tbProcess.SelectedIndex >= 0) refrehData();
if (tbProcess.SelectedIndex >= 0 && curLevel >= 5) refrehData();
}
private void button1_Click(object sender, EventArgs e)

View File

@@ -38,6 +38,13 @@ namespace FPJ0000.JobReport_
//사용자 목록을 선택한다
UpdateUserList();
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if(curLevel < 5)
{
tbProcess.Enabled = false;
cmbUser.Enabled = false;
}
//this.tbMon.Text = DateTime.Now.ToString("yyyy-MM");
refrehData();
binit = true;

View File

@@ -45,6 +45,13 @@ namespace FPJ0000.JobReport_
//사용자 목록을 선택한다
UpdateUserList();
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if(curLevel < 5)
{
tbProcess.Enabled = false;
cmbUser.Enabled = false;
}
refrehData();
binit = true;
}

View File

@@ -47,6 +47,12 @@ namespace FPJ0000.JobReport_
binit = true;
tbProcess.SelectedIndexChanged += tbProcess_SelectedIndexChanged;
var curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.jobreport));
if(curLevel < 5)
{
tbProcess.Enabled = false;
cmbUser.Enabled = false;
}
}
void UpdateUserList()
{

View File

@@ -97,6 +97,8 @@
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ta = new FPJ0000.DSKuntaeTableAdapters.EETGW_HolydayRequestTableAdapter();
this.cmbUser = new System.Windows.Forms.ToolStripComboBox();
this.toolStripLabel1 = new System.Windows.Forms.ToolStripLabel();
((System.ComponentModel.ISupportInitialize)(this.bn)).BeginInit();
this.bn.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.bs)).BeginInit();
@@ -317,6 +319,8 @@
this.dtED,
this.toolStripButton7,
this.toolStripSeparator2,
this.toolStripLabel1,
this.cmbUser,
this.btSearch,
this.toolStripButton2,
this.toolStripSeparator3,
@@ -681,6 +685,18 @@
//
this.ta.ClearBeforeFill = true;
//
// cmbUser
//
this.cmbUser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbUser.Name = "cmbUser";
this.cmbUser.Size = new System.Drawing.Size(150, 37);
//
// toolStripLabel1
//
this.toolStripLabel1.Name = "toolStripLabel1";
this.toolStripLabel1.Size = new System.Drawing.Size(43, 34);
this.toolStripLabel1.Text = "담당자";
//
// fHolyRequest
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
@@ -771,5 +787,7 @@
private System.Windows.Forms.DataGridViewTextBoxColumn Response;
private System.Windows.Forms.DataGridViewTextBoxColumn HolyBackup;
private System.Windows.Forms.DataGridViewTextBoxColumn remarkDataGridViewTextBoxColumn;
private System.Windows.Forms.ToolStripLabel toolStripLabel1;
private System.Windows.Forms.ToolStripComboBox cmbUser;
}
}

View File

@@ -36,6 +36,8 @@ namespace FPJ0000
cmbCf.DisplayMember = "DP";
cmbCf.ValueMember = "VAL";
dv1.EditMode = DataGridViewEditMode.EditProgrammatically;
curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyreq));
}
void __Closed(object sender, FormClosedEventArgs e)
@@ -48,19 +50,71 @@ namespace FPJ0000
private void __Load(object sender, EventArgs e)
{
EnsureVisibleAndUsableSize();
curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyreq));
this.dtED.Text = DateTime.Now.AddMonths(1).ToShortDateString();// sdate.AddMonths(1).AddDays(-1).ToShortDateString();
this.dtSD.Text = DateTime.Now.AddMonths(-1).ToShortDateString();
this.dv1.EditMode = DataGridViewEditMode.EditProgrammatically;
UpdateUserList();
btSave.Visible = false;// curLevel >= 5;
btEdit.Text = curLevel >= 5 ? "승인(&E)" : "편집(&E)";
// binit = true;
if (curLevel < 5)
{
cmbUser.Enabled = false;
}
else
{
if (cmbUser.Items.Count > 0)
cmbUser.SelectedIndex = 0;
}
refreshData();
}
void UpdateUserList(bool datelist = false)
{ //this.cmbUser.DisplayMember = "dispName";
//this.cmbUser.ValueMember = "id";
// this.bs.DataSource = dr;
this.cmbUser.Items.Clear();
this.cmbUser.Items.Add("-- ALL --");
//해당그룹내의 사용자 목록을 가져와야한다
if (datelist == false)
{
var dtUser = FCOMMON.DBM.getActiveUserTable();
foreach (DataRow dr in dtUser.Rows)
{
this.cmbUser.Items.Add(dr["dispname"].ToString());
}
}
else
{
var ta = new dsQueryTableAdapters.HolydayUserListTableAdapter();
var dtUser = ta.GetData(FCOMMON.info.Login.gcode, dtSD.Text, dtED.Text);
foreach (dsQuery.HolydayUserListRow dr in dtUser.Rows)
{
this.cmbUser.Items.Add($"{dr.UserName}({dr.uid})");
}
}
this.cmbUser.Text = $"{FCOMMON.info.Login.nameK}({FCOMMON.info.Login.no})";
//if (this.cmbUser.SelectedIndex < 0) this.cmbUser.SelectedIndex = 0;
}
string GetUIDValue()
{
string uid = "%";
if (cmbUser.SelectedIndex > 0)
{
var si = cmbUser.Text.IndexOf("(");
var ei = cmbUser.Text.LastIndexOf(")");
uid = cmbUser.Text.Substring(si + 1, ei - si - 1);
}
return uid;
}
private bool getKisulInput
{
@@ -112,7 +166,13 @@ namespace FPJ0000
//dsMSSQL.JobReport.Clear();
var sd = dtSD.Text.Trim();
var ed = dtED.Text.Trim();
ta.Fill(this.dSKuntae.EETGW_HolydayRequest, FCOMMON.info.Login.gcode, sd, ed);
var seluid = GetUIDValue();
var uid = "";
if (cmbUser.SelectedIndex == 0) uid = "%";
else uid = GetUIDValue();
ta.Fill(this.dSKuntae.EETGW_HolydayRequest, FCOMMON.info.Login.gcode, sd, ed, uid);
var sum_day = dSKuntae.EETGW_HolydayRequest.AsEnumerable().Where(t => t.conf == 1).Sum(t => t.HolyDays);
var sum_time = dSKuntae.EETGW_HolydayRequest.AsEnumerable().Where(t => t.conf == 1).Sum(t => t.HolyTimes);
@@ -174,7 +234,7 @@ namespace FPJ0000
else
{
search = search.Replace("'", "''");
var collist = new string[] {"name", "Response", "HolyReason","remark" };
var collist = new string[] { "name", "Response", "HolyReason", "remark" };
string filter = ""; // "itemna like ? or package like ? or projectname like ? or process like ? or [type] like ? or description like ?";
foreach (var col in collist)
{
@@ -334,11 +394,11 @@ namespace FPJ0000
}
}
if(FCOMMON.DBM.GetMagamStatus(dr.sdate.Substring(0,7)))
{
if (FCOMMON.DBM.GetMagamStatus(dr.sdate.Substring(0, 7)))
{
FCOMMON.Util.MsgE("마감된 자료이므로 삭제할 수 없습니다");
return;
}
}
if (FCOMMON.DBM.GetMagamStatus(dr.edate.Substring(0, 7)))
{
@@ -381,18 +441,18 @@ namespace FPJ0000
var drv = this.bs.Current as DataRowView;
if (drv == null) return;
var dr = drv.Row as DSKuntae.EETGW_HolydayRequestRow;
if(dr.cate.Equals("외출")==false)
if (dr.cate.Equals("외출") == false)
{
FCOMMON.Util.MsgE("외출 자료만 완료할 수 있습니다");
return;
}
if(dr.conf != 1)
if (dr.conf != 1)
{
FCOMMON.Util.MsgE("관리자 승인된 자료만 완료할 수 있습니다");
return;
}
var f = new OtConfirm.fOutCoomplete(dr);
if(f.ShowDialog() == DialogResult.OK)
if (f.ShowDialog() == DialogResult.OK)
{
dr.stime = f.dtSD.Value.ToString("HH:mm");
dr.etime = f.dtED.Value.ToString("HH:mm");

View File

@@ -130,78 +130,78 @@
<data name="bindingNavigatorMoveFirstItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wQAADsEBuJFr7QAAASpJREFUOE9jGDygcNbz/00Lnv/PnPj4P1QIA4S3P8Apx5A789n/VUfe/8elKL77
wf/ghmu4DciY8vT/wn0fsCqK73n4f+n+///9qy/gNiCh58n/aVveYyiKaL8P1pw56/9/r9ITuA2I7Hr0
v3f1BxRFoa33wJpb1wFt7/z73yX/AG4DApsf/q+b/w6uKLjl7v9Fe///7wBqzpjz879d3c//9hnbcRvg
UXX/f/60NyiK7Ipv/0+f8/u/f9e3/zqF7/5bJKzHbYB96d3/2ZNfYyjSTzn/36ToxX+VrE//jSOX4TbA
Iu/O/9T+11gVGSSd+C+b9vW/bvA83AYYZt3+H9byEqci/dTL/zV8p+E2QCftxn+/6od4Fal4TMBtgFPu
lf8gBXgVDULAwAAA8HbAq6XlmnAAAAAASUVORK5CYII=
wQAADsEBuJFr7QAAATFJREFUOE9jYBg0oHDW8/9NC57/z5z4+D+6HAyEtz/AKceQO/PZ/1VH3v/HpSi+
+8H/4IZrWOXAIGPK0/8L933Aqii+5+H/pfv///evvoAhBwcJPU/+T9vyHkNRRPt9sObMWf//e5WewG1A
ZNej/72rP6AoCm29B9bcuu7/f//Ov/9d8g/gNiCw+eH/uvnv4IqCW+7+X7T3//+Odf//Z8z5+d+u7ud/
+4ztuA3wqLr/P3/aGxRFdsW3/6fP+f3fv+vbf53Cd/8tEtbjNsC+9O7/7MmvMRTpp5z/b1L04r9K1qf/
xpHLcBtgkXfnf2r/a6yKDJJO/JdN+/pfN3gehhwcGGbd/h/W8hKnIv3Uy/81fKdhlQMDnbQb//2qH+JV
pOIxAaccg1Pulf8gBXgVDUoAAPB2wKtYlLYeAAAAAElFTkSuQmCC
</value>
</data>
<data name="bindingNavigatorMovePreviousItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wQAADsEBuJFr7QAAALZJREFUOE9jGDogvP3BfyiTdBDf/eB/cMM18gyI73n4f+n+///9qy+QbkBE+32w
5sxZ//97lZ4gzYDQ1ntgza3rgLZ3/v3vkn+AeAOCW+7+X7T3//8OoOaMOT//29X9/G+fsZ00F9gV3/6f
Puf3f/+ub/91Ct/9t0hYT3oY6Kec/29S9OK/Stan/8aRy0g3AAQMkk78l037+l83eB55BoCAfurl/xq+
08g3AARUPCZQZsBgBQwMANAUYJgEulBVAAAAAElFTkSuQmCC
wQAADsEBuJFr7QAAALtJREFUOE9jYBgyILz9wX90MaJBfPeD/8EN18gzIL7n4f+l+///96++QLoBEe33
wZozZ/3/71V6gjQDQlvvgTW3rvv/37/z73+X/APEGxDccvf/or3//3es+/8/Y87P/3Z1P//bZ2wn3gAQ
sCu+/T99zu///l3f/usUvvtvkbCeNANAQD/l/H+Tohf/VbI+/TeOXEa6ASBgkHTiv2za1/+6wfPIMwAE
9FMv/9fwnUa+ASCg4jGBMgMGLwAA0BRgmCws/7cAAAAASUVORK5CYII=
</value>
</data>
<data name="bindingNavigatorMoveNextItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wQAADsEBuJFr7QAAAKNJREFUOE9jGHygcNbz/1AmeSB35rP/Cd33yDckY8rT//P2//6f0HWHPEMSep78
n73v1//OrX//u5VeJt2QyK5H/6ds+/W/ZOnf/wnT//63yT1LmiGBzQ//t659D9ZsXPLlv3T0tf/GkcuI
N8Sj6v7/krnv4JoVXXpIc4F96d3/gS3PyNMMAhZ5d/7bFFwhTzMIGGbdJl8zCOik3SBf81AEDAwAoH5f
oAc0QjgAAAAASUVORK5CYII=
wQAADsEBuJFr7QAAAKRJREFUOE9jYBh0oHDW8//oYiSB3JnP/id03yPfkIwpT//P2//7f0LXHfIMSeh5
8n/2vl//O7f+/e9Wepl0QyK7Hv2fsu3X/5Klf/8nTP/73yb3LGmGBDY//N+69j1Ys3HJl//S0df+G0cu
I94Qj6r7/0vmvoNrVnTpIV4zCNiX3v0f2PKMPM0gYJF3579NwRXyNIOAYdZt8jWDgE7aDfI1D00AAKB+
X6Bjq5qXAAAAAElFTkSuQmCC
</value>
</data>
<data name="bindingNavigatorMoveLastItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wQAADsEBuJFr7QAAASxJREFUOE9jGFygcNbz/1AmBgDJNS14/j9z4mOcahhyZz77n9B9D6sCkNyqI+//
h7c/wG1AxpSn/+ft//0/oesOhiKQ3MJ9H/4HN1zDbUBCz5P/s/f9+t+59e9/t9LLKApBctO2vP/vX30B
twGRXY/+T9n263/J0r//E6b//W+TexauGCTXu/rDf6/SE7gNCGx++L917XuwZuOSL/+lo6/9N45cBtYA
kqub/+6/S/4B3AZ4VN3/XzL3HVyzoksPXDFILn/am//2GdtxG2Bfevd/YMszDM0gAJLLnvz6v0XCetwG
WOTd+W9TcAVDMwiA5FL7X8O9hBUYZt3GqhkEQHJhLS//6wbPw22ATtoNnJIgOb/qh/81fKfhNgAfcMq9
8l/FYwIYQ4UGBWBgAAC+0b+zuQxOnAAAAABJRU5ErkJggg==
wQAADsEBuJFr7QAAAStJREFUOE9jYBhUoHDW8//oYjAAkmta8Px/5sTHONUw5M589j+h+x5WBSC5VUfe
/w9vf4BVHgwypjz9P2//7/8JXXcwFIHkFu778D+44RqGHBwk9Dz5P3vfr/+dW//+dyu9jKIQJDdty/v/
/tUXcBsQ2fXo/5Rtv/6XLP37P2H63/82uWfhikFyvas//PcqPYHbgMDmh/9b174HazYu+fJfOvraf+PI
ZWANILm6+e/+u+QfwG2AR9X9/yVz38E1K7r0wBWD5PKnvflvn7EdtwH2pXf/B7Y8w9AMk8ue/Pq/RcJ6
3AZY5N35b1NwBUMzTC61/zXcS1iBYdZtrJpBACQX1vLyv27wPKzyYKCTdgOnJEjOr/rhfw3faTjV4AVO
uVf+q3hMAGN0uYEFAL7Rv7NmXVYYAAAAAElFTkSuQmCC
</value>
</data>
<data name="btFind.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="btSave.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE1SURBVDhPY/hPIQAbcOn57//T915BwW1rjoFx/oJz//N6
VqHgsNxeMA03YN3lp/9vv4YYhAtsuQ6h55/9A8aBidVgPtiADZcegzWDFN1/9///qy8IDOKDcPfu1/9/
/vn/v3rt/f9TD38BuwJuwIrT9wka0L79BdiAkuW3MA0A+fnog///V12GKAZ5BxcGGQByDYoXYAbA/Aey
AYRBCkE2N256AnY6SDMoUEF8FANAoQ0zAFkzCCNrhhkAor3CczENwGYzuu1JM8+BaQwDQAGITzOyASDs
4huPMAAkATIA3c/YNIdNPAHGKAaAUhUoBghphhng0rTnv71bGKoBoADE5mR0zVgNACUK9BgAGYbudJBG
GNY0dEYYAMsgMAyKYxAGhTQIg/wLwiBbQRikGSUdkA/+/wcAgXJEf04PwQkAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEzSURBVDhPY/hPIWAAEZee//4/fe8VFNy25hgY5y849z+v
ZxUKDsvtBdNwA9Zdfvr/9muIQbjAlusQev7ZP2AcmFgN5oMN2HDpMVgzSNH9d///v/qCwCA+CHfvfv3/
55///6vX3v8/9fAXsCvgBqw4fZ+gAe3bX4ANKFl+C9MAkJ+PPvj/f9VliGKQd3BhkAEg16B4AWYAzH8g
G0AYpBBkc+OmJ2CngzSDAhXERzEAFNowA5A1gzCyZpgBINorPBfTAGw2o9ueNPMcmMYwABSA+DQjGwDC
Lr7xCANAEiAD0P2MTXPYxBNgjGIAKFWBYoCQZpgBLk17/tu7haEaAApAbE5G14zVAFCiQI8BkGHoTgdp
hGFNQ2dUF4AMgWFQHIMwKKRBGORfEAbZCsIgzSjpgBIAAIFyRH86fUztAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@@ -210,62 +210,62 @@
<data name="toolStripButton8.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANPSURBVFhH7dhZTxNRHAVwPoRx3yuyKWUvqyJu4BqNiYnx
C/giaowRQSiibC0Uyg6CyqKSuEaEQvdCK7tPqAmokWDUiFGgyCrHO+k0oT609w4JvvQk9/n+MjPn/ifX
wx133BGQh8dfX3hwzGy9f7QfDUfIOtyH+kN9qEvoRW18L+4d7MHdAz24s5+sfd2o2duN6rgu3N7TharY
TlTu7kTFrlcojyEr2oKyKAtKIy0oiTCjONyMIkkHlGFkhbajMKQdBcEma36QIZHf3nWWGQdFkAm5gaYJ
fnvXWW5cfqAJeQFG8Nu7zv/AycUswCXgyqLNUEbokSdpQ4FEh6LwDiqc3N9AD1zKkyuM1KL/8UfMTs2j
p/ED5CEaKpxsJwNQKI57cu2V77A4xfFaKlzuDj09UAiuONKER5e6sfBngacBP0cmIQtTUeFy/BiArLjS
qA5UnTJgemKWpwFzM/OoOWuEIthIhcv21dED2XCkFLFafH8/ztNseSHtR16wjhqX5cMApMVxR4kiXIO3
ms88y5auhiFy8KqZcJneLEBKXGG4HqYKx1IM94+S766VGZfppaUH0uCKIkgpLjqWYuzLbxTGqcl3Z2DG
3drOAHSFK4nkS2Gd42m2UlSfMYIMfUG4m54aeqAzHDe+FGE6DHV85Wm2PEvpgzxIKxiXsY0B6AzHzdb8
UB0G2x2BT5N7IQvUCsbdEKnpgc5w3OBXSkwoP6F3fMXT86g8TUBivSBc+lYGoDOcffArQvRoTOxyKMkv
UhJ5TCtk/jpmnHRLGz3QFc4+W2VBGuhL3/A8Wz71jSJT3MyMS9vMAqTA2c+5LLEKA20jPM8WS+0gMnxb
mHBpm1rpgbQ4rhDyAANyJCp8GxrjebY8SepBhncrNS51IwOQFmdvK/fNKRM0mBpf9LNASlN6kqC81FS4
6xsYgCw4e1uz/NSoP2d2KM2P4QlIfZqocCnrVfRAVpy9EBk+KmiUAzzPluyIFipc8joGoBCcvRBS75fo
JH8zM5NzsNQNIlXURIW7traFHigUZy+E1LMFKaLnBNeMdFEbFS5pDQtwCTjaQvyLS1rdTA/8H7irqxiA
BGddbtzllU30Vx/cRQ53V7KcuCsrms7z27vjjjv08fD4CwlIPe+HvHrCAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANNSURBVFhH7dhnT1NRHAZwPoRx74osZZTRUkARFzijMTEx
fgHfiBpjRBCKIKuFQtkgoEBVElwRoaWTLkahvkJNQI0Eg0aMsqc85ia24TYBzrkk+KbPJ/jl3Puc/8nf
w8Mdd9yhz9Ozb689OWMZf3zaDsUpOxQne1B/ogd1cd2oje3Go+M2PDxmQ81RG2qOdKH6cBeqYjrx4FAn
KqM7UHGwA+UH2lEW1Y6ySCtKI6woEVlRHG5BkdCCQoEZ8jAz5KEmFISYkB9sHM/jG+JdHUtmjXGQ8Y3I
CTKOuTqWzFrj8oKMyA1sg6tjyfwPnDSABrgKXGmkBfJwPXIFrcgX6FAoNBPhpP4GciBXHHNyBSIt7M8+
Y3ZqHraGT5CGaIhwkv0UQK445uRMFR+wOEWxWiJczj49OZALrkhkROONLiz8WXDifg1OQBKmJMJl+1EA
aXElEWZUXjBgemzWiZubmUf15TbIgtuIcFm+OnIgHc4CebQWPz6OOnFMXovtyA3WEeMyfSiApDjmKpEJ
NXiv+crCdSr6kROkpsJleNMACXEFQj2M5exSDNiHIQlTUeMyvLTkQBJcYbgRjdfZpRgZmkRBjBqyYAM1
7v5eCuBKuGLRv1KMzzlxTCmqLrUhj88Nl+6pIQcuh2PGlyxMh37zNyeOycukHkj5Ws64tD0UwOVwzGzN
C9Whz8QGvkjshiRIyxl3j6cmBy6HYwa/XGBE2Tk9+xNPz6PiogGSAD0nXOpuCuByOMfgl4Xo0RDfySrJ
76FJSKNUkPjrqHHiXa3kwJVwjtkq4WugL3nnBDL50jOMjIBmalzKThogAc5xz2UGKNHbOshCWmv7kObb
QoVL2aEiB5LimEJIAw3IFijxvX+EhXyeYEOat4oYl7ydAkiKc7SV+efkcRpMjS56LEzPo+S8FuleaiLc
3W0UQBqco62ZfmrUX7GwSvNzYAxinyYiXNJWJTmQFucoRJqPEhp5rxPIJCu8hQiXuIUCyAXnKITY+w06
FP2YmZiDta4PybwmItydzS3kQK44RyHEni1I4r1CMq8ZqbxWIlzCJhrgKnCkhXDFJWxsJgf+D9ztDRRA
Zley1rib65vIVx/MIofZlawl7ta6pquuDnfccYcgfwEJSD3vAJhh2AAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton7.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANSSURBVFhH7djZTxNRGAVw/gjjvq9tVWgBAbUguO+aGE18
8N0XFROj4oYitHbB0haL4i4qGhUTRew27bS1pRSEJ4kkKCZuiRhJpEQE5TjDTKPEpL13TPClJ7nP95e5
c+a7mZRkkklGQu5saSus3RyM3t7UilsbubXhOW6uf46adS24sbYF19c049rqZlxdxa2VEVxZEcHl5U24
VNCEi/lhVC8L40JeI87ncksdQtXSEGxLQji3OIjKnCCs2c9gyeLWogDMmQFUZPijZ9PZfeL2iTPKOJjS
/dCr/L3i9okz2rizKj/KlT6I2yfO/8AZ02iAhLiLBWHY8nwwq12oVLOoyg1JxhlTWXIg6ZOrzGXRer8L
A99+oK3uDSxLGck4w0IKIPGxbmXxZwLVL2HO8UrC6Rd4yYGk75w1n0HP+z6RBwz9HMK9/RGYs33UON18
CiBpIWzqIK7vCgwfcSzfo4Oo3s7CnOWnwp1ReMiBJLhYW61LfHh0vFXkCfn8phemPCcqMv3EOK2cAkiK
ixXClOVBpPaVyBPSwX6EPt1BjNPIaIAUuFghjNkOdEW6RZ4Qr60dOqWbCKeZx5ADaXF8IcxZ3LuW70TP
uz9KMwTcLQzjTKo7Ia5sLgWQFhdrqynDh0s7WQz2/y5Nf+8ArBvc0C/0xMWVznGTA6XgYm01qBg8PNIi
8oR0+D6iVGGPizs9mwIoFccXQq9kUFc0EvjS+4EDOuLiSma5yIFScUYliws7vH8dsXmdC1oFExd3aiYF
UAquXOWDQW3Hl7dRkSaUpHZPI0rljoS4kzOc5EBaHP+d0ygb8DrySaQJYSwvcFpuJ8IVT6cBUuK0qU40
3uwUWULamfcoUTwhxhVPc5ADaXC6NDceHGoWWUK6X3+FJrMBGjlDjDsxlQJIijOkeVG1zTvistDPXRYs
613UuONTKIAkOH58aVVPR5aCu27V7A6iVOakxh2bbCcHkuD42WoqcIo0IXwpSmR2SbijkyiAJDh+tpbN
t6Ppdie+9w0ifKsTp+RPJOOOTHxKDiTB8YNft8AzPCFOyh7/05PjcUUTaIAEuESDnxZXNL6BHPg/cIfH
UQA5XHS0cQfG1pP/+uB/5PD/SkYTd3BM/V5x+2SSSYY8KSm/AHvBPpKbf2MsAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANRSURBVFhH7dhZT5NBGAVgfoRx31daWVpAUCgI7uCaGE28
8N4bFROjgqKApaVspQXZXZBNo2LiAm3pR7+2tpSW5UqCCQKJookYSaRGFOGYIWm1GGDmM4Gbnl/wZCZn
3skbEOCPP/6w58HR7uSGIzZ3/eEu1B3qQt3BTtQmdaImsQP3D3Sger8L9/a5cHevC3f3OHFntxO3d7Wj
KqEdlfEOVOx0oDyuDWWxbSiT2VEaY0dJtB23dthQvN2GoqhX0Ea+gnabFZoIKwrDLe6CMP78dMeMmWcc
1GEW5Egto9MdM2a+cQVSC/IlZkx3zJiFwOWFsgApcZUJDpTEmaGRtaBYxqM01i4YlxfC0wNpcOTkimN5
dD0ewM/vv9DdOAhtDCcYlxvMAKTBTV3rMR5/x1rRC812kyBcTpCJHkiF2+lAUTyHkaFvXuDkxCQeXXBC
E2Vmxqm2MgBpcKQQJTIbqk9bp67Ykx/ucVSc4KGJtDDhssWt9EAanKetRdFmPEvr8gJJPg+OQh1nQGGE
hRqnFDEAaXGeQqgjW+FseOuDfMN/RE6YnhqnCGQBMuA8hciL0mPAOeyDNJX0QCUxUuEUWzh6ICuOFEIT
aUVhvAEj7/8qzSTwMNmB7BDjnLiszQxAVpynrepwM6pO8Rgf+1OasdGfKDpoRE5w66w4+SYjPVAIztPW
XCmHp6kdXiDJG/NHyMW6WXE3NzIAheKmfiUSDo0pvsBe0wfIxfpZcZkbWuiBQnF5Eh7lJ03/XLEmsQVK
MTcrLmM9A1AILl9qRq5Mhy/v3F4cKUnD2TbIRfo5cenrDPRAVhx55xSSJvQ7P3lxJJz2NW6KdFS4G2tZ
gIw4ZYgBbbV9PrgebgiZ4pfUuBtr9PRAFpwq1Ignl10+uOH+r1BENEEh4qhx11czAGlxuaEmlB43+XwW
xtzj0Ca1MOPSVjEAaXBkfCmlzb6lmJhEzRkb5IEGZty1lTp6IA2OzFZ1gsGLIyGlyAzUCcJdXcEApMGR
2Zq1VYf2+j78+DYOR10fMkQvBeNSlzfTA2lwZPCrglqnJkR64PP/OjmCS1nGAqTAzTX4WXEpS5vogQuB
u7KEAUh2JfONu7j4Bf3qgyxyyK5kPnGXFr04N93hjz/+UOQ3e8E+krxr0cIAAAAASUVORK5CYII=
</value>
</data>
<data name="btSearch.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE4SURBVDhPtZPPasJAEMbzPn0FwWA92JtP4NGKB1/DP7ei
QSsovkChh7ZBrCfpyR4sikopUgq9StFzM/UbZ5asSS4FfzAkO7vft5udiZMEnSBpk5dhFJmncjdHxXaG
A+9K4SbFT1luEwQBbXavVO5d0nI3ovnW5yeiMriiu+kt5asXbABEdgRigAUQDr+aHLU3lxoLl/yPJhvF
GsiYJ/vPdX5qPK3bVJ25VFukafztGQNsKHJ791I3w+8KcpNth8XDz5YxACI/gsR1J8sTYcO4UIzwv1gG
cTshgJ5IT8hChTMHsHi+v+fvffmxywVwN2FDkdsGEOK2ceu4feQ0tDqKyKMGqDfqjvprLzyswuX7Tf4E
dBo6zn/3OB7XHovRyuhQ6+hhYKA9DpL+A1keRebNAhkaJH0OHOcP031C4EjYr6wAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEiSURBVDhPY2DAAf6jAXRxVNVIAKYga5bH/7TpTmAMYsNA
3EQb3Ab8+/fv/71PZ/5nzXX+f+3T7v+X320H0yCct8Dz/9rTM/8HtGvDDcPQDAIgBSCNOx9NBuOOiw7/
u644/N9+ZzLYIKwGwARAkvP2dIJpGN52Y/r/9gsO/zuu2P/f+3Qq3ACQhVhtz5jlBLcBJnb43Qyw5p0P
pmB3AcwV6TNcwBLIBmLDKAbAeWQAFAPQbUG2DeYimAsxnA4z4PLnDWD/Hv2AGl0gAAobZAOxGgDSCApt
UKiDQh/ZJbDYgQGcBoDiGxTvoPiHpYUt15Gj7y9uL4BSGijFbb81FYy33pgK1gxKyqAUiuJ0ZAAyAJbG
QQBXPkDXBwfoCuA60MRpAgDTfULg/+7qPQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADlSURBVEhL7dQxCsIwFMbxnMrZWatzUXRUCo5ewSs4ewZR
DyDeQfEETsUbRL7YYNSXNC/UOiQP/kvzyE9KUcQ7y/VJtlHFvQYPjzf50xKsJsFmq81WHq538swMO9il
zhALxkWd7kAOJwsnjjPsYNeGs2B14fR5YTYq5O5c1u7sL987iAUjF+6LIjaMKJyDoiAYmVAvn8lsXHij
KBhGwDWof4APihqHqQ+OKhg2X3U/n7+9ah88CKY+pM9ndTgbplDbmQtnwS7UtmPDWfDf/jIRLnKhOuzY
UMSGmyrBauKE26jiohshHicE2B3dbRrmAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADiSURBVEhL7dVBCsIwEAXQnsq1a62ui6JLpeDSK3gF155B
1AOId1A8gSvxBiNTCcTJJM3E2i6SwN9MhnklDW2WRbvW2wu0EepW8PkBf02CE2xks9vD6f4y6jTYg720
riKCcVCvP4LxbOXEcQ97sNeGi+Bq4PwzMJ+UcLg+a3uON7NHDHODdZzu2dAgmAMQpzUXGgxTfFAsIJ+W
3uhPMAZxBaoH8EExjcPcheMSDOtHPSyWX0ftgwfB3EWitTpcDFNAf6d0z4WLYDqYu0i0x4aL4M4+mZhO
fhJNJsERw22EuvGsNycE2B33w41tAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="HolyDays.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">

View File

@@ -96,7 +96,12 @@ namespace FPJ0000
}
if (curLevel < 5)
{
toolStripButton4.Enabled = false;
cmbUser.Enabled = false;
tbProcess.Enabled = false;
}
//var userList = FCOMMON.DBM.getGroupList("name + '(' + id + ')'", "Users", "[level] > 0 and [level] < 10", false, false);

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,31 @@ ORDER BY projectName</CommandText>
</Mappings>
<Sources />
</TableAdapter>
<TableAdapter BaseClass="System.ComponentModel.Component" DataAccessorModifier="AutoLayout, AnsiClass, Class, Public" DataAccessorName="HolydayUserListTableAdapter" GeneratorDataComponentClassName="HolydayUserListTableAdapter" Name="HolydayUserList" UserDataComponentName="HolydayUserListTableAdapter">
<MainSource>
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.Holyday" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="Fill" GenerateMethods="Both" GenerateShortCommands="false" GeneratorGetMethodName="GetData" GeneratorSourceName="Fill" GetMethodModifier="Public" GetMethodName="GetData" QueryType="Rowset" ScalarCallRetval="System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" UseOptimisticConcurrency="true" UserGetMethodName="GetData" UserSourceName="Fill">
<SelectCommand>
<DbCommand CommandType="Text" ModifiedByUser="true">
<CommandText>SELECT uid, dbo.getUserName(uid) AS UserName
FROM Holyday WITH (NOLOCK)
WHERE (gcode = @gcode) AND (sdate &gt;= @sd) AND (sdate &lt;= @ed)
GROUP BY uid
ORDER BY UserName</CommandText>
<Parameters>
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="EE.dbo.Holyday" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="sd" ColumnName="sdate" DataSourceName="EE.dbo.Holyday" DataTypeServer="date" DbType="AnsiString" Direction="Input" ParameterName="@sd" Precision="0" ProviderType="Date" Scale="0" Size="3" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
<Parameter AllowDbNull="true" AutogeneratedName="ed" ColumnName="sdate" DataSourceName="EE.dbo.Holyday" DataTypeServer="date" DbType="AnsiString" Direction="Input" ParameterName="@ed" Precision="0" ProviderType="Date" Scale="0" Size="3" SourceColumn="sdate" SourceColumnNullMapping="false" SourceVersion="Current" />
</Parameters>
</DbCommand>
</SelectCommand>
</DbSource>
</MainSource>
<Mappings>
<Mapping SourceColumn="uid" DataSetColumn="uid" />
<Mapping SourceColumn="UserName" DataSetColumn="UserName" />
</Mappings>
<Sources />
</TableAdapter>
</Tables>
<Sources>
<DbSource ConnectionRef="gwcs (Settings)" DbObjectType="Unknown" GenerateShortCommands="true" GeneratorSourceName="CheckHoliday" MethodsParameterType="CLR" Modifier="Public" Name="CheckHoliday" QueryType="Scalar" ScalarCallRetval="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" UseOptimisticConcurrency="true" UserGetMethodName="GetDataBy" UserSourceName="CheckHoliday">
@@ -51,21 +76,21 @@ WHERE (pdate = @pdate)</CommandText>
</DataSource>
</xs:appinfo>
</xs:annotation>
<xs:element name="dsQuery" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:EnableTableAdapterManager="true" msprop:Generator_UserDSName="dsQuery" msprop:Generator_DataSetName="dsQuery">
<xs:element name="dsQuery" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:EnableTableAdapterManager="true" msprop:Generator_DataSetName="dsQuery" msprop:Generator_UserDSName="dsQuery">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="JobReportItemList" msprop:Generator_RowEvHandlerName="JobReportItemListRowChangeEventHandler" msprop:Generator_RowDeletedName="JobReportItemListRowDeleted" msprop:Generator_RowDeletingName="JobReportItemListRowDeleting" msprop:Generator_RowEvArgName="JobReportItemListRowChangeEvent" msprop:Generator_TablePropName="JobReportItemList" msprop:Generator_RowChangedName="JobReportItemListRowChanged" msprop:Generator_RowChangingName="JobReportItemListRowChanging" msprop:Generator_TableClassName="JobReportItemListDataTable" msprop:Generator_RowClassName="JobReportItemListRow" msprop:Generator_TableVarName="tableJobReportItemList" msprop:Generator_UserTableName="JobReportItemList">
<xs:element name="JobReportItemList" msprop:Generator_TableClassName="JobReportItemListDataTable" msprop:Generator_RowEvArgName="JobReportItemListRowChangeEvent" msprop:Generator_TableVarName="tableJobReportItemList" msprop:Generator_TablePropName="JobReportItemList" msprop:Generator_RowDeletingName="JobReportItemListRowDeleting" msprop:Generator_RowChangingName="JobReportItemListRowChanging" msprop:Generator_RowDeletedName="JobReportItemListRowDeleted" msprop:Generator_RowEvHandlerName="JobReportItemListRowChangeEventHandler" msprop:Generator_UserTableName="JobReportItemList" msprop:Generator_RowChangedName="JobReportItemListRowChanged" msprop:Generator_RowClassName="JobReportItemListRow">
<xs:complexType>
<xs:sequence>
<xs:element name="pidx" msprop:Generator_ColumnPropNameInRow="pidx" msprop:Generator_ColumnPropNameInTable="pidxColumn" msprop:Generator_ColumnVarNameInTable="columnpidx" msprop:Generator_UserColumnName="pidx" type="xs:int" minOccurs="0" />
<xs:element name="projectName" msprop:Generator_ColumnPropNameInRow="projectName" msprop:Generator_ColumnPropNameInTable="projectNameColumn" msprop:Generator_ColumnVarNameInTable="columnprojectName" msprop:Generator_UserColumnName="projectName" minOccurs="0">
<xs:element name="pidx" msprop:Generator_ColumnVarNameInTable="columnpidx" msprop:Generator_ColumnPropNameInRow="pidx" msprop:Generator_ColumnPropNameInTable="pidxColumn" msprop:Generator_UserColumnName="pidx" type="xs:int" />
<xs:element name="projectName" msprop:Generator_ColumnVarNameInTable="columnprojectName" msprop:Generator_ColumnPropNameInRow="projectName" msprop:Generator_ColumnPropNameInTable="projectNameColumn" msprop:Generator_UserColumnName="projectName">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pdate" msdata:ReadOnly="true" msprop:Generator_ColumnPropNameInRow="pdate" msprop:Generator_ColumnPropNameInTable="pdateColumn" msprop:Generator_ColumnVarNameInTable="columnpdate" msprop:Generator_UserColumnName="pdate" minOccurs="0">
<xs:element name="pdate" msdata:ReadOnly="true" msprop:Generator_ColumnVarNameInTable="columnpdate" msprop:Generator_ColumnPropNameInRow="pdate" msprop:Generator_ColumnPropNameInTable="pdateColumn" msprop:Generator_UserColumnName="pdate" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
@@ -75,6 +100,26 @@ WHERE (pdate = @pdate)</CommandText>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="HolydayUserList" msprop:Generator_TableClassName="HolydayUserListDataTable" msprop:Generator_TableVarName="tableHolydayUserList" msprop:Generator_TablePropName="HolydayUserList" msprop:Generator_RowDeletingName="HolydayUserListRowDeleting" msprop:Generator_RowChangingName="HolydayUserListRowChanging" msprop:Generator_RowEvHandlerName="HolydayUserListRowChangeEventHandler" msprop:Generator_RowDeletedName="HolydayUserListRowDeleted" msprop:Generator_UserTableName="HolydayUserList" msprop:Generator_RowChangedName="HolydayUserListRowChanged" msprop:Generator_RowEvArgName="HolydayUserListRowChangeEvent" msprop:Generator_RowClassName="HolydayUserListRow">
<xs:complexType>
<xs:sequence>
<xs:element name="uid" msprop:Generator_ColumnVarNameInTable="columnuid" msprop:Generator_ColumnPropNameInRow="uid" msprop:Generator_ColumnPropNameInTable="uidColumn" msprop:Generator_UserColumnName="uid">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="UserName" msdata:ReadOnly="true" msprop:Generator_ColumnVarNameInTable="columnUserName" msprop:Generator_ColumnPropNameInRow="UserName" msprop:Generator_ColumnPropNameInTable="UserNameColumn" msprop:Generator_UserColumnName="UserName" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="200" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:unique name="Constraint1" msdata:PrimaryKey="true">
@@ -82,5 +127,9 @@ WHERE (pdate = @pdate)</CommandText>
<xs:field xpath="mstns:pidx" />
<xs:field xpath="mstns:projectName" />
</xs:unique>
<xs:unique name="HolydayUserList_Constraint1" msdata:ConstraintName="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//mstns:HolydayUserList" />
<xs:field xpath="mstns:uid" />
</xs:unique>
</xs:element>
</xs:schema>

View File

@@ -6,8 +6,9 @@
</autogenerated>-->
<DiagramLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ex:showrelationlabel="False" ViewPortX="0" ViewPortY="0" xmlns:ex="urn:schemas-microsoft-com:xml-msdatasource-layout-extended" xmlns="urn:schemas-microsoft-com:xml-msdatasource-layout">
<Shapes>
<Shape ID="DesignTable:JobReportItemList" ZOrder="2" X="40" Y="54" Height="305" Width="216" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="178" />
<Shape ID="DesignSources:QueriesTableAdapter" ZOrder="1" X="392" Y="70" Height="220" Width="189" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="216" />
<Shape ID="DesignTable:JobReportItemList" ZOrder="3" X="40" Y="54" Height="305" Width="216" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="178" />
<Shape ID="DesignSources:QueriesTableAdapter" ZOrder="2" X="392" Y="70" Height="220" Width="189" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="216" />
<Shape ID="DesignTable:HolydayUserList" ZOrder="1" X="260" Y="379" Height="115" Width="209" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="64" />
</Shapes>
<Connectors />
</DiagramLayout>

1
run_claude.bat Normal file
View File

@@ -0,0 +1 @@
claude --dangerously-skip-permissions