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,84 @@
1
+ import { defineStore } from 'pinia'
2
+ import api from '../services/api'
3
+
4
+ export const useCompanyStore = defineStore('company', {
5
+ state: () => ({
6
+ departments: [
7
+ { id: 1, name: 'Engineering', code: 'ENG', budget: 150000 },
8
+ { id: 2, name: 'Marketing', code: 'MKT', budget: 80000 },
9
+ { id: 3, name: 'Human Resources', code: 'HR', budget: 60000 }
10
+ ],
11
+ employees: [
12
+ { id: 1, name: 'Alice Smith', email: 'alice@example.com', role: 'Lead Architect', department: 'Engineering' },
13
+ { id: 2, name: 'Bob Jones', email: 'bob@example.com', role: 'Marketing Specialist', department: 'Marketing' },
14
+ { id: 3, name: 'Charlie Brown', email: 'charlie@example.com', role: 'HR Coordinator', department: 'Human Resources' }
15
+ ],
16
+ loading: false,
17
+ error: null
18
+ }),
19
+ getters: {
20
+ totalDepartments: (state) => state.departments.length,
21
+ totalEmployees: (state) => state.employees.length,
22
+ totalBudget: (state) => state.departments.reduce((sum, d) => sum + Number(d.budget || 0), 0)
23
+ },
24
+ actions: {
25
+ async fetchDepartments() {
26
+ this.loading = true
27
+ try {
28
+ const response = await api.getDepartments()
29
+ this.departments = response.data
30
+ this.error = null
31
+ } catch (err) {
32
+ this.error = err.message
33
+ } finally {
34
+ this.loading = false
35
+ }
36
+ },
37
+ async addDepartment(dept) {
38
+ try {
39
+ const response = await api.createDepartment(dept)
40
+ this.departments.push(response.data)
41
+ } catch (err) {
42
+ const newDept = { ...dept, id: dept.id || Date.now() }
43
+ this.departments.push(newDept)
44
+ }
45
+ },
46
+ async removeDepartment(id) {
47
+ try {
48
+ await api.deleteDepartment(id)
49
+ this.departments = this.departments.filter(d => d.id !== id)
50
+ } catch (err) {
51
+ this.departments = this.departments.filter(d => d.id !== id)
52
+ }
53
+ },
54
+ async fetchEmployees() {
55
+ this.loading = true
56
+ try {
57
+ const response = await api.getEmployees()
58
+ this.employees = response.data
59
+ this.error = null
60
+ } catch (err) {
61
+ this.error = err.message
62
+ } finally {
63
+ this.loading = false
64
+ }
65
+ },
66
+ async addEmployee(emp) {
67
+ try {
68
+ const response = await api.createEmployee(emp)
69
+ this.employees.push(response.data)
70
+ } catch (err) {
71
+ const newEmp = { ...emp, id: emp.id || Date.now() }
72
+ this.employees.push(newEmp)
73
+ }
74
+ },
75
+ async removeEmployee(id) {
76
+ try {
77
+ await api.deleteEmployee(id)
78
+ this.employees = this.employees.filter(e => e.id !== id)
79
+ } catch (err) {
80
+ this.employees = this.employees.filter(e => e.id !== id)
81
+ }
82
+ }
83
+ }
84
+ })
@@ -0,0 +1,64 @@
1
+ <script setup>
2
+ import { onMounted } from 'vue'
3
+ import { useCompanyStore } from '../stores/companyStore'
4
+ import EntityTable from '../components/EntityTable.vue'
5
+ import EntityForm from '../components/EntityForm.vue'
6
+
7
+ const store = useCompanyStore()
8
+
9
+ const departmentFields = [
10
+ { key: 'name', label: 'Department Name', type: 'text', placeholder: 'e.g. Finance' },
11
+ { key: 'code', label: 'Department Code', type: 'text', placeholder: 'e.g. FIN' },
12
+ { key: 'budget', label: 'Annual Budget ($)', type: 'number', placeholder: 'e.g. 50000' }
13
+ ]
14
+
15
+ const departmentColumns = [
16
+ { key: 'id', label: 'ID' },
17
+ { key: 'name', label: 'Name' },
18
+ { key: 'code', label: 'Code' },
19
+ { key: 'budget', label: 'Budget' }
20
+ ]
21
+
22
+ const handleAddDepartment = (formData) => {
23
+ store.addDepartment({
24
+ name: formData.name,
25
+ code: formData.code,
26
+ budget: Number(formData.budget)
27
+ })
28
+ }
29
+
30
+ const handleDeleteDepartment = (id) => {
31
+ store.removeDepartment(id)
32
+ }
33
+
34
+ onMounted(() => {
35
+ store.fetchDepartments()
36
+ })
37
+ </script>
38
+
39
+ <template>
40
+ <div class="manage-departments-view">
41
+ <h2>Manage Departments</h2>
42
+ <EntityForm
43
+ :fields="departmentFields"
44
+ title="Add New Department"
45
+ @submit-form="handleAddDepartment"
46
+ />
47
+ <EntityTable
48
+ :items="store.departments"
49
+ :columns="departmentColumns"
50
+ title="All Departments"
51
+ @delete-item="handleDeleteDepartment"
52
+ />
53
+ </div>
54
+ </template>
55
+
56
+ <style scoped>
57
+ .manage-departments-view {
58
+ padding: 16px 0;
59
+ }
60
+ .manage-departments-view h2 {
61
+ font-size: 1.5rem;
62
+ margin-bottom: 20px;
63
+ }
64
+ </style>
@@ -0,0 +1,67 @@
1
+ <script setup>
2
+ import { onMounted } from 'vue'
3
+ import { useCompanyStore } from '../stores/companyStore'
4
+ import EntityTable from '../components/EntityTable.vue'
5
+ import EntityForm from '../components/EntityForm.vue'
6
+
7
+ const store = useCompanyStore()
8
+
9
+ const employeeFields = [
10
+ { key: 'name', label: 'Employee Name', type: 'text', placeholder: 'e.g. Jane Doe' },
11
+ { key: 'email', label: 'Email Address', type: 'email', placeholder: 'e.g. jane@example.com' },
12
+ { key: 'role', label: 'Job Role', type: 'text', placeholder: 'e.g. Developer' },
13
+ { key: 'department', label: 'Department', type: 'text', placeholder: 'e.g. Engineering' }
14
+ ]
15
+
16
+ const employeeColumns = [
17
+ { key: 'id', label: 'ID' },
18
+ { key: 'name', label: 'Name' },
19
+ { key: 'email', label: 'Email' },
20
+ { key: 'role', label: 'Role' },
21
+ { key: 'department', label: 'Department' }
22
+ ]
23
+
24
+ const handleAddEmployee = (formData) => {
25
+ store.addEmployee({
26
+ name: formData.name,
27
+ email: formData.email,
28
+ role: formData.role,
29
+ department: formData.department
30
+ })
31
+ }
32
+
33
+ const handleDeleteEmployee = (id) => {
34
+ store.removeEmployee(id)
35
+ }
36
+
37
+ onMounted(() => {
38
+ store.fetchEmployees()
39
+ })
40
+ </script>
41
+
42
+ <template>
43
+ <div class="manage-employees-view">
44
+ <h2>Manage Employees</h2>
45
+ <EntityForm
46
+ :fields="employeeFields"
47
+ title="Add New Employee"
48
+ @submit-form="handleAddEmployee"
49
+ />
50
+ <EntityTable
51
+ :items="store.employees"
52
+ :columns="employeeColumns"
53
+ title="All Employees"
54
+ @delete-item="handleDeleteEmployee"
55
+ />
56
+ </div>
57
+ </template>
58
+
59
+ <style scoped>
60
+ .manage-employees-view {
61
+ padding: 16px 0;
62
+ }
63
+ .manage-employees-view h2 {
64
+ font-size: 1.5rem;
65
+ margin-bottom: 20px;
66
+ }
67
+ </style>
@@ -0,0 +1,38 @@
1
+ <script setup>
2
+ import { onMounted } from 'vue'
3
+ import { useCompanyStore } from '../stores/companyStore'
4
+ import SummaryBadge from '../components/SummaryBadge.vue'
5
+
6
+ const store = useCompanyStore()
7
+
8
+ onMounted(() => {
9
+ store.fetchDepartments()
10
+ store.fetchEmployees()
11
+ })
12
+ </script>
13
+
14
+ <template>
15
+ <div class="overview-view">
16
+ <h2>Company Overview</h2>
17
+ <div class="badges-container">
18
+ <SummaryBadge :count="store.totalDepartments" label="Total Departments" />
19
+ <SummaryBadge :count="store.totalEmployees" label="Total Employees" />
20
+ <SummaryBadge :count="'$' + store.totalBudget" label="Total Budget" />
21
+ </div>
22
+ </div>
23
+ </template>
24
+
25
+ <style scoped>
26
+ .overview-view {
27
+ padding: 16px 0;
28
+ }
29
+ .overview-view h2 {
30
+ font-size: 1.5rem;
31
+ margin-bottom: 20px;
32
+ }
33
+ .badges-container {
34
+ display: flex;
35
+ gap: 16px;
36
+ flex-wrap: wrap;
37
+ }
38
+ </style>
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()]
6
+ })
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Vue Axios Router Props</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.js"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "vue-axios-router-props",
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
+ "vue": "^3.5.44",
14
+ "vue-router": "^5.4.0"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-vue": "^6.0.10",
18
+ "vite": "^8.3.4"
19
+ }
20
+ }
@@ -0,0 +1,59 @@
1
+ <script setup>
2
+ </script>
3
+
4
+ <template>
5
+ <div class="app-container">
6
+ <header class="navbar">
7
+ <h2>Company Management</h2>
8
+ <nav class="nav-links">
9
+ <router-link to="/">Departments</router-link>
10
+ <router-link to="/employees">Employees</router-link>
11
+ </nav>
12
+ </header>
13
+ <main class="content">
14
+ <router-view />
15
+ </main>
16
+ </div>
17
+ </template>
18
+
19
+ <style scoped>
20
+ .app-container {
21
+ max-width: 800px;
22
+ margin: 0 auto;
23
+ padding: 24px 16px;
24
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
25
+ color: #2d3748;
26
+ }
27
+ .navbar {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ border-bottom: 2px solid #e2e8f0;
32
+ padding-bottom: 16px;
33
+ margin-bottom: 24px;
34
+ }
35
+ .navbar h2 {
36
+ margin: 0;
37
+ font-size: 22px;
38
+ }
39
+ .nav-links {
40
+ display: flex;
41
+ gap: 12px;
42
+ }
43
+ .nav-links a {
44
+ text-decoration: none;
45
+ color: #4a5568;
46
+ font-weight: 500;
47
+ padding: 6px 14px;
48
+ border-radius: 4px;
49
+ border: 1px solid #cbd5e0;
50
+ }
51
+ .nav-links a.router-link-active {
52
+ background-color: #3182ce;
53
+ border-color: #3182ce;
54
+ color: #fff;
55
+ }
56
+ .content {
57
+ margin-top: 10px;
58
+ }
59
+ </style>
@@ -0,0 +1,53 @@
1
+ <script setup>
2
+ const props = defineProps(['title', 'subtitle', 'id'])
3
+ const emit = defineEmits(['remove'])
4
+
5
+ const handleRemove = () => {
6
+ emit('remove', props.id)
7
+ }
8
+ </script>
9
+
10
+ <template>
11
+ <div class="data-card">
12
+ <div class="card-info">
13
+ <h4>{{ title }}</h4>
14
+ <p>{{ subtitle }}</p>
15
+ </div>
16
+ <button class="btn-remove" @click="handleRemove">Delete</button>
17
+ </div>
18
+ </template>
19
+
20
+ <style scoped>
21
+ .data-card {
22
+ display: flex;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ border: 1px solid #e2e8f0;
26
+ border-radius: 6px;
27
+ padding: 12px 16px;
28
+ margin-bottom: 10px;
29
+ background-color: #fff;
30
+ }
31
+ .card-info h4 {
32
+ margin: 0 0 4px 0;
33
+ font-size: 16px;
34
+ color: #2d3748;
35
+ }
36
+ .card-info p {
37
+ margin: 0;
38
+ font-size: 14px;
39
+ color: #718096;
40
+ }
41
+ .btn-remove {
42
+ background-color: #e53e3e;
43
+ color: white;
44
+ border: none;
45
+ border-radius: 4px;
46
+ padding: 6px 12px;
47
+ cursor: pointer;
48
+ font-size: 13px;
49
+ }
50
+ .btn-remove:hover {
51
+ background-color: #c53030;
52
+ }
53
+ </style>
@@ -0,0 +1,117 @@
1
+ <script setup>
2
+ import { reactive, watch } from 'vue'
3
+
4
+ const props = defineProps(['fields'])
5
+ const emit = defineEmits(['submit-data'])
6
+
7
+ const formData = reactive({})
8
+
9
+ const getFieldName = (field) => {
10
+ if (typeof field === 'string') return field
11
+ return field.name || field.key || ''
12
+ }
13
+
14
+ const getFieldLabel = (field) => {
15
+ if (typeof field === 'string') {
16
+ return field.charAt(0).toUpperCase() + field.slice(1)
17
+ }
18
+ return field.label || field.name || field.key || ''
19
+ }
20
+
21
+ const getFieldType = (field) => {
22
+ if (typeof field === 'object' && field.type) return field.type
23
+ return 'text'
24
+ }
25
+
26
+ const initFormData = () => {
27
+ if (Array.isArray(props.fields)) {
28
+ props.fields.forEach(field => {
29
+ const name = getFieldName(field)
30
+ if (name && !(name in formData)) {
31
+ formData[name] = ''
32
+ }
33
+ })
34
+ }
35
+ }
36
+
37
+ watch(() => props.fields, () => {
38
+ initFormData()
39
+ }, { immediate: true, deep: true })
40
+
41
+ const handleSubmit = () => {
42
+ emit('submit-data', { ...formData })
43
+ Object.keys(formData).forEach(key => {
44
+ formData[key] = ''
45
+ })
46
+ }
47
+ </script>
48
+
49
+ <template>
50
+ <form @submit.prevent="handleSubmit" class="entry-form">
51
+ <div v-for="field in fields" :key="getFieldName(field)" class="form-group">
52
+ <label :for="getFieldName(field)">{{ getFieldLabel(field) }}</label>
53
+ <select
54
+ v-if="typeof field === 'object' && field.type === 'select'"
55
+ :id="getFieldName(field)"
56
+ v-model="formData[getFieldName(field)]"
57
+ :required="field.required !== false"
58
+ >
59
+ <option value="" disabled>{{ field.placeholder || 'Select an option' }}</option>
60
+ <option v-for="opt in field.options" :key="opt.value" :value="opt.value">
61
+ {{ opt.label }}
62
+ </option>
63
+ </select>
64
+ <input
65
+ v-else
66
+ :id="getFieldName(field)"
67
+ v-model="formData[getFieldName(field)]"
68
+ :type="getFieldType(field)"
69
+ :placeholder="typeof field === 'object' && field.placeholder ? field.placeholder : ''"
70
+ :required="typeof field === 'object' && field.required === false ? false : true"
71
+ />
72
+ </div>
73
+ <button type="submit" class="btn-submit">Submit</button>
74
+ </form>
75
+ </template>
76
+
77
+ <style scoped>
78
+ .entry-form {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 12px;
82
+ background-color: #f7fafc;
83
+ padding: 16px;
84
+ border-radius: 6px;
85
+ border: 1px solid #e2e8f0;
86
+ margin-bottom: 20px;
87
+ }
88
+ .form-group {
89
+ display: flex;
90
+ flex-direction: column;
91
+ gap: 4px;
92
+ }
93
+ label {
94
+ font-size: 13px;
95
+ font-weight: 600;
96
+ color: #4a5568;
97
+ }
98
+ input, select {
99
+ padding: 8px 10px;
100
+ border: 1px solid #cbd5e0;
101
+ border-radius: 4px;
102
+ font-size: 14px;
103
+ }
104
+ .btn-submit {
105
+ align-self: flex-start;
106
+ background-color: #3182ce;
107
+ color: white;
108
+ border: none;
109
+ border-radius: 4px;
110
+ padding: 8px 16px;
111
+ cursor: pointer;
112
+ font-size: 14px;
113
+ }
114
+ .btn-submit:hover {
115
+ background-color: #2b6cb0;
116
+ }
117
+ </style>
@@ -0,0 +1,7 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+ import router from './router'
4
+
5
+ const app = createApp(App)
6
+ app.use(router)
7
+ app.mount('#app')
@@ -0,0 +1,23 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+ import DepartmentPage from '../views/DepartmentPage.vue'
3
+ import EmployeePage from '../views/EmployeePage.vue'
4
+
5
+ const routes = [
6
+ {
7
+ path: '/',
8
+ name: 'Departments',
9
+ component: DepartmentPage
10
+ },
11
+ {
12
+ path: '/employees',
13
+ name: 'Employees',
14
+ component: EmployeePage
15
+ }
16
+ ]
17
+
18
+ const router = createRouter({
19
+ history: createWebHistory(),
20
+ routes
21
+ })
22
+
23
+ export default router
@@ -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,77 @@
1
+ <script setup>
2
+ import { ref, onMounted } from 'vue'
3
+ import api from '../services/api'
4
+ import EntryForm from '../components/EntryForm.vue'
5
+ import DataCard from '../components/DataCard.vue'
6
+
7
+ const departments = ref([])
8
+
9
+ const deptFields = [
10
+ { key: 'name', label: 'Department Name', placeholder: 'e.g. Engineering' },
11
+ { key: 'code', label: 'Department Code', placeholder: 'e.g. ENG' }
12
+ ]
13
+
14
+ const fetchDepartments = async () => {
15
+ try {
16
+ const res = await api.get('/departments')
17
+ departments.value = res.data
18
+ } catch (error) {
19
+ console.error(error)
20
+ }
21
+ }
22
+
23
+ const handleCreateDepartment = async (formData) => {
24
+ try {
25
+ const res = await api.post('/departments', formData)
26
+ departments.value.push(res.data)
27
+ } catch (error) {
28
+ console.error(error)
29
+ }
30
+ }
31
+
32
+ const handleRemoveDepartment = async (id) => {
33
+ try {
34
+ await api.delete(`/departments/${id}`)
35
+ departments.value = departments.value.filter(dept => dept.id !== id)
36
+ } catch (error) {
37
+ console.error(error)
38
+ }
39
+ }
40
+
41
+ onMounted(() => {
42
+ fetchDepartments()
43
+ })
44
+ </script>
45
+
46
+ <template>
47
+ <div class="page-container">
48
+ <h2>Departments</h2>
49
+ <EntryForm :fields="deptFields" @submit-data="handleCreateDepartment" />
50
+ <div class="card-list">
51
+ <p v-if="departments.length === 0" class="empty-state">No departments available.</p>
52
+ <DataCard
53
+ v-for="dept in departments"
54
+ :key="dept.id"
55
+ :id="dept.id"
56
+ :title="dept.name"
57
+ :subtitle="dept.code"
58
+ @remove="handleRemoveDepartment"
59
+ />
60
+ </div>
61
+ </div>
62
+ </template>
63
+
64
+ <style scoped>
65
+ .page-container {
66
+ display: flex;
67
+ flex-direction: column;
68
+ }
69
+ .empty-state {
70
+ color: #a0aec0;
71
+ font-style: italic;
72
+ }
73
+ .card-list {
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+ </style>