wad-lab 1.2.2 → 1.2.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 (207) hide show
  1. package/bin/index.js +472 -631
  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
@@ -20,9 +20,34 @@ export const useLibraryStore = defineStore('library', {
20
20
  totalFines: (state) => state.borrowedRecords.reduce((sum, r) => sum + (r.fine || 0), 0)
21
21
  },
22
22
  actions: {
23
+ addBook(book) {
24
+ this.books.push({
25
+ id: Date.now(),
26
+ title: book.title.trim(),
27
+ author: book.author.trim(),
28
+ isbn: book.isbn.trim(),
29
+ category: book.category || 'General',
30
+ copies: Number(book.copies) || 1
31
+ })
32
+ },
33
+ updateBook(id, updated) {
34
+ const idx = this.books.findIndex(b => b.id === Number(id))
35
+ if (idx !== -1) {
36
+ this.books[idx] = { ...this.books[idx], ...updated }
37
+ }
38
+ },
39
+ patchBook(id, partial) {
40
+ const book = this.books.find(b => b.id === Number(id))
41
+ if (book) {
42
+ Object.assign(book, partial)
43
+ }
44
+ },
45
+ deleteBook(id) {
46
+ this.books = this.books.filter(b => b.id !== Number(id))
47
+ },
23
48
  registerMember(member) {
24
49
  const newMember = {
25
- id: member.id || `MEM-${100 + this.members.length + 1}`,
50
+ id: member.id || ('MEM-' + (100 + this.members.length + 1)),
26
51
  name: member.name,
27
52
  email: member.email,
28
53
  membershipType: member.membershipType || 'Standard',
@@ -1,432 +1,51 @@
1
1
  <script setup>
2
- import { ref, computed } from 'vue'
2
+ import { ref } from 'vue'
3
3
  import { useLibraryStore } from '../stores/libraryStore'
4
+ import BookForm from '../components/BookForm.vue'
5
+ import BookTable from '../components/BookTable.vue'
6
+ import BorrowModal from '../components/BorrowModal.vue'
4
7
 
5
- const libraryStore = useLibraryStore()
8
+ const store = useLibraryStore()
9
+ const isModalOpen = ref(false)
10
+ const selectedBook = ref(null)
6
11
 
7
- const searchQuery = ref('')
8
- const selectedCategory = ref('')
9
- const selectedMemberId = ref({})
10
- const notification = ref('')
11
-
12
- const newBook = ref({
13
- title: '',
14
- author: '',
15
- isbn: '',
16
- category: 'Computer Science',
17
- copies: 1
18
- })
19
-
20
- const categories = ['Computer Science', 'Web Dev', 'Algorithms', 'Fiction', 'Science']
21
-
22
- const filteredBooks = computed(() => {
23
- return libraryStore.books.filter((book) => {
24
- const matchesSearch =
25
- book.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
26
- book.author.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
27
- book.isbn.includes(searchQuery.value)
28
- const matchesCategory =
29
- !selectedCategory.value || book.category === selectedCategory.value
30
- return matchesSearch && matchesCategory
31
- })
32
- })
33
-
34
- function handleIssueBook(bookId) {
35
- const memberId = selectedMemberId.value[bookId]
36
- if (!memberId) {
37
- notification.value = 'Please select a member before issuing.'
38
- return
39
- }
40
- const success = libraryStore.issueBook(bookId, memberId)
41
- if (success) {
42
- notification.value = 'Book successfully issued!'
43
- selectedMemberId.value[bookId] = ''
44
- } else {
45
- notification.value = 'Unable to issue book. Check copy availability.'
46
- }
47
- setTimeout(() => {
48
- notification.value = ''
49
- }, 3000)
12
+ function handleBorrow(book) {
13
+ selectedBook.value = book
14
+ isModalOpen.value = true
50
15
  }
51
16
 
52
- function handleAddBook() {
53
- if (!newBook.value.title || !newBook.value.author || !newBook.value.isbn) {
54
- return
55
- }
56
- libraryStore.books.push({
57
- id: Date.now(),
58
- title: newBook.value.title,
59
- author: newBook.value.author,
60
- isbn: newBook.value.isbn,
61
- category: newBook.value.category,
62
- copies: Number(newBook.value.copies) || 1
63
- })
64
- newBook.value = {
65
- title: '',
66
- author: '',
67
- isbn: '',
68
- category: 'Computer Science',
69
- copies: 1
70
- }
71
- notification.value = 'New book added to catalog.'
72
- setTimeout(() => {
73
- notification.value = ''
74
- }, 3000)
17
+ function handleConfirmBorrow({ bookId, memberId }) {
18
+ store.issueBook(bookId, memberId)
19
+ isModalOpen.value = false
75
20
  }
76
21
  </script>
77
22
 
78
23
  <template>
79
24
  <div class="catalog-page">
80
- <div class="stats-bar">
81
- <div class="stat-card">
82
- <span class="stat-num">{{ libraryStore.books.length }}</span>
83
- <span class="stat-label">Total Titles</span>
84
- </div>
85
- <div class="stat-card">
86
- <span class="stat-num">{{ libraryStore.availableBooks.length }}</span>
87
- <span class="stat-label">Available Titles</span>
88
- </div>
89
- <div class="stat-card">
90
- <span class="stat-num">{{ libraryStore.activeLoansCount }}</span>
91
- <span class="stat-label">Active Loans</span>
92
- </div>
93
- <div class="stat-card">
94
- <span class="stat-num">${{ libraryStore.totalFines }}</span>
95
- <span class="stat-label">Accrued Fines</span>
96
- </div>
97
- </div>
98
-
99
- <div v-if="notification" class="alert-box">
100
- {{ notification }}
101
- </div>
102
-
103
- <div class="controls-row">
104
- <input
105
- v-model="searchQuery"
106
- type="text"
107
- placeholder="Search title, author, or ISBN..."
108
- class="search-input"
109
- />
110
- <select v-model="selectedCategory" class="filter-select">
111
- <option value="">All Categories</option>
112
- <option v-for="cat in categories" :key="cat" :value="cat">
113
- {{ cat }}
114
- </option>
115
- </select>
116
- </div>
117
-
118
- <div class="table-card">
119
- <h2>Book Inventory</h2>
120
- <table class="data-table">
121
- <thead>
122
- <tr>
123
- <th>ID</th>
124
- <th>Title</th>
125
- <th>Author</th>
126
- <th>ISBN</th>
127
- <th>Category</th>
128
- <th>Copies</th>
129
- <th>Availability</th>
130
- <th>Issue Book</th>
131
- </tr>
132
- </thead>
133
- <tbody>
134
- <tr v-for="book in filteredBooks" :key="book.id">
135
- <td>#{{ book.id }}</td>
136
- <td class="font-bold">{{ book.title }}</td>
137
- <td>{{ book.author }}</td>
138
- <td><code>{{ book.isbn }}</code></td>
139
- <td><span class="tag">{{ book.category }}</span></td>
140
- <td>
141
- <span class="copies-count" :class="{ 'zero-copies': book.copies === 0 }">
142
- {{ book.copies }}
143
- </span>
144
- </td>
145
- <td>
146
- <span :class="['status-badge', book.copies > 0 ? 'in-stock' : 'out-stock']">
147
- {{ book.copies > 0 ? 'In Stock' : 'Out of Stock' }}
148
- </span>
149
- </td>
150
- <td>
151
- <div class="issue-controls">
152
- <select v-model="selectedMemberId[book.id]" class="member-select" :disabled="book.copies === 0">
153
- <option value="" disabled selected>Select Member</option>
154
- <option v-for="member in libraryStore.members" :key="member.id" :value="member.id">
155
- {{ member.name }} ({{ member.id }})
156
- </option>
157
- </select>
158
- <button
159
- class="btn-issue"
160
- :disabled="book.copies === 0 || !selectedMemberId[book.id]"
161
- @click="handleIssueBook(book.id)"
162
- >
163
- Issue
164
- </button>
165
- </div>
166
- </td>
167
- </tr>
168
- <tr v-if="filteredBooks.length === 0">
169
- <td colspan="8" class="text-center">No books match your criteria.</td>
170
- </tr>
171
- </tbody>
172
- </table>
173
- </div>
174
-
175
- <div class="form-card">
176
- <h3>Add New Book to Inventory</h3>
177
- <form class="add-form" @submit.prevent="handleAddBook">
178
- <div class="form-group">
179
- <label>Book Title</label>
180
- <input v-model="newBook.title" type="text" required placeholder="e.g. Design Patterns" />
181
- </div>
182
- <div class="form-group">
183
- <label>Author</label>
184
- <input v-model="newBook.author" type="text" required placeholder="e.g. Gang of Four" />
185
- </div>
186
- <div class="form-group">
187
- <label>ISBN</label>
188
- <input v-model="newBook.isbn" type="text" required placeholder="e.g. 978-0201633610" />
189
- </div>
190
- <div class="form-group">
191
- <label>Category</label>
192
- <select v-model="newBook.category">
193
- <option v-for="cat in categories" :key="cat" :value="cat">{{ cat }}</option>
194
- </select>
195
- </div>
196
- <div class="form-group">
197
- <label>Copies</label>
198
- <input v-model.number="newBook.copies" type="number" min="1" required />
199
- </div>
200
- <button type="submit" class="btn-submit">Add Book</button>
201
- </form>
202
- </div>
25
+ <h2>Book Catalog & Inventory</h2>
26
+
27
+ <BookForm @submit-book="store.addBook" />
28
+
29
+ <BookTable
30
+ :books="store.books"
31
+ @borrow="handleBorrow"
32
+ @patch="({ id, copies }) => store.patchBook(id, { copies })"
33
+ @delete="(id) => store.deleteBook(id)"
34
+ />
35
+
36
+ <BorrowModal
37
+ :show="isModalOpen"
38
+ :book="selectedBook"
39
+ :members="store.members"
40
+ @close="isModalOpen = false"
41
+ @confirm="handleConfirmBorrow"
42
+ />
203
43
  </div>
204
44
  </template>
205
45
 
206
46
  <style scoped>
207
- .catalog-page {
208
- display: flex;
209
- flex-direction: column;
210
- gap: 20px;
211
- }
212
-
213
- .stats-bar {
214
- display: grid;
215
- grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
216
- gap: 15px;
217
- }
218
-
219
- .stat-card {
220
- background: white;
221
- padding: 16px;
222
- border-radius: 6px;
223
- border: 1px solid #e2e8f0;
224
- display: flex;
225
- flex-direction: column;
226
- align-items: center;
227
- }
228
-
229
- .stat-num {
230
- font-size: 24px;
231
- font-weight: 700;
232
- color: #1e293b;
233
- }
234
-
235
- .stat-label {
236
- font-size: 13px;
237
- color: #64748b;
238
- margin-top: 4px;
239
- }
240
-
241
- .alert-box {
242
- background-color: #e0f2fe;
243
- color: #0369a1;
244
- border: 1px solid #bae6fd;
245
- padding: 12px 16px;
246
- border-radius: 6px;
247
- font-size: 14px;
248
- }
249
-
250
- .controls-row {
251
- display: flex;
252
- gap: 12px;
253
- }
254
-
255
- .search-input {
256
- flex: 1;
257
- padding: 8px 12px;
258
- border: 1px solid #cbd5e1;
259
- border-radius: 4px;
260
- font-size: 14px;
261
- }
262
-
263
- .filter-select {
264
- padding: 8px 12px;
265
- border: 1px solid #cbd5e1;
266
- border-radius: 4px;
267
- font-size: 14px;
268
- background-color: white;
269
- }
270
-
271
- .table-card {
272
- background: white;
273
- border: 1px solid #e2e8f0;
274
- border-radius: 6px;
275
- padding: 20px;
276
- overflow-x: auto;
277
- }
278
-
279
- .table-card h2 {
280
- margin: 0 0 16px 0;
281
- font-size: 18px;
282
- color: #1e293b;
283
- }
284
-
285
- .data-table {
286
- width: 100%;
287
- border-collapse: collapse;
288
- font-size: 14px;
289
- text-align: left;
290
- }
291
-
292
- .data-table th,
293
- .data-table td {
294
- padding: 10px 12px;
295
- border-bottom: 1px solid #e2e8f0;
296
- }
297
-
298
- .data-table th {
299
- background-color: #f8fafc;
300
- color: #475569;
301
- font-weight: 600;
302
- }
303
-
304
- .font-bold {
305
- font-weight: 600;
306
- }
307
-
308
- .tag {
309
- background-color: #f1f5f9;
310
- color: #475569;
311
- padding: 3px 8px;
312
- border-radius: 4px;
313
- font-size: 12px;
314
- }
315
-
316
- .copies-count {
317
- font-weight: 600;
318
- color: #0f172a;
319
- }
320
-
321
- .copies-count.zero-copies {
322
- color: #ef4444;
323
- }
324
-
325
- .status-badge {
326
- display: inline-block;
327
- padding: 3px 8px;
328
- border-radius: 4px;
329
- font-size: 12px;
330
- font-weight: 600;
331
- }
332
-
333
- .status-badge.in-stock {
334
- background-color: #dcfce7;
335
- color: #166534;
336
- }
337
-
338
- .status-badge.out-stock {
339
- background-color: #fee2e2;
340
- color: #991b1b;
341
- }
342
-
343
- .issue-controls {
344
- display: flex;
345
- gap: 8px;
346
- align-items: center;
347
- }
348
-
349
- .member-select {
350
- padding: 6px 8px;
351
- border: 1px solid #cbd5e1;
352
- border-radius: 4px;
353
- font-size: 13px;
354
- max-width: 150px;
355
- }
356
-
357
- .btn-issue {
358
- background-color: #2563eb;
359
- color: white;
360
- border: none;
361
- padding: 6px 12px;
362
- border-radius: 4px;
363
- cursor: pointer;
364
- font-size: 13px;
365
- }
366
-
367
- .btn-issue:disabled {
368
- background-color: #94a3b8;
369
- cursor: not-allowed;
370
- }
371
-
372
- .text-center {
373
- text-align: center;
374
- color: #64748b;
375
- padding: 20px 0;
376
- }
377
-
378
- .form-card {
379
- background: white;
380
- border: 1px solid #e2e8f0;
381
- border-radius: 6px;
382
- padding: 20px;
383
- }
384
-
385
- .form-card h3 {
386
- margin: 0 0 16px 0;
387
- font-size: 16px;
388
- color: #1e293b;
389
- }
390
-
391
- .add-form {
392
- display: grid;
393
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
394
- gap: 12px;
395
- align-items: flex-end;
396
- }
397
-
398
- .form-group {
399
- display: flex;
400
- flex-direction: column;
401
- gap: 6px;
402
- }
403
-
404
- .form-group label {
405
- font-size: 12px;
406
- font-weight: 600;
407
- color: #475569;
408
- }
409
-
410
- .form-group input,
411
- .form-group select {
412
- padding: 8px 10px;
413
- border: 1px solid #cbd5e1;
414
- border-radius: 4px;
415
- font-size: 14px;
416
- }
417
-
418
- .btn-submit {
419
- background-color: #0f172a;
420
- color: white;
421
- border: none;
422
- padding: 9px 16px;
423
- border-radius: 4px;
424
- font-size: 14px;
425
- cursor: pointer;
426
- font-weight: 500;
427
- }
428
-
429
- .btn-submit:hover {
430
- background-color: #1e293b;
47
+ .catalog-page h2 {
48
+ font-size: 22px;
49
+ margin-bottom: 16px;
431
50
  }
432
51
  </style>
@@ -10,37 +10,38 @@ onMounted(() => {
10
10
  </script>
11
11
 
12
12
  <template>
13
- <div class="game-wrapper">
14
- <h1>Vue Pinia Memory Game</h1>
13
+ <div class="game-container">
14
+ <h1>Card Memory Game</h1>
15
15
 
16
16
  <div class="stats">
17
17
  <span>Moves: {{ store.moves }}</span>
18
- <span>Matches: {{ store.matches }} / {{ store.symbols.length }}</span>
18
+ <span>Matched: {{ store.matchedCount }} / {{ store.symbols.length }}</span>
19
19
  </div>
20
20
 
21
- <button type="button" class="btn" @click="store.initGame()">Restart Game</button>
22
-
23
- <div v-if="store.isGameOver" class="win-banner">
24
- 🎉 You won in {{ store.moves }} moves!
21
+ <div v-if="store.isGameWon" class="win-banner">
22
+ You won in {{ store.moves }} moves!
25
23
  </div>
26
24
 
27
- <div class="grid">
25
+ <div class="card-grid">
28
26
  <div
29
- v-for="card in store.cards"
27
+ v-for="(card, index) in store.cards"
30
28
  :key="card.id"
31
29
  class="card"
32
- :class="{ flipped: card.isFlipped, matched: card.isMatched }"
33
- @click="store.flipCard(card)"
30
+ :class="{ flipped: card.flipped || card.matched, matched: card.matched }"
31
+ @click="store.flipCard(index)"
34
32
  >
35
- {{ card.isFlipped || card.isMatched ? card.symbol : '?' }}
33
+ <span v-if="card.flipped || card.matched">{{ card.symbol }}</span>
34
+ <span v-else class="card-back">?</span>
36
35
  </div>
37
36
  </div>
37
+
38
+ <button class="reset-btn" @click="store.initGame">Restart Game</button>
38
39
  </div>
39
40
  </template>
40
41
 
41
42
  <style scoped>
42
- .game-wrapper {
43
- max-width: 360px;
43
+ .game-container {
44
+ max-width: 380px;
44
45
  margin: 30px auto;
45
46
  text-align: center;
46
47
  font-family: Arial, sans-serif;
@@ -48,59 +49,54 @@ onMounted(() => {
48
49
 
49
50
  .stats {
50
51
  display: flex;
51
- justify-content: space-around;
52
- font-size: 18px;
52
+ justify-content: space-between;
53
+ margin-bottom: 15px;
53
54
  font-weight: bold;
54
- margin: 15px 0;
55
- }
56
-
57
- .btn {
58
- padding: 8px 16px;
59
- background-color: #2980b9;
60
- color: white;
61
- border: none;
62
- border-radius: 4px;
63
- cursor: pointer;
64
- margin-bottom: 20px;
65
55
  }
66
56
 
67
57
  .win-banner {
68
- padding: 10px;
69
58
  background-color: #d4edda;
70
59
  color: #155724;
71
- margin-bottom: 15px;
60
+ padding: 10px;
72
61
  border-radius: 4px;
62
+ margin-bottom: 15px;
73
63
  font-weight: bold;
74
64
  }
75
65
 
76
- .grid {
66
+ .card-grid {
77
67
  display: grid;
78
- grid-template-columns: repeat(4, 75px);
68
+ grid-template-columns: repeat(4, 1fr);
79
69
  gap: 10px;
80
- justify-content: center;
81
70
  }
82
71
 
83
72
  .card {
84
- width: 75px;
85
- height: 75px;
73
+ height: 70px;
86
74
  background-color: #34495e;
87
75
  color: white;
88
- font-size: 32px;
89
76
  display: flex;
90
- justify-content: center;
91
77
  align-items: center;
78
+ justify-content: center;
79
+ font-size: 26px;
92
80
  border-radius: 6px;
93
81
  cursor: pointer;
94
- user-select: none;
95
82
  }
96
83
 
97
84
  .card.flipped {
98
- background-color: #ecf0f1;
99
- color: #2c3e50;
85
+ background-color: #3498db;
100
86
  }
101
87
 
102
88
  .card.matched {
103
89
  background-color: #2ecc71;
104
- color: white;
90
+ }
91
+
92
+ .card-back {
93
+ font-size: 20px;
94
+ color: #bdc3c7;
95
+ }
96
+
97
+ .reset-btn {
98
+ margin-top: 20px;
99
+ padding: 8px 16px;
100
+ cursor: pointer;
105
101
  }
106
102
  </style>