wad-lab 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/bin/index.js +477 -627
  2. package/package.json +1 -1
  3. package/templates/fullstack-springboot-vue/frontend/vue-axios-basic/src/App.vue +241 -429
  4. package/templates/fullstack-springboot-vue/frontend/vue-axios-basic/src/components/ItemFormModal.vue +183 -0
  5. package/templates/fullstack-springboot-vue/frontend/vue-axios-basic/src/components/ItemPatchModal.vue +193 -0
  6. package/templates/fullstack-springboot-vue/frontend/vue-axios-basic/src/components/ItemTable.vue +177 -0
  7. package/templates/fullstack-springboot-vue/frontend/vue-axios-pinia/src/App.vue +150 -441
  8. package/templates/fullstack-springboot-vue/frontend/vue-axios-pinia/src/components/ItemFormModal.vue +207 -0
  9. package/templates/fullstack-springboot-vue/frontend/vue-axios-pinia/src/components/ItemStats.vue +64 -0
  10. package/templates/fullstack-springboot-vue/frontend/vue-axios-pinia/src/components/ItemTable.vue +195 -0
  11. package/templates/fullstack-springboot-vue/frontend/vue-axios-router/src/App.vue +41 -90
  12. package/templates/fullstack-springboot-vue/frontend/vue-axios-router/src/components/ItemFormModal.vue +173 -0
  13. package/templates/fullstack-springboot-vue/frontend/vue-axios-router/src/components/ItemTable.vue +102 -0
  14. package/templates/fullstack-springboot-vue/frontend/vue-axios-router/src/components/NavBar.vue +57 -0
  15. package/templates/fullstack-springboot-vue/frontend/vue-axios-router/src/views/DepartmentView.vue +132 -296
  16. package/templates/fullstack-springboot-vue/frontend/vue-axios-router/src/views/EmployeeView.vue +126 -219
  17. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/components/ItemDetail.vue +115 -0
  18. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/components/ItemTable.vue +104 -0
  19. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/components/SideBar.vue +59 -0
  20. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/views/CompanyLayout.vue +2 -51
  21. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/views/DepartmentListView.vue +92 -250
  22. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/views/EmployeeDetailView.vue +59 -181
  23. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-children/src/views/EmployeeListView.vue +67 -315
  24. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/App.vue +19 -70
  25. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/components/ItemFormModal.vue +173 -0
  26. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/components/ItemPatchModal.vue +144 -0
  27. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/components/ItemTable.vue +102 -0
  28. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/components/NavBar.vue +53 -0
  29. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/views/DepartmentView.vue +107 -229
  30. package/templates/fullstack-springboot-vue/frontend/vue-axios-router-pinia/src/views/EmployeeView.vue +143 -257
  31. package/templates/game-bingo/script.js +1 -1
  32. package/templates/game-memory-cards/script.js +2 -2
  33. package/templates/game-rock-paper-scissors/index.html +3 -3
  34. package/templates/game-rock-paper-scissors/script.js +2 -2
  35. package/templates/game-tic-tac-toe/script.js +2 -2
  36. package/templates/game-whack-a-mole/index.html +9 -9
  37. package/templates/servlet-basic/WEB-INF/ItemServlet.java +13 -0
  38. package/templates/servlet-basic/WEB-INF/classes/ItemServlet.class +0 -0
  39. package/templates/servlet-flow/WEB-INF/HelloServlet.java +34 -25
  40. package/templates/servlet-flow/WEB-INF/classes/HelloServlet.class +0 -0
  41. package/templates/servlet-jdbc/WEB-INF/JdbcItemServlet.java +13 -0
  42. package/templates/servlet-jdbc/WEB-INF/classes/JdbcItemServlet.class +0 -0
  43. package/templates/snippets/mongo-queries-cheatsheet.txt +105 -0
  44. package/templates/springboot-2model-aggregation-join/.mvn/wrapper/maven-wrapper.properties +3 -0
  45. package/templates/springboot-2model-aggregation-join/mvnw +295 -0
  46. package/templates/springboot-2model-aggregation-join/mvnw.cmd +189 -0
  47. package/templates/springboot-2model-aggregation-join/pom.xml +54 -0
  48. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/AggregationApplication.java +11 -0
  49. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/controller/DepartmentController.java +65 -0
  50. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/controller/EmployeeController.java +65 -0
  51. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/dto/DepartmentStatsDTO.java +15 -0
  52. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/dto/EmployeeJoinDTO.java +19 -0
  53. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/model/Department.java +20 -0
  54. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/model/Employee.java +21 -0
  55. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/repository/DepartmentRepository.java +9 -0
  56. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/repository/EmployeeRepository.java +9 -0
  57. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/service/DepartmentService.java +81 -0
  58. package/templates/springboot-2model-aggregation-join/src/main/java/com/example/aggregation/service/EmployeeService.java +80 -0
  59. package/templates/springboot-2model-aggregation-join/src/main/resources/application.properties +2 -0
  60. package/templates/springboot-2model-aggregation-join/src/test/java/com/example/aggregation/AggregationApplicationTests.java +13 -0
  61. package/templates/vue-2model-crud-patch/README.md +64 -0
  62. package/templates/vue-2model-crud-patch/index.html +12 -0
  63. package/templates/vue-2model-crud-patch/package-lock.json +1785 -0
  64. package/templates/vue-2model-crud-patch/package.json +21 -0
  65. package/templates/vue-2model-crud-patch/src/App.vue +63 -0
  66. package/templates/vue-2model-crud-patch/src/components/AnalyticsBoard.vue +291 -0
  67. package/templates/vue-2model-crud-patch/src/components/DepartmentFormModal.vue +138 -0
  68. package/templates/vue-2model-crud-patch/src/components/DepartmentPatchModal.vue +210 -0
  69. package/templates/vue-2model-crud-patch/src/components/DepartmentTable.vue +133 -0
  70. package/templates/vue-2model-crud-patch/src/components/EmployeeFormModal.vue +177 -0
  71. package/templates/vue-2model-crud-patch/src/components/EmployeePatchModal.vue +221 -0
  72. package/templates/vue-2model-crud-patch/src/components/EmployeeTable.vue +171 -0
  73. package/templates/vue-2model-crud-patch/src/components/NavBar.vue +156 -0
  74. package/templates/vue-2model-crud-patch/src/main.js +12 -0
  75. package/templates/vue-2model-crud-patch/src/router/index.js +33 -0
  76. package/templates/vue-2model-crud-patch/src/services/api.js +64 -0
  77. package/templates/vue-2model-crud-patch/src/stores/departmentStore.js +149 -0
  78. package/templates/vue-2model-crud-patch/src/stores/employeeStore.js +143 -0
  79. package/templates/vue-2model-crud-patch/src/style.css +415 -0
  80. package/templates/vue-2model-crud-patch/src/views/AnalyticsView.vue +59 -0
  81. package/templates/vue-2model-crud-patch/src/views/DepartmentsView.vue +135 -0
  82. package/templates/vue-2model-crud-patch/src/views/EmployeesView.vue +143 -0
  83. package/templates/vue-2model-crud-patch/vite.config.js +9 -0
  84. package/templates/vue-axios/src/App.vue +255 -264
  85. package/templates/vue-axios/src/components/ItemFormModal.vue +183 -0
  86. package/templates/vue-axios/src/components/ItemPatchModal.vue +193 -0
  87. package/templates/vue-axios/src/components/ItemTable.vue +177 -0
  88. package/templates/vue-axios-basic/src/App.vue +241 -429
  89. package/templates/vue-axios-basic/src/components/ItemFormModal.vue +183 -0
  90. package/templates/vue-axios-basic/src/components/ItemPatchModal.vue +193 -0
  91. package/templates/vue-axios-basic/src/components/ItemTable.vue +177 -0
  92. package/templates/vue-axios-pinia/src/App.vue +150 -441
  93. package/templates/vue-axios-pinia/src/components/ItemFormModal.vue +207 -0
  94. package/templates/vue-axios-pinia/src/components/ItemStats.vue +64 -0
  95. package/templates/vue-axios-pinia/src/components/ItemTable.vue +195 -0
  96. package/templates/vue-axios-router/src/App.vue +41 -90
  97. package/templates/vue-axios-router/src/components/ItemFormModal.vue +173 -0
  98. package/templates/vue-axios-router/src/components/ItemTable.vue +102 -0
  99. package/templates/vue-axios-router/src/components/NavBar.vue +57 -0
  100. package/templates/vue-axios-router/src/views/DepartmentView.vue +132 -296
  101. package/templates/vue-axios-router/src/views/EmployeeView.vue +126 -219
  102. package/templates/vue-axios-router-children/src/components/ItemDetail.vue +115 -0
  103. package/templates/vue-axios-router-children/src/components/ItemTable.vue +104 -0
  104. package/templates/vue-axios-router-children/src/components/SideBar.vue +59 -0
  105. package/templates/vue-axios-router-children/src/views/CompanyLayout.vue +2 -51
  106. package/templates/vue-axios-router-children/src/views/DepartmentListView.vue +92 -250
  107. package/templates/vue-axios-router-children/src/views/EmployeeDetailView.vue +59 -181
  108. package/templates/vue-axios-router-children/src/views/EmployeeListView.vue +67 -315
  109. package/templates/vue-axios-router-pinia/src/App.vue +19 -70
  110. package/templates/vue-axios-router-pinia/src/components/ItemFormModal.vue +173 -0
  111. package/templates/vue-axios-router-pinia/src/components/ItemPatchModal.vue +144 -0
  112. package/templates/vue-axios-router-pinia/src/components/ItemTable.vue +102 -0
  113. package/templates/vue-axios-router-pinia/src/components/NavBar.vue +53 -0
  114. package/templates/vue-axios-router-pinia/src/views/DepartmentView.vue +107 -229
  115. package/templates/vue-axios-router-pinia/src/views/EmployeeView.vue +143 -257
  116. package/templates/vue-banking-app/src/components/AccountOverview.vue +75 -0
  117. package/templates/vue-banking-app/src/components/TransactionForm.vue +89 -0
  118. package/templates/vue-banking-app/src/components/TransactionTable.vue +113 -0
  119. package/templates/vue-banking-app/src/stores/bankStore.js +16 -3
  120. package/templates/vue-banking-app/src/views/DashboardView.vue +23 -71
  121. package/templates/vue-banking-app/src/views/StatementView.vue +11 -158
  122. package/templates/vue-banking-app/src/views/TransactionFormView.vue +23 -121
  123. package/templates/vue-basics/src/App.vue +105 -76
  124. package/templates/vue-basics/src/components/CounterCard.vue +107 -0
  125. package/templates/vue-basics/src/components/ItemList.vue +99 -0
  126. package/templates/vue-basics/src/components/UserForm.vue +138 -0
  127. package/templates/vue-cart-router-pinia/src/components/CartSummary.vue +139 -0
  128. package/templates/vue-cart-router-pinia/src/components/CheckoutModal.vue +149 -0
  129. package/templates/vue-cart-router-pinia/src/components/ProductCard.vue +63 -0
  130. package/templates/vue-cart-router-pinia/src/stores/cartStore.js +11 -0
  131. package/templates/vue-cart-router-pinia/src/views/CartView.vue +52 -118
  132. package/templates/vue-cart-router-pinia/src/views/CheckoutView.vue +2 -5
  133. package/templates/vue-cart-router-pinia/src/views/ProductCatalogView.vue +20 -56
  134. package/templates/vue-employee-system/src/App.vue +75 -344
  135. package/templates/vue-employee-system/src/components/DepartmentFilter.vue +108 -0
  136. package/templates/vue-employee-system/src/components/EmployeeModal.vue +183 -0
  137. package/templates/vue-employee-system/src/components/EmployeeTable.vue +91 -0
  138. package/templates/vue-employee-system/src/stores/employeeStore.js +12 -0
  139. package/templates/vue-fetch/src/App.vue +261 -285
  140. package/templates/vue-fetch/src/components/FetchFormModal.vue +183 -0
  141. package/templates/vue-fetch/src/components/FetchPatchModal.vue +193 -0
  142. package/templates/vue-fetch/src/components/FetchTable.vue +177 -0
  143. package/templates/vue-food-ordering/src/components/CartSummary.vue +134 -0
  144. package/templates/vue-food-ordering/src/components/FoodMenu.vue +126 -0
  145. package/templates/vue-food-ordering/src/components/OrderModal.vue +150 -0
  146. package/templates/vue-food-ordering/src/views/MenuView.vue +11 -132
  147. package/templates/vue-food-ordering/src/views/OrderCartView.vue +31 -164
  148. package/templates/vue-game-rps/src/App.vue +29 -88
  149. package/templates/vue-game-tictactoe/src/App.vue +23 -53
  150. package/templates/vue-game-whackamole/src/App.vue +44 -63
  151. package/templates/vue-hospital-portal/src/components/AppointmentForm.vue +144 -0
  152. package/templates/vue-hospital-portal/src/components/DoctorCard.vue +73 -0
  153. package/templates/vue-hospital-portal/src/components/PatientTable.vue +123 -0
  154. package/templates/vue-hospital-portal/src/stores/hospitalStore.js +10 -1
  155. package/templates/vue-hospital-portal/src/views/OPDRegistrationView.vue +40 -416
  156. package/templates/vue-hospital-portal/src/views/PatientListView.vue +11 -301
  157. package/templates/vue-library-system/src/components/BookForm.vue +125 -0
  158. package/templates/vue-library-system/src/components/BookTable.vue +112 -0
  159. package/templates/vue-library-system/src/components/BorrowModal.vue +131 -0
  160. package/templates/vue-library-system/src/stores/libraryStore.js +26 -1
  161. package/templates/vue-library-system/src/views/BookCatalogView.vue +34 -415
  162. package/templates/vue-memory-game/src/App.vue +36 -40
  163. package/templates/vue-memory-game/src/stores/memoryStore.js +32 -37
  164. package/templates/vue-router/src/App.vue +24 -14
  165. package/templates/vue-router/src/components/AboutCard.vue +38 -0
  166. package/templates/vue-router/src/components/HomeHero.vue +75 -0
  167. package/templates/vue-router/src/components/NavBar.vue +54 -0
  168. package/templates/vue-router/src/views/ItemsLayout.vue +17 -9
  169. package/templates/vue-router/src/views/ListView.vue +106 -18
  170. package/templates/vue-router-pinia/src/App.vue +24 -14
  171. package/templates/vue-router-pinia/src/components/ItemFormModal.vue +163 -0
  172. package/templates/vue-router-pinia/src/components/ItemTable.vue +90 -0
  173. package/templates/vue-router-pinia/src/components/NavBar.vue +54 -0
  174. package/templates/vue-router-pinia/src/stores/itemStore.js +8 -1
  175. package/templates/vue-router-pinia/src/views/ListView.vue +78 -14
  176. package/templates/vue-router-pinia-springboot/frontend/src/App.vue +25 -8
  177. package/templates/vue-router-pinia-springboot/frontend/src/components/ItemFormModal.vue +183 -0
  178. package/templates/vue-router-pinia-springboot/frontend/src/components/ItemPatchModal.vue +193 -0
  179. package/templates/vue-router-pinia-springboot/frontend/src/components/ItemTable.vue +177 -0
  180. package/templates/vue-router-pinia-springboot/frontend/src/components/NavBar.vue +54 -0
  181. package/templates/vue-router-pinia-springboot/frontend/src/views/ListView.vue +92 -76
  182. package/templates/vue-springboot-mongo-aggregation/frontend/src/App.vue +184 -193
  183. package/templates/vue-springboot-mongo-aggregation/frontend/src/components/AnalyticsCards.vue +56 -0
  184. package/templates/vue-springboot-mongo-aggregation/frontend/src/components/CategorySummaryTable.vue +71 -0
  185. package/templates/vue-springboot-mongo-aggregation/frontend/src/components/ItemTable.vue +114 -0
  186. package/templates/vue-springboot-mongo-crud/frontend/src/App.vue +228 -158
  187. package/templates/vue-springboot-mongo-crud/frontend/src/components/ItemFormModal.vue +183 -0
  188. package/templates/vue-springboot-mongo-crud/frontend/src/components/ItemPatchModal.vue +193 -0
  189. package/templates/vue-springboot-mongo-crud/frontend/src/components/ItemTable.vue +177 -0
  190. package/templates/vue-symposium-system/src/App.vue +41 -256
  191. package/templates/vue-symposium-system/src/components/AttendeeTable.vue +100 -0
  192. package/templates/vue-symposium-system/src/components/EventCard.vue +67 -0
  193. package/templates/vue-symposium-system/src/components/RegistrationForm.vue +118 -0
  194. package/templates/vue-symposium-system/src/stores/symposiumStore.js +10 -0
  195. package/templates/vue-todo-pinia/src/App.vue +40 -138
  196. package/templates/vue-todo-pinia/src/components/TodoFilter.vue +74 -0
  197. package/templates/vue-todo-pinia/src/components/TodoInput.vue +53 -0
  198. package/templates/vue-todo-pinia/src/components/TodoList.vue +101 -0
  199. package/templates/vue-todo-pinia/src/stores/todoStore.js +6 -0
  200. package/templates/vue-todo-router/src/App.vue +5 -36
  201. package/templates/vue-todo-router/src/components/TodoForm.vue +78 -0
  202. package/templates/vue-todo-router/src/components/TodoList.vue +84 -0
  203. package/templates/vue-todo-router/src/components/TodoNavBar.vue +47 -0
  204. package/templates/vue-todo-router/src/data/todoState.js +7 -0
  205. package/templates/vue-todo-router/src/views/AddTodoView.vue +7 -66
  206. package/templates/vue-todo-router/src/views/TodoDetailView.vue +15 -1
  207. package/templates/vue-todo-router/src/views/TodoListView.vue +22 -64
@@ -0,0 +1,221 @@
1
+ <template>
2
+ <div v-if="show" class="modal-overlay" @click.self="$emit('close')">
3
+ <div class="modal-container">
4
+ <div class="modal-header">
5
+ <h3 class="modal-title">Partial Update Employee (PATCH)</h3>
6
+ <button type="button" class="modal-close-btn" @click="$emit('close')">&times;</button>
7
+ </div>
8
+
9
+ <form @submit.prevent="handlePatch">
10
+ <div class="modal-body">
11
+ <div class="alert alert-info">
12
+ Select the specific field(s) you wish to update via HTTP PATCH (salary, role, departmentId). Only checked fields will be included in the request payload.
13
+ </div>
14
+
15
+ <div class="form-group">
16
+ <label class="form-label">Employee</label>
17
+ <input
18
+ type="text"
19
+ class="form-control"
20
+ :value="employee ? `${employee.name} (${employee.id})` : ''"
21
+ disabled
22
+ />
23
+ </div>
24
+
25
+ <!-- Salary Field -->
26
+ <div class="form-group">
27
+ <div class="form-check">
28
+ <input
29
+ type="checkbox"
30
+ id="patchSalary"
31
+ class="form-check-input"
32
+ v-model="fieldsToPatch.salary"
33
+ />
34
+ <label for="patchSalary" class="form-label" style="margin-bottom: 0; cursor: pointer;">
35
+ Patch Salary
36
+ </label>
37
+ </div>
38
+ <input
39
+ type="number"
40
+ class="form-control"
41
+ v-model.number="patchValues.salary"
42
+ :disabled="!fieldsToPatch.salary"
43
+ placeholder="New salary"
44
+ min="0"
45
+ step="1000"
46
+ />
47
+ <small class="card-subtitle" v-if="employee">
48
+ Current: ${{ Number(employee.salary || 0).toLocaleString() }}
49
+ </small>
50
+ </div>
51
+
52
+ <!-- Role Field -->
53
+ <div class="form-group">
54
+ <div class="form-check">
55
+ <input
56
+ type="checkbox"
57
+ id="patchRole"
58
+ class="form-check-input"
59
+ v-model="fieldsToPatch.role"
60
+ />
61
+ <label for="patchRole" class="form-label" style="margin-bottom: 0; cursor: pointer;">
62
+ Patch Role
63
+ </label>
64
+ </div>
65
+ <input
66
+ type="text"
67
+ class="form-control"
68
+ v-model="patchValues.role"
69
+ :disabled="!fieldsToPatch.role"
70
+ placeholder="New role title"
71
+ />
72
+ <small class="card-subtitle" v-if="employee">
73
+ Current: {{ employee.role || 'Staff' }}
74
+ </small>
75
+ </div>
76
+
77
+ <!-- Department Field -->
78
+ <div class="form-group">
79
+ <div class="form-check">
80
+ <input
81
+ type="checkbox"
82
+ id="patchDeptId"
83
+ class="form-check-input"
84
+ v-model="fieldsToPatch.departmentId"
85
+ />
86
+ <label for="patchDeptId" class="form-label" style="margin-bottom: 0; cursor: pointer;">
87
+ Patch Department Assignment
88
+ </label>
89
+ </div>
90
+ <select
91
+ class="form-control"
92
+ v-model="patchValues.departmentId"
93
+ :disabled="!fieldsToPatch.departmentId"
94
+ >
95
+ <option value="" disabled>Select Department</option>
96
+ <option
97
+ v-for="dept in departments"
98
+ :key="dept.id"
99
+ :value="dept.id"
100
+ >
101
+ {{ dept.name }} ({{ dept.location || 'N/A' }})
102
+ </option>
103
+ </select>
104
+ <small class="card-subtitle" v-if="employee">
105
+ Current Department ID: {{ employee.departmentId || 'None' }}
106
+ </small>
107
+ </div>
108
+
109
+ <!-- JSON Payload Preview -->
110
+ <div class="form-group">
111
+ <label class="form-label">HTTP PATCH Payload Preview</label>
112
+ <pre class="code-preview">{{ jsonPayload }}</pre>
113
+ </div>
114
+ </div>
115
+
116
+ <div class="modal-footer">
117
+ <button type="button" class="btn btn-secondary" @click="$emit('close')">
118
+ Cancel
119
+ </button>
120
+ <button
121
+ type="submit"
122
+ class="btn btn-patch"
123
+ :disabled="submitting || isPayloadEmpty"
124
+ >
125
+ {{ submitting ? 'Applying...' : 'Apply Patch (PATCH)' }}
126
+ </button>
127
+ </div>
128
+ </form>
129
+ </div>
130
+ </div>
131
+ </template>
132
+
133
+ <script>
134
+ export default {
135
+ name: 'EmployeePatchModal',
136
+ props: {
137
+ show: {
138
+ type: Boolean,
139
+ default: false
140
+ },
141
+ employee: {
142
+ type: Object,
143
+ default: null
144
+ },
145
+ departments: {
146
+ type: Array,
147
+ default: () => []
148
+ }
149
+ },
150
+ emits: ['close', 'patch'],
151
+ data() {
152
+ return {
153
+ fieldsToPatch: {
154
+ salary: false,
155
+ role: false,
156
+ departmentId: false
157
+ },
158
+ patchValues: {
159
+ salary: 0,
160
+ role: '',
161
+ departmentId: ''
162
+ },
163
+ submitting: false
164
+ }
165
+ },
166
+ computed: {
167
+ payload() {
168
+ const result = {}
169
+ if (this.fieldsToPatch.salary) {
170
+ result.salary = Number(this.patchValues.salary)
171
+ }
172
+ if (this.fieldsToPatch.role) {
173
+ result.role = this.patchValues.role
174
+ }
175
+ if (this.fieldsToPatch.departmentId) {
176
+ result.departmentId = this.patchValues.departmentId
177
+ }
178
+ return result
179
+ },
180
+ jsonPayload() {
181
+ return JSON.stringify(this.payload, null, 2)
182
+ },
183
+ isPayloadEmpty() {
184
+ return Object.keys(this.payload).length === 0
185
+ }
186
+ },
187
+ watch: {
188
+ employee: {
189
+ immediate: true,
190
+ handler(val) {
191
+ this.fieldsToPatch = {
192
+ salary: false,
193
+ role: false,
194
+ departmentId: false
195
+ }
196
+ if (val) {
197
+ this.patchValues = {
198
+ salary: val.salary !== undefined ? Number(val.salary) : 0,
199
+ role: val.role || '',
200
+ departmentId: val.departmentId || ''
201
+ }
202
+ }
203
+ }
204
+ }
205
+ },
206
+ methods: {
207
+ async handlePatch() {
208
+ if (this.isPayloadEmpty || !this.employee) return
209
+ this.submitting = true
210
+ try {
211
+ await this.$emit('patch', {
212
+ id: this.employee.id,
213
+ patchData: { ...this.payload }
214
+ })
215
+ } finally {
216
+ this.submitting = false
217
+ }
218
+ }
219
+ }
220
+ }
221
+ </script>
@@ -0,0 +1,171 @@
1
+ <template>
2
+ <div class="card">
3
+ <div class="card-header">
4
+ <div>
5
+ <h2 class="card-title">Employees</h2>
6
+ <p class="card-subtitle">Manage workforce records, roles, salaries, and departmental assignments</p>
7
+ </div>
8
+ <button type="button" class="btn btn-primary" @click="$emit('create')">
9
+ Add Employee
10
+ </button>
11
+ </div>
12
+
13
+ <div class="search-bar">
14
+ <input
15
+ type="text"
16
+ class="form-control search-input"
17
+ placeholder="Search employees by name, email, or role..."
18
+ v-model="searchTerm"
19
+ />
20
+ <select class="form-control" style="width: auto; min-width: 180px;" v-model="selectedDeptFilter">
21
+ <option value="">All Departments</option>
22
+ <option v-for="d in departments" :key="d.id" :value="d.id">
23
+ {{ d.name }}
24
+ </option>
25
+ </select>
26
+ <span class="badge badge-secondary">
27
+ {{ filteredEmployees.length }} Employee{{ filteredEmployees.length === 1 ? '' : 's' }}
28
+ </span>
29
+ </div>
30
+
31
+ <div v-if="loading" class="alert alert-info">
32
+ Loading employees...
33
+ </div>
34
+
35
+ <div v-else-if="filteredEmployees.length === 0" class="alert alert-info">
36
+ No employees found. Click "Add Employee" to create one.
37
+ </div>
38
+
39
+ <div v-else class="table-responsive">
40
+ <table class="data-table">
41
+ <thead>
42
+ <tr>
43
+ <th>ID</th>
44
+ <th>Name</th>
45
+ <th>Email</th>
46
+ <th>Role</th>
47
+ <th>Salary</th>
48
+ <th>Department</th>
49
+ <th style="text-align: right;">Actions</th>
50
+ </tr>
51
+ </thead>
52
+ <tbody>
53
+ <tr v-for="emp in filteredEmployees" :key="emp.id">
54
+ <td>
55
+ <code>{{ emp.id }}</code>
56
+ </td>
57
+ <td>
58
+ <strong>{{ emp.name }}</strong>
59
+ </td>
60
+ <td>{{ emp.email }}</td>
61
+ <td>
62
+ <span class="badge badge-primary">{{ emp.role || 'Staff' }}</span>
63
+ </td>
64
+ <td>
65
+ <span class="badge badge-success">
66
+ {{ formatCurrency(emp.salary) }}
67
+ </span>
68
+ </td>
69
+ <td>
70
+ <span class="badge badge-secondary">
71
+ {{ getDepartmentName(emp.departmentId) }}
72
+ </span>
73
+ </td>
74
+ <td style="text-align: right;">
75
+ <div class="btn-group" style="justify-content: flex-end;">
76
+ <button
77
+ type="button"
78
+ class="btn btn-secondary btn-sm"
79
+ title="Full Update (PUT)"
80
+ @click="$emit('edit', emp)"
81
+ >
82
+ Edit
83
+ </button>
84
+ <button
85
+ type="button"
86
+ class="btn btn-patch btn-sm"
87
+ title="Partial Update (PATCH)"
88
+ @click="$emit('patch', emp)"
89
+ >
90
+ Patch
91
+ </button>
92
+ <button
93
+ type="button"
94
+ class="btn btn-danger btn-sm"
95
+ title="Delete (DELETE)"
96
+ @click="$emit('delete', emp.id)"
97
+ >
98
+ Delete
99
+ </button>
100
+ </div>
101
+ </td>
102
+ </tr>
103
+ </tbody>
104
+ </table>
105
+ </div>
106
+ </div>
107
+ </template>
108
+
109
+ <script>
110
+ export default {
111
+ name: 'EmployeeTable',
112
+ props: {
113
+ employees: {
114
+ type: Array,
115
+ default: () => []
116
+ },
117
+ departments: {
118
+ type: Array,
119
+ default: () => []
120
+ },
121
+ loading: {
122
+ type: Boolean,
123
+ default: false
124
+ }
125
+ },
126
+ emits: ['create', 'edit', 'patch', 'delete'],
127
+ data() {
128
+ return {
129
+ searchTerm: '',
130
+ selectedDeptFilter: ''
131
+ }
132
+ },
133
+ computed: {
134
+ filteredEmployees() {
135
+ let result = this.employees
136
+
137
+ if (this.selectedDeptFilter) {
138
+ result = result.filter(
139
+ (e) =>
140
+ e.departmentId === this.selectedDeptFilter ||
141
+ (this.getDepartmentName(e.departmentId) === this.selectedDeptFilter)
142
+ )
143
+ }
144
+
145
+ if (this.searchTerm.trim()) {
146
+ const term = this.searchTerm.toLowerCase()
147
+ result = result.filter((e) => {
148
+ const name = (e.name || '').toLowerCase()
149
+ const email = (e.email || '').toLowerCase()
150
+ const role = (e.role || '').toLowerCase()
151
+ return name.includes(term) || email.includes(term) || role.includes(term)
152
+ })
153
+ }
154
+
155
+ return result
156
+ }
157
+ },
158
+ methods: {
159
+ getDepartmentName(deptId) {
160
+ if (!deptId) return 'Unassigned'
161
+ const dept = this.departments.find((d) => d.id === deptId || d.name === deptId)
162
+ return dept ? dept.name : deptId
163
+ },
164
+ formatCurrency(val) {
165
+ const num = Number(val)
166
+ if (isNaN(num)) return '$0'
167
+ return '$' + num.toLocaleString()
168
+ }
169
+ }
170
+ }
171
+ </script>
@@ -0,0 +1,156 @@
1
+ <template>
2
+ <header class="navbar">
3
+ <div class="navbar-container">
4
+ <div class="navbar-brand">
5
+ <span class="brand-title">Enterprise Resource Manager</span>
6
+ <span class="badge badge-secondary">Spring Boot + Vue 3</span>
7
+ </div>
8
+
9
+ <nav class="navbar-tabs">
10
+ <button
11
+ type="button"
12
+ class="tab-btn"
13
+ :class="{ active: currentTab === 'departments' }"
14
+ @click="selectTab('departments')"
15
+ >
16
+ Departments
17
+ </button>
18
+ <button
19
+ type="button"
20
+ class="tab-btn"
21
+ :class="{ active: currentTab === 'employees' }"
22
+ @click="selectTab('employees')"
23
+ >
24
+ Employees
25
+ </button>
26
+ <button
27
+ type="button"
28
+ class="tab-btn"
29
+ :class="{ active: currentTab === 'analytics' }"
30
+ @click="selectTab('analytics')"
31
+ >
32
+ Analytics Board
33
+ </button>
34
+ </nav>
35
+
36
+ <div class="navbar-status">
37
+ <span class="status-indicator"></span>
38
+ <span class="status-text">API: localhost:8080/api</span>
39
+ </div>
40
+ </div>
41
+ </header>
42
+ </template>
43
+
44
+ <script>
45
+ export default {
46
+ name: 'NavBar',
47
+ props: {
48
+ currentTab: {
49
+ type: String,
50
+ default: 'departments'
51
+ }
52
+ },
53
+ emits: ['tab-change'],
54
+ methods: {
55
+ selectTab(tab) {
56
+ this.$emit('tab-change', tab)
57
+ if (this.$router && this.$route && this.$route.path !== `/${tab === 'departments' ? '' : tab}`) {
58
+ this.$router.push(tab === 'departments' ? '/' : `/${tab}`).catch(() => {})
59
+ }
60
+ }
61
+ }
62
+ }
63
+ </script>
64
+
65
+ <style scoped>
66
+ .navbar {
67
+ background-color: #0f172a;
68
+ color: #f8fafc;
69
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
70
+ position: sticky;
71
+ top: 0;
72
+ z-index: 100;
73
+ }
74
+
75
+ .navbar-container {
76
+ max-width: 1200px;
77
+ margin: 0 auto;
78
+ padding: 0 16px;
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: space-between;
82
+ height: 60px;
83
+ gap: 16px;
84
+ }
85
+
86
+ .navbar-brand {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 10px;
90
+ }
91
+
92
+ .brand-title {
93
+ font-size: 1.1rem;
94
+ font-weight: 700;
95
+ letter-spacing: -0.01em;
96
+ color: #ffffff;
97
+ }
98
+
99
+ .navbar-tabs {
100
+ display: flex;
101
+ gap: 4px;
102
+ }
103
+
104
+ .tab-btn {
105
+ background: transparent;
106
+ border: none;
107
+ color: #94a3b8;
108
+ font-size: 0.875rem;
109
+ font-weight: 500;
110
+ padding: 8px 16px;
111
+ border-radius: var(--radius-sm);
112
+ cursor: pointer;
113
+ transition: all 0.15s ease-in-out;
114
+ }
115
+
116
+ .tab-btn:hover {
117
+ color: #ffffff;
118
+ background-color: rgba(255, 255, 255, 0.08);
119
+ }
120
+
121
+ .tab-btn.active {
122
+ color: #ffffff;
123
+ background-color: var(--primary);
124
+ font-weight: 600;
125
+ }
126
+
127
+ .navbar-status {
128
+ display: flex;
129
+ align-items: center;
130
+ gap: 8px;
131
+ font-size: 0.75rem;
132
+ color: #94a3b8;
133
+ }
134
+
135
+ .status-indicator {
136
+ width: 8px;
137
+ height: 8px;
138
+ background-color: #22c55e;
139
+ border-radius: 50%;
140
+ display: inline-block;
141
+ }
142
+
143
+ @media (max-width: 768px) {
144
+ .navbar-container {
145
+ height: auto;
146
+ padding: 12px 16px;
147
+ flex-direction: column;
148
+ align-items: flex-start;
149
+ }
150
+
151
+ .navbar-tabs {
152
+ width: 100%;
153
+ overflow-x: auto;
154
+ }
155
+ }
156
+ </style>
@@ -0,0 +1,12 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+ import App from './App.vue'
4
+ import router from './router'
5
+ import './style.css'
6
+
7
+ const app = createApp(App)
8
+
9
+ app.use(createPinia())
10
+ app.use(router)
11
+
12
+ app.mount('#app')
@@ -0,0 +1,33 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+ import DepartmentsView from '../views/DepartmentsView.vue'
3
+ import EmployeesView from '../views/EmployeesView.vue'
4
+ import AnalyticsView from '../views/AnalyticsView.vue'
5
+
6
+ const routes = [
7
+ {
8
+ path: '/',
9
+ name: 'Departments',
10
+ component: DepartmentsView
11
+ },
12
+ {
13
+ path: '/departments',
14
+ redirect: '/'
15
+ },
16
+ {
17
+ path: '/employees',
18
+ name: 'Employees',
19
+ component: EmployeesView
20
+ },
21
+ {
22
+ path: '/analytics',
23
+ name: 'Analytics',
24
+ component: AnalyticsView
25
+ }
26
+ ]
27
+
28
+ const router = createRouter({
29
+ history: createWebHistory(),
30
+ routes
31
+ })
32
+
33
+ export default router
@@ -0,0 +1,64 @@
1
+ import axios from 'axios'
2
+
3
+ const client = axios.create({
4
+ baseURL: 'http://localhost:8080/api',
5
+ headers: {
6
+ 'Content-Type': 'application/json'
7
+ }
8
+ })
9
+
10
+ export const departments = {
11
+ getAll() {
12
+ return client.get('/departments')
13
+ },
14
+ getById(id) {
15
+ return client.get(`/departments/${id}`)
16
+ },
17
+ create(data) {
18
+ return client.post('/departments', data)
19
+ },
20
+ update(id, data) {
21
+ return client.put(`/departments/${id}`, data)
22
+ },
23
+ patch(id, data) {
24
+ return client.patch(`/departments/${id}`, data)
25
+ },
26
+ remove(id) {
27
+ return client.delete(`/departments/${id}`)
28
+ },
29
+ getStats() {
30
+ return client.get('/departments/stats')
31
+ }
32
+ }
33
+
34
+ export const employees = {
35
+ getAll() {
36
+ return client.get('/employees')
37
+ },
38
+ getById(id) {
39
+ return client.get(`/employees/${id}`)
40
+ },
41
+ create(data) {
42
+ return client.post('/employees', data)
43
+ },
44
+ update(id, data) {
45
+ return client.put(`/employees/${id}`, data)
46
+ },
47
+ patch(id, data) {
48
+ return client.patch(`/employees/${id}`, data)
49
+ },
50
+ remove(id) {
51
+ return client.delete(`/employees/${id}`)
52
+ },
53
+ getJoined() {
54
+ return client.get('/employees/joined')
55
+ }
56
+ }
57
+
58
+ export const api = {
59
+ client,
60
+ departments,
61
+ employees
62
+ }
63
+
64
+ export default api