diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 359cd76..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "dotnet.preferCSharpExtension": true -} \ No newline at end of file diff --git a/Project/Web/Controller/CommonController.cs b/Project/Web/Controller/CommonController.cs index 997dfe8..b7b8cfc 100644 --- a/Project/Web/Controller/CommonController.cs +++ b/Project/Web/Controller/CommonController.cs @@ -333,6 +333,82 @@ namespace Project.Web.Controllers } } + [HttpGet] + public HttpResponseMessage GetNavigationMenu() + { + try + { + // 메뉴 정보를 하드코딩하거나 데이터베이스에서 가져올 수 있습니다. + // 향후 사용자 권한에 따른 메뉴 표시/숨김 기능도 추가 가능합니다. + var menuItems = new[] + { + new { + key = "dashboard", + title = "대시보드", + url = "/Dashboard/", + icon = "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z", + isVisible = true, + sortOrder = 1 + }, + new { + key = "common", + title = "공용코드", + url = "/Common", + icon = "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z", + isVisible = true, + sortOrder = 2 + }, + new { + key = "jobreport", + title = "업무일지", + url = "/Jobreport/", + icon = "M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2", + isVisible = true, + sortOrder = 3 + }, + new { + key = "kuntae", + title = "근태관리", + url = "/Kuntae/", + icon = "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z", + isVisible = true, + sortOrder = 4 + }, + new { + key = "todo", + title = "할일관리", + url = "/Todo/", + icon = "M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4", + isVisible = true, + sortOrder = 5 + } + }; + + // 사용자 권한에 따른 메뉴 필터링 로직을 여기에 추가할 수 있습니다. + // 예: var userLevel = FCOMMON.info.Login.level; + // if (userLevel < 5) { /* 특정 메뉴 숨김 */ } + + var response = new + { + Success = true, + Data = menuItems, + Message = "메뉴 정보를 성공적으로 가져왔습니다." + }; + + return CreateJsonResponse(response); + } + catch (Exception ex) + { + var response = new + { + Success = false, + Data = (object)null, + Message = "메뉴 정보를 가져오는 중 오류가 발생했습니다: " + ex.Message + }; + return CreateJsonResponse(response); + } + } + private HttpResponseMessage CreateJsonResponse(object data) { var json = JsonConvert.SerializeObject(data, new JsonSerializerSettings diff --git a/Project/Web/Controller/TodoController.cs b/Project/Web/Controller/TodoController.cs new file mode 100644 index 0000000..ecf8cde --- /dev/null +++ b/Project/Web/Controller/TodoController.cs @@ -0,0 +1,389 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Web.Http; +using Newtonsoft.Json; +using FCOMMON; +using Project.Web.Model; + +namespace Project.Web.Controllers +{ + public class TodoController : BaseController + { + [HttpGet] + public HttpResponseMessage Index() + { + var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", "wwwroot", "Todo", "index.html"); + var contents = string.Empty; + + if (System.IO.File.Exists(filePath)) + { + contents = System.IO.File.ReadAllText(filePath, System.Text.Encoding.UTF8); + } + else + { + contents = "

404 - File Not Found

The requested file was not found: " + filePath + "

"; + } + + ApplyCommonValue(ref contents); + + var resp = new HttpResponseMessage() + { + Content = new StringContent( + contents, + System.Text.Encoding.UTF8, + "text/html") + }; + + return resp; + } + + [HttpGet] + public HttpResponseMessage GetTodos() + { + try + { + var currentUser = GetCurrentUser(); + if (currentUser == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "로그인되지 않은 상태입니다." + }); + } + + string gcode = FCOMMON.info.Login.gcode; + string uid = FCOMMON.info.Login.no; + + var sql = "SELECT * FROM EETGW_Todo WHERE gcode = @gcode AND uid = @uid ORDER BY flag DESC, seqno DESC, expire ASC, wdate ASC"; + var todos = DBM.Query(sql, new { gcode = gcode, uid = uid }); + + return CreateJsonResponse(new + { + Success = true, + Data = todos + }); + } + catch (Exception ex) + { + return CreateJsonResponse(new + { + Success = false, + Message = "Todo 목록을 가져오는 중 오류가 발생했습니다: " + ex.Message + }); + } + } + + [HttpGet] + public HttpResponseMessage GetUrgentTodos() + { + try + { + var currentUser = GetCurrentUser(); + if (currentUser == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "로그인되지 않은 상태입니다." + }); + } + + string gcode = FCOMMON.info.Login.gcode; + string uid = FCOMMON.info.Login.no; + + var sql = @" + SELECT TOP 5 * FROM EETGW_Todo + WHERE gcode = @gcode AND uid = @uid + AND (expire IS NULL OR CAST(expire AS DATE) >= CAST(GETDATE() AS DATE)) + ORDER BY flag DESC, seqno DESC, expire ASC, wdate ASC"; + + var todos = DBM.Query(sql, new { gcode = gcode, uid = uid }); + + return CreateJsonResponse(new + { + Success = true, + Data = todos + }); + } + catch (Exception ex) + { + return CreateJsonResponse(new + { + Success = false, + Message = "급한 Todo 목록을 가져오는 중 오류가 발생했습니다: " + ex.Message + }); + } + } + + [HttpPost] + public HttpResponseMessage CreateTodo([FromBody] TodoModel todo) + { + try + { + var currentUser = GetCurrentUser(); + if (currentUser == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "로그인되지 않은 상태입니다." + }); + } + + if (string.IsNullOrEmpty(todo.remark)) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일 내용은 필수입니다." + }); + } + + todo.gcode = FCOMMON.info.Login.gcode; + todo.uid = FCOMMON.info.Login.no; + todo.wuid = FCOMMON.info.Login.no; + todo.wdate = DateTime.Now; + + if (todo.seqno == null) todo.seqno = 0; + if (todo.flag == null) todo.flag = false; + + var sql = @" + INSERT INTO EETGW_Todo (gcode, uid, title, remark, flag, expire, seqno, request, wuid, wdate) + VALUES (@gcode, @uid, @title, @remark, @flag, @expire, @seqno, @request, @wuid, @wdate); + SELECT SCOPE_IDENTITY();"; + + var newId = DBM.QuerySingle(sql, todo); + + return CreateJsonResponse(new + { + Success = true, + Message = "할일이 추가되었습니다.", + Data = new { idx = newId } + }); + } + catch (Exception ex) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일 추가 중 오류가 발생했습니다: " + ex.Message + }); + } + } + + [HttpPut] + public HttpResponseMessage UpdateTodo([FromBody] TodoModel todo) + { + try + { + var currentUser = GetCurrentUser(); + if (currentUser == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "로그인되지 않은 상태입니다." + }); + } + + if (todo.idx <= 0) + { + return CreateJsonResponse(new + { + Success = false, + Message = "유효하지 않은 Todo ID입니다." + }); + } + + if (string.IsNullOrEmpty(todo.remark)) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일 내용은 필수입니다." + }); + } + + string gcode = FCOMMON.info.Login.gcode; + string uid = FCOMMON.info.Login.no; + + var sql = @" + UPDATE EETGW_Todo + SET title = @title, remark = @remark, flag = @flag, expire = @expire, seqno = @seqno, request = @request + WHERE idx = @idx AND gcode = @gcode AND uid = @uid"; + + var affectedRows = DBM.Execute(sql, new + { + title = todo.title, + remark = todo.remark, + flag = todo.flag ?? false, + expire = todo.expire, + seqno = todo.seqno ?? 0, + request = todo.request, + idx = todo.idx, + gcode = gcode, + uid = uid + }); + + if (affectedRows == 0) + { + return CreateJsonResponse(new + { + Success = false, + Message = "수정할 할일을 찾을 수 없습니다." + }); + } + + return CreateJsonResponse(new + { + Success = true, + Message = "할일이 수정되었습니다." + }); + } + catch (Exception ex) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일 수정 중 오류가 발생했습니다: " + ex.Message + }); + } + } + + [HttpDelete] + public HttpResponseMessage DeleteTodo(int id) + { + try + { + var currentUser = GetCurrentUser(); + if (currentUser == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "로그인되지 않은 상태입니다." + }); + } + + if (id <= 0) + { + return CreateJsonResponse(new + { + Success = false, + Message = "유효하지 않은 Todo ID입니다." + }); + } + + string gcode = FCOMMON.info.Login.gcode; + string uid = FCOMMON.info.Login.no; + + var sql = "DELETE FROM EETGW_Todo WHERE idx = @idx AND gcode = @gcode AND uid = @uid"; + var affectedRows = DBM.Execute(sql, new { idx = id, gcode = gcode, uid = uid }); + + if (affectedRows == 0) + { + return CreateJsonResponse(new + { + Success = false, + Message = "삭제할 할일을 찾을 수 없습니다." + }); + } + + return CreateJsonResponse(new + { + Success = true, + Message = "할일이 삭제되었습니다." + }); + } + catch (Exception ex) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일 삭제 중 오류가 발생했습니다: " + ex.Message + }); + } + } + + [HttpGet] + public HttpResponseMessage GetTodo(int id) + { + try + { + var currentUser = GetCurrentUser(); + if (currentUser == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "로그인되지 않은 상태입니다." + }); + } + + if (id <= 0) + { + return CreateJsonResponse(new + { + Success = false, + Message = "유효하지 않은 Todo ID입니다." + }); + } + + string gcode = FCOMMON.info.Login.gcode; + string uid = FCOMMON.info.Login.no; + + var sql = "SELECT * FROM EETGW_Todo WHERE idx = @idx AND gcode = @gcode AND uid = @uid"; + var todo = DBM.QuerySingleOrDefault(sql, new { idx = id, gcode = gcode, uid = uid }); + + if (todo == null) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일을 찾을 수 없습니다." + }); + } + + return CreateJsonResponse(new + { + Success = true, + Data = todo + }); + } + catch (Exception ex) + { + return CreateJsonResponse(new + { + Success = false, + Message = "할일 조회 중 오류가 발생했습니다: " + ex.Message + }); + } + } + + private object GetCurrentUser() + { + if (string.IsNullOrEmpty(FCOMMON.info.Login.no)) return null; + else return FCOMMON.info.Login; + } + + private HttpResponseMessage CreateJsonResponse(object data) + { + var json = JsonConvert.SerializeObject(data, new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DateFormatString = "yyyy-MM-dd HH:mm:ss" + }); + + return new HttpResponseMessage() + { + Content = new StringContent( + json, + System.Text.Encoding.UTF8, + "application/json") + }; + } + } +} diff --git a/Project/Web/Model/TodoModel.cs b/Project/Web/Model/TodoModel.cs new file mode 100644 index 0000000..8591d18 --- /dev/null +++ b/Project/Web/Model/TodoModel.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Project.Web.Model +{ + public class TodoModel + { + /// + /// 데이터베이스 고유 번호(자동증가) + /// + public int idx { get; set; } + + /// + /// 사용자 그룹코드(부서코드) + /// + public string gcode { get; set; } + + /// + /// 사용자ID + /// + public string uid { get; set; } + + /// + /// 할일제목(제목없이 내용만 있을 수 있다) + /// + public string title { get; set; } + + /// + /// 할일내용(내용은 반드시 있어야 한다) + /// + public string remark { get; set; } + + /// + /// 플래그지정된것은 상단에 표시된다. + /// + public bool? flag { get; set; } + + /// + /// 만료일(작업만료일) + /// + public DateTime? expire { get; set; } + + /// + /// 작업중요도 높을수록 위에 표시된다 기본값 0 + /// + public int? seqno { get; set; } + + /// + /// 업무요청자 + /// + public string request { get; set; } + + /// + /// 자료생성자id + /// 로그인된 사용자id로 자동셋팅 + /// + public string wuid { get; set; } + + /// + /// 자료생성일시 + /// 자동 셋팅 + /// + public DateTime wdate { get; set; } + } +} diff --git a/Project/Web/Startup.cs b/Project/Web/Startup.cs index 99d848b..24c845e 100644 --- a/Project/Web/Startup.cs +++ b/Project/Web/Startup.cs @@ -44,17 +44,7 @@ namespace Project.OWIN app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); - // 정적 파일 서빙 설정 - var options = new FileServerOptions - { - EnableDefaultFiles = true, - DefaultFilesOptions = { DefaultFileNames = { "index.html" } }, - FileSystem = new Microsoft.Owin.FileSystems.PhysicalFileSystem("Web/wwwroot") - }; - - app.UseFileServer(options); - - // 캐시 방지 미들웨어 추가 + // 캐시 방지 미들웨어 추가 (정적 파일 서빙 전에 적용) app.Use(async (context, next) => { if (context.Request.Path.Value.EndsWith(".js") || context.Request.Path.Value.EndsWith(".css")) @@ -66,6 +56,16 @@ namespace Project.OWIN await next(); }); + // 정적 파일 서빙 설정 + var options = new FileServerOptions + { + EnableDefaultFiles = true, + DefaultFilesOptions = { DefaultFileNames = { "index.html" } }, + FileSystem = new Microsoft.Owin.FileSystems.PhysicalFileSystem("Web/wwwroot") + }; + + app.UseFileServer(options); + //appBuilder.UseFileServer(new FileServerOptions //{ // RequestPath = new PathString(string.Empty), diff --git a/Project/Web/wwwroot/Common.html b/Project/Web/wwwroot/Common.html index aaa8d52..9c56ce8 100644 --- a/Project/Web/wwwroot/Common.html +++ b/Project/Web/wwwroot/Common.html @@ -96,12 +96,6 @@
- -
-

공용코드 관리

-

시스템 공용코드를 관리합니다

-
-
@@ -277,41 +271,72 @@ class CommonNavigation { constructor(currentPage = '') { this.currentPage = currentPage; + this.menuItems = []; this.init(); } - init() { + async init() { + try { + await this.loadMenuItems(); + this.createNavigation(); + this.addEventListeners(); + } catch (error) { + console.error('Navigation initialization failed:', error); + this.createFallbackNavigation(); + } + } + + async loadMenuItems() { + try { + const response = await fetch('/Common/GetNavigationMenu'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + + if (data.Success && data.Data) { + this.menuItems = data.Data; + } else { + throw new Error(data.Message || 'Failed to load menu items'); + } + } catch (error) { + console.error('Failed to load navigation menu:', error); + this.menuItems = this.getDefaultMenuItems(); + } + } + + getDefaultMenuItems() { + return [ + { key: 'dashboard', title: '대시보드', url: '/Dashboard/', icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z', isVisible: true, sortOrder: 1 }, + { key: 'common', title: '공용코드', url: '/Common', icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z', isVisible: true, sortOrder: 2 }, + { key: 'jobreport', title: '업무일지', url: '/Jobreport/', icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2', isVisible: true, sortOrder: 3 }, + { key: 'kuntae', title: '근태관리', url: '/Kuntae/', icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z', isVisible: true, sortOrder: 4 }, + { key: 'todo', title: '할일관리', url: '/Todo/', icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4', isVisible: true, sortOrder: 5 } + ]; + } + + createFallbackNavigation() { this.createNavigation(); - this.addEventListeners(); } createNavigation() { const nav = document.createElement('nav'); nav.className = 'glass-effect border-b border-white/10'; nav.innerHTML = this.getNavigationHTML(); - - // body의 첫 번째 자식으로 추가 document.body.insertBefore(nav, document.body.firstChild); } getNavigationHTML() { + const visibleItems = this.menuItems.filter(item => item.isVisible).sort((a, b) => a.sortOrder - b.sortOrder); return `
-

GroupWare

- - - -
- -
`; } - getMenuItemHTML(pageKey, href, text, svgPath) { - const isActive = this.currentPage === pageKey; + getMenuItemHTML(item) { + const isActive = this.currentPage === item.key; const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10'; return ` - + - + - ${text} + ${item.title} `; } - getMobileMenuItemHTML(pageKey, href, text, svgPath) { - const isActive = this.currentPage === pageKey; + getMobileMenuItemHTML(item) { + const isActive = this.currentPage === item.key; const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10'; return ` - + - + - ${text} + ${item.title} `; } @@ -373,18 +393,6 @@ } } - // 전역 함수로 내비게이션 초기화 - function initNavigation(currentPage = '') { - // DOM이 로드된 후에 실행 - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => { - new CommonNavigation(currentPage); - }); - } else { - new CommonNavigation(currentPage); - } - } - let currentData = []; let deleteTargetIdx = null; let groupData = []; @@ -709,6 +717,17 @@ } }); + // 전역 함수로 내비게이션 초기화 + function initNavigation(currentPage = '') { + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + new CommonNavigation(currentPage); + }); + } else { + new CommonNavigation(currentPage); + } + } + // 공통 네비게이션 초기화 initNavigation('common'); diff --git a/Project/Web/wwwroot/DashBoard/index.html b/Project/Web/wwwroot/DashBoard/index.html index 4ce4b96..6aa93d3 100644 --- a/Project/Web/wwwroot/DashBoard/index.html +++ b/Project/Web/wwwroot/DashBoard/index.html @@ -101,7 +101,7 @@ /* 스크롤바 스타일링 */ .custom-scrollbar::-webkit-scrollbar { - width: var(--scrollbar-width, 16px); /* 동적 스크롤바 너비 */ + width: 16px; } .custom-scrollbar::-webkit-scrollbar-track { @@ -124,21 +124,6 @@
-

근태현황 대시보드

-

-- 기능 테스트 중입니다 --

- - -
- - -
@@ -218,36 +203,70 @@
+
+ + +
- -
-
-

- - - - 휴가/기타 현황 -

+ +
+ +
+
+

+ + + + 휴가/기타 현황 +

+
+
+ + + + + + + + + + + + + +
이름형태종류기간사유
+
-
- - - - - - - - - - - - - - -
이름형태종류시작일종료일사유
+ + +
+
+

+ + + + + 할일 + + +

+
+
+
+ +
+ + + + 급한 할일이 없습니다 +
+
+
@@ -459,6 +478,99 @@
+ + + \ No newline at end of file diff --git a/Project/Web/wwwroot/Jobreport/index.html b/Project/Web/wwwroot/Jobreport/index.html index a69cc80..26665b8 100644 --- a/Project/Web/wwwroot/Jobreport/index.html +++ b/Project/Web/wwwroot/Jobreport/index.html @@ -125,20 +125,6 @@
- -
-

업무일지

-
- -
- -
-
-
-
@@ -349,50 +335,78 @@
+ + + + +
+ +
+
+

+ + + + 내 할일 목록 +

+ +
+
+ + + + + + + + + + + + + + + +
상태제목내용요청자중요도만료일작업
+
+
+ + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/Project/Web/wwwroot/js/navigation.js b/Project/Web/wwwroot/js/navigation.js new file mode 100644 index 0000000..be75b65 --- /dev/null +++ b/Project/Web/wwwroot/js/navigation.js @@ -0,0 +1,195 @@ +/** + * 공통 네비게이션 컴포넌트 + * 서버에서 메뉴 정보를 받아와서 동적으로 네비게이션을 생성합니다. + */ +class CommonNavigation { + constructor(currentPage = '') { + this.currentPage = currentPage; + this.menuItems = []; + this.init(); + } + + async init() { + try { + await this.loadMenuItems(); + this.createNavigation(); + this.addEventListeners(); + } catch (error) { + console.error('Navigation initialization failed:', error); + // 오류 발생 시 기본 메뉴로 폴백 + this.createFallbackNavigation(); + } + } + + async loadMenuItems() { + try { + const response = await fetch('/api/Common/GetNavigationMenu'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + + if (data.Success && data.Data) { + this.menuItems = data.Data; + } else { + throw new Error(data.Message || 'Failed to load menu items'); + } + } catch (error) { + console.error('Failed to load navigation menu:', error); + // 기본 메뉴 항목으로 폴백 + this.menuItems = this.getDefaultMenuItems(); + } + } + + getDefaultMenuItems() { + return [ + { + key: 'dashboard', + title: '대시보드', + url: '/Dashboard/', + icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z', + isVisible: true, + sortOrder: 1 + }, + { + key: 'common', + title: '공용코드', + url: '/Common', + icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z', + isVisible: true, + sortOrder: 2 + }, + { + key: 'jobreport', + title: '업무일지', + url: '/Jobreport/', + icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2', + isVisible: true, + sortOrder: 3 + }, + { + key: 'kuntae', + title: '근태관리', + url: '/Kuntae/', + icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z', + isVisible: true, + sortOrder: 4 + }, + { + key: 'todo', + title: '할일관리', + url: '/Todo/', + icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4', + isVisible: true, + sortOrder: 5 + } + ]; + } + + createNavigation() { + const nav = document.createElement('nav'); + nav.className = 'glass-effect border-b border-white/10'; + nav.innerHTML = this.getNavigationHTML(); + + // body의 첫 번째 자식으로 추가 + document.body.insertBefore(nav, document.body.firstChild); + } + + createFallbackNavigation() { + console.log('Creating fallback navigation...'); + this.createNavigation(); + } + + getNavigationHTML() { + const visibleItems = this.menuItems + .filter(item => item.isVisible) + .sort((a, b) => a.sortOrder - b.sortOrder); + + return ` +
+
+ +
+

GroupWare

+
+ + + + + +
+ +
+
+ + + +
+ `; + } + + getMenuItemHTML(item) { + const isActive = this.currentPage === item.key; + const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10'; + + return ` + + + + + ${item.title} + + `; + } + + getMobileMenuItemHTML(item) { + const isActive = this.currentPage === item.key; + const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10'; + + return ` + + + + + ${item.title} + + `; + } + + addEventListeners() { + // 모바일 메뉴 토글 + const mobileMenuButton = document.getElementById('mobile-menu-button'); + const mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenuButton && mobileMenu) { + mobileMenuButton.addEventListener('click', function() { + mobileMenu.classList.toggle('hidden'); + }); + } + } +} + +// 전역 함수로 내비게이션 초기화 +function initNavigation(currentPage = '') { + // DOM이 로드된 후에 실행 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + new CommonNavigation(currentPage); + }); + } else { + new CommonNavigation(currentPage); + } +} + +// ES6 모듈로도 사용 가능하도록 export (필요시) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { CommonNavigation, initNavigation }; +} \ No newline at end of file