jichoi / calendar star
최정임 최정임 01-24
250124 최정임
@a46c9551aa9f3c41ef863426e0245f34ce1ed2c9
client/resources/css/style.css
--- client/resources/css/style.css
+++ client/resources/css/style.css
@@ -39,6 +39,7 @@
 }
 
 button{margin: 0 3px;}
+input{border: 0;}
 
 /*--------------------------------------------------------------
 # Main
@@ -368,7 +369,6 @@
 
 .logo img {
   max-height: 26px;
-  margin-right: 6px;
 }
 
 .logo span {
client/views/index.html
--- client/views/index.html
+++ client/views/index.html
@@ -1,29 +1,41 @@
 <!DOCTYPE html>
 <html>
-    <head>
-        <meta charset="UTF-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-        <meta name="description" content="Node Vue Web">
-        <link rel="icon" href="" />
-        <!-- Vendor CSS Files -->
-  <link href="/client/resources/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
-  <link href="/client/resources/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
-  <link href="/client/resources/vendor/boxicons/css/boxicons.min.css" rel="stylesheet">
-  <link href="/client/resources/vendor/quill/quill.snow.css" rel="stylesheet">
-  <link href="/client/resources/vendor/quill/quill.bubble.css" rel="stylesheet">
-  <link href="/client/resources/vendor/remixicon/remixicon.css" rel="stylesheet">
-  <link href="/client/resources/vendor/simple-datatables/style.css" rel="stylesheet">
 
-  <!-- Template Main CSS File -->
-  <link href="/client/resources/css/style.css" rel="stylesheet">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta name="description" content="Node Vue Web">
+    <link rel="icon" href="" />
+    <!-- Vendor CSS Files -->
+    <link href="/client/resources/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+    <link href="/client/resources/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
+    <link href="/client/resources/vendor/boxicons/css/boxicons.min.css" rel="stylesheet">
+    <link href="/client/resources/vendor/quill/quill.snow.css" rel="stylesheet">
+    <link href="/client/resources/vendor/quill/quill.bubble.css" rel="stylesheet">
+    <link href="/client/resources/vendor/remixicon/remixicon.css" rel="stylesheet">
+    <link href="/client/resources/vendor/simple-datatables/style.css" rel="stylesheet">
 
-  <script src="/client/resources/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
-        <title>Node Vue Web</title>
-    </head>
+    <!-- Template Main CSS File -->
+    <link href="/client/resources/css/style.css" rel="stylesheet">
 
-    <body>
-        <div id="root"></div>
-        <script src="/client/build/bundle.js"></script>
-    </body>
-</html>
+    <script src="/client/resources/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
+    
+    <script src="/client/resources/vendor/tinymce/tinymce.min.js"></script>
+    <script src="/client/resources/vendor/quill/quill.js"></script>
+    <script src="/client/resources/vendor/php-email-form/validate.js"></script>
+
+    <script src="/client/resources/js/main.js"></script>
+    <title>테이큰소프트</title>
+</head>
+
+<body>
+    <div id="root"></div>
+    <script src="/client/build/bundle.js"></script>
+    <script src="/client/resources/vendor/simple-datatables/simple-datatables.js"></script>
+    <script>
+        window.simpleDatatables = simpleDatatables; // 전역으로 접근 가능하게 설정
+      </script>
+</body>
+
+</html>
(파일 끝에 줄바꿈 문자 없음)
client/views/index.js
--- client/views/index.js
+++ client/views/index.js
@@ -6,7 +6,9 @@
 import { createApp } from 'vue';
 import AppRouter from './pages/AppRouter.js';
 import App from './pages/App.vue';
+import { createPinia } from 'pinia';
 
-const vue = createApp(App).use(AppRouter).mount('#root');
-
-
+createApp(App)
+  .use(createPinia())
+  .use(AppRouter)
+  .mount('#root');
(파일 끝에 줄바꿈 문자 없음)
client/views/layout/Header.vue
--- client/views/layout/Header.vue
+++ client/views/layout/Header.vue
@@ -10,12 +10,6 @@
   <i class="bi bi-list toggle-sidebar-btn" @click="toggleSidebar"></i>
 </div><!-- End Logo -->
 
-<div class="search-bar">
-  <form class="search-form d-flex align-items-center" method="POST" action="#">
-    <input type="text" name="query" placeholder="Search" title="Enter search keyword">
-    <button type="submit" title="Search"><i class="bi bi-search"></i></button>
-  </form>
-</div><!-- End Search Bar -->
 
 <nav class="header-nav ms-auto">
   <ul class="d-flex align-items-center">
@@ -170,14 +164,11 @@
     <li class="nav-item dropdown pe-3">
 
       <a class="nav-link nav-profile d-flex align-items-center pe-0" href="#" data-bs-toggle="dropdown">
-        <span class="d-none d-md-block dropdown-toggle ps-2">K. Anderson</span>
+        <span class="d-none d-md-block dropdown-toggle ps-2">{{ userName }}</span>
       </a><!-- End Profile Iamge Icon -->
 
       <ul class="dropdown-menu dropdown-menu-end dropdown-menu-arrow profile">
-        <li class="dropdown-header">
-          <h6>Kevin Anderson</h6>
-          <span>Web Designer</span>
-        </li>
+        
         <li>
           <hr class="dropdown-divider">
         </li>
@@ -233,9 +224,7 @@
   name: "ProfileImage",
   data() {
     return {
-      messages1: "/client/resources/img/messages-1.png", // 경로를 Vue 프로젝트 구조에 맞게 설정
-      messages2: "/client/resources/img/messages-2.png", // 경로를 Vue 프로젝트 구조에 맞게 설정
-      messages3: "/client/resources/img/messages-3.png", // 경로를 Vue 프로젝트 구조에 맞게 설정
+      userName: '',
       logo: "/client/resources/img/logo_t.png", // 경로를 Vue 프로젝트 구조에 맞게 설정
     };
   },
 
client/views/layout/Menu.vue (deleted)
--- client/views/layout/Menu.vue
@@ -1,29 +0,0 @@
-<template>
-    <div>
-        메뉴 : 
-        <router-link to="/"><span>Main</span></router-link>
-        ,
-        <router-link to="/main2"><span>Main2</span></router-link>
-    </div>
-</template>
- 
-<script>
-export default {
-    data () {
-        return {
-        }
-    },
-    methods: {
-
-    },
-    watch: {
-
-    },
-    computed: {
-
-    },
-    mounted() {
-        console.log('Menu mounted');
-    }
-}
-</script>(파일 끝에 줄바꿈 문자 없음)
client/views/layout/SideBar.vue
--- client/views/layout/SideBar.vue
+++ client/views/layout/SideBar.vue
@@ -32,14 +32,23 @@
           
         </ul>
       </li>
-  <li class="nav-item">
-    
-  </li><!-- End F.A.Q Page Nav -->
-
-  <li class="nav-item">
-    <router-link to="/ProjectList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i>
-        <span>프로젝트관리</span></router-link>
-  </li><!-- End Contact Page Nav -->
+      <li class="nav-item">
+        <a class="nav-link collapsed" data-bs-target="#components-nav" data-bs-toggle="collapse" href="#">
+          <i class="bi bi-journal-text"></i>
+        <span>프로젝트관리</span><i class="bi bi-chevron-down ms-auto"></i>
+        </a>
+        <ul id="components-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">
+          <li>
+            <router-link to="/ProjectInsert" class="nav-link " active-class="active"><i class="bi bi-circle"></i><span>프로젝트 등록</span></router-link>
+          </li>
+          
+          <li>
+            <router-link to="/ProjectList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i>
+              <span>프로젝트 내역</span></router-link>
+          </li>
+         
+        </ul>
+      </li>
 
   <li class="nav-item">
     <router-link to="/NoticeList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i>
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -1,6 +1,9 @@
 import { createWebHistory, createRouter } from "vue-router";
 
 import Main from '../pages/main/Main.vue';
+import Login from '../pages/User/Login.vue';
+import Join from '../pages/User/Join.vue';
+
 import Main2 from '../pages/main/Main2.vue';
 
 import HomeView from "../pages/HomeView.vue";
@@ -11,6 +14,7 @@
 import HyugaList from '../pages/Employee/HyugaList.vue';
 import HyugaInsert from '../pages/Employee/HyugaInsert.vue';
 import ProjectList from '../pages/Employee/ProjectList.vue';
+import ProjectInsert from '../pages/Employee/ProjectInsert.vue';
 // 관리자
 import DeptList from '../pages/Manager/DeptList.vue';
 import EmployeeList from '../pages/Manager/EmployeeList.vue';
@@ -20,10 +24,13 @@
 const routes = [
     /* 메인화면 */
     { path: '/', name: '/', component: Main},
-    { path: '/main2.page', name: '/main2', component: Main2},
+    { path: '/Login', name: 'Login', component: Login},
+    { path: '/Join', name: 'Join', component: Join},
+    
     { path: '/ChuljangList', name: 'ChuljangList', component: ChuljangList },
     { path: '/HyugaList', name: 'HyugaList', component: HyugaList },
     { path: '/HyugaInsert', name: 'HyugaInsert', component: HyugaInsert },
+    { path: '/ProjectInsert', name: 'ProjectInsert', component: ProjectInsert },
     { path: '/ProjectList', name: 'ProjectList', component: ProjectList },
     { path: '/DeptList', name: 'DeptList', component: DeptList },
     { path: '/EmployeeList', name: 'EmployeeList', component: EmployeeList },
client/views/pages/Employee/ChuljangList.vue
--- client/views/pages/Employee/ChuljangList.vue
+++ client/views/pages/Employee/ChuljangList.vue
@@ -1,54 +1,158 @@
 <template>
-   <div class="pagetitle">
-      <h1>출장관리</h1>
-    
-    </div><!-- End Page Title -->
-    <section class="section">
-      <div class="row">
-        <div class="col-lg-12">
+  <div class="pagetitle">
+    <h1>출장내역</h1>
+  </div>
+  <!-- End Page Title -->
+  <section class="section">
+    <div class="row">
 
-          <div class="card">
-            <div class="card-body">
-              <h5 class="card-title">출장관리</h5>
-              <p></p>
 
-              <!-- Table with stripped rows -->
-        <Table :rows="tableData" />
-              <!-- End Table with stripped rows -->
+      <div class="col-lg-12">
+        <div class="card">
+          <div class="card-body">
+            <h5 class="card-title">출장내역</h5>
+            <div class="d-flex pb-3 justify-content-between ">
+
+              <div class="datatable-search d-flex gap-1 ">
+                <div class="col-xxl-5 col-md-3">
+                  <select class="form-select " v-model="selectedYear">
+                    <option value="">연도</option>
+                    <option v-for="year in years" :key="year" :value="year">{{ year }}</option>
+                  </select>
+                </div>
+                <div class="col-xxl-5 col-md-3">
+                  <select class="form-select" v-model="selectedMonth">
+                    <option value="">월</option>
+                    <option v-for="month in months" :key="month" :value="month">{{ month }}</option>
+                  </select>
+                </div>
+                <button type="button" class="btn btn-outline-secondary col-xxl-5 col-xl-3"
+                  @click="filterData">조회</button>
+
+              </div>
+              <div class="d-flex justify-content-end ">
+                <button type="button" class="btn btn-outline-secondary" @click="registerLeave">
+                  등록
+                </button>
+                <button type="button" class="btn btn-outline-success" @click="saveChanges">
+                  저장
+                </button>
+                <button type="button" class="btn btn-outline-danger" @click="deletePending">
+                  삭제
+                </button>
+              </div>
 
             </div>
-            
-          </div>
+            <!-- Table  -->
+            <table id="myTable" class="table datatable table-hover">
+              <!-- 동적으로 <th> 생성 -->
+              <thead>
+                <tr>
+                  <th>No </th>
+                  <th data-type="date" data-format="YYYY/DD/MM">시작일자</th>
+                  <th data-type="date" data-format="YYYY/DD/MM">종료일자</th>
+                  <th>출장지</th>
+                </tr>
+              </thead>
+              <!-- 동적으로 <td> 생성 -->
+              <tbody>
+                <tr v-for="(item, index) in ChuljangData" :key="item.startDate + index">
+  <td>
+    <div class="form-check">
+      <label class="form-check-label" for="acceptTerms">{{ index + 1 }}</label>
+      <input v-model="item.acceptTerms" class="form-check-input" type="checkbox" />
+    </div>
+  </td>
+  <td><input type="date" v-model="item.startDate" /></td>
+  <td><input type="date" v-model="item.endDate" /></td>
+  <td><input type="text" v-model="item.where" /></td>
+</tr>
+              </tbody>
+            </table>
 
+            <!-- End Table -->
+          </div>
         </div>
       </div>
-    </section>
-  </template>
-  
-  <script>
-  import Table from "../../component/Table.vue";
-  export default {
+    </div>
+  </section>
+</template>
+
+<script>
+import { DataTable } from 'simple-datatables'
+export default {
   data() {
     return {
-      tableData: [
-        { name: 'Unity Pugh', extension: '9958', city: 'Curicó', startDate: '2005/02/11', completion: 37 },
-        { name: 'Zelenia Roman', extension: '7516', city: 'Redwater', startDate: '2012/03/03', completion: 31 },
-        { name: 'Zelenia Roman', extension: '7516', city: 'Redwater', startDate: '2012/03/03', completion: 31 },
-        { name: 'Zelenia Roman', extension: '7516', city: 'Redwater', startDate: '2012/03/03', completion: 31 },
-        { name: 'Zelenia Roman', extension: '7516', city: 'Redwater', startDate: '2012/03/03', completion: 31 },
-        { name: 'Zelenia Roman', extension: '7516', city: 'Redwater', startDate: '2012/03/03', completion: 31 },
-      ]
+      // 데이터 초기화
+      years: [2023, 2024, 2025], // 연도 목록
+      months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록
+      selectedYear: '',
+      selectedMonth: '',
+      ChuljangData: [
+      { startDate: '', endDate: '', where: '대구', acceptTerms: false },
+      { startDate: '', endDate: '', where: '경산', acceptTerms: false },
+        // 더 많은 데이터 추가...
+      ],
+      filteredData: [],
     };
   },
-  methods: {
+  computed: {
+
   },
-  components: {
-    Table,
+  methods: {
+    
+    registerLeave() {
+      console.log("등록 버튼 클릭됨");
+
+      // Vue의 반응성 문제를 피하기 위해, 새로운 객체를 추가합니다.
+      this.ChuljangData = [
+        ...this.ChuljangData,
+        { startDate: '', endDate: '', where: '', acceptTerms: false }
+      ];
+
+      console.log(this.ChuljangData); // 배열 상태 출력
+    },
+    saveChanges() {
+      console.log("저장된 데이터:", this.ChuljangData);
+      alert("저장되었습니다.");
+    },
+    deletePending() {
+    // 선택된 항목만 필터링하여 삭제
+    const selectedItems = this.ChuljangData.filter(item => item.acceptTerms);
+
+    // 승인된 항목이 없으면 삭제 진행
+    if (selectedItems.length > 0) {
+      this.ChuljangData = this.ChuljangData.filter(item => !item.acceptTerms);
+      alert(`${selectedItems.length}개의 항목이 삭제되었습니다.`);
+    } else {
+      alert("선택된 항목이 없습니다.");
+    }
+  },
+    // 날짜 필터 적용
+    filterData() {
+      this.filteredData = this.ChuljangData.filter(item => {
+        const itemYear = new Date(item.startDate).getFullYear();
+        const itemMonth = new Date(item.startDate).getMonth() + 1; // 월은 0부터 시작하므로 1을 더해줍니다.
+
+        return (
+          (!this.selectedYear || itemYear === parseInt(this.selectedYear)) &&
+          (!this.selectedMonth || itemMonth === parseInt(this.selectedMonth))
+        );
+      });
+    },
+
+    // 페이지 변경
+    changePage(page) {
+      this.currentPage = page;
+    },
+  },
+  mounted() {
+    
+    // 처음에는 모든 데이터를 표시
+    this.filteredData = this.ChuljangData;
+  
   },
 };
-  </script>
-  
-  <style scoped>
+</script>
 
-  </style>
-  
(파일 끝에 줄바꿈 문자 없음)
+<style scoped></style>
client/views/pages/Employee/HyugaInsert.vue
--- client/views/pages/Employee/HyugaInsert.vue
+++ client/views/pages/Employee/HyugaInsert.vue
@@ -84,6 +84,7 @@
 </template>
 
 <script>
+import { useAuthStore } from '../../stores/authStore';
 export default {
   data() {
     const today = new Date().toISOString().split('T')[0];
@@ -97,6 +98,13 @@
       reason: "", // 사유
     };
   },
+  computed: {
+    // Pinia Store의 상태를 가져옵니다.
+    loginUser() {
+      const authStore = useAuthStore();
+      return authStore.getLoginUser;
+    },
+  },
   methods: {
     // 폼 검증 메서드
     validateForm() {
client/views/pages/Employee/HyugaList.vue
--- client/views/pages/Employee/HyugaList.vue
+++ client/views/pages/Employee/HyugaList.vue
@@ -1,25 +1,32 @@
 <template>
   <div class="pagetitle">
-    <h1>휴가관리</h1>
+    <h1>휴가내역</h1>
   </div>
   <!-- End Page Title -->
   <section class="section">
     <div class="row">
+      <!-- 해당년도 연차 수 -->
       <!-- 전체 -->
       <div class="col-xxl-2 col-md-3">
-        <h5 class="card-title"><span>전체</span>12개</h5>
+        <button type="button" class="btn btn-light mb-2">
+          전체 <span class="badge bg-secondary text-light">12개</span>
+        </button>
       </div>
       <!-- End 전체 -->
 
       <!-- 사용 -->
       <div class="col-xxl-2 col-md-3">
-        <h5 class="card-title"><span>사용</span>1개</h5>
+        <button type="button" class="btn btn-light mb-2">
+          사용 <span class="badge bg-secondary text-light">1개</span>
+        </button>
       </div>
       <!-- End 사용 -->
 
       <!-- 미사용 -->
       <div class="col-xxl-2 col-xl-3">
-        <h5 class="card-title"><span>미사용</span>1개</h5>
+        <button type="button" class="btn btn-light mb-2">
+          미사용 <span class="badge bg-secondary text-light">1개</span>
+        </button>
       </div>
       <!-- End 미사용 -->
 
@@ -27,91 +34,64 @@
         <div class="card">
           <div class="card-body">
             <h5 class="card-title">휴가내역</h5>
-            
+            <div class="d-flex pb-3 justify-content-between ">
+
+              <div class="datatable-search d-flex gap-1 ">
+                <div class="col-xxl-5 col-md-3">
+                  <select class="form-select " v-model="selectedYear">
+                    <option value="">전체</option>
+                    <option v-for="year in years" :key="year" :value="year">{{ year }}</option>
+                  </select>
+                </div>
+                <div class="col-xxl-5 col-md-3">
+                  <select class="form-select" v-model="selectedMonth">
+                    <option value="">월</option>
+                    <option v-for="month in months" :key="month" :value="month">{{ month }}</option>
+                  </select>
+                </div>
+                <button type="button" class="btn btn-outline-secondary col-xxl-5 col-xl-3"
+                  @click="filterData">조회</button>
+
+              </div>
+              <div class="d-flex justify-content-end ">
+                <button type="button" class="btn btn-outline-danger" @click="deletePending">
+                  삭제
+                </button>
+              </div>
+
+            </div>
             <!-- Table  -->
-            <div class="datatable-wrapper datatable-loading no-footer sortable searchable fixed-columns">
-
-
-<div class="datatable-container">
-  <div class="d-flex justify-content-end ">
-          <button
-            type="button"
-            class="btn btn-outline-primary"
-            @click="requestApproval"
-          >
-            승인요청
-          </button>
-          <button
-            type="button"
-            class="btn btn-outline-secondary"
-            @click="registerLeave"
-          >
-            등록
-          </button>
-          <button
-            type="button"
-            class="btn btn-outline-success"
-            @click="saveChanges"
-          >
-            저장
-          </button>
-          <button
-            type="button"
-            class="btn btn-outline-danger"
-            @click="deletePending"
-          >
-            삭제
-          </button>
-        </div>
-  <table class="table datatable datatable-table">
-    <!-- 동적으로 <th> 생성 -->
-    <thead>
-      <tr>
-        <th>No</th>
-        <th></th>
-        <th>구분</th>
-        <th>시작일자</th>
-        <th>종료일자</th>
-        <th>사용일</th>
-        <th>사유</th>
-        <th>승인여부</th>
-      </tr>
-    </thead>
-    <!-- 동적으로 <td> 생성 -->
-    <tbody>
-      <tr>
-        <td>{{ index }}</td>
-        <td><input type="checkbox"></td>
-        <td></td>
-        <td></td>
-        <td></td>
-        <td></td>
-        <td></td>
-        <td></td>
-      </tr>
-    </tbody>
-  </table>
-</div>
-
-<div class="datatable-bottom justify-content-center pagination">
-
-  <nav class="datatable-pagination">
-    <ul class="datatable-pagination-list">
-      <li :class="{ 'datatable-disabled': currentPage === 1 }" @click="changePage(currentPage - 1)">
-        <button class="datatable-pagination-list-item-link">‹</button>
-      </li>
-      <li v-for="page in totalPages" :key="page" :class="{ 'datatable-active': currentPage === page }">
-        <button class="datatable-pagination-list-item-link" @click="changePage(page)">
-          {{ page }}
-        </button>
-      </li>
-      <li :class="{ 'datatable-disabled': currentPage === totalPages }" @click="changePage(currentPage + 1)">
-        <button class="datatable-pagination-list-item-link">›</button>
-      </li>
-    </ul>
-  </nav>
-</div>
-</div>
+            <table id="myTable" class="table datatable table-hover">
+              <!-- 동적으로 <th> 생성 -->
+              <thead>
+                <tr>
+                  <th>No</th>
+                  <th>구분</th>
+                  <th data-type="date" data-format="YYYY/DD/MM">시작일자</th>
+                  <th data-type="date" data-format="YYYY/DD/MM">종료일자</th>
+                  <th>사용일</th>
+                  <th>사유</th>
+                  <th>승인여부</th>
+                </tr>
+              </thead>
+              <!-- 동적으로 <td> 생성 -->
+              <tbody>
+                <tr v-for="(item, index) in filteredData" :key="index">
+                  <td>
+                    <div class="form-check">
+                      <label class="form-check-label" for="acceptTerms">{{ index + 1 }}</label>
+                      <input v-model="item.acceptTerms" class="form-check-input" type="checkbox" />
+                    </div>
+                  </td>
+                  <td>{{ item.category }}</td>
+                  <td>{{ item.startDate }}</td>
+                  <td>{{ item.endDate }}</td>
+                  <td>{{ item.usedDays }}</td>
+                  <td>{{ item.reason }}</td>
+                  <td>{{ item.approvalStatus ? '승인' : '대기' }}</td>
+                </tr>
+              </tbody>
+            </table>
             <!-- End Table -->
           </div>
         </div>
@@ -121,19 +101,75 @@
 </template>
 
 <script>
-import Table from "../../component/Table.vue";
-
+import { DataTable } from 'simple-datatables'
 export default {
   data() {
     return {
+      // 데이터 초기화
+      years: [2023, 2024, 2025], // 연도 목록
+      months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록
+      selectedYear: '',
+      selectedMonth: '',
+      vacationData: [
+        { category: '연차', startDate: '2023-01-01', endDate: '2023-01-03', usedDays: 2, reason: '휴식', approvalStatus: true, acceptTerms: false },
+        { category: '병가', startDate: '2024-02-10', endDate: '2024-02-12', usedDays: 2, reason: '병원', approvalStatus: false, acceptTerms: false },
+        { category: '병가', startDate: '2025-01-10', endDate: '2024-02-12', usedDays: 2, reason: '병원', approvalStatus: false, acceptTerms: false },
+      ],
+      filteredData: [],
     };
   },
+  computed: {
+  },
   methods: {
-   
+    deletePending() {
+    // 선택된 항목만 필터링하여 삭제
+    const selectedItems = this.vacationData.filter(item => item.acceptTerms);
+
+    // 승인된 항목인지 확인하여 삭제할 수 없으면 경고
+    const nonDeletableItems = selectedItems.filter(item => item.approvalStatus === true);
+    
+    // 삭제할 수 없는 항목이 있으면 경고
+    if (nonDeletableItems.length > 0) {
+      alert("승인된 건은 삭제할 수 없습니다.");
+      return;  // 승인된 항목이 있으면 삭제를 진행하지 않음
+    }
+
+    // 승인된 항목이 없으면 삭제 진행
+    if (selectedItems.length > 0) {
+      this.vacationData = this.vacationData.filter(item => !item.acceptTerms);
+      alert(`${selectedItems.length}개의 항목이 삭제되었습니다.`);
+    } else {
+      alert("선택된 항목이 없습니다.");
+    }
   },
-  components: {
-    Table,
+    // 날짜 필터 적용
+    filterData() {
+    // 전체 선택 시 필터링 없이 모든 데이터를 표시
+    if (!this.selectedYear && !this.selectedMonth) {
+      this.filteredData = this.vacationData;
+    } else {
+      // 선택된 연도와 월에 맞게 필터링
+      this.filteredData = this.vacationData.filter(item => {
+        const itemYear = new Date(item.startDate).getFullYear();
+        const itemMonth = new Date(item.startDate).getMonth() + 1; // 월은 0부터 시작하므로 1을 더해줍니다.
+
+        return (
+          (!this.selectedYear || itemYear === parseInt(this.selectedYear)) &&
+          (!this.selectedMonth || itemMonth === parseInt(this.selectedMonth))
+        );
+      });
+    }
   },
+  
+
+  },
+  mounted() {
+    const currentYear = new Date().getFullYear();
+    this.selectedYear = currentYear; 
+    // 처음에는 현재년도 데이터를 표시
+    this.filteredData = this.vacationData.filter(item => new Date(item.startDate).getFullYear() === currentYear);
+  },
+  
 };
 </script>
 
 
client/views/pages/User/Join.vue (added)
+++ client/views/pages/User/Join.vue
@@ -0,0 +1,109 @@
+<template>
+       <div class="container">
+
+<section class="section register min-vh-100 d-flex flex-column align-items-center justify-content-center py-4">
+  <div class="container">
+    <div class="row justify-content-center">
+      <div class="col-lg-4 col-md-6 d-flex flex-column align-items-center justify-content-center">
+
+        <div class="d-flex justify-content-center py-4">
+          <a href="index.html" class="logo d-flex align-items-center w-auto">
+            <span class="d-none d-lg-block"> <img :src="logo" alt=""></span>
+          </a>
+        </div><!-- End Logo -->
+
+        <div class="card mb-3">
+
+          <div class="card-body">
+
+            <div class=" pb-2">
+              <h5 class="card-title text-center pb-0 fs-4">회원가입</h5>
+            </div>
+
+            <form class="row g-3 needs-validation "  :class="{ 'was-validated': formSubmitted }" @submit.prevent="handleRegister" novalidate>
+              <div class="col-12">
+                <label for="yourName" class="form-label">이름</label>
+                <input  v-model="name" type="text" name="name" class="form-control" id="yourName" required>
+                <div class="invalid-feedback">이름을 입력해주세요.</div>
+              </div>
+
+              <div class="col-12">
+                <label for="yourUsername" class="form-label">Email</label>
+                <div class="input-group has-validation">
+                  <span class="input-group-text" id="inputGroupPrepend">@</span>
+                  <input  v-model="email" type="text" name="username" class="form-control" id="yourUsername" required>
+                  <div class="invalid-feedback">회사 이메일을 입력해주세요.</div>
+                </div>
+              </div>
+
+              <div class="col-12">
+                <label for="yourPassword" class="form-label">비밀번호</label>
+                <input  v-model="password" type="password" name="password" class="form-control" id="yourPassword" required>
+                <div class="invalid-feedback">비밀번호를 입력해주세요.</div>
+              </div>
+
+              <div class="col-12">
+                <div class="form-check">
+                  <input v-model="acceptTerms"  class="form-check-input" name="terms" type="checkbox" value="" id="acceptTerms" required>
+                  <label class="form-check-label" for="acceptTerms">이용약관에 동의합니다.</label>
+                  <div class="invalid-feedback">이용약관에 동의하셔야 합니다.</div>
+                </div>
+              </div>
+              <div class="col-12">
+                <button class="btn btn-primary w-100"  type="submit">계정 생성</button>
+              </div>
+              <div class="col-12">
+                <p class="small mb-0">이미 계정이 있으신가요?  <router-link to="Login">로그인</router-link></p>
+              </div>
+            </form>
+
+          </div>
+        </div>
+
+      </div>
+    </div>
+  </div>
+
+</section>
+
+</div>
+  </template>
+  
+  <script>
+    export default {
+  data() {
+    return {
+      name: '',
+      email: '',
+      password: '',
+      acceptTerms: false,
+      formSubmitted: false,
+      logo: "/client/resources/img/logo_t.png", // 경로를 Vue 프로젝트 구조에 맞게 설정
+    };
+  },
+  methods: {
+    handleRegister() {
+      this.formSubmitted = true; 
+      // 이메일과 비밀번호가 빈 값이 아니어야 한다
+      if (this.email && this.password && this.name && this.acceptTerms) {
+        // 로컬 스토리지에 회원가입 정보 저장
+        const userData = {
+          name: this.name,
+          email: this.email,
+          password: this.password,
+        };
+        localStorage.setItem("user", JSON.stringify(userData));  // 로컬 스토리지에 데이터 저장
+        
+        // 계정 생성 후 로그인 페이지로 리디렉션
+        this.$router.push("/login");
+      } else {
+      }
+    },
+  },
+};
+  </script>
+  
+  <style scoped>
+
+  </style>
+  (파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/User/Login.vue (added)
+++ client/views/pages/User/Login.vue
@@ -0,0 +1,97 @@
+<template>
+     <div class="container">
+
+<section class="section register min-vh-100 d-flex flex-column align-items-center justify-content-center py-4">
+  <div class="container">
+    <div class="row justify-content-center">
+      <div class="col-lg-4 col-md-6 d-flex flex-column align-items-center justify-content-center">
+        <div class="d-flex justify-content-center py-4">
+          <a href="index.html" class="logo d-flex align-items-center w-auto">
+           
+            <span class="d-none d-lg-block"> <img :src="logo" alt=""></span>
+          </a>
+        </div><!-- End Logo -->
+
+        <div class="card mb-3">
+
+          <div class="card-body">
+
+            <div class=" pb-2">
+              <h5 class="card-title text-center pb-0 fs-4">로그인</h5>
+            </div>
+
+            <form class="row g-3 needs-validation" :class="{ 'was-validated': formSubmitted }" novalidate  @submit.prevent="handleLogin">
+
+              <div class="col-12">
+                <label for="yourUsername" class="form-label">E-mail</label>
+                <div class="input-group has-validation">
+                  <span class="input-group-text" id="inputGroupPrepend">@</span>
+                  <input  v-model="email" type="text" name="username" class="form-control" id="yourUsername" required>
+                  <div class="invalid-feedback">회사 이메일을 입력해주세요.</div>
+                </div>
+              </div>
+
+              <div class="col-12">
+                <label for="yourPassword" class="form-label">Password</label>
+                <input v-model="password" type="password" name="password" class="form-control" id="yourPassword" required>
+                <div class="invalid-feedback">비밀번호를 입력해주세요.</div>
+              </div>
+
+              <div class="col-12">
+                <div class="form-check">
+                  <input class="form-check-input" type="checkbox" name="remember" value="true" id="rememberMe">
+                  <label class="form-check-label" for="rememberMe">로그인 유지하기</label>
+                </div>
+              </div>
+              <div class="col-12">
+                <button class="btn btn-primary w-100" type="submit">로그인</button>
+              </div>
+              <div class="col-12">
+                <p class="small mb-0"> <router-link to="Join">회원가입</router-link></p>
+              </div>
+            </form>
+
+          </div>
+        </div>
+
+
+      </div>
+    </div>
+  </div>
+
+</section>
+
+</div>
+  </template>
+  
+  <script>
+  export default {
+  data() {
+    return {
+        // 임시 로그인 정보
+        email: '',
+      password: '',
+      validEmail: 'test@takensoft.co.kr',
+      validPassword: '1234',
+      validName: '테이큰소프트',
+      formSubmitted: false,
+      logo: "/client/resources/img/logo_t.png", // 경로를 Vue 프로젝트 구조에 맞게 설정
+    };
+  },
+  methods: {
+    handleLogin() {
+      this.formSubmitted = true; 
+      if (this.email === this.validEmail && this.password === this.validPassword) {
+        alert('로그인 성공!');
+        this.$router.push("/main");
+      } else {
+      }
+    },
+  },
+};
+  </script>
+  
+  <style scoped>
+
+  </style>
+  (파일 끝에 줄바꿈 문자 없음)
 
client/views/stores/authStore.js (added)
+++ client/views/stores/authStore.js
@@ -0,0 +1,23 @@
+// src/stores/authStore.js
+import { defineStore } from 'pinia';
+
+export const useAuthStore = defineStore('auth', {
+  state: () => ({
+    loginUser: null,
+    key: null,
+  }),
+  getters: {
+    // Vuex의 getters를 Pinia의 computed 속성처럼 사용합니다.
+    getLoginUser: (state) => state.loginUser,
+    getKey: (state) => state.key,
+  },
+  actions: {
+    // Vuex의 mutations과 actions을 Pinia에서는 하나의 `actions`로 합칩니다.
+    setLoginUser(newValue) {
+      this.loginUser = newValue;
+    },
+    setKey(newValue) {
+      this.key = newValue;
+    },
+  },
+});
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -1,5 +1,5 @@
 {
-  "name": "node_vue_web_server_framework_v1.0-master",
+  "name": "calendar",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
@@ -13,8 +13,11 @@
         "file-loader": "6.2.0",
         "fs": "0.0.1-security",
         "pg": "8.8.0",
+        "pinia": "^2.2.0",
+        "realgrid": "^2.8.8",
+        "simple-datatables": "^9.2.1",
         "url-loader": "4.1.1",
-        "vue": "3.2.40",
+        "vue": "^3.5.13",
         "vue-loader": "^17.0.0",
         "vue-router": "4.1.5",
         "vue-style-loader": "4.1.3",
@@ -244,17 +247,19 @@
       }
     },
     "node_modules/@babel/helper-string-parser": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
-      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+      "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+      "license": "MIT",
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.19.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
-      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+      "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+      "license": "MIT",
       "engines": {
         "node": ">=6.9.0"
       }
@@ -294,9 +299,13 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz",
-      "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==",
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz",
+      "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.26.5"
+      },
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -338,13 +347,13 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz",
-      "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==",
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz",
+      "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==",
+      "license": "MIT",
       "dependencies": {
-        "@babel/helper-string-parser": "^7.19.4",
-        "@babel/helper-validator-identifier": "^7.19.1",
-        "to-fast-properties": "^2.0.0"
+        "@babel/helper-string-parser": "^7.25.9",
+        "@babel/helper-validator-identifier": "^7.25.9"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -450,111 +459,110 @@
       "integrity": "sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw=="
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz",
-      "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
+      "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
+      "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/shared": "3.2.40",
+        "@babel/parser": "^7.25.3",
+        "@vue/shared": "3.5.13",
+        "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
-        "source-map": "^0.6.1"
+        "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz",
-      "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
+      "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/compiler-core": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-core": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz",
-      "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
+      "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
+      "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.40",
-        "@vue/compiler-dom": "3.2.40",
-        "@vue/compiler-ssr": "3.2.40",
-        "@vue/reactivity-transform": "3.2.40",
-        "@vue/shared": "3.2.40",
+        "@babel/parser": "^7.25.3",
+        "@vue/compiler-core": "3.5.13",
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7",
-        "postcss": "^8.1.10",
-        "source-map": "^0.6.1"
+        "magic-string": "^0.30.11",
+        "postcss": "^8.4.48",
+        "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz",
-      "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
+      "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/compiler-dom": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "node_modules/@vue/devtools-api": {
-      "version": "6.4.3",
-      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.4.3.tgz",
-      "integrity": "sha512-9WCRwdROJvWcHAdyrR7SZMM/qUvllDZnpndHXokThkUsjnJ2xe4/pvsH9FZrxFe22L+JmDKczL79HjLJ7DK9rg=="
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.40.tgz",
-      "integrity": "sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
+      "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/shared": "3.2.40"
-      }
-    },
-    "node_modules/@vue/reactivity-transform": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz",
-      "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==",
-      "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.40",
-        "@vue/shared": "3.2.40",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7"
+        "@vue/shared": "3.5.13"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.40.tgz",
-      "integrity": "sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
+      "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/reactivity": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/reactivity": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz",
-      "integrity": "sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
+      "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/runtime-core": "3.2.40",
-        "@vue/shared": "3.2.40",
-        "csstype": "^2.6.8"
+        "@vue/reactivity": "3.5.13",
+        "@vue/runtime-core": "3.5.13",
+        "@vue/shared": "3.5.13",
+        "csstype": "^3.1.3"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.40.tgz",
-      "integrity": "sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
+      "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/compiler-ssr": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13"
       },
       "peerDependencies": {
-        "vue": "3.2.40"
+        "vue": "3.5.13"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz",
-      "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ=="
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
+      "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
+      "license": "MIT"
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.11.1",
@@ -1230,9 +1238,16 @@
       }
     },
     "node_modules/csstype": {
-      "version": "2.6.21",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
-      "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "license": "MIT"
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.13",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+      "license": "MIT"
     },
     "node_modules/debug": {
       "version": "4.3.4",
@@ -1266,6 +1281,12 @@
         "node": ">= 0.8",
         "npm": "1.2.8000 || >= 1.4.16"
       }
+    },
+    "node_modules/diff-dom": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/diff-dom/-/diff-dom-5.1.4.tgz",
+      "integrity": "sha512-TSEaVdVGictY1KHg7VpVw2nuM02YKC9C8/qBkGiCnkiAybVbu1zQTMj2/dnVLRO7Z62UsqzHGpXweiOj5/jaZg==",
+      "license": "LGPL-3.0"
     },
     "node_modules/ee-first": {
       "version": "1.1.1",
@@ -1303,6 +1324,18 @@
       },
       "engines": {
         "node": ">=10.13.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
       }
     },
     "node_modules/envinfo": {
@@ -1384,7 +1417,8 @@
     "node_modules/estree-walker": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "license": "MIT"
     },
     "node_modules/etag": {
       "version": "1.8.1",
@@ -1995,12 +2029,19 @@
       }
     },
     "node_modules/magic-string": {
-      "version": "0.25.9",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
-      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "version": "0.30.17",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "license": "MIT",
       "dependencies": {
-        "sourcemap-codec": "^1.4.8"
+        "@jridgewell/sourcemap-codec": "^1.5.0"
       }
+    },
+    "node_modules/magic-string/node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "license": "MIT"
     },
     "node_modules/make-dir": {
       "version": "2.1.0",
@@ -2103,9 +2144,16 @@
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "node_modules/nanoid": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
       "bin": {
         "nanoid": "bin/nanoid.cjs"
       },
@@ -2322,9 +2370,10 @@
       }
     },
     "node_modules/picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
     },
     "node_modules/picomatch": {
       "version": "2.3.1",
@@ -2346,6 +2395,58 @@
         "node": ">=6"
       }
     },
+    "node_modules/pinia": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.0.tgz",
+      "integrity": "sha512-iPrIh26GMqfpUlMOGyxuDowGmYousTecbTHFwT0xZ1zJvh23oQ+Cj99ZoPQA1TnUPhU6AuRPv6/drkTCJ0VHQA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.3",
+        "vue-demi": "^0.14.8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.4.0",
+        "typescript": ">=4.4.4",
+        "vue": "^2.6.14 || ^3.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia/node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/pkg-dir": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -2358,9 +2459,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.17",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
-      "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
+      "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
       "funding": [
         {
           "type": "opencollective",
@@ -2369,12 +2470,17 @@
         {
           "type": "tidelift",
           "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
         }
       ],
+      "license": "MIT",
       "dependencies": {
-        "nanoid": "^3.3.4",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
+        "nanoid": "^3.3.8",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
       },
       "engines": {
         "node": "^10 || ^12 || >=14"
@@ -2562,6 +2668,12 @@
       "engines": {
         "node": ">=8.10.0"
       }
+    },
+    "node_modules/realgrid": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/realgrid/-/realgrid-2.8.8.tgz",
+      "integrity": "sha512-BTICyug9txZIaXZ/t3PujX99ipbJmIaAYbBQCn8cq+IvRHkyUrDwnaxBj7Dc26Qg9WmLiR17mKdl6DOAXeFmgg==",
+      "license": "license.txt"
     },
     "node_modules/rechoir": {
       "version": "0.7.1",
@@ -2769,6 +2881,16 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/simple-datatables": {
+      "version": "9.2.1",
+      "resolved": "https://registry.npmjs.org/simple-datatables/-/simple-datatables-9.2.1.tgz",
+      "integrity": "sha512-R+KRyZ4GNQdqj5IoW08OYi9FHqCbndbw+MfdOJDnwOGs6me9+BhbNuj2QHZR/GXQ04TLra43Uj2/Lk1KcLiJRw==",
+      "license": "LGPL-3.0",
+      "dependencies": {
+        "dayjs": "^1.11.10",
+        "diff-dom": "^5.1.3"
+      }
+    },
     "node_modules/slash": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
@@ -2786,9 +2908,10 @@
       }
     },
     "node_modules/source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
       "engines": {
         "node": ">=0.10.0"
       }
@@ -2801,11 +2924,6 @@
         "buffer-from": "^1.0.0",
         "source-map": "^0.6.0"
       }
-    },
-    "node_modules/sourcemap-codec": {
-      "version": "1.4.8",
-      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
     },
     "node_modules/split2": {
       "version": "4.1.0",
@@ -2904,14 +3022,6 @@
         "uglify-js": {
           "optional": true
         }
-      }
-    },
-    "node_modules/to-fast-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "engines": {
-        "node": ">=4"
       }
     },
     "node_modules/to-regex-range": {
@@ -3035,15 +3145,24 @@
       }
     },
     "node_modules/vue": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.40.tgz",
-      "integrity": "sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
+      "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
+      "license": "MIT",
       "dependencies": {
-        "@vue/compiler-dom": "3.2.40",
-        "@vue/compiler-sfc": "3.2.40",
-        "@vue/runtime-dom": "3.2.40",
-        "@vue/server-renderer": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-sfc": "3.5.13",
+        "@vue/runtime-dom": "3.5.13",
+        "@vue/server-renderer": "3.5.13",
+        "@vue/shared": "3.5.13"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
       }
     },
     "node_modules/vue-loader": {
@@ -3504,14 +3623,14 @@
       }
     },
     "@babel/helper-string-parser": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
-      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw=="
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+      "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
     },
     "@babel/helper-validator-identifier": {
-      "version": "7.19.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
-      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+      "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
     },
     "@babel/helper-validator-option": {
       "version": "7.18.6",
@@ -3539,9 +3658,12 @@
       }
     },
     "@babel/parser": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz",
-      "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA=="
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz",
+      "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==",
+      "requires": {
+        "@babel/types": "^7.26.5"
+      }
     },
     "@babel/template": {
       "version": "7.18.10",
@@ -3571,13 +3693,12 @@
       }
     },
     "@babel/types": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz",
-      "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==",
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz",
+      "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==",
       "requires": {
-        "@babel/helper-string-parser": "^7.19.4",
-        "@babel/helper-validator-identifier": "^7.19.1",
-        "to-fast-properties": "^2.0.0"
+        "@babel/helper-string-parser": "^7.25.9",
+        "@babel/helper-validator-identifier": "^7.25.9"
       }
     },
     "@discoveryjs/json-ext": {
@@ -3668,108 +3789,97 @@
       "integrity": "sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw=="
     },
     "@vue/compiler-core": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz",
-      "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
+      "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
       "requires": {
-        "@babel/parser": "^7.16.4",
-        "@vue/shared": "3.2.40",
+        "@babel/parser": "^7.25.3",
+        "@vue/shared": "3.5.13",
+        "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
-        "source-map": "^0.6.1"
+        "source-map-js": "^1.2.0"
       }
     },
     "@vue/compiler-dom": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz",
-      "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
+      "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
       "requires": {
-        "@vue/compiler-core": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-core": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "@vue/compiler-sfc": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz",
-      "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
+      "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
       "requires": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.40",
-        "@vue/compiler-dom": "3.2.40",
-        "@vue/compiler-ssr": "3.2.40",
-        "@vue/reactivity-transform": "3.2.40",
-        "@vue/shared": "3.2.40",
+        "@babel/parser": "^7.25.3",
+        "@vue/compiler-core": "3.5.13",
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7",
-        "postcss": "^8.1.10",
-        "source-map": "^0.6.1"
+        "magic-string": "^0.30.11",
+        "postcss": "^8.4.48",
+        "source-map-js": "^1.2.0"
       }
     },
     "@vue/compiler-ssr": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz",
-      "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
+      "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
       "requires": {
-        "@vue/compiler-dom": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "@vue/devtools-api": {
-      "version": "6.4.3",
-      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.4.3.tgz",
-      "integrity": "sha512-9WCRwdROJvWcHAdyrR7SZMM/qUvllDZnpndHXokThkUsjnJ2xe4/pvsH9FZrxFe22L+JmDKczL79HjLJ7DK9rg=="
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
     },
     "@vue/reactivity": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.40.tgz",
-      "integrity": "sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
+      "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
       "requires": {
-        "@vue/shared": "3.2.40"
-      }
-    },
-    "@vue/reactivity-transform": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz",
-      "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==",
-      "requires": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.40",
-        "@vue/shared": "3.2.40",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7"
+        "@vue/shared": "3.5.13"
       }
     },
     "@vue/runtime-core": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.40.tgz",
-      "integrity": "sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
+      "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
       "requires": {
-        "@vue/reactivity": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/reactivity": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "@vue/runtime-dom": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz",
-      "integrity": "sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
+      "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
       "requires": {
-        "@vue/runtime-core": "3.2.40",
-        "@vue/shared": "3.2.40",
-        "csstype": "^2.6.8"
+        "@vue/reactivity": "3.5.13",
+        "@vue/runtime-core": "3.5.13",
+        "@vue/shared": "3.5.13",
+        "csstype": "^3.1.3"
       }
     },
     "@vue/server-renderer": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.40.tgz",
-      "integrity": "sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
+      "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
       "requires": {
-        "@vue/compiler-ssr": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "@vue/shared": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz",
-      "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ=="
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
+      "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
     },
     "@webassemblyjs/ast": {
       "version": "1.11.1",
@@ -4289,9 +4399,14 @@
       "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
     },
     "csstype": {
-      "version": "2.6.21",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
-      "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "dayjs": {
+      "version": "1.11.13",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
     },
     "debug": {
       "version": "4.3.4",
@@ -4310,6 +4425,11 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
       "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
+    },
+    "diff-dom": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/diff-dom/-/diff-dom-5.1.4.tgz",
+      "integrity": "sha512-TSEaVdVGictY1KHg7VpVw2nuM02YKC9C8/qBkGiCnkiAybVbu1zQTMj2/dnVLRO7Z62UsqzHGpXweiOj5/jaZg=="
     },
     "ee-first": {
       "version": "1.1.1",
@@ -4339,6 +4459,11 @@
         "graceful-fs": "^4.2.4",
         "tapable": "^2.2.0"
       }
+    },
+    "entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
     },
     "envinfo": {
       "version": "7.8.1",
@@ -4851,11 +4976,18 @@
       }
     },
     "magic-string": {
-      "version": "0.25.9",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
-      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "version": "0.30.17",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
       "requires": {
-        "sourcemap-codec": "^1.4.8"
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      },
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+          "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+        }
       }
     },
     "make-dir": {
@@ -4931,9 +5063,9 @@
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "nanoid": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="
     },
     "negotiator": {
       "version": "0.6.3",
@@ -5089,9 +5221,9 @@
       }
     },
     "picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
     },
     "picomatch": {
       "version": "2.3.1",
@@ -5104,6 +5236,23 @@
       "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
       "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
     },
+    "pinia": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.0.tgz",
+      "integrity": "sha512-iPrIh26GMqfpUlMOGyxuDowGmYousTecbTHFwT0xZ1zJvh23oQ+Cj99ZoPQA1TnUPhU6AuRPv6/drkTCJ0VHQA==",
+      "requires": {
+        "@vue/devtools-api": "^6.6.3",
+        "vue-demi": "^0.14.8"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.14.10",
+          "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+          "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+          "requires": {}
+        }
+      }
+    },
     "pkg-dir": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -5113,13 +5262,13 @@
       }
     },
     "postcss": {
-      "version": "8.4.17",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
-      "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
+      "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
       "requires": {
-        "nanoid": "^3.3.4",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
+        "nanoid": "^3.3.8",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
       }
     },
     "postcss-modules-extract-imports": {
@@ -5245,6 +5394,11 @@
       "requires": {
         "picomatch": "^2.2.1"
       }
+    },
+    "realgrid": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/realgrid/-/realgrid-2.8.8.tgz",
+      "integrity": "sha512-BTICyug9txZIaXZ/t3PujX99ipbJmIaAYbBQCn8cq+IvRHkyUrDwnaxBj7Dc26Qg9WmLiR17mKdl6DOAXeFmgg=="
     },
     "rechoir": {
       "version": "0.7.1",
@@ -5399,6 +5553,15 @@
         "object-inspect": "^1.9.0"
       }
     },
+    "simple-datatables": {
+      "version": "9.2.1",
+      "resolved": "https://registry.npmjs.org/simple-datatables/-/simple-datatables-9.2.1.tgz",
+      "integrity": "sha512-R+KRyZ4GNQdqj5IoW08OYi9FHqCbndbw+MfdOJDnwOGs6me9+BhbNuj2QHZR/GXQ04TLra43Uj2/Lk1KcLiJRw==",
+      "requires": {
+        "dayjs": "^1.11.10",
+        "diff-dom": "^5.1.3"
+      }
+    },
     "slash": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
@@ -5410,9 +5573,9 @@
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
     },
     "source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
     },
     "source-map-support": {
       "version": "0.5.21",
@@ -5422,11 +5585,6 @@
         "buffer-from": "^1.0.0",
         "source-map": "^0.6.0"
       }
-    },
-    "sourcemap-codec": {
-      "version": "1.4.8",
-      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
     },
     "split2": {
       "version": "4.1.0",
@@ -5478,11 +5636,6 @@
         "serialize-javascript": "^6.0.0",
         "terser": "^5.14.1"
       }
-    },
-    "to-fast-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
     },
     "to-regex-range": {
       "version": "5.0.1",
@@ -5555,15 +5708,15 @@
       "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
     },
     "vue": {
-      "version": "3.2.40",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.40.tgz",
-      "integrity": "sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==",
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
+      "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
       "requires": {
-        "@vue/compiler-dom": "3.2.40",
-        "@vue/compiler-sfc": "3.2.40",
-        "@vue/runtime-dom": "3.2.40",
-        "@vue/server-renderer": "3.2.40",
-        "@vue/shared": "3.2.40"
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-sfc": "3.5.13",
+        "@vue/runtime-dom": "3.5.13",
+        "@vue/server-renderer": "3.5.13",
+        "@vue/shared": "3.5.13"
       }
     },
     "vue-loader": {
package.json
--- package.json
+++ package.json
@@ -8,16 +8,17 @@
     "file-loader": "6.2.0",
     "fs": "0.0.1-security",
     "pg": "8.8.0",
+    "pinia": "^2.2.0",
+    "realgrid": "^2.8.8",
+    "simple-datatables": "^9.2.1",
     "url-loader": "4.1.1",
-    "vue": "3.2.40",
+    "vue": "^3.5.13",
     "vue-loader": "^17.0.0",
     "vue-router": "4.1.5",
     "vue-style-loader": "4.1.3",
     "vue3-sfc-loader": "^0.8.4",
     "webpack": "5.74.0",
-    "pinia": "^2.3.0",
     "webpack-cli": "4.10.0"
-    
   },
   "scripts": {
     "prod": "set NODE_ENV=production&&node ./server/modules/web/server.js",
Add a comment
List