Add Dashboard todo edit/delete/complete features and Note view count tracking
This commit is contained in:
@@ -363,6 +363,9 @@
|
|||||||
<Compile Include="Web\MachineBridge\MachineBridge.Holiday.cs" />
|
<Compile Include="Web\MachineBridge\MachineBridge.Holiday.cs" />
|
||||||
<Compile Include="Web\MachineBridge\MachineBridge.Holyday.cs" />
|
<Compile Include="Web\MachineBridge\MachineBridge.Holyday.cs" />
|
||||||
<Compile Include="Web\MachineBridge\MachineBridge.MailForm.cs" />
|
<Compile Include="Web\MachineBridge\MachineBridge.MailForm.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.MailData.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Note.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Board.cs" />
|
||||||
<Compile Include="Web\MachineBridge\MachineBridge.UserGroup.cs" />
|
<Compile Include="Web\MachineBridge\MachineBridge.UserGroup.cs" />
|
||||||
<Compile Include="Web\MachineBridge\MachineBridge.UserAuth.cs" />
|
<Compile Include="Web\MachineBridge\MachineBridge.UserAuth.cs" />
|
||||||
<Compile Include="Web\MachineBridge\WebSocketServer.cs" />
|
<Compile Include="Web\MachineBridge\WebSocketServer.cs" />
|
||||||
|
|||||||
158
Project/Web/MachineBridge/MachineBridge.Board.cs
Normal file
158
Project/Web/MachineBridge/MachineBridge.Board.cs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Linq;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 게시판 목록 조회 (bidx로 구분: 5=패치내역, 기타=일반게시판)
|
||||||
|
/// </summary>
|
||||||
|
public string Board_GetList(int bidx, string searchKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var connStr = Project.Properties.Settings.Default.CS;// Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(connStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
var sql = @"
|
||||||
|
SELECT idx, bidx, header, cate, title, contents, [file], guid, url, wuid, wdate, project, pidx, gcode, [close], remark,
|
||||||
|
dbo.getUserName(wuid) AS wuid_name
|
||||||
|
FROM Board WITH (nolock)
|
||||||
|
WHERE gcode = @gcode AND bidx = @bidx
|
||||||
|
AND (ISNULL(title,'') LIKE @search OR ISNULL(contents,'') LIKE @search OR ISNULL(wuid,'') LIKE @search)
|
||||||
|
ORDER BY wdate DESC";
|
||||||
|
|
||||||
|
if(bidx == 5) //패치내역은 모두가 다 확인할 수있도록 그룹코드를 제한하지 않는다
|
||||||
|
{
|
||||||
|
sql = @"
|
||||||
|
SELECT idx, bidx, header, cate, title, contents, [file], guid, url, wuid, wdate, project, pidx, gcode, [close], remark,
|
||||||
|
dbo.getUserName(wuid) AS wuid_name
|
||||||
|
FROM Board WITH (nolock)
|
||||||
|
WHERE bidx = @bidx
|
||||||
|
AND (ISNULL(title,'') LIKE @search OR ISNULL(contents,'') LIKE @search OR ISNULL(wuid,'') LIKE @search)
|
||||||
|
ORDER BY wdate DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd = new SqlCommand(sql, conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@bidx", SqlDbType.Int).Value = bidx;
|
||||||
|
cmd.Parameters.Add("@search", SqlDbType.NVarChar).Value = $"%{searchKey}%";
|
||||||
|
|
||||||
|
var list = new List<object>();
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
list.Add(new
|
||||||
|
{
|
||||||
|
idx = reader.GetInt32(0),
|
||||||
|
bidx = reader.GetInt32(1),
|
||||||
|
header = reader.IsDBNull(2) ? "" : (reader.GetBoolean(2) ? "공지" : ""),
|
||||||
|
cate = reader.IsDBNull(3) ? "" : reader.GetString(3),
|
||||||
|
title = reader.IsDBNull(4) ? "" : reader.GetString(4),
|
||||||
|
contents = reader.IsDBNull(5) ? "" : reader.GetString(5),
|
||||||
|
file = reader.IsDBNull(6) ? "" : reader.GetString(6),
|
||||||
|
guid = reader.IsDBNull(7) ? "" : reader.GetString(7),
|
||||||
|
url = reader.IsDBNull(8) ? "" : reader.GetString(8),
|
||||||
|
wuid = reader.IsDBNull(9) ? "" : reader.GetString(9),
|
||||||
|
wdate = reader.IsDBNull(10) ? (DateTime?)null : reader.GetDateTime(10),
|
||||||
|
project = reader.IsDBNull(11) ? "" : reader.GetInt32(11).ToString(),
|
||||||
|
pidx = reader.IsDBNull(12) ? -1 : reader.GetInt32(12),
|
||||||
|
gcode = reader.IsDBNull(13) ? "" : reader.GetString(13),
|
||||||
|
close = reader.IsDBNull(14) ? false : reader.GetBoolean(14),
|
||||||
|
remark = reader.IsDBNull(15) ? "" : reader.GetString(15),
|
||||||
|
wuid_name = reader.IsDBNull(16) ? "" : reader.GetString(16)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = list });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 게시판 상세 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Board_GetDetail(int idx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var connStr = Project.Properties.Settings.Default.CS;//Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(connStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
var cmd = new SqlCommand(@"
|
||||||
|
SELECT idx, bidx, header, cate, title, contents, [file], guid, url, wuid, wdate, project, pidx, gcode, [close], remark,
|
||||||
|
dbo.getUserName(wuid) AS wuid_name
|
||||||
|
FROM Board WITH (nolock)
|
||||||
|
WHERE idx = @idx AND gcode = @gcode", conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
var data = new
|
||||||
|
{
|
||||||
|
idx = reader.GetInt32(0),
|
||||||
|
bidx = reader.GetInt32(1),
|
||||||
|
header = reader.IsDBNull(2) ? "" : (reader.GetBoolean(2) ? "공지" : ""),
|
||||||
|
cate = reader.IsDBNull(3) ? "" : reader.GetString(3),
|
||||||
|
title = reader.IsDBNull(4) ? "" : reader.GetString(4),
|
||||||
|
contents = reader.IsDBNull(5) ? "" : reader.GetString(5),
|
||||||
|
file = reader.IsDBNull(6) ? "" : reader.GetString(6),
|
||||||
|
guid = reader.IsDBNull(7) ? "" : reader.GetString(7),
|
||||||
|
url = reader.IsDBNull(8) ? "" : reader.GetString(8),
|
||||||
|
wuid = reader.IsDBNull(9) ? "" : reader.GetString(9),
|
||||||
|
wdate = reader.IsDBNull(10) ? (DateTime?)null : reader.GetDateTime(10),
|
||||||
|
project = reader.IsDBNull(11) ? "" : reader.GetInt32(11).ToString(),
|
||||||
|
pidx = reader.IsDBNull(12) ? -1 : reader.GetInt32(12),
|
||||||
|
gcode = reader.IsDBNull(13) ? "" : reader.GetString(13),
|
||||||
|
close = reader.IsDBNull(14) ? false : reader.GetBoolean(14),
|
||||||
|
remark = reader.IsDBNull(15) ? "" : reader.GetString(15),
|
||||||
|
wuid_name = reader.IsDBNull(16) ? "" : reader.GetString(16)
|
||||||
|
};
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = data });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "데이터를 찾을 수 없습니다." });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
Project/Web/MachineBridge/MachineBridge.MailData.cs
Normal file
75
Project/Web/MachineBridge/MachineBridge.MailData.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Linq;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 메일 발신 내역 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Mail_GetList(string startDate, string endDate, string searchKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var connStr = Project.Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(connStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
var cmd = new SqlCommand(@"
|
||||||
|
SELECT idx, gcode, subject, body, fromlist, tolist, cc AS cclist, bcc AS bcclist, project, cate, pdate
|
||||||
|
FROM MailData WITH (nolock)
|
||||||
|
WHERE gcode = @gcode
|
||||||
|
AND (pdate BETWEEN @startDate AND @endDate)
|
||||||
|
AND (ISNULL(subject,'') LIKE @search OR ISNULL(fromlist,'') LIKE @search OR ISNULL(tolist,'') LIKE @search OR ISNULL(cate,'') LIKE @search)
|
||||||
|
ORDER BY pdate DESC", conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@startDate", SqlDbType.VarChar).Value = startDate;
|
||||||
|
cmd.Parameters.Add("@endDate", SqlDbType.VarChar).Value = endDate;
|
||||||
|
cmd.Parameters.Add("@search", SqlDbType.NVarChar).Value = $"%{searchKey}%";
|
||||||
|
|
||||||
|
var list = new List<object>();
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
list.Add(new
|
||||||
|
{
|
||||||
|
idx = reader.GetInt32(0),
|
||||||
|
gcode = reader.IsDBNull(1) ? "" : reader.GetString(1),
|
||||||
|
uid = "", // uid 컬럼 없음
|
||||||
|
subject = reader.IsDBNull(2) ? "" : reader.GetString(2),
|
||||||
|
htmlbody = reader.IsDBNull(3) ? "" : reader.GetString(3), // body를 htmlbody로 반환
|
||||||
|
fromlist = reader.IsDBNull(4) ? "" : reader.GetString(4),
|
||||||
|
tolist = reader.IsDBNull(5) ? "" : reader.GetString(5),
|
||||||
|
cclist = reader.IsDBNull(6) ? "" : reader.GetString(6),
|
||||||
|
bcclist = reader.IsDBNull(7) ? "" : reader.GetString(7),
|
||||||
|
project = reader.IsDBNull(8) ? "" : reader.GetInt32(8).ToString(),
|
||||||
|
cate = reader.IsDBNull(9) ? "" : reader.GetString(9),
|
||||||
|
wdate = reader.IsDBNull(10) ? "" : reader.GetString(10) // pdate를 wdate로 반환 (프론트엔드 호환)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = list });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
343
Project/Web/MachineBridge/MachineBridge.Note.cs
Normal file
343
Project/Web/MachineBridge/MachineBridge.Note.cs
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Note API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메모장 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Note_GetList(string startDate, string endDate, string uid = "")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 로그인 체크
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var connStr = Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(connStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
var cmd = new SqlCommand();
|
||||||
|
cmd.Connection = conn;
|
||||||
|
|
||||||
|
// 권한 체크: 레벨5 미만이면 자기 것만 보거나 공유된 것만 조회
|
||||||
|
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.jobreport));
|
||||||
|
|
||||||
|
if (curLevel >= 5)
|
||||||
|
{
|
||||||
|
// 관리자: 모든 메모 조회 가능
|
||||||
|
if (string.IsNullOrEmpty(uid))
|
||||||
|
{
|
||||||
|
cmd.CommandText = @"
|
||||||
|
SELECT idx, gcode, pdate, title, uid, share, wuid, wdate, guid,
|
||||||
|
ISNULL(viewcount, 0) as viewcount, viewdate,
|
||||||
|
'' as description, '' as description2
|
||||||
|
FROM EETGW_Note WITH (nolock)
|
||||||
|
WHERE gcode = @gcode AND pdate BETWEEN @startDate AND @endDate
|
||||||
|
ORDER BY ISNULL(viewdate, '1900-01-01') DESC, ISNULL(viewcount, 0) DESC, pdate DESC";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd.CommandText = @"
|
||||||
|
SELECT idx, gcode, pdate, title, uid, share, wuid, wdate, guid,
|
||||||
|
ISNULL(viewcount, 0) as viewcount, viewdate,
|
||||||
|
'' as description, '' as description2
|
||||||
|
FROM EETGW_Note WITH (nolock)
|
||||||
|
WHERE gcode = @gcode AND pdate BETWEEN @startDate AND @endDate AND uid = @uid
|
||||||
|
ORDER BY ISNULL(viewdate, '1900-01-01') DESC, ISNULL(viewcount, 0) DESC, pdate DESC";
|
||||||
|
cmd.Parameters.Add("@uid", SqlDbType.VarChar).Value = uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 일반 사용자: 자신이 작성했거나 공유된 메모만 조회
|
||||||
|
cmd.CommandText = @"
|
||||||
|
SELECT idx, gcode, pdate, title, uid, share, wuid, wdate, guid,
|
||||||
|
ISNULL(viewcount, 0) as viewcount, viewdate,
|
||||||
|
'' as description, '' as description2
|
||||||
|
FROM EETGW_Note WITH (nolock)
|
||||||
|
WHERE (gcode = @gcode AND pdate BETWEEN @startDate AND @endDate AND uid = @currentUid)
|
||||||
|
OR (gcode = @gcode AND pdate BETWEEN @startDate AND @endDate AND ISNULL(share, 0) = 1)
|
||||||
|
ORDER BY ISNULL(viewdate, '1900-01-01') DESC, ISNULL(viewcount, 0) DESC, pdate DESC";
|
||||||
|
cmd.Parameters.Add("@currentUid", SqlDbType.VarChar).Value = info.Login.no;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@startDate", SqlDbType.VarChar).Value = startDate;
|
||||||
|
cmd.Parameters.Add("@endDate", SqlDbType.VarChar).Value = endDate;
|
||||||
|
|
||||||
|
var list = new List<object>();
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
list.Add(new
|
||||||
|
{
|
||||||
|
idx = reader["idx"],
|
||||||
|
gcode = reader["gcode"],
|
||||||
|
pdate = reader["pdate"],
|
||||||
|
title = reader["title"],
|
||||||
|
uid = reader["uid"],
|
||||||
|
share = reader["share"],
|
||||||
|
wuid = reader["wuid"],
|
||||||
|
wdate = reader["wdate"],
|
||||||
|
guid = reader["guid"],
|
||||||
|
viewcount = reader["viewcount"],
|
||||||
|
viewdate = reader["viewdate"] != DBNull.Value ? reader["viewdate"] : null,
|
||||||
|
description = reader["description"],
|
||||||
|
description2 = reader["description2"]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = list });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메모장 상세 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Note_GetDetail(int idx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(cs))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
// 조회수 증가 및 조회일 업데이트
|
||||||
|
var updateCmd = new SqlCommand(@"
|
||||||
|
UPDATE EETGW_Note
|
||||||
|
SET viewcount = ISNULL(viewcount, 0) + 1, viewdate = GETDATE()
|
||||||
|
WHERE gcode = @gcode AND idx = @idx", conn);
|
||||||
|
updateCmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
updateCmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
updateCmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
var cmd = new SqlCommand(@"
|
||||||
|
SELECT idx, gcode, pdate, title, uid, description, description2, share, wuid, wdate, guid,
|
||||||
|
ISNULL(viewcount, 0) as viewcount, viewdate
|
||||||
|
FROM EETGW_Note WITH (nolock)
|
||||||
|
WHERE gcode = @gcode AND idx = @idx", conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
var item = new
|
||||||
|
{
|
||||||
|
idx = reader["idx"],
|
||||||
|
gcode = reader["gcode"],
|
||||||
|
pdate = reader["pdate"],
|
||||||
|
title = reader["title"],
|
||||||
|
uid = reader["uid"],
|
||||||
|
description = reader["description"],
|
||||||
|
description2 = reader["description2"],
|
||||||
|
share = reader["share"],
|
||||||
|
wuid = reader["wuid"],
|
||||||
|
wdate = reader["wdate"],
|
||||||
|
guid = reader["guid"],
|
||||||
|
viewcount = reader["viewcount"],
|
||||||
|
viewdate = reader["viewdate"] != DBNull.Value ? reader["viewdate"] : null
|
||||||
|
};
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = item });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "데이터를 찾을 수 없습니다." });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메모장 추가
|
||||||
|
/// </summary>
|
||||||
|
public string Note_Add(string pdate, string title, string uid, string description, string description2, bool share, string guid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUID가 비어있으면 생성
|
||||||
|
if (string.IsNullOrEmpty(guid))
|
||||||
|
{
|
||||||
|
guid = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
using (var conn = new SqlConnection(cs))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
var cmd = new SqlCommand(@"
|
||||||
|
INSERT INTO EETGW_Note (gcode, pdate, title, uid, description, description2, share, wuid, wdate, guid)
|
||||||
|
VALUES (@gcode, @pdate, @title, @uid, @description, @description2, @share, @wuid, @wdate, @guid);
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() AS INT);", conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@pdate", SqlDbType.VarChar).Value = pdate;
|
||||||
|
cmd.Parameters.Add("@title", SqlDbType.NVarChar).Value = title;
|
||||||
|
cmd.Parameters.Add("@uid", SqlDbType.VarChar).Value = uid;
|
||||||
|
cmd.Parameters.Add("@description", SqlDbType.NVarChar).Value = description ?? "";
|
||||||
|
cmd.Parameters.Add("@description2", SqlDbType.NText).Value = description2 ?? "";
|
||||||
|
cmd.Parameters.Add("@share", SqlDbType.Bit).Value = share;
|
||||||
|
cmd.Parameters.Add("@wuid", SqlDbType.VarChar).Value = info.Login.no;
|
||||||
|
cmd.Parameters.Add("@wdate", SqlDbType.DateTime).Value = DateTime.Now;
|
||||||
|
cmd.Parameters.Add("@guid", SqlDbType.VarChar).Value = guid;
|
||||||
|
|
||||||
|
var newIdx = cmd.ExecuteScalar();
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Idx = newIdx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메모장 수정
|
||||||
|
/// </summary>
|
||||||
|
public string Note_Edit(int idx, string pdate, string title, string uid, string description, string description2, bool share, string guid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var connStr = Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(connStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
// 권한 체크: 자신의 메모이거나 관리자인 경우만 수정 가능
|
||||||
|
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.jobreport));
|
||||||
|
|
||||||
|
var checkCmd = new SqlCommand(@"
|
||||||
|
SELECT uid FROM EETGW_Note WHERE gcode = @gcode AND idx = @idx", conn);
|
||||||
|
checkCmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
checkCmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
|
||||||
|
var originalUid = checkCmd.ExecuteScalar()?.ToString();
|
||||||
|
if (originalUid != info.Login.no && curLevel < 5)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "타인의 자료는 수정할 수 없습니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd = new SqlCommand(@"
|
||||||
|
UPDATE EETGW_Note
|
||||||
|
SET pdate = @pdate, title = @title, uid = @uid,
|
||||||
|
description = @description, description2 = @description2,
|
||||||
|
share = @share, guid = @guid, wuid = @wuid, wdate = @wdate
|
||||||
|
WHERE gcode = @gcode AND idx = @idx", conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
cmd.Parameters.Add("@pdate", SqlDbType.VarChar).Value = pdate;
|
||||||
|
cmd.Parameters.Add("@title", SqlDbType.NVarChar).Value = title;
|
||||||
|
cmd.Parameters.Add("@uid", SqlDbType.VarChar).Value = uid;
|
||||||
|
cmd.Parameters.Add("@description", SqlDbType.NVarChar).Value = description ?? "";
|
||||||
|
cmd.Parameters.Add("@description2", SqlDbType.NText).Value = description2 ?? "";
|
||||||
|
cmd.Parameters.Add("@share", SqlDbType.Bit).Value = share;
|
||||||
|
cmd.Parameters.Add("@guid", SqlDbType.VarChar).Value = guid;
|
||||||
|
cmd.Parameters.Add("@wuid", SqlDbType.VarChar).Value = info.Login.no;
|
||||||
|
cmd.Parameters.Add("@wdate", SqlDbType.DateTime).Value = DateTime.Now;
|
||||||
|
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 멤모장 삭제
|
||||||
|
/// </summary>
|
||||||
|
public string Note_Delete(int idx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var connStr = Properties.Settings.Default.CS;
|
||||||
|
using (var conn = new SqlConnection(connStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
// 권한 체크: 자신의 메모이거나 관리자인 경우만 삭제 가능
|
||||||
|
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.jobreport));
|
||||||
|
|
||||||
|
var checkCmd = new SqlCommand(@"
|
||||||
|
SELECT uid FROM EETGW_Note WHERE gcode = @gcode AND idx = @idx", conn);
|
||||||
|
checkCmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
checkCmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
|
||||||
|
var originalUid = checkCmd.ExecuteScalar()?.ToString();
|
||||||
|
if (originalUid != info.Login.no && curLevel < 5)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "타인의 자료는 삭제할 수 없습니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd = new SqlCommand(@"
|
||||||
|
DELETE FROM EETGW_Note
|
||||||
|
WHERE gcode = @gcode AND idx = @idx", conn);
|
||||||
|
|
||||||
|
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
cmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
|
||||||
|
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -836,6 +836,99 @@ namespace Project.Web
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
// ===== Note API (메모장) =====
|
||||||
|
case "NOTE_GET_LIST":
|
||||||
|
{
|
||||||
|
string startDate = json.startDate ?? "";
|
||||||
|
string endDate = json.endDate ?? "";
|
||||||
|
string uid = json.uid ?? "";
|
||||||
|
string result = _bridge.Note_GetList(startDate, endDate, uid);
|
||||||
|
var response = new { type = "NOTE_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "NOTE_GET_DETAIL":
|
||||||
|
{
|
||||||
|
int idx = json.idx ?? 0;
|
||||||
|
string result = _bridge.Note_GetDetail(idx);
|
||||||
|
var response = new { type = "NOTE_DETAIL_DATA", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "NOTE_ADD":
|
||||||
|
{
|
||||||
|
string pdate = json.pdate ?? "";
|
||||||
|
string title = json.title ?? "";
|
||||||
|
string uid = json.uid ?? "";
|
||||||
|
string description = json.description ?? "";
|
||||||
|
string description2 = json.description2 ?? "";
|
||||||
|
bool share = json.share ?? false;
|
||||||
|
string guid = json.guid ?? "";
|
||||||
|
string result = _bridge.Note_Add(pdate, title, uid, description, description2, share, guid);
|
||||||
|
var response = new { type = "NOTE_ADDED", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "NOTE_EDIT":
|
||||||
|
{
|
||||||
|
int idx = json.idx ?? 0;
|
||||||
|
string pdate = json.pdate ?? "";
|
||||||
|
string title = json.title ?? "";
|
||||||
|
string uid = json.uid ?? "";
|
||||||
|
string description = json.description ?? "";
|
||||||
|
string description2 = json.description2 ?? "";
|
||||||
|
bool share = json.share ?? false;
|
||||||
|
string guid = json.guid ?? "";
|
||||||
|
string result = _bridge.Note_Edit(idx, pdate, title, uid, description, description2, share, guid);
|
||||||
|
var response = new { type = "NOTE_EDITED", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "NOTE_DELETE":
|
||||||
|
{
|
||||||
|
int idx = json.idx ?? 0;
|
||||||
|
string result = _bridge.Note_Delete(idx);
|
||||||
|
var response = new { type = "NOTE_DELETED", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ===== Board API (게시판 - 패치내역 등) =====
|
||||||
|
case "BOARD_GET_LIST":
|
||||||
|
{
|
||||||
|
int bidx = json.bidx ?? 5;
|
||||||
|
string searchKey = json.searchKey ?? "";
|
||||||
|
string result = _bridge.Board_GetList(bidx, searchKey);
|
||||||
|
var response = new { type = "BOARD_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "BOARD_GET_DETAIL":
|
||||||
|
{
|
||||||
|
int idx = json.idx ?? 0;
|
||||||
|
string result = _bridge.Board_GetDetail(idx);
|
||||||
|
var response = new { type = "BOARD_DETAIL_DATA", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ===== Mail API (메일 발신 내역) =====
|
||||||
|
case "MAIL_GET_LIST":
|
||||||
|
{
|
||||||
|
string startDate = json.startDate ?? "";
|
||||||
|
string endDate = json.endDate ?? "";
|
||||||
|
string searchKey = json.searchKey ?? "";
|
||||||
|
string result = _bridge.Mail_GetList(startDate, endDate, searchKey);
|
||||||
|
var response = new { type = "MAIL_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
|
||||||
|
await Send(socket, JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// ===== Holiday API (월별근무표) =====
|
// ===== Holiday API (월별근무표) =====
|
||||||
case "HOLIDAY_GET_LIST":
|
case "HOLIDAY_GET_LIST":
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { HashRouter, Routes, Route } from 'react-router-dom';
|
import { HashRouter, Routes, Route } from 'react-router-dom';
|
||||||
import { Layout } from '@/components/layout';
|
import { Layout } from '@/components/layout';
|
||||||
import { Dashboard, Todo, Kuntae, Jobreport, Project, Login, CommonCodePage, ItemsPage, UserListPage, MonthlyWorkPage, MailFormPage, UserAuthPage } from '@/pages';
|
import { Dashboard, Todo, Kuntae, Jobreport, Project, Login, CommonCodePage, ItemsPage, UserListPage, MonthlyWorkPage, MailFormPage, UserAuthPage, Note } from '@/pages';
|
||||||
|
import { PatchList } from '@/pages/PatchList';
|
||||||
|
import { MailList } from '@/pages/MailList';
|
||||||
import { comms } from '@/communication';
|
import { comms } from '@/communication';
|
||||||
import { UserInfo } from '@/types';
|
import { UserInfo } from '@/types';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
@@ -92,6 +94,9 @@ export default function App() {
|
|||||||
<Route path="/user/auth" element={<UserAuthPage />} />
|
<Route path="/user/auth" element={<UserAuthPage />} />
|
||||||
<Route path="/monthly-work" element={<MonthlyWorkPage />} />
|
<Route path="/monthly-work" element={<MonthlyWorkPage />} />
|
||||||
<Route path="/mail-form" element={<MailFormPage />} />
|
<Route path="/mail-form" element={<MailFormPage />} />
|
||||||
|
<Route path="/note" element={<Note />} />
|
||||||
|
<Route path="/patch-list" element={<PatchList />} />
|
||||||
|
<Route path="/mail-list" element={<MailList />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
{/* Tailwind Breakpoint Indicator - 개발용 */}
|
{/* Tailwind Breakpoint Indicator - 개발용 */}
|
||||||
|
|||||||
@@ -1,3 +1,48 @@
|
|||||||
|
import type {
|
||||||
|
ApiResponse,
|
||||||
|
LoginStatusResponse,
|
||||||
|
CheckAuthResponse,
|
||||||
|
TodoModel,
|
||||||
|
PurchaseItem,
|
||||||
|
PurchaseCount,
|
||||||
|
JobReportItem,
|
||||||
|
JobReportUser,
|
||||||
|
JobReportPermission,
|
||||||
|
JobReportTypeItem,
|
||||||
|
JobTypeItem,
|
||||||
|
MailFormItem,
|
||||||
|
UserGroupItem,
|
||||||
|
PermissionInfo,
|
||||||
|
AuthItem,
|
||||||
|
AuthFieldInfo,
|
||||||
|
AuthType,
|
||||||
|
MyAuthInfo,
|
||||||
|
ProjectSearchItem,
|
||||||
|
NoteItem,
|
||||||
|
KuntaeModel,
|
||||||
|
HolydayBalance,
|
||||||
|
HolyUser,
|
||||||
|
HolyRequestUser,
|
||||||
|
LoginResult,
|
||||||
|
UserGroup,
|
||||||
|
PreviousLoginInfo,
|
||||||
|
UserInfoDetail,
|
||||||
|
CommonCodeGroup,
|
||||||
|
CommonCode,
|
||||||
|
ItemInfo,
|
||||||
|
ItemDetail,
|
||||||
|
SupplierStaff,
|
||||||
|
PurchaseHistoryItem,
|
||||||
|
UserLevelInfo,
|
||||||
|
GroupUser,
|
||||||
|
UserFullData,
|
||||||
|
AppVersionInfo,
|
||||||
|
HolidayItem,
|
||||||
|
MachineBridgeInterface,
|
||||||
|
BoardItem,
|
||||||
|
MailItem,
|
||||||
|
} from '@/types';
|
||||||
|
|
||||||
// WebView2 환경 감지
|
// WebView2 환경 감지
|
||||||
const isWebView = typeof window !== 'undefined' &&
|
const isWebView = typeof window !== 'undefined' &&
|
||||||
window.chrome?.webview?.hostObjects !== undefined;
|
window.chrome?.webview?.hostObjects !== undefined;
|
||||||
@@ -799,7 +844,7 @@ class CommunicationLayer {
|
|||||||
|
|
||||||
public async getJobReportTypeList(sd: string, ed: string, uid: string = ''): Promise<ApiResponse<JobReportTypeItem[]>> {
|
public async getJobReportTypeList(sd: string, ed: string, uid: string = ''): Promise<ApiResponse<JobReportTypeItem[]>> {
|
||||||
if (isWebView && machine) {
|
if (isWebView && machine) {
|
||||||
const result = await machine.Jobreport_GetTypeList(sd, ed, uid);
|
const result = await machine.Jobreport_GetList(sd, ed, uid, '', '');
|
||||||
return JSON.parse(result);
|
return JSON.parse(result);
|
||||||
} else {
|
} else {
|
||||||
return this.wsRequest<ApiResponse<JobReportTypeItem[]>>('JOBREPORT_GET_TYPE_LIST', 'JOBREPORT_TYPE_LIST_DATA', { sd, ed, uid });
|
return this.wsRequest<ApiResponse<JobReportTypeItem[]>>('JOBREPORT_GET_TYPE_LIST', 'JOBREPORT_TYPE_LIST_DATA', { sd, ed, uid });
|
||||||
@@ -1080,6 +1125,159 @@ class CommunicationLayer {
|
|||||||
return this.wsRequest<ApiResponse<{ idx: number, name: string, status: string }[]>>('PROJECT_GET_USER_PROJECTS', 'PROJECT_USER_PROJECTS_DATA');
|
return this.wsRequest<ApiResponse<{ idx: number, name: string, status: string }[]>>('PROJECT_GET_USER_PROJECTS', 'PROJECT_USER_PROJECTS_DATA');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Note API (메모장) =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메모장 목록 조회
|
||||||
|
* @param startDate 시작일 (yyyy-MM-dd)
|
||||||
|
* @param endDate 종료일 (yyyy-MM-dd)
|
||||||
|
* @param uid 사용자 ID (빈 문자열이면 전체)
|
||||||
|
* @returns ApiResponse<NoteItem[]>
|
||||||
|
*/
|
||||||
|
public async getNoteList(startDate: string, endDate: string, uid: string = ''): Promise<ApiResponse<NoteItem[]>> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Note_GetList(startDate, endDate, uid);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse<NoteItem[]>>('NOTE_GET_LIST', 'NOTE_LIST_DATA', { startDate, endDate, uid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메모장 상세 조회
|
||||||
|
* @param idx 메모 인덱스
|
||||||
|
* @returns ApiResponse<NoteItem>
|
||||||
|
*/
|
||||||
|
public async getNoteDetail(idx: number): Promise<ApiResponse<NoteItem>> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Note_GetDetail(idx);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse<NoteItem>>('NOTE_GET_DETAIL', 'NOTE_DETAIL_DATA', { idx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메모장 추가
|
||||||
|
* @param pdate 날짜 (yyyy-MM-dd)
|
||||||
|
* @param title 제목
|
||||||
|
* @param uid 작성자 ID
|
||||||
|
* @param description 내용 (plain text)
|
||||||
|
* @param description2 내용 (RTF - not used in web)
|
||||||
|
* @param share 공유 여부
|
||||||
|
* @param guid 폴더 GUID (빈 문자열이면 자동 생성)
|
||||||
|
* @returns ApiResponse with new Idx
|
||||||
|
*/
|
||||||
|
public async addNote(
|
||||||
|
pdate: string,
|
||||||
|
title: string,
|
||||||
|
uid: string,
|
||||||
|
description: string,
|
||||||
|
description2: string = '',
|
||||||
|
share: boolean = false,
|
||||||
|
guid: string = ''
|
||||||
|
): Promise<ApiResponse<{ Idx: number }>> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Note_Add(pdate, title, uid, description, description2, share, guid);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse<{ Idx: number }>>('NOTE_ADD', 'NOTE_ADDED', {
|
||||||
|
pdate, title, uid, description, description2, share, guid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메모장 수정
|
||||||
|
* @param idx 메모 인덱스
|
||||||
|
* @param pdate 날짜 (yyyy-MM-dd)
|
||||||
|
* @param title 제목
|
||||||
|
* @param uid 작성자 ID
|
||||||
|
* @param description 내용 (plain text)
|
||||||
|
* @param description2 내용 (RTF - not used in web)
|
||||||
|
* @param share 공유 여부
|
||||||
|
* @param guid 폴더 GUID
|
||||||
|
* @returns ApiResponse
|
||||||
|
*/
|
||||||
|
public async editNote(
|
||||||
|
idx: number,
|
||||||
|
pdate: string,
|
||||||
|
title: string,
|
||||||
|
uid: string,
|
||||||
|
description: string,
|
||||||
|
description2: string = '',
|
||||||
|
share: boolean = false,
|
||||||
|
guid: string = ''
|
||||||
|
): Promise<ApiResponse> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Note_Edit(idx, pdate, title, uid, description, description2, share, guid);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse>('NOTE_EDIT', 'NOTE_EDITED', {
|
||||||
|
idx, pdate, title, uid, description, description2, share, guid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메모장 삭제
|
||||||
|
* @param idx 메모 인덱스
|
||||||
|
* @returns ApiResponse
|
||||||
|
*/
|
||||||
|
public async deleteNote(idx: number): Promise<ApiResponse> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Note_Delete(idx);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse>('NOTE_DELETE', 'NOTE_DELETED', { idx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 게시판 목록 조회
|
||||||
|
* @param bidx 게시판 인덱스 (5=패치내역)
|
||||||
|
* @param searchKey 검색어
|
||||||
|
* @returns ApiResponse<BoardItem[]>
|
||||||
|
*/
|
||||||
|
public async getBoardList(bidx: number, searchKey: string = ''): Promise<ApiResponse<BoardItem[]>> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Board_GetList(bidx, searchKey);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse<BoardItem[]>>('BOARD_GET_LIST', 'BOARD_LIST_DATA', { bidx, searchKey });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 게시판 상세 조회
|
||||||
|
* @param idx 게시판 글 인덱스
|
||||||
|
* @returns ApiResponse<BoardItem>
|
||||||
|
*/
|
||||||
|
public async getBoardDetail(idx: number): Promise<ApiResponse<BoardItem>> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Board_GetDetail(idx);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse<BoardItem>>('BOARD_GET_DETAIL', 'BOARD_DETAIL_DATA', { idx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메일 발신 내역 조회
|
||||||
|
* @param startDate 시작일 (yyyy-MM-dd)
|
||||||
|
* @param endDate 종료일 (yyyy-MM-dd)
|
||||||
|
* @param searchKey 검색어
|
||||||
|
* @returns ApiResponse<MailItem[]>
|
||||||
|
*/
|
||||||
|
public async getMailList(startDate: string, endDate: string, searchKey: string = ''): Promise<ApiResponse<MailItem[]>> {
|
||||||
|
if (isWebView && machine) {
|
||||||
|
const result = await machine.Mail_GetList(startDate, endDate, searchKey);
|
||||||
|
return JSON.parse(result);
|
||||||
|
} else {
|
||||||
|
return this.wsRequest<ApiResponse<MailItem[]>>('MAIL_GET_LIST', 'MAIL_LIST_DATA', { startDate, endDate, searchKey });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const comms = new CommunicationLayer();
|
export const comms = new CommunicationLayer();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { X, ChevronLeft, ChevronRight, Download } from 'lucide-react';
|
import { X, ChevronLeft, ChevronRight, Download } from 'lucide-react';
|
||||||
import { comms } from '@/communication';
|
import { comms } from '@/communication';
|
||||||
import { JobReportDayItem, HolidayItem } from '@/types';
|
import { HolidayItem } from '@/types';
|
||||||
|
|
||||||
interface JobReportDayDialogProps {
|
interface JobReportDayDialogProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -32,8 +32,6 @@ export function JobReportDayDialog({ isOpen, onClose, initialMonth }: JobReportD
|
|||||||
const [dayColumns, setDayColumns] = useState<DayColumn[]>([]);
|
const [dayColumns, setDayColumns] = useState<DayColumn[]>([]);
|
||||||
const [userRows, setUserRows] = useState<UserRow[]>([]);
|
const [userRows, setUserRows] = useState<UserRow[]>([]);
|
||||||
const [currentUserId, setCurrentUserId] = useState<string>('');
|
const [currentUserId, setCurrentUserId] = useState<string>('');
|
||||||
const [currentUserLevel, setCurrentUserLevel] = useState<number>(0);
|
|
||||||
const [authLevel, setAuthLevel] = useState<number>(0);
|
|
||||||
const [canViewAll, setCanViewAll] = useState<boolean>(false);
|
const [canViewAll, setCanViewAll] = useState<boolean>(false);
|
||||||
|
|
||||||
// 요일 배열
|
// 요일 배열
|
||||||
@@ -54,12 +52,10 @@ export function JobReportDayDialog({ isOpen, onClose, initialMonth }: JobReportD
|
|||||||
const userId = loginStatus.User.Id;
|
const userId = loginStatus.User.Id;
|
||||||
const userLevel = loginStatus.User.Level || 0;
|
const userLevel = loginStatus.User.Level || 0;
|
||||||
setCurrentUserId(userId);
|
setCurrentUserId(userId);
|
||||||
setCurrentUserLevel(userLevel);
|
|
||||||
|
|
||||||
// 업무일지(jobreport) 권한 가져오기
|
// 업무일지(jobreport) 권한 가져오기
|
||||||
const authResponse = await comms.checkAuth('jobreport', 5);
|
const authResponse = await comms.checkAuth('jobreport', 5);
|
||||||
const jobReportAuthLevel = authResponse.EffectiveLevel || 0;
|
const jobReportAuthLevel = authResponse.EffectiveLevel || 0;
|
||||||
setAuthLevel(jobReportAuthLevel);
|
|
||||||
|
|
||||||
// 유효 권한 레벨 = Max(사용자레벨, 권한레벨)
|
// 유효 권한 레벨 = Max(사용자레벨, 권한레벨)
|
||||||
const effectiveLevel = Math.max(userLevel, jobReportAuthLevel);
|
const effectiveLevel = Math.max(userLevel, jobReportAuthLevel);
|
||||||
|
|||||||
@@ -115,6 +115,15 @@ const dropdownMenus: DropdownMenuConfig[] = [
|
|||||||
{ type: 'link', path: '/mail-form', icon: Mail, label: '메일양식' },
|
{ type: 'link', path: '/mail-form', icon: Mail, label: '메일양식' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '문서',
|
||||||
|
icon: FileText,
|
||||||
|
items: [
|
||||||
|
{ type: 'link', path: '/note', icon: FileText, label: '메모장' },
|
||||||
|
{ type: 'link', path: '/patch-list', icon: FileText, label: '패치 내역' },
|
||||||
|
{ type: 'link', path: '/mail-list', icon: Mail, label: '메일 내역' },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function DropdownNavMenu({
|
function DropdownNavMenu({
|
||||||
|
|||||||
283
Project/frontend/src/components/note/NoteEditModal.tsx
Normal file
283
Project/frontend/src/components/note/NoteEditModal.tsx
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { X, Save, User, Calendar } from 'lucide-react';
|
||||||
|
import { NoteItem } from '@/types';
|
||||||
|
import { comms } from '@/communication';
|
||||||
|
|
||||||
|
interface NoteEditModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
editingItem: NoteItem | null;
|
||||||
|
processing: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onSave: (formData: {
|
||||||
|
pdate: string;
|
||||||
|
title: string;
|
||||||
|
uid: string;
|
||||||
|
description: string;
|
||||||
|
share: boolean;
|
||||||
|
guid: string;
|
||||||
|
}) => void;
|
||||||
|
initialEditMode?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NoteEditModal({
|
||||||
|
isOpen,
|
||||||
|
editingItem,
|
||||||
|
processing,
|
||||||
|
onClose,
|
||||||
|
onSave,
|
||||||
|
initialEditMode = false,
|
||||||
|
}: NoteEditModalProps) {
|
||||||
|
const [pdate, setPdate] = useState('');
|
||||||
|
const [title, setTitle] = useState('');
|
||||||
|
const [uid, setUid] = useState('');
|
||||||
|
const [description, setDescription] = useState('');
|
||||||
|
const [share, setShare] = useState(false);
|
||||||
|
const [guid, setGuid] = useState('');
|
||||||
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
|
|
||||||
|
// 현재 로그인 사용자 정보 로드
|
||||||
|
const [currentUserId, setCurrentUserId] = useState('');
|
||||||
|
const [currentUserLevel, setCurrentUserLevel] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadUserInfo = async () => {
|
||||||
|
try {
|
||||||
|
const loginStatus = await comms.checkLoginStatus();
|
||||||
|
if (loginStatus.Success && loginStatus.IsLoggedIn && loginStatus.User) {
|
||||||
|
setCurrentUserId(loginStatus.User.Id);
|
||||||
|
setCurrentUserLevel(loginStatus.User.Level);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('로그인 정보 로드 오류:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadUserInfo();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 모달이 열릴 때 폼 데이터 초기화
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
if (editingItem) {
|
||||||
|
// 기존 메모
|
||||||
|
setPdate(editingItem.pdate ? editingItem.pdate.split('T')[0] : '');
|
||||||
|
setTitle(editingItem.title || '');
|
||||||
|
setUid(editingItem.uid || currentUserId);
|
||||||
|
setDescription(editingItem.description || '');
|
||||||
|
setShare(editingItem.share || false);
|
||||||
|
setGuid(editingItem.guid || '');
|
||||||
|
setIsEditMode(initialEditMode);
|
||||||
|
} else {
|
||||||
|
// 신규 메모 - 편집 모드로 시작
|
||||||
|
setPdate(new Date().toISOString().split('T')[0]);
|
||||||
|
setTitle('');
|
||||||
|
setUid(currentUserId);
|
||||||
|
setDescription('');
|
||||||
|
setShare(false);
|
||||||
|
setGuid('');
|
||||||
|
setIsEditMode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isOpen, editingItem, currentUserId, initialEditMode]);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onSave({ pdate, title, uid, description, share, guid });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
// 권한 체크: 자신의 메모이거나 관리자만 수정 가능
|
||||||
|
const canEdit = !editingItem || editingItem.uid === currentUserId || currentUserLevel >= 5;
|
||||||
|
const canChangeUser = currentUserLevel >= 5;
|
||||||
|
const canChangeShare = !editingItem || editingItem.uid === currentUserId;
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||||||
|
<div className="bg-gray-900 rounded-2xl shadow-2xl w-full max-w-5xl max-h-[90vh] overflow-hidden border border-white/10">
|
||||||
|
{/* 헤더 */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10">
|
||||||
|
<h2 className="text-xl font-bold text-white">
|
||||||
|
{!editingItem ? '새 메모 작성' : '메모 편집'}
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-white/50 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<X className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 본문 - 좌우 레이아웃 */}
|
||||||
|
<form onSubmit={handleSubmit} className="overflow-y-auto max-h-[calc(90vh-140px)]">
|
||||||
|
<div className="flex gap-6 p-6">
|
||||||
|
{/* 좌측 - 정보 영역 */}
|
||||||
|
<div className="w-64 flex-shrink-0 space-y-4">
|
||||||
|
{/* 날짜 */}
|
||||||
|
<div>
|
||||||
|
<label className="flex items-center text-sm font-medium text-white/70 mb-2">
|
||||||
|
<Calendar className="w-4 h-4 mr-2" />
|
||||||
|
날짜 {isEditMode && '*'}
|
||||||
|
</label>
|
||||||
|
{isEditMode ? (
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={pdate}
|
||||||
|
onChange={(e) => setPdate(e.target.value)}
|
||||||
|
required
|
||||||
|
disabled={!canEdit}
|
||||||
|
className="w-full h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="text-white text-sm">{pdate}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 작성자 */}
|
||||||
|
<div>
|
||||||
|
<label className="flex items-center text-sm font-medium text-white/70 mb-2">
|
||||||
|
<User className="w-4 h-4 mr-2" />
|
||||||
|
작성자 {isEditMode && '*'}
|
||||||
|
</label>
|
||||||
|
{isEditMode ? (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={uid}
|
||||||
|
onChange={(e) => setUid(e.target.value)}
|
||||||
|
required
|
||||||
|
disabled={!canEdit || !canChangeUser}
|
||||||
|
className="w-full h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
placeholder="사용자 ID"
|
||||||
|
/>
|
||||||
|
{!canChangeUser && (
|
||||||
|
<p className="text-xs text-white/50 mt-1">
|
||||||
|
관리자만 변경 가능
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-white text-sm">{uid}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 공유 여부 */}
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-white/70 mb-2 block">
|
||||||
|
공유 설정
|
||||||
|
</label>
|
||||||
|
{isEditMode ? (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="note-share"
|
||||||
|
checked={share}
|
||||||
|
onChange={(e) => setShare(e.target.checked)}
|
||||||
|
disabled={!canEdit || !canChangeShare}
|
||||||
|
className="w-4 h-4 bg-white/20 border border-white/30 rounded focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
/>
|
||||||
|
<label htmlFor="note-share" className="text-sm text-white/70 cursor-pointer">
|
||||||
|
공유
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{!canChangeShare && (
|
||||||
|
<p className="text-xs text-white/50 mt-1">
|
||||||
|
본인 메모만 변경 가능
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-white text-sm">{share ? '공유됨' : '비공유'}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!canEdit && isEditMode && (
|
||||||
|
<div className="bg-red-500/20 border border-red-500/50 rounded-lg p-3 text-red-300 text-xs">
|
||||||
|
타인의 자료는 수정할 수 없습니다.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 우측 - 제목 및 내용 영역 */}
|
||||||
|
<div className="flex-1 space-y-4">
|
||||||
|
{/* 제목 */}
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium text-white/70 mb-2 block">
|
||||||
|
제목 {isEditMode && '*'}
|
||||||
|
</label>
|
||||||
|
{isEditMode ? (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={title}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
required
|
||||||
|
disabled={!canEdit}
|
||||||
|
className="w-full h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
placeholder="메모 제목을 입력하세요"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="text-white text-lg font-semibold">{title}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 내용 */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="text-sm font-medium text-white/70 mb-2 block">
|
||||||
|
내용
|
||||||
|
</label>
|
||||||
|
{isEditMode ? (
|
||||||
|
<textarea
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
disabled={!canEdit}
|
||||||
|
rows={18}
|
||||||
|
className="w-full bg-white/20 border border-white/30 rounded-lg p-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed resize-none"
|
||||||
|
placeholder="메모 내용을 입력하세요"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="bg-white/10 border border-white/20 rounded-lg p-4 text-white whitespace-pre-wrap min-h-[400px]">
|
||||||
|
{description || '내용이 없습니다.'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{/* 하단 버튼 */}
|
||||||
|
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-white/10 bg-white/5">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-4 py-2 rounded-lg bg-white/10 hover:bg-white/20 text-white transition-colors"
|
||||||
|
disabled={processing}
|
||||||
|
>
|
||||||
|
닫기
|
||||||
|
</button>
|
||||||
|
{isEditMode && canEdit && (
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
disabled={processing}
|
||||||
|
className="px-4 py-2 rounded-lg bg-primary-500 hover:bg-primary-600 text-white transition-colors flex items-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{processing ? (
|
||||||
|
<>
|
||||||
|
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"></div>
|
||||||
|
처리 중...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Save className="w-4 h-4 mr-2" />
|
||||||
|
저장
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
}
|
||||||
74
Project/frontend/src/components/note/NoteViewModal.tsx
Normal file
74
Project/frontend/src/components/note/NoteViewModal.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { X, Trash2 } from 'lucide-react';
|
||||||
|
import { NoteItem } from '@/types';
|
||||||
|
|
||||||
|
interface NoteViewModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
note: NoteItem | null;
|
||||||
|
onClose: () => void;
|
||||||
|
onEdit: (note: NoteItem) => void;
|
||||||
|
onDelete?: (note: NoteItem) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NoteViewModal({ isOpen, note, onClose, onEdit, onDelete }: NoteViewModalProps) {
|
||||||
|
if (!isOpen || !note) return null;
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (window.confirm('정말 삭제하시겠습니까?')) {
|
||||||
|
onDelete?.(note);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||||||
|
<div className="bg-gray-900 rounded-2xl shadow-2xl w-full max-w-2xl max-h-[80vh] overflow-hidden border border-white/10">
|
||||||
|
{/* 헤더 */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10">
|
||||||
|
<h2 className="text-xl font-bold text-white">{note.title || '제목 없음'}</h2>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-white/50 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<X className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 본문 - 메모 내용만 표시 */}
|
||||||
|
<div className="overflow-y-auto max-h-[calc(80vh-140px)] p-6">
|
||||||
|
<div className="bg-white/10 border border-white/20 rounded-lg p-4 text-white whitespace-pre-wrap min-h-[300px]">
|
||||||
|
{note.description || '내용이 없습니다.'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 하단 버튼 */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-t border-white/10 bg-white/5">
|
||||||
|
<button
|
||||||
|
onClick={handleDelete}
|
||||||
|
className="px-4 py-2 rounded-lg bg-danger-500 hover:bg-danger-600 text-white transition-colors flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4" />
|
||||||
|
삭제
|
||||||
|
</button>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-4 py-2 rounded-lg bg-white/10 hover:bg-white/20 text-white transition-colors"
|
||||||
|
>
|
||||||
|
닫기
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
onEdit(note);
|
||||||
|
}}
|
||||||
|
className="px-4 py-2 bg-success-500 hover:bg-success-600 text-white rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
편집
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,9 +9,20 @@ import {
|
|||||||
RefreshCw,
|
RefreshCw,
|
||||||
ClipboardList,
|
ClipboardList,
|
||||||
Clock,
|
Clock,
|
||||||
|
FileText,
|
||||||
|
Share2,
|
||||||
|
Lock,
|
||||||
|
List,
|
||||||
|
Plus,
|
||||||
|
X,
|
||||||
|
Loader2,
|
||||||
|
Edit2,
|
||||||
|
Trash2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { comms } from '@/communication';
|
import { comms } from '@/communication';
|
||||||
import { TodoModel, PurchaseItem } from '@/types';
|
import { TodoModel, TodoStatus, TodoPriority, PurchaseItem, NoteItem, JobReportItem } from '@/types';
|
||||||
|
import { NoteViewModal } from '@/components/note/NoteViewModal';
|
||||||
|
import { NoteEditModal } from '@/components/note/NoteEditModal';
|
||||||
|
|
||||||
interface StatCardProps {
|
interface StatCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -55,10 +66,31 @@ export function Dashboard() {
|
|||||||
const [urgentTodos, setUrgentTodos] = useState<TodoModel[]>([]);
|
const [urgentTodos, setUrgentTodos] = useState<TodoModel[]>([]);
|
||||||
const [purchaseNRList, setPurchaseNRList] = useState<PurchaseItem[]>([]);
|
const [purchaseNRList, setPurchaseNRList] = useState<PurchaseItem[]>([]);
|
||||||
const [purchaseCRList, setPurchaseCRList] = useState<PurchaseItem[]>([]);
|
const [purchaseCRList, setPurchaseCRList] = useState<PurchaseItem[]>([]);
|
||||||
|
const [recentNotes, setRecentNotes] = useState<NoteItem[]>([]);
|
||||||
|
|
||||||
// 모달 상태
|
// 모달 상태
|
||||||
const [showNRModal, setShowNRModal] = useState(false);
|
const [showNRModal, setShowNRModal] = useState(false);
|
||||||
const [showCRModal, setShowCRModal] = useState(false);
|
const [showCRModal, setShowCRModal] = useState(false);
|
||||||
|
const [showNoteModal, setShowNoteModal] = useState(false);
|
||||||
|
const [showNoteEditModal, setShowNoteEditModal] = useState(false);
|
||||||
|
const [showNoteAddModal, setShowNoteAddModal] = useState(false);
|
||||||
|
const [selectedNote, setSelectedNote] = useState<NoteItem | null>(null);
|
||||||
|
const [editingNote, setEditingNote] = useState<NoteItem | null>(null);
|
||||||
|
const [processing, setProcessing] = useState(false);
|
||||||
|
|
||||||
|
// 할일 추가 모달 상태
|
||||||
|
const [showTodoAddModal, setShowTodoAddModal] = useState(false);
|
||||||
|
const [showTodoEditModal, setShowTodoEditModal] = useState(false);
|
||||||
|
const [editingTodo, setEditingTodo] = useState<TodoModel | null>(null);
|
||||||
|
const [todoFormData, setTodoFormData] = useState({
|
||||||
|
title: '',
|
||||||
|
remark: '',
|
||||||
|
expire: '',
|
||||||
|
seqno: 0 as TodoPriority,
|
||||||
|
flag: false,
|
||||||
|
request: '',
|
||||||
|
status: '0' as TodoStatus,
|
||||||
|
});
|
||||||
|
|
||||||
const loadDashboardData = useCallback(async () => {
|
const loadDashboardData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -83,11 +115,13 @@ export function Dashboard() {
|
|||||||
urgentTodosResponse,
|
urgentTodosResponse,
|
||||||
allTodosResponse,
|
allTodosResponse,
|
||||||
jobreportResponse,
|
jobreportResponse,
|
||||||
|
notesResponse,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
comms.getPurchaseWaitCount(),
|
comms.getPurchaseWaitCount(),
|
||||||
comms.getUrgentTodos(),
|
comms.getUrgentTodos(),
|
||||||
comms.getTodos(),
|
comms.getTodos(),
|
||||||
comms.getJobReportList(todayStr, todayStr, currentUserId, ''),
|
comms.getJobReportList(todayStr, todayStr, currentUserId, ''),
|
||||||
|
comms.getNoteList('2000-01-01', todayStr, ''),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setPurchaseNR(purchaseCount.NR);
|
setPurchaseNR(purchaseCount.NR);
|
||||||
@@ -99,17 +133,22 @@ export function Dashboard() {
|
|||||||
|
|
||||||
if (allTodosResponse.Success && allTodosResponse.Data) {
|
if (allTodosResponse.Success && allTodosResponse.Data) {
|
||||||
// 진행, 대기 상태의 할일만 카운트 (보류, 취소 제외)
|
// 진행, 대기 상태의 할일만 카운트 (보류, 취소 제외)
|
||||||
const pendingCount = allTodosResponse.Data.filter(t => t.status === '0' || t.status === '1').length;
|
const pendingCount = allTodosResponse.Data.filter((t: TodoModel) => t.status === '0' || t.status === '1').length;
|
||||||
setTodoCount(pendingCount);
|
setTodoCount(pendingCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 오늘 업무일지 작성시간 계산
|
// 오늘 업무일지 작성시간 계산
|
||||||
if (jobreportResponse.Success && jobreportResponse.Data) {
|
if (jobreportResponse.Success && jobreportResponse.Data) {
|
||||||
const totalHrs = jobreportResponse.Data.reduce((acc, item) => acc + (item.hrs || 0), 0);
|
const totalHrs = jobreportResponse.Data.reduce((acc: number, item: JobReportItem) => acc + (item.hrs || 0), 0);
|
||||||
setTodayWorkHrs(totalHrs);
|
setTodayWorkHrs(totalHrs);
|
||||||
} else {
|
} else {
|
||||||
setTodayWorkHrs(0);
|
setTodayWorkHrs(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 최근 메모 목록 (최대 10개)
|
||||||
|
if (notesResponse.Success && notesResponse.Data) {
|
||||||
|
setRecentNotes(notesResponse.Data.slice(0, 10));
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('대시보드 데이터 로드 오류:', error);
|
console.error('대시보드 데이터 로드 오류:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -199,23 +238,263 @@ export function Dashboard() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const handleNoteClick = async (note: NoteItem) => {
|
||||||
<div className="space-y-6 animate-fade-in">
|
try {
|
||||||
{/* 헤더 */}
|
const response = await comms.getNoteDetail(note.idx);
|
||||||
<div className="flex items-center justify-between">
|
if (response.Success && response.Data) {
|
||||||
<h2 className="text-2xl font-bold text-white">오늘의 현황</h2>
|
setSelectedNote(response.Data);
|
||||||
<button
|
setShowNoteModal(true);
|
||||||
onClick={handleRefresh}
|
}
|
||||||
disabled={refreshing}
|
} catch (error) {
|
||||||
className="flex items-center space-x-2 px-4 py-2 glass-effect rounded-lg text-white/70 hover:text-white transition-colors"
|
console.error('메모 조회 오류:', error);
|
||||||
>
|
}
|
||||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin' : ''}`} />
|
};
|
||||||
<span>새로고침</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 통계 카드 */}
|
const handleNoteAdd = () => {
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
setEditingNote(null);
|
||||||
|
setShowNoteAddModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNoteDelete = async (note: NoteItem) => {
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = await comms.deleteNote(note.idx);
|
||||||
|
if (response.Success) {
|
||||||
|
setShowNoteModal(false);
|
||||||
|
loadDashboardData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '삭제에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('삭제 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다: ' + (error instanceof Error ? error.message : String(error)));
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTodoAdd = () => {
|
||||||
|
setTodoFormData({
|
||||||
|
title: '',
|
||||||
|
remark: '',
|
||||||
|
expire: '',
|
||||||
|
seqno: 0,
|
||||||
|
flag: false,
|
||||||
|
request: '',
|
||||||
|
status: '0',
|
||||||
|
});
|
||||||
|
setShowTodoAddModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTodoEdit = (todo: TodoModel) => {
|
||||||
|
setEditingTodo(todo);
|
||||||
|
setTodoFormData({
|
||||||
|
title: todo.title || '',
|
||||||
|
remark: todo.remark,
|
||||||
|
expire: todo.expire || '',
|
||||||
|
seqno: todo.seqno as TodoPriority,
|
||||||
|
flag: todo.flag,
|
||||||
|
request: todo.request || '',
|
||||||
|
status: todo.status as TodoStatus,
|
||||||
|
});
|
||||||
|
setShowTodoEditModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTodoSave = async () => {
|
||||||
|
if (!todoFormData.remark.trim()) {
|
||||||
|
alert('할일 내용을 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = await comms.createTodo(
|
||||||
|
todoFormData.title,
|
||||||
|
todoFormData.remark,
|
||||||
|
todoFormData.expire || null,
|
||||||
|
todoFormData.seqno,
|
||||||
|
todoFormData.flag,
|
||||||
|
todoFormData.request || null,
|
||||||
|
todoFormData.status
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.Success) {
|
||||||
|
setShowTodoAddModal(false);
|
||||||
|
loadDashboardData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '할일 추가에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('할일 추가 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다: ' + (error instanceof Error ? error.message : String(error)));
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTodoUpdate = async () => {
|
||||||
|
if (!editingTodo || !todoFormData.remark.trim()) {
|
||||||
|
alert('할일 내용을 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = await comms.updateTodo(
|
||||||
|
editingTodo.idx,
|
||||||
|
todoFormData.title,
|
||||||
|
todoFormData.remark,
|
||||||
|
todoFormData.expire || null,
|
||||||
|
todoFormData.seqno,
|
||||||
|
todoFormData.flag,
|
||||||
|
todoFormData.request || null,
|
||||||
|
todoFormData.status
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.Success) {
|
||||||
|
setShowTodoEditModal(false);
|
||||||
|
setEditingTodo(null);
|
||||||
|
loadDashboardData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '할일 수정에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('할일 수정 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다: ' + (error instanceof Error ? error.message : String(error)));
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTodoDelete = async () => {
|
||||||
|
if (!editingTodo) return;
|
||||||
|
if (!confirm('정말로 이 할일을 삭제하시겠습니까?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = await comms.deleteTodo(editingTodo.idx);
|
||||||
|
if (response.Success) {
|
||||||
|
setShowTodoEditModal(false);
|
||||||
|
setEditingTodo(null);
|
||||||
|
loadDashboardData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '할일 삭제에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('할일 삭제 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다.');
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTodoComplete = async () => {
|
||||||
|
if (!editingTodo) return;
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = await comms.updateTodo(
|
||||||
|
editingTodo.idx,
|
||||||
|
editingTodo.title,
|
||||||
|
editingTodo.remark,
|
||||||
|
editingTodo.expire,
|
||||||
|
editingTodo.seqno,
|
||||||
|
editingTodo.flag,
|
||||||
|
editingTodo.request,
|
||||||
|
'5' // 완료 상태
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.Success) {
|
||||||
|
setShowTodoEditModal(false);
|
||||||
|
setEditingTodo(null);
|
||||||
|
loadDashboardData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '할일 완료 처리에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('할일 완료 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다.');
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNoteSave = async (formData: {
|
||||||
|
pdate: string;
|
||||||
|
title: string;
|
||||||
|
uid: string;
|
||||||
|
description: string;
|
||||||
|
share: boolean;
|
||||||
|
guid: string;
|
||||||
|
}) => {
|
||||||
|
if (!formData.pdate) {
|
||||||
|
alert('날짜를 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!formData.title.trim()) {
|
||||||
|
alert('제목을 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = editingNote
|
||||||
|
? await comms.editNote(
|
||||||
|
editingNote.idx,
|
||||||
|
formData.pdate,
|
||||||
|
formData.title,
|
||||||
|
formData.uid,
|
||||||
|
formData.description,
|
||||||
|
'',
|
||||||
|
formData.share,
|
||||||
|
formData.guid
|
||||||
|
)
|
||||||
|
: await comms.addNote(
|
||||||
|
formData.pdate,
|
||||||
|
formData.title,
|
||||||
|
formData.uid,
|
||||||
|
formData.description,
|
||||||
|
'',
|
||||||
|
formData.share,
|
||||||
|
formData.guid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.Success) {
|
||||||
|
setShowNoteEditModal(false);
|
||||||
|
setShowNoteAddModal(false);
|
||||||
|
loadDashboardData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '저장에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('저장 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다: ' + (error instanceof Error ? error.message : String(error)));
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gap-6 animate-fade-in">
|
||||||
|
{/* 메인 컨텐츠 */}
|
||||||
|
<div className="flex-1 space-y-6">
|
||||||
|
{/* 헤더 */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-2xl font-bold text-white">오늘의 현황</h2>
|
||||||
|
<button
|
||||||
|
onClick={handleRefresh}
|
||||||
|
disabled={refreshing}
|
||||||
|
className="flex items-center space-x-2 px-4 py-2 glass-effect rounded-lg text-white/70 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin' : ''}`} />
|
||||||
|
<span>새로고침</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 통계 카드 */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
<StatCard
|
<StatCard
|
||||||
title="구매요청 (NR)"
|
title="구매요청 (NR)"
|
||||||
value={purchaseNR}
|
value={purchaseNR}
|
||||||
@@ -244,21 +523,31 @@ export function Dashboard() {
|
|||||||
color="text-cyan-400"
|
color="text-cyan-400"
|
||||||
onClick={() => navigate('/jobreport')}
|
onClick={() => navigate('/jobreport')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 급한 할일 목록 */}
|
{/* 할일 목록 */}
|
||||||
<div className="glass-effect rounded-2xl overflow-hidden">
|
<div className="glass-effect rounded-2xl overflow-hidden">
|
||||||
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
|
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
|
||||||
<h3 className="text-lg font-semibold text-white flex items-center">
|
<h3 className="text-lg font-semibold text-white flex items-center">
|
||||||
<AlertTriangle className="w-5 h-5 mr-2 text-warning-400" />
|
<AlertTriangle className="w-5 h-5 mr-2 text-warning-400" />
|
||||||
급한 할일
|
할일
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<div className="flex items-center gap-2">
|
||||||
onClick={() => navigate('/todo')}
|
<button
|
||||||
className="text-sm text-primary-400 hover:text-primary-300 transition-colors"
|
onClick={handleTodoAdd}
|
||||||
>
|
className="p-1.5 rounded-lg bg-primary-500/20 text-primary-400 hover:bg-primary-500/30 transition-colors"
|
||||||
전체보기
|
title="할일 추가"
|
||||||
</button>
|
>
|
||||||
|
<Plus className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => navigate('/todo')}
|
||||||
|
className="p-1.5 rounded-lg bg-white/10 text-white/70 hover:bg-white/20 transition-colors"
|
||||||
|
title="전체보기"
|
||||||
|
>
|
||||||
|
<List className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="divide-y divide-white/10">
|
<div className="divide-y divide-white/10">
|
||||||
@@ -267,7 +556,7 @@ export function Dashboard() {
|
|||||||
<div
|
<div
|
||||||
key={todo.idx}
|
key={todo.idx}
|
||||||
className="px-6 py-4 hover:bg-white/5 transition-colors cursor-pointer"
|
className="px-6 py-4 hover:bg-white/5 transition-colors cursor-pointer"
|
||||||
onClick={() => navigate('/todo')}
|
onClick={() => handleTodoEdit(todo)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
@@ -302,25 +591,436 @@ export function Dashboard() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="px-6 py-8 text-center text-white/50">
|
<div className="px-6 py-8 text-center text-white/50">
|
||||||
<CheckCircle className="w-12 h-12 mx-auto mb-3 text-success-400/50" />
|
<CheckCircle className="w-12 h-12 mx-auto mb-3 text-success-400/50" />
|
||||||
<p>급한 할일이 없습니다</p>
|
<p>할일이 없습니다</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* NR 모달 */}
|
||||||
|
{showNRModal && (
|
||||||
|
<Modal title="구매요청 (NR) 목록" onClose={() => setShowNRModal(false)}>
|
||||||
|
<PurchaseTable data={purchaseNRList} />
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* CR 모달 */}
|
||||||
|
{showCRModal && (
|
||||||
|
<Modal title="구매요청 (CR) 목록" onClose={() => setShowCRModal(false)}>
|
||||||
|
<PurchaseTable data={purchaseCRList} />
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 메모 보기 모달 */}
|
||||||
|
<NoteViewModal
|
||||||
|
isOpen={showNoteModal}
|
||||||
|
note={selectedNote}
|
||||||
|
onClose={() => setShowNoteModal(false)}
|
||||||
|
onEdit={(note) => {
|
||||||
|
setShowNoteModal(false);
|
||||||
|
setEditingNote(note);
|
||||||
|
setShowNoteEditModal(true);
|
||||||
|
}}
|
||||||
|
onDelete={handleNoteDelete}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 메모 편집 모달 */}
|
||||||
|
<NoteEditModal
|
||||||
|
isOpen={showNoteEditModal}
|
||||||
|
editingItem={editingNote}
|
||||||
|
processing={processing}
|
||||||
|
onClose={() => setShowNoteEditModal(false)}
|
||||||
|
onSave={handleNoteSave}
|
||||||
|
initialEditMode={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 메모 추가 모달 */}
|
||||||
|
<NoteEditModal
|
||||||
|
isOpen={showNoteAddModal}
|
||||||
|
editingItem={null}
|
||||||
|
processing={processing}
|
||||||
|
onClose={() => setShowNoteAddModal(false)}
|
||||||
|
onSave={handleNoteSave}
|
||||||
|
initialEditMode={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 할일 추가 모달 */}
|
||||||
|
{showTodoAddModal && (
|
||||||
|
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50" onClick={() => setShowTodoAddModal(false)}>
|
||||||
|
<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"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{/* 헤더 */}
|
||||||
|
<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">
|
||||||
|
<Plus className="w-5 h-5 mr-2" />
|
||||||
|
할일 추가
|
||||||
|
</h2>
|
||||||
|
<button onClick={() => setShowTodoAddModal(false)} className="text-white/70 hover:text-white transition-colors">
|
||||||
|
<X className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 내용 */}
|
||||||
|
<div className="p-6 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={todoFormData.title}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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={todoFormData.expire}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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={todoFormData.remark}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-white/70 text-sm font-medium mb-2">요청자</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={todoFormData.request}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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: '대기' },
|
||||||
|
{ value: '1', label: '진행' },
|
||||||
|
{ value: '3', label: '보류' },
|
||||||
|
{ value: '2', label: '취소' },
|
||||||
|
{ value: '5', label: '완료' },
|
||||||
|
].map((option) => (
|
||||||
|
<button
|
||||||
|
key={option.value}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setTodoFormData(prev => ({ ...prev, status: option.value as TodoStatus }))}
|
||||||
|
className={`px-3 py-1 rounded-lg text-xs font-medium border transition-all ${
|
||||||
|
todoFormData.status === option.value
|
||||||
|
? getStatusClass(option.value)
|
||||||
|
: 'bg-white/10 text-white/50 border-white/20 hover:bg-white/20'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-white/70 text-sm font-medium mb-2">중요도</label>
|
||||||
|
<select
|
||||||
|
value={todoFormData.seqno}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, seqno: parseInt(e.target.value) as TodoPriority }))}
|
||||||
|
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 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={todoFormData.flag}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, flag: e.target.checked }))}
|
||||||
|
className="mr-2 text-primary-500 focus:ring-primary-400 focus:ring-offset-0 rounded"
|
||||||
|
/>
|
||||||
|
플래그 (상단 고정)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 푸터 */}
|
||||||
|
<div className="px-6 py-4 border-t border-white/10 flex justify-end space-x-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowTodoAddModal(false)}
|
||||||
|
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
취소
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleTodoSave}
|
||||||
|
disabled={processing}
|
||||||
|
className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg transition-colors flex items-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{processing ? (
|
||||||
|
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
추가
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 할일 수정 모달 */}
|
||||||
|
{showTodoEditModal && editingTodo && (
|
||||||
|
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50" onClick={() => setShowTodoEditModal(false)}>
|
||||||
|
<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"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{/* 헤더 */}
|
||||||
|
<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">
|
||||||
|
<Edit2 className="w-5 h-5 mr-2" />
|
||||||
|
할일 수정
|
||||||
|
</h2>
|
||||||
|
<button onClick={() => setShowTodoEditModal(false)} className="text-white/70 hover:text-white transition-colors">
|
||||||
|
<X className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 내용 */}
|
||||||
|
<div className="p-6 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={todoFormData.title}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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={todoFormData.expire}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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={todoFormData.remark}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-white/70 text-sm font-medium mb-2">요청자</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={todoFormData.request}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, 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: '대기' },
|
||||||
|
{ value: '1', label: '진행' },
|
||||||
|
{ value: '3', label: '보류' },
|
||||||
|
{ value: '2', label: '취소' },
|
||||||
|
{ value: '5', label: '완료' },
|
||||||
|
].map((option) => (
|
||||||
|
<button
|
||||||
|
key={option.value}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setTodoFormData(prev => ({ ...prev, status: option.value as TodoStatus }))}
|
||||||
|
className={`px-3 py-1 rounded-lg text-xs font-medium border transition-all ${
|
||||||
|
todoFormData.status === option.value
|
||||||
|
? getStatusClass(option.value)
|
||||||
|
: 'bg-white/10 text-white/50 border-white/20 hover:bg-white/20'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-white/70 text-sm font-medium mb-2">중요도</label>
|
||||||
|
<select
|
||||||
|
value={todoFormData.seqno}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, seqno: parseInt(e.target.value) as TodoPriority }))}
|
||||||
|
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 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={todoFormData.flag}
|
||||||
|
onChange={(e) => setTodoFormData(prev => ({ ...prev, flag: e.target.checked }))}
|
||||||
|
className="mr-2 text-primary-500 focus:ring-primary-400 focus:ring-offset-0 rounded"
|
||||||
|
/>
|
||||||
|
플래그 (상단 고정)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 푸터 */}
|
||||||
|
<div className="px-6 py-4 border-t border-white/10 flex justify-between">
|
||||||
|
{/* 왼쪽: 삭제 버튼 */}
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleTodoDelete}
|
||||||
|
disabled={processing}
|
||||||
|
className="bg-danger-500 hover:bg-danger-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
|
삭제
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 오른쪽: 취소, 완료, 수정 버튼 */}
|
||||||
|
<div className="flex space-x-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowTodoEditModal(false)}
|
||||||
|
className="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
취소
|
||||||
|
</button>
|
||||||
|
{editingTodo.status !== '5' && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleTodoComplete}
|
||||||
|
disabled={processing}
|
||||||
|
className="bg-success-500 hover:bg-success-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<CheckCircle className="w-4 h-4 mr-2" />
|
||||||
|
완료 처리
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleTodoUpdate}
|
||||||
|
disabled={processing}
|
||||||
|
className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg transition-colors flex items-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{processing ? (
|
||||||
|
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Edit2 className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
수정
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* NR 모달 */}
|
{/* 우측 사이드바 - 메모 리스트 */}
|
||||||
{showNRModal && (
|
<div className="w-60 space-y-4">
|
||||||
<Modal title="구매요청 (NR) 목록" onClose={() => setShowNRModal(false)}>
|
<div className="glass-effect rounded-2xl overflow-hidden">
|
||||||
<PurchaseTable data={purchaseNRList} />
|
<div className="px-4 py-3 border-b border-white/10 flex items-center justify-between">
|
||||||
</Modal>
|
<h3 className="text-base font-semibold text-white flex items-center">
|
||||||
)}
|
<FileText className="w-4 h-4 mr-2" />
|
||||||
|
최근 메모
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={handleNoteAdd}
|
||||||
|
className="p-1.5 rounded-lg bg-primary-500/20 text-primary-400 hover:bg-primary-500/30 transition-colors"
|
||||||
|
title="메모 추가"
|
||||||
|
>
|
||||||
|
<Plus className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => navigate('/note')}
|
||||||
|
className="p-1.5 rounded-lg bg-white/10 text-white/70 hover:bg-white/20 transition-colors"
|
||||||
|
title="전체보기"
|
||||||
|
>
|
||||||
|
<List className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* CR 모달 */}
|
<div className="divide-y divide-white/10 max-h-[calc(100vh-200px)] overflow-y-auto">
|
||||||
{showCRModal && (
|
{recentNotes.length > 0 ? (
|
||||||
<Modal title="구매요청 (CR) 목록" onClose={() => setShowCRModal(false)}>
|
recentNotes.map((note) => (
|
||||||
<PurchaseTable data={purchaseCRList} />
|
<div
|
||||||
</Modal>
|
key={note.idx}
|
||||||
)}
|
className="px-4 py-2.5 hover:bg-white/5 transition-colors cursor-pointer group"
|
||||||
|
onClick={() => handleNoteClick(note)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{note.share ? (
|
||||||
|
<Share2 className="w-3 h-3 text-green-400 flex-shrink-0" />
|
||||||
|
) : (
|
||||||
|
<Lock className="w-3 h-3 text-blue-400 flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
<p className="text-white text-sm truncate flex-1">
|
||||||
|
{(note.title || '제목 없음').length > 15 ? `${(note.title || '제목 없음').substring(0, 15)}...` : (note.title || '제목 없음')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="px-4 py-8 text-center text-white/50">
|
||||||
|
<FileText className="w-10 h-10 mx-auto mb-2 text-white/30" />
|
||||||
|
<p className="text-sm">등록된 메모가 없습니다</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,12 +117,12 @@ export function Jobreport() {
|
|||||||
initialize();
|
initialize();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 초기화 완료 후 조회 실행
|
// 초기화 완료 후 조회 실행 (최초 1회만)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialized && startDate && endDate && selectedUser) {
|
if (initialized && startDate && endDate && selectedUser) {
|
||||||
handleSearchAndLoadToday();
|
handleSearchAndLoadToday();
|
||||||
}
|
}
|
||||||
}, [initialized, startDate, endDate, selectedUser]);
|
}, [initialized]); // startDate, endDate, selectedUser 의존성 제거 (날짜 변경 시 자동 조회 방지)
|
||||||
|
|
||||||
// 검색 + 오늘 근무시간 로드 (순차 실행)
|
// 검색 + 오늘 근무시간 로드 (순차 실행)
|
||||||
const handleSearchAndLoadToday = async () => {
|
const handleSearchAndLoadToday = async () => {
|
||||||
@@ -373,6 +373,34 @@ export function Jobreport() {
|
|||||||
handleSearch();
|
handleSearch();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 빠른 날짜 선택 함수들
|
||||||
|
const setToday = () => {
|
||||||
|
const today = new Date();
|
||||||
|
setStartDate(formatDateLocal(today));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setThisMonth = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
|
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||||
|
setStartDate(formatDateLocal(startOfMonth));
|
||||||
|
setEndDate(formatDateLocal(endOfMonth));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setYesterday = () => {
|
||||||
|
const yesterday = new Date();
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
setStartDate(formatDateLocal(yesterday));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setLastMonth = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||||
|
const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0);
|
||||||
|
setStartDate(formatDateLocal(lastMonthStart));
|
||||||
|
setEndDate(formatDateLocal(lastMonthEnd));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 animate-fade-in">
|
<div className="space-y-6 animate-fade-in">
|
||||||
{/* 검색 필터 */}
|
{/* 검색 필터 */}
|
||||||
@@ -381,6 +409,38 @@ export function Jobreport() {
|
|||||||
{/* 좌측: 필터 영역 */}
|
{/* 좌측: 필터 영역 */}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
|
{/* 빠른 날짜 선택 버튼 */}
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<button
|
||||||
|
onClick={setToday}
|
||||||
|
className="h-8 bg-white/10 hover:bg-white/20 text-white text-xs px-3 rounded-lg transition-colors whitespace-nowrap"
|
||||||
|
title="오늘 날짜로 설정"
|
||||||
|
>
|
||||||
|
오늘
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={setYesterday}
|
||||||
|
className="h-8 bg-white/10 hover:bg-white/20 text-white text-xs px-3 rounded-lg transition-colors whitespace-nowrap"
|
||||||
|
title="어제 날짜로 설정"
|
||||||
|
>
|
||||||
|
어제
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={setThisMonth}
|
||||||
|
className="h-8 bg-white/10 hover:bg-white/20 text-white text-xs px-3 rounded-lg transition-colors whitespace-nowrap"
|
||||||
|
title="이번 달 1일부터 말일까지"
|
||||||
|
>
|
||||||
|
이번달
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={setLastMonth}
|
||||||
|
className="h-8 bg-white/10 hover:bg-white/20 text-white text-xs px-3 rounded-lg transition-colors whitespace-nowrap"
|
||||||
|
title="저번달 1일부터 말일까지"
|
||||||
|
>
|
||||||
|
저번달
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 필터 입력 영역: 2행 2열 */}
|
{/* 필터 입력 영역: 2행 2열 */}
|
||||||
<div className="grid grid-cols-2 gap-x-4 gap-y-3">
|
<div className="grid grid-cols-2 gap-x-4 gap-y-3">
|
||||||
{/* 1행: 시작일, 담당자 */}
|
{/* 1행: 시작일, 담당자 */}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
Calendar,
|
Calendar,
|
||||||
Search,
|
Search,
|
||||||
Trash2,
|
Trash2,
|
||||||
AlertTriangle,
|
|
||||||
Clock,
|
Clock,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
XCircle,
|
XCircle,
|
||||||
@@ -23,7 +22,7 @@ import { KuntaeEditModal, KuntaeFormData } from '@/components/kuntae/KuntaeEditM
|
|||||||
export function Kuntae() {
|
export function Kuntae() {
|
||||||
const [kuntaeList, setKuntaeList] = useState<KuntaeModel[]>([]);
|
const [kuntaeList, setKuntaeList] = useState<KuntaeModel[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [processing, setProcessing] = useState(false);
|
const [_processing, setProcessing] = useState(false);
|
||||||
|
|
||||||
// 검색 조건
|
// 검색 조건
|
||||||
const [startDate, setStartDate] = useState('');
|
const [startDate, setStartDate] = useState('');
|
||||||
@@ -441,7 +440,7 @@ export function Kuntae() {
|
|||||||
{item.contents || '-'}
|
{item.contents || '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-white text-sm">
|
<td className="px-4 py-3 text-white text-sm">
|
||||||
{item.UserName || item.uname || item.uid}
|
{item.UserName || item.uid}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-white/50 text-xs">
|
<td className="px-4 py-3 text-white/50 text-xs">
|
||||||
{item.extcate ? `${item.extcate}` : '-'}
|
{item.extcate ? `${item.extcate}` : '-'}
|
||||||
|
|||||||
288
Project/frontend/src/pages/MailList.tsx
Normal file
288
Project/frontend/src/pages/MailList.tsx
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { Mail, Search, RefreshCw, Calendar } from 'lucide-react';
|
||||||
|
import { comms } from '@/communication';
|
||||||
|
import { MailItem, UserInfo } from '@/types';
|
||||||
|
|
||||||
|
export function MailList() {
|
||||||
|
const [mailList, setMailList] = useState<MailItem[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [startDate, setStartDate] = useState('');
|
||||||
|
const [endDate, setEndDate] = useState('');
|
||||||
|
const [searchKey, setSearchKey] = useState('');
|
||||||
|
const [selectedItem, setSelectedItem] = useState<MailItem | null>(null);
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [currentUser, setCurrentUser] = useState<UserInfo | null>(null);
|
||||||
|
|
||||||
|
const formatDateLocal = (date: Date) => {
|
||||||
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 로그인 정보 로드
|
||||||
|
const loadLoginInfo = async () => {
|
||||||
|
try {
|
||||||
|
const response = await comms.checkLoginStatus();
|
||||||
|
if (response.Success && response.IsLoggedIn && response.User) {
|
||||||
|
setCurrentUser(response.User);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('로그인 정보 로드 오류:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadLoginInfo();
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const tenDaysAgo = new Date();
|
||||||
|
tenDaysAgo.setDate(now.getDate() - 10);
|
||||||
|
|
||||||
|
const start = formatDateLocal(tenDaysAgo);
|
||||||
|
const end = formatDateLocal(now);
|
||||||
|
|
||||||
|
setStartDate(start);
|
||||||
|
setEndDate(end);
|
||||||
|
|
||||||
|
// 날짜 설정 후 바로 데이터 로드
|
||||||
|
setTimeout(() => {
|
||||||
|
loadDataWithDates(start, end);
|
||||||
|
}, 100);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadDataWithDates = async (start: string, end: string) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
console.log('메일내역 조회:', { start, end, searchKey });
|
||||||
|
const response = await comms.getMailList(start, end, searchKey);
|
||||||
|
console.log('메일내역 응답:', response);
|
||||||
|
if (response.Success && response.Data) {
|
||||||
|
setMailList(response.Data);
|
||||||
|
} else {
|
||||||
|
console.warn('메일내역 없음:', response.Message);
|
||||||
|
setMailList([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('메일내역 로드 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
if (!startDate || !endDate) return;
|
||||||
|
await loadDataWithDates(startDate, endDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
if (new Date(startDate) > new Date(endDate)) {
|
||||||
|
alert('시작일은 종료일보다 늦을 수 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = (item: MailItem) => {
|
||||||
|
// 레벨 9 이상(개발자)만 상세보기 가능
|
||||||
|
if (!currentUser || currentUser.Level < 9) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelectedItem(item);
|
||||||
|
setShowModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (dateStr: string | null) => {
|
||||||
|
if (!dateStr) return '-';
|
||||||
|
try {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const yy = String(date.getFullYear()).slice(-2);
|
||||||
|
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hh = String(date.getHours()).padStart(2, '0');
|
||||||
|
const mi = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
return `${yy}.${mm}.${dd} ${hh}:${mi}`;
|
||||||
|
} catch {
|
||||||
|
return dateStr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6 animate-fade-in">
|
||||||
|
{/* 검색 필터 */}
|
||||||
|
<div className="glass-effect rounded-2xl p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<label className="text-white/70 text-sm font-medium whitespace-nowrap">기간</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={startDate}
|
||||||
|
onChange={(e) => setStartDate(e.target.value)}
|
||||||
|
className="w-36 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
<span className="text-white/70">~</span>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={endDate}
|
||||||
|
onChange={(e) => setEndDate(e.target.value)}
|
||||||
|
className="w-36 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 flex-1">
|
||||||
|
<label className="text-white/70 text-sm font-medium whitespace-nowrap">검색어</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={searchKey}
|
||||||
|
onChange={(e) => setSearchKey(e.target.value)}
|
||||||
|
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||||
|
placeholder="제목, 발신자, 수신자 등"
|
||||||
|
className="flex-1 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSearch}
|
||||||
|
disabled={loading}
|
||||||
|
className="h-10 bg-primary-500 hover:bg-primary-600 text-white px-6 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Search className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
조회
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 메일 내역 목록 */}
|
||||||
|
<div className="glass-effect rounded-2xl overflow-hidden">
|
||||||
|
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-semibold text-white flex items-center">
|
||||||
|
<Mail className="w-5 h-5 mr-2" />
|
||||||
|
메일 발신 내역
|
||||||
|
</h3>
|
||||||
|
<span className="text-white/60 text-sm">{mailList.length}건</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-white/10 max-h-[calc(100vh-300px)] overflow-y-auto">
|
||||||
|
{loading ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<RefreshCw className="w-5 h-5 mr-2 animate-spin text-white/50" />
|
||||||
|
<span className="text-white/50">데이터를 불러오는 중...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : mailList.length === 0 ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<Mail className="w-12 h-12 mx-auto mb-3 text-white/30" />
|
||||||
|
<p className="text-white/50">조회된 데이터가 없습니다.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
mailList.map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.idx}
|
||||||
|
className={`px-6 py-4 transition-colors ${currentUser && currentUser.Level >= 9 ? 'hover:bg-white/5 cursor-pointer' : 'cursor-default'}`}
|
||||||
|
onClick={() => handleRowClick(item)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
{item.cate && (
|
||||||
|
<span className="px-2 py-0.5 bg-primary-500/20 text-primary-400 text-xs rounded">
|
||||||
|
{item.cate}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{item.project && (
|
||||||
|
<span className="px-2 py-0.5 bg-white/10 text-white/70 text-xs rounded">
|
||||||
|
{item.project}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h4 className="text-white font-medium mb-1">{item.subject}</h4>
|
||||||
|
<div className="flex items-center gap-4 text-white/60 text-sm">
|
||||||
|
<div>발신: {item.fromlist}</div>
|
||||||
|
<div>수신: {item.tolist}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-end gap-1 flex-shrink-0">
|
||||||
|
<div className="flex items-center text-white/60 text-xs">
|
||||||
|
<Calendar className="w-3 h-3 mr-1" />
|
||||||
|
{formatDate(item.wdate)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 상세 모달 */}
|
||||||
|
{showModal && selectedItem && (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||||||
|
<div className="bg-gray-900 rounded-2xl shadow-2xl w-full max-w-4xl max-h-[90vh] overflow-hidden border border-white/10">
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{selectedItem.cate && (
|
||||||
|
<span className="px-2 py-1 bg-primary-500/20 text-primary-400 text-sm rounded">
|
||||||
|
{selectedItem.cate}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<h2 className="text-xl font-bold text-white ml-2">{selectedItem.subject}</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="text-white/50 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<span className="text-2xl">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6 py-4 border-b border-white/10 space-y-2 text-sm">
|
||||||
|
<div className="flex items-start gap-2 text-white/70">
|
||||||
|
<span className="font-medium w-16">발신:</span>
|
||||||
|
<span className="text-white">{selectedItem.fromlist}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-2 text-white/70">
|
||||||
|
<span className="font-medium w-16">수신:</span>
|
||||||
|
<span className="text-white">{selectedItem.tolist}</span>
|
||||||
|
</div>
|
||||||
|
{selectedItem.cclist && (
|
||||||
|
<div className="flex items-start gap-2 text-white/70">
|
||||||
|
<span className="font-medium w-16">참조:</span>
|
||||||
|
<span className="text-white">{selectedItem.cclist}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedItem.bcclist && (
|
||||||
|
<div className="flex items-start gap-2 text-white/70">
|
||||||
|
<span className="font-medium w-16">숨은참조:</span>
|
||||||
|
<span className="text-white">{selectedItem.bcclist}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-2 text-white/60">
|
||||||
|
<Calendar className="w-4 h-4" />
|
||||||
|
{formatDate(selectedItem.wdate)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-y-auto max-h-[calc(90vh-280px)] p-6">
|
||||||
|
<div
|
||||||
|
className="prose prose-invert max-w-none"
|
||||||
|
dangerouslySetInnerHTML={{ __html: selectedItem.htmlbody }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end px-6 py-4 border-t border-white/10 bg-white/5">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="px-4 py-2 rounded-lg bg-white/10 hover:bg-white/20 text-white transition-colors"
|
||||||
|
>
|
||||||
|
닫기
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
452
Project/frontend/src/pages/Note.tsx
Normal file
452
Project/frontend/src/pages/Note.tsx
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import {
|
||||||
|
FileText,
|
||||||
|
Search,
|
||||||
|
RefreshCw,
|
||||||
|
Plus,
|
||||||
|
Edit,
|
||||||
|
Trash2,
|
||||||
|
Share2,
|
||||||
|
Lock,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import { comms } from '@/communication';
|
||||||
|
import { NoteItem } from '@/types';
|
||||||
|
import { NoteEditModal } from '@/components/note/NoteEditModal';
|
||||||
|
import { NoteViewModal } from '@/components/note/NoteViewModal';
|
||||||
|
|
||||||
|
export function Note() {
|
||||||
|
const [noteList, setNoteList] = useState<NoteItem[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [processing, setProcessing] = useState(false);
|
||||||
|
|
||||||
|
// 검색 조건
|
||||||
|
const [startDate, setStartDate] = useState('');
|
||||||
|
const [endDate, setEndDate] = useState('');
|
||||||
|
const [searchKey, setSearchKey] = useState('');
|
||||||
|
|
||||||
|
// 모달 상태
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [showViewModal, setShowViewModal] = useState(false);
|
||||||
|
const [editingItem, setEditingItem] = useState<NoteItem | null>(null);
|
||||||
|
const [selectedNote, setSelectedNote] = useState<NoteItem | null>(null);
|
||||||
|
|
||||||
|
// 페이징 상태
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const pageSize = 10;
|
||||||
|
|
||||||
|
// 날짜 포맷 헬퍼 함수 (로컬 시간 기준)
|
||||||
|
const formatDateLocal = (date: Date) => {
|
||||||
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 초기화 완료 플래그
|
||||||
|
const [initialized, setInitialized] = useState(false);
|
||||||
|
|
||||||
|
// 날짜 초기화
|
||||||
|
useEffect(() => {
|
||||||
|
const now = new Date();
|
||||||
|
// 2000년부터 현재까지 데이터 조회
|
||||||
|
const startOfPeriod = new Date(2000, 0, 1);
|
||||||
|
|
||||||
|
const sd = formatDateLocal(startOfPeriod);
|
||||||
|
const ed = formatDateLocal(now);
|
||||||
|
|
||||||
|
setStartDate(sd);
|
||||||
|
setEndDate(ed);
|
||||||
|
|
||||||
|
// 초기화 완료 표시
|
||||||
|
setInitialized(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 초기화 완료 후 조회 실행 (최초 1회만)
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialized && startDate && endDate) {
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
}, [initialized]);
|
||||||
|
|
||||||
|
// 데이터 로드
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
if (!startDate || !endDate) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
console.log('메모장 조회 요청:', { startDate, endDate });
|
||||||
|
const response = await comms.getNoteList(startDate, endDate, '');
|
||||||
|
console.log('메모장 조회 응답:', response);
|
||||||
|
if (response.Success && response.Data) {
|
||||||
|
console.log('메모장 데이터 개수:', response.Data.length);
|
||||||
|
setNoteList(response.Data);
|
||||||
|
} else {
|
||||||
|
console.log('메모장 조회 실패 또는 데이터 없음:', response);
|
||||||
|
setNoteList([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('메모장 목록 로드 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [startDate, endDate]);
|
||||||
|
|
||||||
|
// 검색
|
||||||
|
const handleSearch = async () => {
|
||||||
|
if (new Date(startDate) > new Date(endDate)) {
|
||||||
|
alert('시작일은 종료일보다 늦을 수 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 새 메모 추가 모달
|
||||||
|
const openAddModal = () => {
|
||||||
|
setEditingItem(null);
|
||||||
|
setShowModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 메모 클릭 (보기 모달)
|
||||||
|
const handleNoteClick = async (item: NoteItem) => {
|
||||||
|
try {
|
||||||
|
const response = await comms.getNoteDetail(item.idx);
|
||||||
|
if (response.Success && response.Data) {
|
||||||
|
setSelectedNote(response.Data);
|
||||||
|
setShowViewModal(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('메모 조회 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 편집 모달
|
||||||
|
const openEditModal = async (item: NoteItem) => {
|
||||||
|
try {
|
||||||
|
const response = await comms.getNoteDetail(item.idx);
|
||||||
|
if (response.Success && response.Data) {
|
||||||
|
setEditingItem(response.Data);
|
||||||
|
setShowModal(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('메모 조회 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 저장
|
||||||
|
const handleSave = async (formData: {
|
||||||
|
pdate: string;
|
||||||
|
title: string;
|
||||||
|
uid: string;
|
||||||
|
description: string;
|
||||||
|
share: boolean;
|
||||||
|
guid: string;
|
||||||
|
}) => {
|
||||||
|
if (!formData.pdate) {
|
||||||
|
alert('날짜를 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!formData.title.trim()) {
|
||||||
|
alert('제목을 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
let response;
|
||||||
|
if (editingItem) {
|
||||||
|
response = await comms.editNote(
|
||||||
|
editingItem.idx,
|
||||||
|
formData.pdate,
|
||||||
|
formData.title,
|
||||||
|
formData.uid,
|
||||||
|
formData.description,
|
||||||
|
'',
|
||||||
|
formData.share,
|
||||||
|
formData.guid
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
response = await comms.addNote(
|
||||||
|
formData.pdate,
|
||||||
|
formData.title,
|
||||||
|
formData.uid,
|
||||||
|
formData.description,
|
||||||
|
'',
|
||||||
|
formData.share,
|
||||||
|
formData.guid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.Success) {
|
||||||
|
setShowModal(false);
|
||||||
|
loadData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '저장에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('저장 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다: ' + (error instanceof Error ? error.message : String(error)));
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 삭제
|
||||||
|
const handleDelete = async (idx: number) => {
|
||||||
|
if (!confirm('정말로 이 메모를 삭제하시겠습니까?')) return;
|
||||||
|
|
||||||
|
setProcessing(true);
|
||||||
|
try {
|
||||||
|
const response = await comms.deleteNote(idx);
|
||||||
|
if (response.Success) {
|
||||||
|
alert('삭제되었습니다.');
|
||||||
|
loadData();
|
||||||
|
} else {
|
||||||
|
alert(response.Message || '삭제에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('삭제 오류:', error);
|
||||||
|
alert('서버 연결에 실패했습니다.');
|
||||||
|
} finally {
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 날짜 포맷 (YY.MM.DD)
|
||||||
|
const formatDate = (dateStr: string | null) => {
|
||||||
|
if (!dateStr) return '-';
|
||||||
|
try {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const yy = String(date.getFullYear()).slice(-2);
|
||||||
|
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${yy}.${mm}.${dd}`;
|
||||||
|
} catch {
|
||||||
|
return dateStr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 필터링된 목록 (검색어 적용)
|
||||||
|
const filteredList = noteList.filter(item => {
|
||||||
|
if (!searchKey.trim()) return true;
|
||||||
|
const search = searchKey.toLowerCase();
|
||||||
|
return (
|
||||||
|
item.title?.toLowerCase().includes(search) ||
|
||||||
|
item.description?.toLowerCase().includes(search) ||
|
||||||
|
item.uid?.toLowerCase().includes(search)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 페이징 계산
|
||||||
|
const totalPages = Math.ceil(filteredList.length / pageSize);
|
||||||
|
const paginatedList = filteredList.slice(
|
||||||
|
(currentPage - 1) * pageSize,
|
||||||
|
currentPage * pageSize
|
||||||
|
);
|
||||||
|
|
||||||
|
// 검색 시 페이지 초기화
|
||||||
|
const handleSearchWithReset = () => {
|
||||||
|
setCurrentPage(1);
|
||||||
|
handleSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6 animate-fade-in">
|
||||||
|
{/* 검색 필터 */}
|
||||||
|
<div className="glass-effect rounded-2xl p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<label className="text-white/70 text-sm font-medium whitespace-nowrap">기간</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={startDate}
|
||||||
|
onChange={(e) => setStartDate(e.target.value)}
|
||||||
|
className="w-36 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
<span className="text-white/70">~</span>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={endDate}
|
||||||
|
onChange={(e) => setEndDate(e.target.value)}
|
||||||
|
className="w-36 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<label className="text-white/70 text-sm font-medium whitespace-nowrap">검색어</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={searchKey}
|
||||||
|
onChange={(e) => setSearchKey(e.target.value)}
|
||||||
|
onKeyDown={(e) => e.key === 'Enter' && handleSearchWithReset()}
|
||||||
|
placeholder="제목, 내용, 작성자 등"
|
||||||
|
className="w-60 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSearchWithReset}
|
||||||
|
disabled={loading}
|
||||||
|
className="h-10 bg-primary-500 hover:bg-primary-600 text-white px-6 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Search className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
조회
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={openAddModal}
|
||||||
|
className="h-10 bg-success-500 hover:bg-success-600 text-white px-6 rounded-lg transition-colors flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
새 메모
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 메모 리스트 */}
|
||||||
|
<div className="glass-effect rounded-2xl overflow-hidden">
|
||||||
|
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-semibold text-white flex items-center">
|
||||||
|
<FileText className="w-5 h-5 mr-2" />
|
||||||
|
메모장 목록
|
||||||
|
</h3>
|
||||||
|
<span className="text-white/60 text-sm">{filteredList.length}건</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-white/10 max-h-[calc(100vh-300px)] overflow-y-auto">
|
||||||
|
{loading ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<RefreshCw className="w-5 h-5 mr-2 animate-spin text-white/50" />
|
||||||
|
<span className="text-white/50">데이터를 불러오는 중...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : filteredList.length === 0 ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<FileText className="w-12 h-12 mx-auto mb-3 text-white/30" />
|
||||||
|
<p className="text-white/50">조회된 데이터가 없습니다.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
paginatedList.map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.idx}
|
||||||
|
className="px-6 py-3 hover:bg-white/5 transition-colors cursor-pointer group"
|
||||||
|
onClick={() => handleNoteClick(item)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||||
|
{item.share ? (
|
||||||
|
<Share2 className="w-4 h-4 text-green-400 flex-shrink-0" />
|
||||||
|
) : (
|
||||||
|
<Lock className="w-4 h-4 text-blue-400 flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
<p className="text-white text-sm font-medium truncate flex-1">
|
||||||
|
{(item.title || '제목 없음').length > 15 ? `${(item.title || '제목 없음').substring(0, 15)}...` : (item.title || '제목 없음')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 flex-shrink-0">
|
||||||
|
<span className="text-white/60 text-xs">{item.uid || '-'}</span>
|
||||||
|
<span className="text-white/60 text-xs">{formatDate(item.pdate)}</span>
|
||||||
|
<span className="text-white/50 text-xs">조회 {item.viewcount || 0}</span>
|
||||||
|
<div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openEditModal(item);
|
||||||
|
}}
|
||||||
|
className="text-white/40 hover:text-primary-400 transition-colors"
|
||||||
|
title="편집"
|
||||||
|
>
|
||||||
|
<Edit className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDelete(item.idx);
|
||||||
|
}}
|
||||||
|
className="text-white/40 hover:text-red-400 transition-colors"
|
||||||
|
title="삭제"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 페이징 */}
|
||||||
|
{totalPages > 1 && (
|
||||||
|
<div className="px-6 py-4 border-t border-white/10 flex items-center justify-between">
|
||||||
|
<div className="text-white/50 text-sm">
|
||||||
|
총 {filteredList.length}건 중 {(currentPage - 1) * pageSize + 1}-{Math.min(currentPage * pageSize, filteredList.length)}건
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentPage(1)}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
className="px-3 py-1 rounded bg-white/10 text-white/70 hover:bg-white/20 disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||||
|
>
|
||||||
|
«
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
className="px-3 py-1 rounded bg-white/10 text-white/70 hover:bg-white/20 disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||||
|
>
|
||||||
|
‹
|
||||||
|
</button>
|
||||||
|
<span className="text-white/70 px-3">
|
||||||
|
{currentPage} / {totalPages}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
className="px-3 py-1 rounded bg-white/10 text-white/70 hover:bg-white/20 disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||||
|
>
|
||||||
|
›
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentPage(totalPages)}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
className="px-3 py-1 rounded bg-white/10 text-white/70 hover:bg-white/20 disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||||
|
>
|
||||||
|
»
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 메모 보기 모달 */}
|
||||||
|
<NoteViewModal
|
||||||
|
isOpen={showViewModal}
|
||||||
|
note={selectedNote}
|
||||||
|
onClose={() => setShowViewModal(false)}
|
||||||
|
onEdit={(note) => {
|
||||||
|
setShowViewModal(false);
|
||||||
|
setEditingItem(note);
|
||||||
|
setShowModal(true);
|
||||||
|
}}
|
||||||
|
onDelete={(note) => {
|
||||||
|
setShowViewModal(false);
|
||||||
|
handleDelete(note.idx);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 추가/수정 모달 */}
|
||||||
|
<NoteEditModal
|
||||||
|
isOpen={showModal}
|
||||||
|
editingItem={editingItem}
|
||||||
|
processing={processing}
|
||||||
|
onClose={() => setShowModal(false)}
|
||||||
|
onSave={handleSave}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
219
Project/frontend/src/pages/PatchList.tsx
Normal file
219
Project/frontend/src/pages/PatchList.tsx
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { FileText, Search, RefreshCw, Calendar, User } from 'lucide-react';
|
||||||
|
import { comms } from '@/communication';
|
||||||
|
import { BoardItem } from '@/types';
|
||||||
|
|
||||||
|
export function PatchList() {
|
||||||
|
const [boardList, setBoardList] = useState<BoardItem[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [searchKey, setSearchKey] = useState('');
|
||||||
|
const [selectedItem, setSelectedItem] = useState<BoardItem | null>(null);
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
console.log('패치내역 조회:', { bidx: 5, searchKey });
|
||||||
|
const response = await comms.getBoardList(5, searchKey); // bidx=5: 패치내역
|
||||||
|
console.log('패치내역 응답:', response);
|
||||||
|
if (response.Success && response.Data) {
|
||||||
|
setBoardList(response.Data);
|
||||||
|
} else {
|
||||||
|
console.warn('패치내역 없음:', response.Message);
|
||||||
|
setBoardList([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('패치내역 로드 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = async (item: BoardItem) => {
|
||||||
|
try {
|
||||||
|
const response = await comms.getBoardDetail(item.idx);
|
||||||
|
if (response.Success && response.Data) {
|
||||||
|
setSelectedItem(response.Data);
|
||||||
|
setShowModal(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('상세 조회 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (dateStr: string | null) => {
|
||||||
|
if (!dateStr) return '-';
|
||||||
|
try {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const yy = String(date.getFullYear()).slice(-2);
|
||||||
|
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${yy}.${mm}.${dd}`;
|
||||||
|
} catch {
|
||||||
|
return dateStr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6 animate-fade-in">
|
||||||
|
{/* 검색 필터 */}
|
||||||
|
<div className="glass-effect rounded-2xl p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex items-center gap-2 flex-1">
|
||||||
|
<label className="text-white/70 text-sm font-medium whitespace-nowrap">검색어</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={searchKey}
|
||||||
|
onChange={(e) => setSearchKey(e.target.value)}
|
||||||
|
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||||
|
placeholder="제목, 내용, 작성자 등"
|
||||||
|
className="flex-1 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSearch}
|
||||||
|
disabled={loading}
|
||||||
|
className="h-10 bg-primary-500 hover:bg-primary-600 text-white px-6 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Search className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
조회
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 패치내역 목록 */}
|
||||||
|
<div className="glass-effect rounded-2xl overflow-hidden">
|
||||||
|
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-semibold text-white flex items-center">
|
||||||
|
<FileText className="w-5 h-5 mr-2" />
|
||||||
|
패치 내역
|
||||||
|
</h3>
|
||||||
|
<span className="text-white/60 text-sm">{boardList.length}건</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-white/10 max-h-[calc(100vh-300px)] overflow-y-auto">
|
||||||
|
{loading ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<RefreshCw className="w-5 h-5 mr-2 animate-spin text-white/50" />
|
||||||
|
<span className="text-white/50">데이터를 불러오는 중...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : boardList.length === 0 ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<FileText className="w-12 h-12 mx-auto mb-3 text-white/30" />
|
||||||
|
<p className="text-white/50">조회된 데이터가 없습니다.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
boardList.map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.idx}
|
||||||
|
className="px-6 py-4 hover:bg-white/5 transition-colors cursor-pointer"
|
||||||
|
onClick={() => handleRowClick(item)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
{item.header && (
|
||||||
|
<span className="px-2 py-0.5 bg-primary-500/20 text-primary-400 text-xs rounded">
|
||||||
|
{item.header}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{item.cate && (
|
||||||
|
<span className="px-2 py-0.5 bg-white/10 text-white/70 text-xs rounded">
|
||||||
|
{item.cate}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h4 className="text-white font-medium mb-1">{item.title}</h4>
|
||||||
|
<p className="text-white/60 text-sm line-clamp-1">{item.contents}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-end gap-1 flex-shrink-0">
|
||||||
|
<div className="flex items-center text-white/60 text-xs">
|
||||||
|
<User className="w-3 h-3 mr-1" />
|
||||||
|
{item.wuid_name || item.wuid}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-white/60 text-xs">
|
||||||
|
<Calendar className="w-3 h-3 mr-1" />
|
||||||
|
{formatDate(item.wdate)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 상세 모달 */}
|
||||||
|
{showModal && selectedItem && (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||||||
|
<div className="bg-gray-900 rounded-2xl shadow-2xl w-full max-w-4xl max-h-[90vh] overflow-hidden border border-white/10">
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{selectedItem.header && (
|
||||||
|
<span className="px-2 py-1 bg-primary-500/20 text-primary-400 text-sm rounded">
|
||||||
|
{selectedItem.header}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{selectedItem.cate && (
|
||||||
|
<span className="px-2 py-1 bg-white/10 text-white/70 text-sm rounded">
|
||||||
|
{selectedItem.cate}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<h2 className="text-xl font-bold text-white ml-2">{selectedItem.title}</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="text-white/50 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<span className="text-2xl">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6 py-4 border-b border-white/10 flex items-center gap-4 text-sm text-white/60">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<User className="w-4 h-4 mr-1" />
|
||||||
|
{selectedItem.wuid_name || selectedItem.wuid}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Calendar className="w-4 h-4 mr-1" />
|
||||||
|
{formatDate(selectedItem.wdate)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-y-auto max-h-[calc(90vh-180px)] p-6">
|
||||||
|
<div className="prose prose-invert max-w-none">
|
||||||
|
<div className="text-white whitespace-pre-wrap">{selectedItem.contents}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end px-6 py-4 border-t border-white/10 bg-white/5">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="px-4 py-2 rounded-lg bg-white/10 hover:bg-white/20 text-white transition-colors"
|
||||||
|
>
|
||||||
|
닫기
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,3 +12,4 @@ export { MonthlyWorkPage } from './MonthlyWork';
|
|||||||
export { MailFormPage } from './MailForm';
|
export { MailFormPage } from './MailForm';
|
||||||
export { UserGroupPage } from './UserGroup';
|
export { UserGroupPage } from './UserGroup';
|
||||||
export { default as UserAuthPage } from './UserAuth';
|
export { default as UserAuthPage } from './UserAuth';
|
||||||
|
export { Note } from './Note';
|
||||||
|
|||||||
@@ -448,6 +448,20 @@ export interface MachineBridgeInterface {
|
|||||||
Project_GetList(statusFilter: string, category: string, process: string, userFilter: string, yearStart: string, yearEnd: string, dateType: string): Promise<string>;
|
Project_GetList(statusFilter: string, category: string, process: string, userFilter: string, yearStart: string, yearEnd: string, dateType: string): Promise<string>;
|
||||||
Project_GetHistory(projectIdx: number): Promise<string>;
|
Project_GetHistory(projectIdx: number): Promise<string>;
|
||||||
Project_GetDailyMemo(projectIdx: number): Promise<string>;
|
Project_GetDailyMemo(projectIdx: number): Promise<string>;
|
||||||
|
|
||||||
|
// Note API (메모장)
|
||||||
|
Note_GetList(startDate: string, endDate: string, uid: string): Promise<string>;
|
||||||
|
Note_GetDetail(idx: number): Promise<string>;
|
||||||
|
Note_Add(pdate: string, title: string, uid: string, description: string, description2: string, share: boolean, guid: string): Promise<string>;
|
||||||
|
Note_Edit(idx: number, pdate: string, title: string, uid: string, description: string, description2: string, share: boolean, guid: string): Promise<string>;
|
||||||
|
Note_Delete(idx: number): Promise<string>;
|
||||||
|
|
||||||
|
// Board API (게시판 - 패치내역 등)
|
||||||
|
Board_GetList(bidx: number, searchKey: string): Promise<string>;
|
||||||
|
Board_GetDetail(idx: number): Promise<string>;
|
||||||
|
|
||||||
|
// Mail API (메일 발신 내역)
|
||||||
|
Mail_GetList(startDate: string, endDate: string, searchKey: string): Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용자 권한 정보 타입
|
// 사용자 권한 정보 타입
|
||||||
@@ -512,6 +526,23 @@ export interface HolidayItem {
|
|||||||
wdate?: string;
|
wdate?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 메모장 관련 타입 (EETGW_Note 테이블)
|
||||||
|
export interface NoteItem {
|
||||||
|
idx: number;
|
||||||
|
gcode: string;
|
||||||
|
pdate: string; // 날짜
|
||||||
|
title: string; // 제목
|
||||||
|
uid: string; // 작성자 ID
|
||||||
|
description: string; // 내용 (plain text)
|
||||||
|
description2: string; // 내용 (RTF format - not used in web)
|
||||||
|
share: boolean; // 공유 여부
|
||||||
|
wuid: string; // 등록자 ID
|
||||||
|
wdate: string; // 등록일
|
||||||
|
guid: string; // 폴더 GUID
|
||||||
|
viewcount?: number; // 조회수
|
||||||
|
viewdate?: string; // 최종 조회일
|
||||||
|
}
|
||||||
|
|
||||||
// 메일양식 항목 타입
|
// 메일양식 항목 타입
|
||||||
export interface MailFormItem {
|
export interface MailFormItem {
|
||||||
idx: number;
|
idx: number;
|
||||||
@@ -792,3 +823,39 @@ export interface JobReportDayData {
|
|||||||
holidays: HolidayItem[];
|
holidays: HolidayItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Board 게시판 타입 (패치내역 등)
|
||||||
|
export interface BoardItem {
|
||||||
|
idx: number;
|
||||||
|
bidx: number;
|
||||||
|
header: string;
|
||||||
|
cate: string;
|
||||||
|
title: string;
|
||||||
|
contents: string;
|
||||||
|
file: string;
|
||||||
|
guid: string;
|
||||||
|
url: string;
|
||||||
|
wuid: string;
|
||||||
|
wdate: string | null;
|
||||||
|
project: string;
|
||||||
|
pidx: number;
|
||||||
|
gcode: string;
|
||||||
|
close: boolean;
|
||||||
|
remark: string;
|
||||||
|
wuid_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mail 발신 내역 타입
|
||||||
|
export interface MailItem {
|
||||||
|
idx: number;
|
||||||
|
gcode: string;
|
||||||
|
uid: string;
|
||||||
|
subject: string;
|
||||||
|
htmlbody: string;
|
||||||
|
fromlist: string;
|
||||||
|
tolist: string;
|
||||||
|
cclist: string;
|
||||||
|
bcclist: string;
|
||||||
|
project: string;
|
||||||
|
cate: string;
|
||||||
|
wdate: string | null;
|
||||||
|
}
|
||||||
|
|||||||
496
SubProject/FPJ0000/DSNote.Designer.cs
generated
496
SubProject/FPJ0000/DSNote.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
|||||||
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.EETGW_Note" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="Fill" GenerateMethods="Both" GenerateShortCommands="true" 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">
|
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.EETGW_Note" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="Fill" GenerateMethods="Both" GenerateShortCommands="true" 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">
|
||||||
<DeleteCommand>
|
<DeleteCommand>
|
||||||
<DbCommand CommandType="Text" ModifiedByUser="false">
|
<DbCommand CommandType="Text" ModifiedByUser="false">
|
||||||
<CommandText>DELETE FROM [EETGW_Note] WHERE (([idx] = @Original_idx) AND ([gcode] = @Original_gcode) AND ((@IsNull_pdate = 1 AND [pdate] IS NULL) OR ([pdate] = @Original_pdate)) AND ((@IsNull_title = 1 AND [title] IS NULL) OR ([title] = @Original_title)) AND ((@IsNull_uid = 1 AND [uid] IS NULL) OR ([uid] = @Original_uid)) AND ((@IsNull_share = 1 AND [share] IS NULL) OR ([share] = @Original_share)) AND ([wuid] = @Original_wuid) AND ([wdate] = @Original_wdate) AND ((@IsNull_guid = 1 AND [guid] IS NULL) OR ([guid] = @Original_guid)))</CommandText>
|
<CommandText>DELETE FROM [EETGW_Note] WHERE (([idx] = @Original_idx) AND ([gcode] = @Original_gcode) AND ((@IsNull_pdate = 1 AND [pdate] IS NULL) OR ([pdate] = @Original_pdate)) AND ((@IsNull_title = 1 AND [title] IS NULL) OR ([title] = @Original_title)) AND ((@IsNull_uid = 1 AND [uid] IS NULL) OR ([uid] = @Original_uid)) AND ((@IsNull_share = 1 AND [share] IS NULL) OR ([share] = @Original_share)) AND ([wuid] = @Original_wuid) AND ([wdate] = @Original_wdate) AND ((@IsNull_guid = 1 AND [guid] IS NULL) OR ([guid] = @Original_guid)) AND ((@IsNull_viewcount = 1 AND [viewcount] IS NULL) OR ([viewcount] = @Original_viewcount)) AND ((@IsNull_viewdate = 1 AND [viewdate] IS NULL) OR ([viewdate] = @Original_viewdate)))</CommandText>
|
||||||
<Parameters>
|
<Parameters>
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@Original_idx" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@Original_idx" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
@@ -28,13 +28,17 @@
|
|||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@Original_wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@Original_wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_guid" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="true" SourceVersion="Original" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_guid" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_viewcount" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewcount" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@Original_viewcount" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewcount" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_viewdate" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewdate" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@Original_viewdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="viewdate" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
</Parameters>
|
</Parameters>
|
||||||
</DbCommand>
|
</DbCommand>
|
||||||
</DeleteCommand>
|
</DeleteCommand>
|
||||||
<InsertCommand>
|
<InsertCommand>
|
||||||
<DbCommand CommandType="Text" ModifiedByUser="false">
|
<DbCommand CommandType="Text" ModifiedByUser="false">
|
||||||
<CommandText>INSERT INTO [EETGW_Note] ([gcode], [pdate], [title], [uid], [description2], [share], [wuid], [wdate], [description], [guid]) VALUES (@gcode, @pdate, @title, @uid, @description2, @share, @wuid, @wdate, @description, @guid);
|
<CommandText>INSERT INTO [EETGW_Note] ([gcode], [pdate], [title], [uid], [description2], [share], [wuid], [wdate], [description], [guid], [viewcount], [viewdate]) VALUES (@gcode, @pdate, @title, @uid, @description2, @share, @wuid, @wdate, @description, @guid, @viewcount, @viewdate);
|
||||||
SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid FROM EETGW_Note WHERE (idx = SCOPE_IDENTITY()) ORDER BY pdate DESC</CommandText>
|
SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid, viewcount, viewdate FROM EETGW_Note WITH (nolock) WHERE (idx = SCOPE_IDENTITY()) ORDER BY pdate DESC</CommandText>
|
||||||
<Parameters>
|
<Parameters>
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@pdate" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="pdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@pdate" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="pdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
@@ -46,12 +50,14 @@ SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, descript
|
|||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="String" Direction="Input" ParameterName="@description" Precision="0" ProviderType="NVarChar" Scale="0" Size="0" SourceColumn="description" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="String" Direction="Input" ParameterName="@description" Precision="0" ProviderType="NVarChar" Scale="0" Size="0" SourceColumn="description" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@viewcount" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewcount" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@viewdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="viewdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
</Parameters>
|
</Parameters>
|
||||||
</DbCommand>
|
</DbCommand>
|
||||||
</InsertCommand>
|
</InsertCommand>
|
||||||
<SelectCommand>
|
<SelectCommand>
|
||||||
<DbCommand CommandType="Text" ModifiedByUser="false">
|
<DbCommand CommandType="Text" ModifiedByUser="false">
|
||||||
<CommandText>SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid
|
<CommandText>SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid, viewcount, viewdate
|
||||||
FROM EETGW_Note WITH (nolock)
|
FROM EETGW_Note WITH (nolock)
|
||||||
WHERE (gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (uid = @uid) OR
|
WHERE (gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (uid = @uid) OR
|
||||||
(gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (ISNULL(share, 0) = 1)
|
(gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (ISNULL(share, 0) = 1)
|
||||||
@@ -66,8 +72,8 @@ ORDER BY pdate DESC</CommandText>
|
|||||||
</SelectCommand>
|
</SelectCommand>
|
||||||
<UpdateCommand>
|
<UpdateCommand>
|
||||||
<DbCommand CommandType="Text" ModifiedByUser="false">
|
<DbCommand CommandType="Text" ModifiedByUser="false">
|
||||||
<CommandText>UPDATE [EETGW_Note] SET [gcode] = @gcode, [pdate] = @pdate, [title] = @title, [uid] = @uid, [description2] = @description2, [share] = @share, [wuid] = @wuid, [wdate] = @wdate, [description] = @description, [guid] = @guid WHERE (([idx] = @Original_idx) AND ([gcode] = @Original_gcode) AND ((@IsNull_pdate = 1 AND [pdate] IS NULL) OR ([pdate] = @Original_pdate)) AND ((@IsNull_title = 1 AND [title] IS NULL) OR ([title] = @Original_title)) AND ((@IsNull_uid = 1 AND [uid] IS NULL) OR ([uid] = @Original_uid)) AND ((@IsNull_share = 1 AND [share] IS NULL) OR ([share] = @Original_share)) AND ([wuid] = @Original_wuid) AND ([wdate] = @Original_wdate) AND ((@IsNull_guid = 1 AND [guid] IS NULL) OR ([guid] = @Original_guid)));
|
<CommandText>UPDATE [EETGW_Note] SET [gcode] = @gcode, [pdate] = @pdate, [title] = @title, [uid] = @uid, [description2] = @description2, [share] = @share, [wuid] = @wuid, [wdate] = @wdate, [description] = @description, [guid] = @guid, [viewcount] = @viewcount, [viewdate] = @viewdate WHERE (([idx] = @Original_idx) AND ([gcode] = @Original_gcode) AND ((@IsNull_pdate = 1 AND [pdate] IS NULL) OR ([pdate] = @Original_pdate)) AND ((@IsNull_title = 1 AND [title] IS NULL) OR ([title] = @Original_title)) AND ((@IsNull_uid = 1 AND [uid] IS NULL) OR ([uid] = @Original_uid)) AND ((@IsNull_share = 1 AND [share] IS NULL) OR ([share] = @Original_share)) AND ([wuid] = @Original_wuid) AND ([wdate] = @Original_wdate) AND ((@IsNull_guid = 1 AND [guid] IS NULL) OR ([guid] = @Original_guid)) AND ((@IsNull_viewcount = 1 AND [viewcount] IS NULL) OR ([viewcount] = @Original_viewcount)) AND ((@IsNull_viewdate = 1 AND [viewdate] IS NULL) OR ([viewdate] = @Original_viewdate)));
|
||||||
SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid FROM EETGW_Note WHERE (idx = @idx) ORDER BY pdate DESC</CommandText>
|
SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid, viewcount, viewdate FROM EETGW_Note WITH (nolock) WHERE (idx = @idx) ORDER BY pdate DESC</CommandText>
|
||||||
<Parameters>
|
<Parameters>
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@pdate" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="pdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@pdate" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="pdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
@@ -79,6 +85,8 @@ SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, descript
|
|||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="String" Direction="Input" ParameterName="@description" Precision="0" ProviderType="NVarChar" Scale="0" Size="0" SourceColumn="description" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="String" Direction="Input" ParameterName="@description" Precision="0" ProviderType="NVarChar" Scale="0" Size="0" SourceColumn="description" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@viewcount" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewcount" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@viewdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="viewdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@Original_idx" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@Original_idx" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_gcode" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="gcode" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_pdate" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="pdate" SourceColumnNullMapping="true" SourceVersion="Original" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_pdate" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="pdate" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
@@ -93,7 +101,11 @@ SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, descript
|
|||||||
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@Original_wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="false" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@Original_wdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="wdate" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_guid" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="true" SourceVersion="Original" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_guid" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" SourceColumnNullMapping="false" SourceVersion="Original" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="AnsiString" Direction="Input" ParameterName="@Original_guid" Precision="0" ProviderType="VarChar" Scale="0" Size="0" SourceColumn="guid" 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="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_viewcount" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewcount" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@Original_viewcount" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewcount" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="Int32" Direction="Input" ParameterName="@IsNull_viewdate" Precision="0" ProviderType="Int" Scale="0" Size="0" SourceColumn="viewdate" SourceColumnNullMapping="true" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="true" AutogeneratedName="" DataSourceName="" DbType="DateTime" Direction="Input" ParameterName="@Original_viewdate" Precision="0" ProviderType="SmallDateTime" Scale="0" Size="0" SourceColumn="viewdate" SourceColumnNullMapping="false" SourceVersion="Original" />
|
||||||
|
<Parameter AllowDbNull="false" AutogeneratedName="idx" ColumnName="idx" DataSourceName="EE.dbo.EETGW_Note" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
</Parameters>
|
</Parameters>
|
||||||
</DbCommand>
|
</DbCommand>
|
||||||
</UpdateCommand>
|
</UpdateCommand>
|
||||||
@@ -111,14 +123,14 @@ SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, descript
|
|||||||
<Mapping SourceColumn="wdate" DataSetColumn="wdate" />
|
<Mapping SourceColumn="wdate" DataSetColumn="wdate" />
|
||||||
<Mapping SourceColumn="description" DataSetColumn="description" />
|
<Mapping SourceColumn="description" DataSetColumn="description" />
|
||||||
<Mapping SourceColumn="guid" DataSetColumn="guid" />
|
<Mapping SourceColumn="guid" DataSetColumn="guid" />
|
||||||
|
<Mapping SourceColumn="viewcount" DataSetColumn="viewcount" />
|
||||||
|
<Mapping SourceColumn="viewdate" DataSetColumn="viewdate" />
|
||||||
</Mappings>
|
</Mappings>
|
||||||
<Sources>
|
<Sources>
|
||||||
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.EETGW_Note" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="FillByIdx" GenerateMethods="Both" GenerateShortCommands="true" GeneratorGetMethodName="GetbyIdx" GeneratorSourceName="FillByIdx" GetMethodModifier="Public" GetMethodName="GetbyIdx" QueryType="Rowset" ScalarCallRetval="System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" UseOptimisticConcurrency="true" UserGetMethodName="GetbyIdx" UserSourceName="FillByIdx">
|
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.EETGW_Note" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="FillByIdx" GenerateMethods="Both" GenerateShortCommands="true" GeneratorGetMethodName="GetbyIdx" GeneratorSourceName="FillByIdx" GetMethodModifier="Public" GetMethodName="GetbyIdx" QueryType="Rowset" ScalarCallRetval="System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" UseOptimisticConcurrency="true" UserGetMethodName="GetbyIdx" UserSourceName="FillByIdx">
|
||||||
<SelectCommand>
|
<SelectCommand>
|
||||||
<DbCommand CommandType="Text" ModifiedByUser="true">
|
<DbCommand CommandType="Text" ModifiedByUser="true">
|
||||||
<CommandText>SELECT idx, gcode, pdate, title, uid, description2, share, wuid, wdate, description, guid
|
<CommandText>SELECT description, description2, gcode, guid, idx, pdate, share, title, uid, viewcount, viewdate, wdate, wuid FROM EETGW_Note WITH (nolock) WHERE (idx = @idx)</CommandText>
|
||||||
FROM EETGW_Note WITH (nolock)
|
|
||||||
WHERE idx = @idx</CommandText>
|
|
||||||
<Parameters>
|
<Parameters>
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="idx" ColumnName="idx" DataSourceName="EE.dbo.EETGW_Note" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="false" AutogeneratedName="idx" ColumnName="idx" DataSourceName="EE.dbo.EETGW_Note" DataTypeServer="int" DbType="Int32" Direction="Input" ParameterName="@idx" Precision="0" ProviderType="Int" Scale="0" Size="4" SourceColumn="idx" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
</Parameters>
|
</Parameters>
|
||||||
@@ -128,11 +140,7 @@ WHERE idx = @idx</CommandText>
|
|||||||
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.EETGW_Note" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="FillByNoDesc" GenerateMethods="Both" GenerateShortCommands="true" GeneratorGetMethodName="GetByNoDesc" GeneratorSourceName="FillByNoDesc" GetMethodModifier="Public" GetMethodName="GetByNoDesc" QueryType="Rowset" ScalarCallRetval="System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" UseOptimisticConcurrency="true" UserGetMethodName="GetByNoDesc" UserSourceName="FillByNoDesc">
|
<DbSource ConnectionRef="gwcs (Settings)" DbObjectName="EE.dbo.EETGW_Note" DbObjectType="Table" FillMethodModifier="Public" FillMethodName="FillByNoDesc" GenerateMethods="Both" GenerateShortCommands="true" GeneratorGetMethodName="GetByNoDesc" GeneratorSourceName="FillByNoDesc" GetMethodModifier="Public" GetMethodName="GetByNoDesc" QueryType="Rowset" ScalarCallRetval="System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" UseOptimisticConcurrency="true" UserGetMethodName="GetByNoDesc" UserSourceName="FillByNoDesc">
|
||||||
<SelectCommand>
|
<SelectCommand>
|
||||||
<DbCommand CommandType="Text" ModifiedByUser="true">
|
<DbCommand CommandType="Text" ModifiedByUser="true">
|
||||||
<CommandText>SELECT '' AS description, '' AS description2, gcode, guid, idx, pdate, share, title, uid, wdate, wuid
|
<CommandText>SELECT gcode, guid, idx, pdate, share, title, uid, viewcount, viewdate, wdate, wuid FROM EETGW_Note WITH (nolock) WHERE (gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (uid = @uid) OR (gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (ISNULL(share, 0) = 1) ORDER BY pdate DESC</CommandText>
|
||||||
FROM EETGW_Note WITH (nolock)
|
|
||||||
WHERE (gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (uid = @uid) OR
|
|
||||||
(gcode = @gcode) AND (pdate BETWEEN @sd AND @ed) AND (ISNULL(share, 0) = 1)
|
|
||||||
ORDER BY pdate DESC</CommandText>
|
|
||||||
<Parameters>
|
<Parameters>
|
||||||
<Parameter AllowDbNull="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="EE.dbo.EETGW_Note" 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="false" AutogeneratedName="gcode" ColumnName="gcode" DataSourceName="EE.dbo.EETGW_Note" 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="pdate" DataSourceName="EE.dbo.EETGW_Note" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sd" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="pdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
<Parameter AllowDbNull="true" AutogeneratedName="sd" ColumnName="pdate" DataSourceName="EE.dbo.EETGW_Note" DataTypeServer="varchar(10)" DbType="AnsiString" Direction="Input" ParameterName="@sd" Precision="0" ProviderType="VarChar" Scale="0" Size="10" SourceColumn="pdate" SourceColumnNullMapping="false" SourceVersion="Current" />
|
||||||
@@ -149,71 +157,73 @@ ORDER BY pdate DESC</CommandText>
|
|||||||
</DataSource>
|
</DataSource>
|
||||||
</xs:appinfo>
|
</xs:appinfo>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
<xs:element name="DSNote" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:Generator_UserDSName="DSNote" msprop:EnableTableAdapterManager="true" msprop:Generator_DataSetName="DSNote">
|
<xs:element name="DSNote" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msprop:EnableTableAdapterManager="true" msprop:Generator_DataSetName="DSNote" msprop:Generator_UserDSName="DSNote">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element name="EETGW_Note" msprop:Generator_RowClassName="EETGW_NoteRow" msprop:Generator_RowEvHandlerName="EETGW_NoteRowChangeEventHandler" msprop:Generator_RowDeletedName="EETGW_NoteRowDeleted" msprop:Generator_RowDeletingName="EETGW_NoteRowDeleting" msprop:Generator_RowEvArgName="EETGW_NoteRowChangeEvent" msprop:Generator_TablePropName="EETGW_Note" msprop:Generator_RowChangedName="EETGW_NoteRowChanged" msprop:Generator_UserTableName="EETGW_Note" msprop:Generator_RowChangingName="EETGW_NoteRowChanging" msprop:Generator_TableClassName="EETGW_NoteDataTable" msprop:Generator_TableVarName="tableEETGW_Note">
|
<xs:element name="EETGW_Note" msprop:Generator_UserTableName="EETGW_Note" msprop:Generator_RowEvArgName="EETGW_NoteRowChangeEvent" msprop:Generator_TableVarName="tableEETGW_Note" msprop:Generator_TablePropName="EETGW_Note" msprop:Generator_RowDeletingName="EETGW_NoteRowDeleting" msprop:Generator_RowChangingName="EETGW_NoteRowChanging" msprop:Generator_RowEvHandlerName="EETGW_NoteRowChangeEventHandler" msprop:Generator_RowDeletedName="EETGW_NoteRowDeleted" msprop:Generator_TableClassName="EETGW_NoteDataTable" msprop:Generator_RowChangedName="EETGW_NoteRowChanged" msprop:Generator_RowClassName="EETGW_NoteRow">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="idx" msdata:ReadOnly="true" msdata:AutoIncrement="true" msdata:AutoIncrementSeed="-1" msdata:AutoIncrementStep="-1" msprop:Generator_UserColumnName="idx" msprop:Generator_ColumnPropNameInTable="idxColumn" msprop:Generator_ColumnPropNameInRow="idx" msprop:Generator_ColumnVarNameInTable="columnidx" type="xs:int" />
|
<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:Generator_UserColumnName="gcode" msprop:Generator_ColumnPropNameInTable="gcodeColumn" msprop:Generator_ColumnPropNameInRow="gcode" msprop:Generator_ColumnVarNameInTable="columngcode">
|
<xs:element name="gcode" msprop:Generator_ColumnVarNameInTable="columngcode" msprop:Generator_ColumnPropNameInRow="gcode" msprop:Generator_ColumnPropNameInTable="gcodeColumn" msprop:Generator_UserColumnName="gcode">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="10" />
|
<xs:maxLength value="10" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="pdate" msprop:Generator_ColumnPropNameInTable="pdateColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="pdate" msprop:Generator_UserColumnName="pdate" msprop:Generator_ColumnVarNameInTable="columnpdate" minOccurs="0">
|
<xs:element name="pdate" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="pdate" msprop:Generator_ColumnVarNameInTable="columnpdate" msprop:Generator_ColumnPropNameInTable="pdateColumn" msprop:Generator_UserColumnName="pdate" minOccurs="0">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="10" />
|
<xs:maxLength value="10" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="title" msprop:Generator_ColumnPropNameInTable="titleColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="title" msprop:Generator_UserColumnName="title" msprop:Generator_ColumnVarNameInTable="columntitle" minOccurs="0">
|
<xs:element name="title" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="title" msprop:Generator_ColumnVarNameInTable="columntitle" msprop:Generator_ColumnPropNameInTable="titleColumn" msprop:Generator_UserColumnName="title" minOccurs="0">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="50" />
|
<xs:maxLength value="50" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</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:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="20" />
|
<xs:maxLength value="20" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="description2" msprop:Generator_ColumnPropNameInTable="description2Column" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="description2" msprop:Generator_UserColumnName="description2" msprop:Generator_ColumnVarNameInTable="columndescription2" minOccurs="0">
|
<xs:element name="description2" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="description2" msprop:Generator_ColumnVarNameInTable="columndescription2" msprop:Generator_ColumnPropNameInTable="description2Column" msprop:Generator_UserColumnName="description2" minOccurs="0">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="2147483647" />
|
<xs:maxLength value="2147483647" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="share" msprop:Generator_ColumnPropNameInTable="shareColumn" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="share" msprop:Generator_UserColumnName="share" msprop:Generator_ColumnVarNameInTable="columnshare" type="xs:boolean" minOccurs="0" />
|
<xs:element name="share" msprop:nullValue="0" msprop:Generator_ColumnPropNameInRow="share" msprop:Generator_ColumnVarNameInTable="columnshare" msprop:Generator_ColumnPropNameInTable="shareColumn" msprop:Generator_UserColumnName="share" type="xs:boolean" minOccurs="0" />
|
||||||
<xs:element name="wuid" msprop:Generator_ColumnPropNameInTable="wuidColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="wuid" msprop:Generator_UserColumnName="wuid" msprop:Generator_ColumnVarNameInTable="columnwuid">
|
<xs:element name="wuid" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="wuid" msprop:Generator_ColumnVarNameInTable="columnwuid" msprop:Generator_ColumnPropNameInTable="wuidColumn" msprop:Generator_UserColumnName="wuid">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="20" />
|
<xs:maxLength value="20" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="wdate" msprop:Generator_UserColumnName="wdate" msprop:Generator_ColumnPropNameInTable="wdateColumn" msprop:Generator_ColumnPropNameInRow="wdate" msprop:Generator_ColumnVarNameInTable="columnwdate" type="xs:dateTime" />
|
<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="description" msprop:Generator_ColumnPropNameInTable="descriptionColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="description" msprop:Generator_UserColumnName="description" msprop:Generator_ColumnVarNameInTable="columndescription" minOccurs="0">
|
<xs:element name="description" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="description" msprop:Generator_ColumnVarNameInTable="columndescription" msprop:Generator_ColumnPropNameInTable="descriptionColumn" msprop:Generator_UserColumnName="description" minOccurs="0">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="2147483647" />
|
<xs:maxLength value="2147483647" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:element name="guid" msprop:Generator_ColumnPropNameInTable="guidColumn" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="guid" msprop:Generator_UserColumnName="guid" msprop:Generator_ColumnVarNameInTable="columnguid" minOccurs="0">
|
<xs:element name="guid" msprop:nullValue="_empty" msprop:Generator_ColumnPropNameInRow="guid" msprop:Generator_ColumnVarNameInTable="columnguid" msprop:Generator_ColumnPropNameInTable="guidColumn" msprop:Generator_UserColumnName="guid" minOccurs="0">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
<xs:maxLength value="50" />
|
<xs:maxLength value="50" />
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="viewcount" msprop:Generator_ColumnVarNameInTable="columnviewcount" msprop:Generator_ColumnPropNameInRow="viewcount" msprop:Generator_ColumnPropNameInTable="viewcountColumn" msprop:Generator_UserColumnName="viewcount" type="xs:int" minOccurs="0" />
|
||||||
|
<xs:element name="viewdate" msprop:Generator_ColumnVarNameInTable="columnviewdate" msprop:Generator_ColumnPropNameInRow="viewdate" msprop:Generator_ColumnPropNameInTable="viewdateColumn" msprop:Generator_UserColumnName="viewdate" type="xs:dateTime" minOccurs="0" />
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Changes to this file may cause incorrect behavior and will be lost if
|
Changes to this file may cause incorrect behavior and will be lost if
|
||||||
the code is regenerated.
|
the code is regenerated.
|
||||||
</autogenerated>-->
|
</autogenerated>-->
|
||||||
<DiagramLayout xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ex:showrelationlabel="False" ViewPortX="115" 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="115" ViewPortY="0" xmlns:ex="urn:schemas-microsoft-com:xml-msdatasource-layout-extended" xmlns="urn:schemas-microsoft-com:xml-msdatasource-layout">
|
||||||
<Shapes>
|
<Shapes>
|
||||||
<Shape ID="DesignTable:EETGW_Note" ZOrder="1" X="260" Y="172" Height="324" Width="300" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="235" />
|
<Shape ID="DesignTable:EETGW_Note" ZOrder="1" X="260" Y="172" Height="324" Width="300" AdapterExpanded="true" DataTableExpanded="true" OldAdapterHeight="0" OldDataTableHeight="0" SplitterPosition="235" />
|
||||||
</Shapes>
|
</Shapes>
|
||||||
|
|||||||
4
SubProject/FPJ0000/Note/fNote.Designer.cs
generated
4
SubProject/FPJ0000/Note/fNote.Designer.cs
generated
@@ -174,7 +174,6 @@
|
|||||||
//
|
//
|
||||||
this.bindingNavigatorPositionItem.AccessibleName = "위치";
|
this.bindingNavigatorPositionItem.AccessibleName = "위치";
|
||||||
this.bindingNavigatorPositionItem.AutoSize = false;
|
this.bindingNavigatorPositionItem.AutoSize = false;
|
||||||
this.bindingNavigatorPositionItem.Font = new System.Drawing.Font("맑은 고딕", 9F);
|
|
||||||
this.bindingNavigatorPositionItem.Name = "bindingNavigatorPositionItem";
|
this.bindingNavigatorPositionItem.Name = "bindingNavigatorPositionItem";
|
||||||
this.bindingNavigatorPositionItem.Size = new System.Drawing.Size(50, 23);
|
this.bindingNavigatorPositionItem.Size = new System.Drawing.Size(50, 23);
|
||||||
this.bindingNavigatorPositionItem.Text = "0";
|
this.bindingNavigatorPositionItem.Text = "0";
|
||||||
@@ -276,7 +275,6 @@
|
|||||||
// tbFind
|
// tbFind
|
||||||
//
|
//
|
||||||
this.tbFind.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
this.tbFind.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
this.tbFind.Font = new System.Drawing.Font("맑은 고딕", 9F);
|
|
||||||
this.tbFind.Name = "tbFind";
|
this.tbFind.Name = "tbFind";
|
||||||
this.tbFind.Size = new System.Drawing.Size(100, 25);
|
this.tbFind.Size = new System.Drawing.Size(100, 25);
|
||||||
this.tbFind.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tbFind_KeyDown);
|
this.tbFind.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tbFind_KeyDown);
|
||||||
@@ -475,7 +473,6 @@
|
|||||||
// dtSD
|
// dtSD
|
||||||
//
|
//
|
||||||
this.dtSD.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
this.dtSD.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
this.dtSD.Font = new System.Drawing.Font("맑은 고딕", 9F);
|
|
||||||
this.dtSD.Name = "dtSD";
|
this.dtSD.Name = "dtSD";
|
||||||
this.dtSD.Size = new System.Drawing.Size(90, 37);
|
this.dtSD.Size = new System.Drawing.Size(90, 37);
|
||||||
this.dtSD.Text = "1982-11-23";
|
this.dtSD.Text = "1982-11-23";
|
||||||
@@ -490,7 +487,6 @@
|
|||||||
// dtED
|
// dtED
|
||||||
//
|
//
|
||||||
this.dtED.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
this.dtED.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
this.dtED.Font = new System.Drawing.Font("맑은 고딕", 9F);
|
|
||||||
this.dtED.Name = "dtED";
|
this.dtED.Name = "dtED";
|
||||||
this.dtED.Size = new System.Drawing.Size(90, 37);
|
this.dtED.Size = new System.Drawing.Size(90, 37);
|
||||||
this.dtED.Text = "1982-11-23";
|
this.dtED.Text = "1982-11-23";
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace FPJ0000.Note
|
|||||||
string fn_fpcolsize = "";
|
string fn_fpcolsize = "";
|
||||||
public fNote()
|
public fNote()
|
||||||
{
|
{
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
fn_fpcolsize = util.MakeFilePath(util.CurrentPath, "formSetting", "fp_" + this.Name + ".ini");
|
fn_fpcolsize = util.MakeFilePath(util.CurrentPath, "formSetting", "fp_" + this.Name + ".ini");
|
||||||
this.ds1.EETGW_Note.TableNewRow += Projects_TableNewRow;
|
this.ds1.EETGW_Note.TableNewRow += Projects_TableNewRow;
|
||||||
|
|||||||
@@ -130,102 +130,102 @@
|
|||||||
<data name="bindingNavigatorMoveFirstItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="bindingNavigatorMoveFirstItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAASpJREFUOE9jGDygcNbz/00Lnv/PnPj4P1QIA4S3P8Apx5A789n/VUfe/8elKL77
|
wwAADsMBx2+oZAAAATFJREFUOE9jYBg0oHDW8/9NC57/z5z4+D+6HAyEtz/AKceQO/PZ/1VH3v/HpSi+
|
||||||
wf/ghmu4DciY8vT/wn0fsCqK73n4f+n+///9qy/gNiCh58n/aVveYyiKaL8P1pw56/9/r9ITuA2I7Hr0
|
+8H/4IZrWOXAIGPK0/8L933Aqii+5+H/pfv///evvoAhBwcJPU/+T9vyHkNRRPt9sObMWf//e5WewG1A
|
||||||
v3f1BxRFoa33wJpb1wFt7/z73yX/AG4DApsf/q+b/w6uKLjl7v9Fe///7wBqzpjz879d3c//9hnbcRvg
|
ZNej/72rP6AoCm29B9bcuu7/f//Ov/9d8g/gNiCw+eH/uvnv4IqCW+7+X7T3//+Odf//Z8z5+d+u7ud/
|
||||||
UXX/f/60NyiK7Ipv/0+f8/u/f9e3/zqF7/5bJKzHbYB96d3/2ZNfYyjSTzn/36ToxX+VrE//jSOX4TbA
|
+4ztuA3wqLr/P3/aGxRFdsW3/6fP+f3fv+vbf53Cd/8tEtbjNsC+9O7/7MmvMRTpp5z/b1L04r9K1qf/
|
||||||
Iu/O/9T+11gVGSSd+C+b9vW/bvA83AYYZt3+H9byEqci/dTL/zV8p+E2QCftxn+/6od4Fal4TMBtgFPu
|
xpHLcBtgkXfnf2r/a6yKDJJO/JdN+/pfN3gehhwcGGbd/h/W8hKnIv3Uy/81fKdhlQMDnbQb//2qH+JV
|
||||||
lf8gBXgVDULAwAAA8HbAq6XlmnAAAAAASUVORK5CYII=
|
pOIxAaccg1Pulf8gBXgVDUoAAPB2wKtYlLYeAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="bindingNavigatorMovePreviousItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="bindingNavigatorMovePreviousItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAALZJREFUOE9jGDogvP3BfyiTdBDf/eB/cMM18gyI73n4f+n+///9qy+QbkBE+32w
|
wwAADsMBx2+oZAAAALtJREFUOE9jYBgyILz9wX90MaJBfPeD/8EN18gzIL7n4f+l+///96++QLoBEe33
|
||||||
5sxZ//97lZ4gzYDQ1ntgza3rgLZ3/v3vkn+AeAOCW+7+X7T3//8OoOaMOT//29X9/G+fsZ00F9gV3/6f
|
wZozZ/3/71V6gjQDQlvvgTW3rvv/37/z73+X/APEGxDccvf/or3//3es+/8/Y87P/3Z1P//bZ2wn3gAQ
|
||||||
Puf3f/+ub/91Ct/9t0hYT3oY6Kec/29S9OK/Stan/8aRy0g3AAQMkk78l037+l83eB55BoCAfurl/xq+
|
sCu+/T99zu///l3f/usUvvtvkbCeNANAQD/l/H+Tohf/VbI+/TeOXEa6ASBgkHTiv2za1/+6wfPIMwAE
|
||||||
08g3AARUPCZQZsBgBQwMANAUYJgEulBVAAAAAElFTkSuQmCC
|
9FMv/9fwnUa+ASCg4jGBMgMGLwAA0BRgmCws/7cAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="bindingNavigatorMoveNextItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="bindingNavigatorMoveNextItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAKNJREFUOE9jGHygcNbz/1AmeSB35rP/Cd33yDckY8rT//P2//6f0HWHPEMSep78
|
wwAADsMBx2+oZAAAAKRJREFUOE9jYBh0oHDW8//oYiSB3JnP/id03yPfkIwpT//P2//7f0LXHfIMSeh5
|
||||||
n73v1//OrX//u5VeJt2QyK5H/6ds+/W/ZOnf/wnT//63yT1LmiGBzQ//t659D9ZsXPLlv3T0tf/GkcuI
|
8n/2vl//O7f+/e9Wepl0QyK7Hv2fsu3X/5Klf/8nTP/73yb3LGmGBDY//N+69j1Ys3HJl//S0df+G0cu
|
||||||
N8Sj6v7/krnv4JoVXXpIc4F96d3/gS3PyNMMAhZ5d/7bFFwhTzMIGGbdJl8zCOik3SBf81AEDAwAoH5f
|
I94Qj6r7/0vmvoNrVnTpIV4zCNiX3v0f2PKMPM0gYJF3579NwRXyNIOAYdZt8jWDgE7aDfI1D00AAKB+
|
||||||
oAc0QjgAAAAASUVORK5CYII=
|
X6Bjq5qXAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="bindingNavigatorMoveLastItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="bindingNavigatorMoveLastItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAASxJREFUOE9jGFygcNbz/1AmBgDJNS14/j9z4mOcahhyZz77n9B9D6sCkNyqI+//
|
wwAADsMBx2+oZAAAAStJREFUOE9jYBhUoHDW8//oYjAAkmta8Px/5sTHONUw5M589j+h+x5WBSC5VUfe
|
||||||
h7c/wG1AxpSn/+ft//0/oesOhiKQ3MJ9H/4HN1zDbUBCz5P/s/f9+t+59e9/t9LLKApBctO2vP/vX30B
|
/w9vf4BVHgwypjz9P2//7/8JXXcwFIHkFu778D+44RqGHBwk9Dz5P3vfr/+dW//+dyu9jKIQJDdty/v/
|
||||||
twGRXY/+T9n263/J0r//E6b//W+TexauGCTXu/rDf6/SE7gNCGx++L917XuwZuOSL/+lo6/9N45cBtYA
|
/tUXcBsQ2fXo/5Rtv/6XLP37P2H63/82uWfhikFyvas//PcqPYHbgMDmh/9b174HazYu+fJfOvraf+PI
|
||||||
kqub/+6/S/4B3AZ4VN3/XzL3HVyzoksPXDFILn/am//2GdtxG2Bfevd/YMszDM0gAJLLnvz6v0XCetwG
|
ZWANILm6+e/+u+QfwG2AR9X9/yVz38E1K7r0wBWD5PKnvflvn7EdtwH2pXf/B7Y8w9AMk8ue/Pq/RcJ6
|
||||||
WOTd+W9TcAVDMwiA5FL7X8O9hBUYZt3GqhkEQHJhLS//6wbPw22ATtoNnJIgOb/qh/81fKfhNgAfcMq9
|
3AZY5N35b1NwBUMzTC61/zXcS1iBYdZtrJpBACQX1vLyv27wPKzyYKCTdgOnJEjOr/rhfw3faTjV4AVO
|
||||||
8l/FYwIYQ4UGBWBgAAC+0b+zuQxOnAAAAABJRU5ErkJggg==
|
uVf+q3hMAGN0uYEFAL7Rv7NmXVYYAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btAdd.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btAdd.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAUpJREFUOE9jGLzg7gL2/7fmcf6/Oofr/8UZvP+hwsSD60CNfx41/v/zsOH/yckC
|
wwAADsMBx2+oZAAAAVdJREFUOE/Nz0tLAmEUBmB3kWRoCUVEISFUJGb1OywiKrDsIpZdkJAkDUvDQkij
|
||||||
pBtwfjov3ICDPSKkG3B8kiBQc93/Pw+q/u9oFydswKWZPP/PTuX7fxKo8Ui/0P993SJAzeX//94r+r++
|
UKSbVIvatKhNi9oERRAGEQXhjJdp7Hd83/eGs2jhLGQ20QtndTgP71Gp/m0KZ1XInlTjM6XG+4EG5fuK
|
||||||
Qeb/qhq5/0srFf/PL1X+P6tIFdPAU0B//nlYD9RUC8SV///cKwHivP9/72b+/3sn+f/f23H//92MAOKQ
|
yaTUIN8bIMUQ0gmtcuBtX/MLPMT0yoHnuA6kuA4iruI20lAZ+DiswWuyFum4Dk+7dbiP6kHEFVDBg+tQ
|
||||||
/5NyNDENONQrDHbu3/ulQI0FQI3ZQI2pQI0J///digZqDPv/70bQ/3/X/f53peliGrCzXeL/lmap/+vA
|
My4DLbjwG3DqbcORxygHXxJakGIQRFwDEf0gwjKI4AYtzIHmHaA5Oxg/CsYPIb7YIQced+qluvTLCyIs
|
||||||
zpX/v6RC8f/fWzFAjeH/p+Zp/J+QpfW/O0P3f3uq/v/mREPCYTIb6E+Qc//dCPjfk6FDWAM6APnz3w1/
|
gRYWQPNO0NwkWNYGxg+DcYNgGSu2Z0xy4C7SiJtwE66kuq049xlAs2Ng/AiS7nbszXci6jIh4jQjPGWR
|
||||||
IPb735qsT7oB3em6YP+CcH2cEekGtCQZ/G+IN/xfE2v8vzLahHQD6AQYGAAkI9iedfyIaQAAAABJRU5E
|
A+U59hiluowbQMzVVfmgPKU/GdcPxlmx5TArB6KzJunf0gTtPcqBzeluhCYsCIz3wm/rUw78WX4AJCPY
|
||||||
rkJggg==
|
nlwVm9EAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btEdit.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btEdit.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEvSURBVDhPpZI/S0JRGIed+i6Nbk0t0iCuSkuDi1MfIKfm
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEuSURBVDhPpZKxS0JBHMff1P/S2NbUIg7RqrQ0tDj5B9jU
|
||||||
+gAOQktTQkMggYtRkwgidKmgyQwMCgqz0CivvvK8cK7neG4KdeHhHs45v+c9/xLyz08Ft713OQ+6SqXV
|
nH+Ag9DSlOAQhNCi6BRBBD1SaDIDg4JCLVSqpz/5HJzdz3vp4MH3vcfdfT73e3cXyJot4NHs9qUSdkxK
|
||||||
kfLlnXJw1lSK5VrERqGkMB4JCCLpvQ7lZfDlQJ+B4EnwI9nTkbYdAZMbjxOPq4eJPH1MvXC2sD8XsOzP
|
t20p1lsmJxc3JkfFq3m2MwUTxucCQCTd96G8DcYq9NkAnoc/kiqPzLcSMPn6eeKl8TSRl8+pB6cyx38C
|
||||||
0bcX/C3MXEfAfmzBsnCnP10uWBWu3IS+gJOm0w5fHCZiw0aQzu3GC0xYgm2R+poTRnh8HeqNOALu920w
|
yv4afXvgfzBzlYD/cQXL4HZvulywCi49RL6AnabThWv5IBa2gt10Nl5gYQn3RaobCkZ4dh+ZE1ECzvdj
|
||||||
9MK0F8NGkMrs+ALewqrwUXss3ed+vKB6H+rh2OT3SjpO0IBgcyvnCjgDBGCq8mcMiQ3FHAGdLB/J4vMF
|
MPRgvhdhK0jsHfgC7sIq+PTuVzqvvXjB5WNkNsfNYa5gxgFtEOwk01rAHiAgdlXejCFxw2JKQCflI1m8
|
||||||
KhoI83LXk6m5gCpmufbyOWlgv0BVIMx4JPj7JzIDGHRUPz2nxiQAAAAASUVORK5CYII=
|
voQVbYC5uZtbCV2BLdctn50m/C9hVQKs7sE6bQYYdFQ/+SVRqQAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btDel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btDel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAW9JREFUOE+1kE0ow2Ecx3dV3krt4oJaOSCTvIRkMqSxyITIzCQHDouEdnFwIOVC
|
wwAADsMBx2+oZAAAAWtJREFUOE+1kE0ow2Ecx/9X5a2UiwtKOSCTmJBMhuQlMo3IvCUHDouEXHZwIOVC
|
||||||
DrhIDiQl5UTiNG/z2ppafy1S2gX/uDwfY6i1v7Hie3nqeb7fz+/7/FR/Ilwn0G0Exw4fV5GJlXlEZxXC
|
DrhIDiQl5USy07zNa2tKf2laaRf84/J8xBCetab4XL/f76fn+SnKX4DrGLqrwbHDzywkWJlHdJYjLEbY
|
||||||
rIet9bAQvB5Ymgn2sLYAvSZEux7RUQFzE4qQt4bCXAYjPaHvnDoCkLpsRGMB2JqCTGLIijDlwqQ9bEMV
|
Wg8q4eYKlma+d1hbgF4TotWIaC+FuYmAktcXCksx2HrknBOHX1KbiTDngrXhW0kMdSBM2TA5Io+/wuI0
|
||||||
i9OIytR3EMNWcJ/BWH8A6j8/bOGFxwXNxYEvGbMQ9XnQ1/K78KfY3/VXzkMY0qFGG2H4RoLGQshJQNbG
|
oiz5TcRwB7hPYazfLx3rDz7+gCsXNBb4v1SdgajTQ19TaOMP2NtFmPSIilSo0v1y7FHBnAdZMWi6aO51
|
||||||
86CNhdrsX9a/uQZTPhQl4rMY4OLofbl3aX7I8uwPC7y/g1YdjyVJuEvT8e1tfwUYteHUxCCfHChDeHmG
|
kVCTGZoEzzWYciA/Dl9bBZwfvh3XmxIJy7PBJdx5odnAQ2E87qJUfPbtzwGjVpxJEWjH+4ElPD/BYBsY
|
||||||
QQvokjlOU+PbWA0x3pZnILVVI3uvQyHsbiLnqnGmRCF1NYD8pDhpRxOH7HQoAKZGkFKjceszQbpSrumX
|
EjhKicW3sSoVb0vSUFsq0W6upUxhdxMtOxZnYhhqVz1oj3JJUZSdpCg0p0POmLKhJofjNqaDeikX3tFG
|
||||||
bO+G80MFwKUTxgfgcO/b8D9IpXoFiiMDHIQm0skAAAAASUVORK5CYII=
|
uuHsQM65cML4ABzY5fA/eQGKIwMcVjm2bAAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btFind.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btFind.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
|
||||||
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
|
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
|
||||||
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
|
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
|
||||||
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
|
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
|
||||||
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
|
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
|
||||||
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
|
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<metadata name="cm.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="cm.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
@@ -279,22 +279,22 @@
|
|||||||
<data name="btSearch.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btSearch.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE4SURBVDhPtZPPasJAEMbzPn0FwWA92JtP4NGKB1/DP7ei
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEiSURBVDhPY2DAAf6jAXRxVNVIAKYga5bH/7TpTmAMYsNA
|
||||||
QSsovkChh7ZBrCfpyR4sikopUgq9StFzM/UbZ5asSS4FfzAkO7vft5udiZMEnSBpk5dhFJmncjdHxXaG
|
3EQb3Ab8+/fv/71PZ/5nzXX+f+3T7v+X320H0yCct8Dz/9rTM/8HtGvDDcPQDAIgBSCNOx9NBuOOiw7/
|
||||||
A+9K4SbFT1luEwQBbXavVO5d0nI3ovnW5yeiMriiu+kt5asXbABEdgRigAUQDr+aHLU3lxoLl/yPJhvF
|
u644/N9+ZzLYIKwGwARAkvP2dIJpGN52Y/r/9gsO/zuu2P/f+3Qq3ACQhVhtz5jlBLcBJnb43Qyw5p0P
|
||||||
GsiYJ/vPdX5qPK3bVJ25VFukafztGQNsKHJ791I3w+8KcpNth8XDz5YxACI/gsR1J8sTYcO4UIzwv1gG
|
pmB3AcwV6TNcwBLIBmLDKAbAeWQAFAPQbUG2DeYimAsxnA4z4PLnDWD/Hv2AGl0gAAobZAOxGgDSCApt
|
||||||
cTshgJ5IT8hChTMHsHi+v+fvffmxywVwN2FDkdsGEOK2ceu4feQ0tDqKyKMGqDfqjvprLzyswuX7Tf4E
|
UKiDQh/ZJbDYgQGcBoDiGxTvoPiHpYUt15Gj7y9uL4BSGijFbb81FYy33pgK1gxKyqAUiuJ0ZAAyAJbG
|
||||||
dBo6zn/3OB7XHovRyuhQ6+hhYKA9DpL+A1keRebNAhkaJH0OHOcP031C4EjYr6wAAAAASUVORK5CYII=
|
QQBXPkDXBwfoCuA60MRpAgDTfULg/+7qPQAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADlSURBVEhL7dQxCsIwFMbxnMrZWatzUXRUCo5ewSs4ewZR
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADiSURBVEhL7dVBCsIwEAXQnsq1a62ui6JLpeDSK3gF155B
|
||||||
DyDeQfEETsUbRL7YYNSXNC/UOiQP/kvzyE9KUcQ7y/VJtlHFvQYPjzf50xKsJsFmq81WHq538swMO9il
|
1AOId1A8gSvxBiNTCcTJJM3E2i6SwN9MhnklDW2WRbvW2wu0EepW8PkBf02CE2xks9vD6f4y6jTYg720
|
||||||
zhALxkWd7kAOJwsnjjPsYNeGs2B14fR5YTYq5O5c1u7sL987iAUjF+6LIjaMKJyDoiAYmVAvn8lsXHij
|
riKCcVCvP4LxbOXEcQ97sNeGi+Bq4PwzMJ+UcLg+a3uON7NHDHODdZzu2dAgmAMQpzUXGgxTfFAsIJ+W
|
||||||
KBhGwDWof4APihqHqQ+OKhg2X3U/n7+9ah88CKY+pM9ndTgbplDbmQtnwS7UtmPDWfDf/jIRLnKhOuzY
|
3uhPMAZxBaoH8EExjcPcheMSDOtHPSyWX0ftgwfB3EWitTpcDFNAf6d0z4WLYDqYu0i0x4aL4M4+mZhO
|
||||||
UMSGmyrBauKE26jiohshHicE2B3dbRrmAAAAAElFTkSuQmCC
|
fhJNJsERw22EuvGsNycE2B33w41tAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<metadata name="ta.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="ta.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
|||||||
Reference in New Issue
Block a user