wad-lab 1.1.2 → 1.1.3

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 (145) hide show
  1. package/bin/index.js +12 -1
  2. package/package.json +1 -1
  3. package/templates/springboot-2model-1controller-api/pom.xml +42 -0
  4. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/CompanyApplication.java +11 -0
  5. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/controller/CompanyController.java +74 -0
  6. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/model/Department.java +51 -0
  7. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/model/Employee.java +73 -0
  8. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/repository/DepartmentRepository.java +12 -0
  9. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/repository/EmployeeRepository.java +12 -0
  10. package/templates/springboot-2model-1controller-api/src/main/java/com/example/company/service/CompanyService.java +60 -0
  11. package/templates/springboot-2model-1controller-api/src/main/resources/application.properties +4 -0
  12. package/templates/vue-axios-basic/index.html +12 -0
  13. package/templates/{frontend → vue-axios-basic}/package.json +6 -9
  14. package/templates/vue-axios-basic/src/App.vue +405 -0
  15. package/templates/{frontend → vue-axios-basic}/src/main.js +0 -2
  16. package/templates/vue-axios-basic/src/services/api.js +7 -0
  17. package/templates/vue-axios-basic/vite.config.js +9 -0
  18. package/templates/{frontend → vue-axios-interceptor}/index.html +2 -3
  19. package/templates/vue-axios-interceptor/package.json +20 -0
  20. package/templates/vue-axios-interceptor/src/App.vue +159 -0
  21. package/templates/vue-axios-interceptor/src/components/ToastNotification.vue +47 -0
  22. package/templates/vue-axios-interceptor/src/main.js +7 -0
  23. package/templates/vue-axios-interceptor/src/services/api.js +32 -0
  24. package/templates/vue-axios-interceptor/src/stores/toastStore.js +23 -0
  25. package/templates/vue-axios-interceptor/vite.config.js +6 -0
  26. package/templates/vue-axios-pinia/index.html +12 -0
  27. package/templates/vue-axios-pinia/package.json +20 -0
  28. package/templates/vue-axios-pinia/src/App.vue +427 -0
  29. package/templates/vue-axios-pinia/src/main.js +5 -0
  30. package/templates/vue-axios-pinia/src/services/api.js +7 -0
  31. package/templates/vue-axios-pinia/src/stores/companyStore.js +96 -0
  32. package/templates/vue-axios-pinia/vite.config.js +9 -0
  33. package/templates/vue-axios-pinia-props/index.html +12 -0
  34. package/templates/vue-axios-pinia-props/package.json +20 -0
  35. package/templates/vue-axios-pinia-props/src/App.vue +69 -0
  36. package/templates/vue-axios-pinia-props/src/components/DepartmentSection.vue +152 -0
  37. package/templates/vue-axios-pinia-props/src/components/EmployeeSection.vue +181 -0
  38. package/templates/vue-axios-pinia-props/src/main.js +5 -0
  39. package/templates/vue-axios-pinia-props/src/services/api.js +7 -0
  40. package/templates/vue-axios-pinia-props/src/stores/companyStore.js +35 -0
  41. package/templates/vue-axios-pinia-props/vite.config.js +6 -0
  42. package/templates/vue-axios-props-emits/index.html +12 -0
  43. package/templates/vue-axios-props-emits/package.json +19 -0
  44. package/templates/vue-axios-props-emits/src/App.vue +167 -0
  45. package/templates/vue-axios-props-emits/src/components/DepartmentForm.vue +98 -0
  46. package/templates/vue-axios-props-emits/src/components/DepartmentList.vue +91 -0
  47. package/templates/vue-axios-props-emits/src/components/EmployeeForm.vue +128 -0
  48. package/templates/vue-axios-props-emits/src/components/EmployeeList.vue +94 -0
  49. package/templates/vue-axios-props-emits/src/main.js +4 -0
  50. package/templates/vue-axios-props-emits/src/services/api.js +7 -0
  51. package/templates/vue-axios-props-emits/vite.config.js +9 -0
  52. package/templates/vue-axios-router/index.html +12 -0
  53. package/templates/vue-axios-router/package.json +20 -0
  54. package/templates/vue-axios-router/src/App.vue +90 -0
  55. package/templates/vue-axios-router/src/main.js +5 -0
  56. package/templates/vue-axios-router/src/router/index.js +19 -0
  57. package/templates/vue-axios-router/src/services/api.js +7 -0
  58. package/templates/vue-axios-router/src/views/AddEmployeeView.vue +247 -0
  59. package/templates/vue-axios-router/src/views/DepartmentView.vue +273 -0
  60. package/templates/vue-axios-router/src/views/EmployeeView.vue +196 -0
  61. package/templates/vue-axios-router/src/views/HomeDashboardView.vue +263 -0
  62. package/templates/vue-axios-router/vite.config.js +6 -0
  63. package/templates/vue-axios-router-children/index.html +12 -0
  64. package/templates/vue-axios-router-children/package.json +20 -0
  65. package/templates/vue-axios-router-children/src/App.vue +20 -0
  66. package/templates/vue-axios-router-children/src/main.js +5 -0
  67. package/templates/vue-axios-router-children/src/router/index.js +42 -0
  68. package/templates/vue-axios-router-children/src/services/api.js +7 -0
  69. package/templates/vue-axios-router-children/src/views/CompanyLayout.vue +75 -0
  70. package/templates/vue-axios-router-children/src/views/CompanyOverview.vue +109 -0
  71. package/templates/vue-axios-router-children/src/views/DepartmentListView.vue +231 -0
  72. package/templates/vue-axios-router-children/src/views/EmployeeDetailView.vue +159 -0
  73. package/templates/vue-axios-router-children/src/views/EmployeeListView.vue +295 -0
  74. package/templates/vue-axios-router-children/vite.config.js +6 -0
  75. package/templates/vue-axios-router-pinia/index.html +12 -0
  76. package/templates/vue-axios-router-pinia/package.json +21 -0
  77. package/templates/vue-axios-router-pinia/src/App.vue +92 -0
  78. package/templates/vue-axios-router-pinia/src/main.js +9 -0
  79. package/templates/vue-axios-router-pinia/src/router/index.js +17 -0
  80. package/templates/vue-axios-router-pinia/src/services/api.js +7 -0
  81. package/templates/vue-axios-router-pinia/src/stores/companyStore.js +70 -0
  82. package/templates/vue-axios-router-pinia/src/views/DashboardView.vue +77 -0
  83. package/templates/vue-axios-router-pinia/src/views/DepartmentView.vue +209 -0
  84. package/templates/vue-axios-router-pinia/src/views/EmployeeView.vue +237 -0
  85. package/templates/vue-axios-router-pinia/vite.config.js +6 -0
  86. package/templates/vue-axios-router-pinia-props/index.html +12 -0
  87. package/templates/vue-axios-router-pinia-props/package.json +21 -0
  88. package/templates/vue-axios-router-pinia-props/src/App.vue +67 -0
  89. package/templates/vue-axios-router-pinia-props/src/components/EntityForm.vue +101 -0
  90. package/templates/vue-axios-router-pinia-props/src/components/EntityTable.vue +92 -0
  91. package/templates/vue-axios-router-pinia-props/src/components/SummaryBadge.vue +43 -0
  92. package/templates/vue-axios-router-pinia-props/src/main.js +9 -0
  93. package/templates/vue-axios-router-pinia-props/src/router/index.js +29 -0
  94. package/templates/vue-axios-router-pinia-props/src/services/api.js +29 -0
  95. package/templates/vue-axios-router-pinia-props/src/stores/companyStore.js +84 -0
  96. package/templates/vue-axios-router-pinia-props/src/views/ManageDepartmentsView.vue +64 -0
  97. package/templates/vue-axios-router-pinia-props/src/views/ManageEmployeesView.vue +67 -0
  98. package/templates/vue-axios-router-pinia-props/src/views/OverviewView.vue +38 -0
  99. package/templates/vue-axios-router-pinia-props/vite.config.js +6 -0
  100. package/templates/vue-axios-router-props/index.html +12 -0
  101. package/templates/vue-axios-router-props/package.json +20 -0
  102. package/templates/vue-axios-router-props/src/App.vue +59 -0
  103. package/templates/vue-axios-router-props/src/components/DataCard.vue +53 -0
  104. package/templates/vue-axios-router-props/src/components/EntryForm.vue +117 -0
  105. package/templates/vue-axios-router-props/src/main.js +7 -0
  106. package/templates/vue-axios-router-props/src/router/index.js +23 -0
  107. package/templates/vue-axios-router-props/src/services/api.js +7 -0
  108. package/templates/vue-axios-router-props/src/views/DepartmentPage.vue +77 -0
  109. package/templates/vue-axios-router-props/src/views/EmployeePage.vue +116 -0
  110. package/templates/vue-axios-router-props/vite.config.js +6 -0
  111. package/templates/backend/.gitattributes +0 -2
  112. package/templates/backend/.idea/compiler.xml +0 -21
  113. package/templates/backend/.idea/encodings.xml +0 -6
  114. package/templates/backend/.idea/jarRepositories.xml +0 -20
  115. package/templates/backend/.idea/misc.xml +0 -12
  116. package/templates/backend/.idea/vcs.xml +0 -6
  117. package/templates/backend/pom.xml +0 -104
  118. package/templates/backend/src/main/java/com/example/crud/CrudApplication.java +0 -13
  119. package/templates/backend/src/main/java/com/example/crud/controller/ItemController.java +0 -42
  120. package/templates/backend/src/main/java/com/example/crud/model/Item.java +0 -20
  121. package/templates/backend/src/main/java/com/example/crud/repository/ItemRepository.java +0 -9
  122. package/templates/backend/src/main/java/com/example/crud/service/ItemService.java +0 -37
  123. package/templates/backend/src/main/resources/application.properties +0 -4
  124. package/templates/backend/src/test/java/com/example/crud/CrudApplicationTests.java +0 -13
  125. package/templates/frontend/.vscode/extensions.json +0 -3
  126. package/templates/frontend/README.md +0 -38
  127. package/templates/frontend/jsconfig.json +0 -8
  128. package/templates/frontend/public/favicon.ico +0 -0
  129. package/templates/frontend/src/App.vue +0 -50
  130. package/templates/frontend/src/assets/base.css +0 -86
  131. package/templates/frontend/src/assets/logo.svg +0 -1
  132. package/templates/frontend/src/assets/main.css +0 -35
  133. package/templates/frontend/src/components/HelloWorld.vue +0 -44
  134. package/templates/frontend/src/components/TheWelcome.vue +0 -95
  135. package/templates/frontend/src/components/WelcomeItem.vue +0 -87
  136. package/templates/frontend/src/components/icons/IconCommunity.vue +0 -7
  137. package/templates/frontend/src/components/icons/IconDocumentation.vue +0 -7
  138. package/templates/frontend/src/components/icons/IconEcosystem.vue +0 -7
  139. package/templates/frontend/src/components/icons/IconSupport.vue +0 -7
  140. package/templates/frontend/src/components/icons/IconTooling.vue +0 -19
  141. package/templates/frontend/src/services/ItemService.js +0 -21
  142. package/templates/frontend/vite.config.js +0 -18
  143. /package/templates/{backend → springboot-2model-1controller-api}/.mvn/wrapper/maven-wrapper.properties +0 -0
  144. /package/templates/{backend → springboot-2model-1controller-api}/mvnw +0 -0
  145. /package/templates/{backend → springboot-2model-1controller-api}/mvnw.cmd +0 -0
@@ -0,0 +1,405 @@
1
+ <template>
2
+ <div class="container">
3
+ <header class="header">
4
+ <h1>Company Directory</h1>
5
+ <p>Vue 3 + Axios Direct CRUD</p>
6
+ </header>
7
+
8
+ <div class="grid">
9
+ <section class="card">
10
+ <h2>Departments</h2>
11
+
12
+ <form @submit.prevent="createDepartment" class="form">
13
+ <div class="form-group">
14
+ <label for="dept-name">Department Name</label>
15
+ <input
16
+ id="dept-name"
17
+ v-model="deptForm.name"
18
+ type="text"
19
+ required
20
+ placeholder="e.g. Engineering"
21
+ />
22
+ </div>
23
+ <div class="form-group">
24
+ <label for="dept-code">Department Code</label>
25
+ <input
26
+ id="dept-code"
27
+ v-model="deptForm.code"
28
+ type="text"
29
+ required
30
+ placeholder="e.g. ENG"
31
+ />
32
+ </div>
33
+ <button type="submit" class="btn btn-primary">Add Department</button>
34
+ </form>
35
+
36
+ <div class="table-wrapper">
37
+ <table>
38
+ <thead>
39
+ <tr>
40
+ <th>ID</th>
41
+ <th>Name</th>
42
+ <th>Code</th>
43
+ <th>Action</th>
44
+ </tr>
45
+ </thead>
46
+ <tbody>
47
+ <tr v-if="departments.length === 0">
48
+ <td colspan="4" class="empty">No departments found</td>
49
+ </tr>
50
+ <tr v-for="dept in departments" :key="dept.id">
51
+ <td>{{ dept.id }}</td>
52
+ <td>{{ dept.name }}</td>
53
+ <td><code>{{ dept.code }}</code></td>
54
+ <td>
55
+ <button @click="deleteDepartment(dept.id)" class="btn btn-danger">Delete</button>
56
+ </td>
57
+ </tr>
58
+ </tbody>
59
+ </table>
60
+ </div>
61
+ </section>
62
+
63
+ <section class="card">
64
+ <h2>Employees</h2>
65
+
66
+ <form @submit.prevent="createEmployee" class="form">
67
+ <div class="form-group">
68
+ <label for="emp-name">Full Name</label>
69
+ <input
70
+ id="emp-name"
71
+ v-model="empForm.name"
72
+ type="text"
73
+ required
74
+ placeholder="e.g. Jane Doe"
75
+ />
76
+ </div>
77
+ <div class="form-group">
78
+ <label for="emp-email">Email Address</label>
79
+ <input
80
+ id="emp-email"
81
+ v-model="empForm.email"
82
+ type="email"
83
+ required
84
+ placeholder="e.g. jane@example.com"
85
+ />
86
+ </div>
87
+ <div class="form-group">
88
+ <label for="emp-dept">Department</label>
89
+ <select id="emp-dept" v-model="empForm.departmentId" required>
90
+ <option value="" disabled>Select Department</option>
91
+ <option v-for="dept in departments" :key="dept.id" :value="dept.id">
92
+ {{ dept.name }} ({{ dept.code }})
93
+ </option>
94
+ </select>
95
+ </div>
96
+ <div class="form-group">
97
+ <label for="emp-salary">Salary</label>
98
+ <input
99
+ id="emp-salary"
100
+ v-model.number="empForm.salary"
101
+ type="number"
102
+ min="0"
103
+ step="any"
104
+ required
105
+ placeholder="e.g. 75000"
106
+ />
107
+ </div>
108
+ <button type="submit" class="btn btn-primary">Add Employee</button>
109
+ </form>
110
+
111
+ <div class="table-wrapper">
112
+ <table>
113
+ <thead>
114
+ <tr>
115
+ <th>ID</th>
116
+ <th>Name</th>
117
+ <th>Email</th>
118
+ <th>Department</th>
119
+ <th>Salary</th>
120
+ <th>Action</th>
121
+ </tr>
122
+ </thead>
123
+ <tbody>
124
+ <tr v-if="employees.length === 0">
125
+ <td colspan="6" class="empty">No employees found</td>
126
+ </tr>
127
+ <tr v-for="emp in employees" :key="emp.id">
128
+ <td>{{ emp.id }}</td>
129
+ <td>{{ emp.name }}</td>
130
+ <td>{{ emp.email }}</td>
131
+ <td>{{ getDepartmentName(emp.departmentId) }}</td>
132
+ <td>${{ Number(emp.salary).toLocaleString() }}</td>
133
+ <td>
134
+ <button @click="deleteEmployee(emp.id)" class="btn btn-danger">Delete</button>
135
+ </td>
136
+ </tr>
137
+ </tbody>
138
+ </table>
139
+ </div>
140
+ </section>
141
+ </div>
142
+ </div>
143
+ </template>
144
+
145
+ <script setup>
146
+ import { ref, reactive, onMounted } from 'vue'
147
+ import api from './services/api'
148
+
149
+ const departments = ref([])
150
+ const employees = ref([])
151
+
152
+ const deptForm = reactive({
153
+ name: '',
154
+ code: ''
155
+ })
156
+
157
+ const empForm = reactive({
158
+ name: '',
159
+ email: '',
160
+ departmentId: '',
161
+ salary: ''
162
+ })
163
+
164
+ const fetchDepartments = async () => {
165
+ try {
166
+ const response = await api.get('/departments')
167
+ departments.value = response.data
168
+ } catch (error) {
169
+ console.error(error)
170
+ }
171
+ }
172
+
173
+ const fetchEmployees = async () => {
174
+ try {
175
+ const response = await api.get('/employees')
176
+ employees.value = response.data
177
+ } catch (error) {
178
+ console.error(error)
179
+ }
180
+ }
181
+
182
+ const createDepartment = async () => {
183
+ try {
184
+ await api.post('/departments', {
185
+ name: deptForm.name,
186
+ code: deptForm.code
187
+ })
188
+ deptForm.name = ''
189
+ deptForm.code = ''
190
+ await fetchDepartments()
191
+ } catch (error) {
192
+ console.error(error)
193
+ }
194
+ }
195
+
196
+ const deleteDepartment = async (id) => {
197
+ try {
198
+ await api.delete(`/departments/${id}`)
199
+ await fetchDepartments()
200
+ await fetchEmployees()
201
+ } catch (error) {
202
+ console.error(error)
203
+ }
204
+ }
205
+
206
+ const createEmployee = async () => {
207
+ try {
208
+ await api.post('/employees', {
209
+ name: empForm.name,
210
+ email: empForm.email,
211
+ departmentId: Number(empForm.departmentId),
212
+ salary: Number(empForm.salary)
213
+ })
214
+ empForm.name = ''
215
+ empForm.email = ''
216
+ empForm.departmentId = ''
217
+ empForm.salary = ''
218
+ await fetchEmployees()
219
+ } catch (error) {
220
+ console.error(error)
221
+ }
222
+ }
223
+
224
+ const deleteEmployee = async (id) => {
225
+ try {
226
+ await api.delete(`/employees/${id}`)
227
+ await fetchEmployees()
228
+ } catch (error) {
229
+ console.error(error)
230
+ }
231
+ }
232
+
233
+ const getDepartmentName = (deptId) => {
234
+ const dept = departments.value.find((d) => d.id === deptId)
235
+ return dept ? dept.name : `Dept #${deptId}`
236
+ }
237
+
238
+ onMounted(() => {
239
+ fetchDepartments()
240
+ fetchEmployees()
241
+ })
242
+ </script>
243
+
244
+ <style>
245
+ * {
246
+ box-sizing: border-box;
247
+ margin: 0;
248
+ padding: 0;
249
+ }
250
+
251
+ body {
252
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
253
+ background-color: #f4f5f7;
254
+ color: #333333;
255
+ line-height: 1.5;
256
+ }
257
+
258
+ .container {
259
+ max-width: 1200px;
260
+ margin: 0 auto;
261
+ padding: 32px 16px;
262
+ }
263
+
264
+ .header {
265
+ margin-bottom: 24px;
266
+ }
267
+
268
+ .header h1 {
269
+ font-size: 28px;
270
+ color: #1a1a1a;
271
+ }
272
+
273
+ .header p {
274
+ color: #666666;
275
+ font-size: 14px;
276
+ }
277
+
278
+ .grid {
279
+ display: grid;
280
+ grid-template-columns: 1fr;
281
+ gap: 24px;
282
+ }
283
+
284
+ @media (min-width: 900px) {
285
+ .grid {
286
+ grid-template-columns: 1fr 1fr;
287
+ }
288
+ }
289
+
290
+ .card {
291
+ background: #ffffff;
292
+ border: 1px solid #e1e4e8;
293
+ border-radius: 8px;
294
+ padding: 20px;
295
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
296
+ }
297
+
298
+ .card h2 {
299
+ font-size: 20px;
300
+ margin-bottom: 16px;
301
+ color: #222222;
302
+ border-bottom: 1px solid #edf0f2;
303
+ padding-bottom: 8px;
304
+ }
305
+
306
+ .form {
307
+ display: flex;
308
+ flex-direction: column;
309
+ gap: 12px;
310
+ margin-bottom: 20px;
311
+ }
312
+
313
+ .form-group {
314
+ display: flex;
315
+ flex-direction: column;
316
+ gap: 4px;
317
+ }
318
+
319
+ .form-group label {
320
+ font-size: 13px;
321
+ font-weight: 600;
322
+ color: #444444;
323
+ }
324
+
325
+ .form-group input,
326
+ .form-group select {
327
+ padding: 8px 10px;
328
+ border: 1px solid #cccccc;
329
+ border-radius: 4px;
330
+ font-size: 14px;
331
+ background-color: #ffffff;
332
+ }
333
+
334
+ .form-group input:focus,
335
+ .form-group select:focus {
336
+ outline: none;
337
+ border-color: #0066cc;
338
+ }
339
+
340
+ .btn {
341
+ padding: 8px 14px;
342
+ border: none;
343
+ border-radius: 4px;
344
+ cursor: pointer;
345
+ font-size: 13px;
346
+ font-weight: 600;
347
+ transition: background-color 0.15s ease;
348
+ }
349
+
350
+ .btn-primary {
351
+ background-color: #0066cc;
352
+ color: #ffffff;
353
+ }
354
+
355
+ .btn-primary:hover {
356
+ background-color: #0052a3;
357
+ }
358
+
359
+ .btn-danger {
360
+ background-color: #d9383a;
361
+ color: #ffffff;
362
+ padding: 4px 8px;
363
+ font-size: 12px;
364
+ }
365
+
366
+ .btn-danger:hover {
367
+ background-color: #b8282a;
368
+ }
369
+
370
+ .table-wrapper {
371
+ overflow-x: auto;
372
+ }
373
+
374
+ table {
375
+ width: 100%;
376
+ border-collapse: collapse;
377
+ font-size: 13px;
378
+ margin-top: 8px;
379
+ }
380
+
381
+ th, td {
382
+ text-align: left;
383
+ padding: 8px 10px;
384
+ border-bottom: 1px solid #eeeeee;
385
+ }
386
+
387
+ th {
388
+ background-color: #fafbfc;
389
+ color: #555555;
390
+ font-weight: 600;
391
+ }
392
+
393
+ code {
394
+ background: #f0f0f0;
395
+ padding: 2px 4px;
396
+ border-radius: 3px;
397
+ font-size: 12px;
398
+ }
399
+
400
+ .empty {
401
+ text-align: center;
402
+ color: #888888;
403
+ padding: 16px;
404
+ }
405
+ </style>
@@ -1,5 +1,3 @@
1
- import './assets/main.css'
2
-
3
1
  import { createApp } from 'vue'
4
2
  import App from './App.vue'
5
3
 
@@ -0,0 +1,7 @@
1
+ import axios from 'axios'
2
+
3
+ const api = axios.create({
4
+ baseURL: 'http://localhost:8080/api'
5
+ })
6
+
7
+ export default api
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ server: {
7
+ port: 5173
8
+ }
9
+ })
@@ -1,10 +1,9 @@
1
1
  <!DOCTYPE html>
2
- <html lang="">
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <link rel="icon" href="/favicon.ico">
6
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Vite App</title>
6
+ <title>Vue Axios Interceptor</title>
8
7
  </head>
9
8
  <body>
10
9
  <div id="app"></div>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "vue-axios-interceptor",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "axios": "^1.7.7",
13
+ "pinia": "^4.1.0",
14
+ "vue": "^3.5.44"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-vue": "^6.0.10",
18
+ "vite": "^8.3.4"
19
+ }
20
+ }
@@ -0,0 +1,159 @@
1
+ <script setup>
2
+ import { ref, reactive } from 'vue'
3
+ import api from './services/api'
4
+ import ToastNotification from './components/ToastNotification.vue'
5
+
6
+ const departments = ref([
7
+ { id: 1, name: 'Engineering', location: 'Floor 3' },
8
+ { id: 2, name: 'Marketing', location: 'Floor 2' }
9
+ ])
10
+
11
+ const newDept = reactive({
12
+ name: '',
13
+ location: ''
14
+ })
15
+
16
+ async function fetchDepartments() {
17
+ try {
18
+ const res = await api.get('/departments')
19
+ departments.value = res.data
20
+ } catch (err) {
21
+ }
22
+ }
23
+
24
+ async function addDepartment() {
25
+ if (!newDept.name) return
26
+ try {
27
+ const res = await api.post('/departments', {
28
+ name: newDept.name,
29
+ location: newDept.location
30
+ })
31
+ departments.value.push(res.data || { id: Date.now(), name: newDept.name, location: newDept.location })
32
+ newDept.name = ''
33
+ newDept.location = ''
34
+ } catch (err) {
35
+ }
36
+ }
37
+
38
+ async function triggerError() {
39
+ try {
40
+ await api.get('/departments/invalid-id')
41
+ } catch (err) {
42
+ }
43
+ }
44
+ </script>
45
+
46
+ <template>
47
+ <div class="container">
48
+ <ToastNotification />
49
+ <h1>Department Management</h1>
50
+
51
+ <div class="actions">
52
+ <button @click="fetchDepartments">Fetch Departments (GET)</button>
53
+ <button class="btn-error" @click="triggerError">Trigger 404 Error (GET)</button>
54
+ </div>
55
+
56
+ <form class="form" @submit.prevent="addDepartment">
57
+ <input v-model="newDept.name" placeholder="Department Name" required />
58
+ <input v-model="newDept.location" placeholder="Location" required />
59
+ <button type="submit">Add Department (POST)</button>
60
+ </form>
61
+
62
+ <table class="table">
63
+ <thead>
64
+ <tr>
65
+ <th>ID</th>
66
+ <th>Name</th>
67
+ <th>Location</th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>
71
+ <tr v-for="dept in departments" :key="dept.id">
72
+ <td>{{ dept.id }}</td>
73
+ <td>{{ dept.name }}</td>
74
+ <td>{{ dept.location }}</td>
75
+ </tr>
76
+ </tbody>
77
+ </table>
78
+ </div>
79
+ </template>
80
+
81
+ <style>
82
+ body {
83
+ margin: 0;
84
+ font-family: sans-serif;
85
+ background-color: #f7f9fa;
86
+ color: #333;
87
+ }
88
+
89
+ .container {
90
+ max-width: 800px;
91
+ margin: 40px auto;
92
+ padding: 20px;
93
+ background: #fff;
94
+ border: 1px solid #ddd;
95
+ border-radius: 6px;
96
+ }
97
+
98
+ h1 {
99
+ margin-top: 0;
100
+ font-size: 22px;
101
+ }
102
+
103
+ .actions {
104
+ display: flex;
105
+ gap: 10px;
106
+ margin-bottom: 20px;
107
+ }
108
+
109
+ button {
110
+ padding: 8px 14px;
111
+ border: 1px solid #1976d2;
112
+ background-color: #1976d2;
113
+ color: #fff;
114
+ border-radius: 4px;
115
+ cursor: pointer;
116
+ }
117
+
118
+ button:hover {
119
+ background-color: #1565c0;
120
+ }
121
+
122
+ button.btn-error {
123
+ border-color: #d32f2f;
124
+ background-color: #d32f2f;
125
+ }
126
+
127
+ button.btn-error:hover {
128
+ background-color: #b71c1c;
129
+ }
130
+
131
+ .form {
132
+ display: flex;
133
+ gap: 10px;
134
+ margin-bottom: 20px;
135
+ }
136
+
137
+ input {
138
+ padding: 8px;
139
+ border: 1px solid #ccc;
140
+ border-radius: 4px;
141
+ flex: 1;
142
+ }
143
+
144
+ .table {
145
+ width: 100%;
146
+ border-collapse: collapse;
147
+ }
148
+
149
+ .table th,
150
+ .table td {
151
+ border: 1px solid #ddd;
152
+ padding: 10px;
153
+ text-align: left;
154
+ }
155
+
156
+ .table th {
157
+ background-color: #f0f0f0;
158
+ }
159
+ </style>
@@ -0,0 +1,47 @@
1
+ <script setup>
2
+ import { useToastStore } from '../stores/toastStore'
3
+
4
+ const toastStore = useToastStore()
5
+ </script>
6
+
7
+ <template>
8
+ <div v-if="toastStore.visible" :class="['toast-banner', toastStore.type]">
9
+ <span>{{ toastStore.message }}</span>
10
+ <button class="close-btn" @click="toastStore.hide">&times;</button>
11
+ </div>
12
+ </template>
13
+
14
+ <style scoped>
15
+ .toast-banner {
16
+ position: fixed;
17
+ top: 20px;
18
+ right: 20px;
19
+ padding: 12px 18px;
20
+ border-radius: 4px;
21
+ color: #fff;
22
+ z-index: 9999;
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 12px;
26
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.15);
27
+ font-family: sans-serif;
28
+ font-size: 14px;
29
+ }
30
+
31
+ .toast-banner.success {
32
+ background-color: #2e7d32;
33
+ }
34
+
35
+ .toast-banner.error {
36
+ background-color: #d32f2f;
37
+ }
38
+
39
+ .close-btn {
40
+ background: transparent;
41
+ border: none;
42
+ color: #fff;
43
+ font-size: 18px;
44
+ cursor: pointer;
45
+ line-height: 1;
46
+ }
47
+ </style>
@@ -0,0 +1,7 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+ import App from './App.vue'
4
+
5
+ const app = createApp(App)
6
+ app.use(createPinia())
7
+ app.mount('#app')
@@ -0,0 +1,32 @@
1
+ import axios from 'axios'
2
+ import { useToastStore } from '../stores/toastStore'
3
+
4
+ const api = axios.create({
5
+ baseURL: 'http://localhost:8080/api'
6
+ })
7
+
8
+ api.interceptors.request.use(
9
+ (config) => {
10
+ config.headers.Authorization = 'Bearer test-token'
11
+ console.log(config.method.toUpperCase(), config.url)
12
+ return config
13
+ },
14
+ (error) => {
15
+ return Promise.reject(error)
16
+ }
17
+ )
18
+
19
+ api.interceptors.response.use(
20
+ (res) => {
21
+ const toastStore = useToastStore()
22
+ toastStore.show('Request Succeeded: ' + res.config.method.toUpperCase(), 'success')
23
+ return res
24
+ },
25
+ (err) => {
26
+ const toastStore = useToastStore()
27
+ toastStore.show('API Error: ' + (err.response?.data?.message || err.message), 'error')
28
+ return Promise.reject(err)
29
+ }
30
+ )
31
+
32
+ export default api