wad-lab 1.0.7 → 1.0.9

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 (48) hide show
  1. package/bin/index.js +24 -18
  2. package/package.json +1 -1
  3. package/templates/game-bingo/index.html +32 -0
  4. package/templates/game-bingo/script.js +118 -0
  5. package/templates/game-bingo/style.css +104 -0
  6. package/templates/springboot-course-registration/.mvn/wrapper/maven-wrapper.properties +3 -0
  7. package/templates/springboot-course-registration/mvnw +295 -0
  8. package/templates/springboot-course-registration/mvnw.cmd +189 -0
  9. package/templates/springboot-course-registration/pom.xml +41 -0
  10. package/templates/springboot-course-registration/src/main/java/com/example/coursereg/CourseRegApplication.java +12 -0
  11. package/templates/springboot-course-registration/src/main/java/com/example/coursereg/controller/CourseController.java +42 -0
  12. package/templates/springboot-course-registration/src/main/java/com/example/coursereg/model/Course.java +99 -0
  13. package/templates/springboot-course-registration/src/main/java/com/example/coursereg/repository/CourseRepository.java +16 -0
  14. package/templates/springboot-course-registration/src/main/java/com/example/coursereg/service/CourseService.java +60 -0
  15. package/templates/springboot-course-registration/src/main/resources/application.properties +3 -0
  16. package/templates/springboot-course-registration/src/test/java/com/example/coursereg/CourseRegApplicationTests.java +12 -0
  17. package/templates/vue-banking-app/index.html +12 -0
  18. package/templates/vue-banking-app/package.json +20 -0
  19. package/templates/vue-banking-app/src/App.vue +80 -0
  20. package/templates/vue-banking-app/src/main.js +9 -0
  21. package/templates/vue-banking-app/src/router/index.js +17 -0
  22. package/templates/vue-banking-app/src/stores/bankStore.js +48 -0
  23. package/templates/vue-banking-app/src/views/DashboardView.vue +88 -0
  24. package/templates/vue-banking-app/src/views/StatementView.vue +171 -0
  25. package/templates/vue-banking-app/src/views/TransactionFormView.vue +144 -0
  26. package/templates/vue-banking-app/vite.config.js +6 -0
  27. package/templates/vue-employee-system/index.html +12 -0
  28. package/templates/vue-employee-system/package.json +19 -0
  29. package/templates/vue-employee-system/src/App.vue +388 -0
  30. package/templates/vue-employee-system/src/main.js +5 -0
  31. package/templates/vue-employee-system/src/stores/employeeStore.js +54 -0
  32. package/templates/vue-employee-system/vite.config.js +6 -0
  33. package/templates/vue-food-ordering/index.html +12 -0
  34. package/templates/vue-food-ordering/package.json +20 -0
  35. package/templates/vue-food-ordering/src/App.vue +109 -0
  36. package/templates/vue-food-ordering/src/main.js +6 -0
  37. package/templates/vue-food-ordering/src/router/index.js +17 -0
  38. package/templates/vue-food-ordering/src/stores/foodStore.js +65 -0
  39. package/templates/vue-food-ordering/src/views/MenuView.vue +145 -0
  40. package/templates/vue-food-ordering/src/views/OrderCartView.vue +181 -0
  41. package/templates/vue-food-ordering/src/views/OrderSuccessView.vue +93 -0
  42. package/templates/vue-food-ordering/vite.config.js +6 -0
  43. package/templates/vue-symposium-system/index.html +12 -0
  44. package/templates/vue-symposium-system/package.json +19 -0
  45. package/templates/vue-symposium-system/src/App.vue +297 -0
  46. package/templates/vue-symposium-system/src/main.js +5 -0
  47. package/templates/vue-symposium-system/src/stores/symposiumStore.js +44 -0
  48. package/templates/vue-symposium-system/vite.config.js +6 -0
@@ -0,0 +1,388 @@
1
+ <script setup>
2
+ import { reactive, computed } from 'vue'
3
+ import { useEmployeeStore } from './stores/employeeStore'
4
+
5
+ const store = useEmployeeStore()
6
+
7
+ const form = reactive({
8
+ id: '',
9
+ name: '',
10
+ department: 'Engineering',
11
+ experience: '',
12
+ baseSalary: ''
13
+ })
14
+
15
+ const formDepartments = computed(() => {
16
+ return store.departments.filter(dept => dept !== 'All')
17
+ })
18
+
19
+ const handleAddEmployee = () => {
20
+ if (!form.id || !form.name || !form.department || form.experience === '' || form.baseSalary === '') {
21
+ return
22
+ }
23
+ store.addEmployee({
24
+ id: Number(form.id),
25
+ name: form.name.trim(),
26
+ department: form.department,
27
+ experience: Number(form.experience),
28
+ baseSalary: Number(form.baseSalary)
29
+ })
30
+ form.id = ''
31
+ form.name = ''
32
+ form.department = 'Engineering'
33
+ form.experience = ''
34
+ form.baseSalary = ''
35
+ }
36
+
37
+ const formatCurrency = (amount) => {
38
+ return Number(amount || 0).toLocaleString('en-US', {
39
+ minimumFractionDigits: 2,
40
+ maximumFractionDigits: 2
41
+ })
42
+ }
43
+ </script>
44
+
45
+ <template>
46
+ <div class="container">
47
+ <header class="header">
48
+ <h1>Employee Management System</h1>
49
+ </header>
50
+
51
+ <div class="summary-cards">
52
+ <div class="card">
53
+ <h3>Total Employees</h3>
54
+ <p class="card-value">{{ store.totalEmployees }}</p>
55
+ </div>
56
+ <div class="card">
57
+ <h3>Total Payroll ($)</h3>
58
+ <p class="card-value">${{ formatCurrency(store.totalPayroll) }}</p>
59
+ </div>
60
+ <div class="card">
61
+ <h3>Average Salary ($)</h3>
62
+ <p class="card-value">${{ formatCurrency(store.averageSalary) }}</p>
63
+ </div>
64
+ </div>
65
+
66
+ <section class="section form-section">
67
+ <h2>Add New Employee</h2>
68
+ <form @submit.prevent="handleAddEmployee" class="employee-form">
69
+ <div class="form-group">
70
+ <label for="emp-id">Employee ID</label>
71
+ <input
72
+ id="emp-id"
73
+ v-model="form.id"
74
+ type="number"
75
+ min="1"
76
+ placeholder="e.g. 5"
77
+ required
78
+ />
79
+ </div>
80
+ <div class="form-group">
81
+ <label for="emp-name">Name</label>
82
+ <input
83
+ id="emp-name"
84
+ v-model="form.name"
85
+ type="text"
86
+ placeholder="e.g. Jane Doe"
87
+ required
88
+ />
89
+ </div>
90
+ <div class="form-group">
91
+ <label for="emp-department">Department</label>
92
+ <select id="emp-department" v-model="form.department" required>
93
+ <option v-for="dept in formDepartments" :key="dept" :value="dept">
94
+ {{ dept }}
95
+ </option>
96
+ </select>
97
+ </div>
98
+ <div class="form-group">
99
+ <label for="emp-experience">Experience (Years)</label>
100
+ <input
101
+ id="emp-experience"
102
+ v-model="form.experience"
103
+ type="number"
104
+ min="0"
105
+ step="1"
106
+ placeholder="e.g. 3"
107
+ required
108
+ />
109
+ </div>
110
+ <div class="form-group">
111
+ <label for="emp-salary">Base Salary ($)</label>
112
+ <input
113
+ id="emp-salary"
114
+ v-model="form.baseSalary"
115
+ type="number"
116
+ min="0"
117
+ step="0.01"
118
+ placeholder="e.g. 50000"
119
+ required
120
+ />
121
+ </div>
122
+ <div class="form-actions">
123
+ <button type="submit" class="btn btn-primary">Add Employee</button>
124
+ </div>
125
+ </form>
126
+ </section>
127
+
128
+ <section class="section">
129
+ <div class="tabs-header">
130
+ <h2>Department Filter</h2>
131
+ <div class="filter-tabs">
132
+ <button
133
+ v-for="dept in store.departments"
134
+ :key="dept"
135
+ type="button"
136
+ class="tab-btn"
137
+ :class="{ active: store.selectedDept === dept }"
138
+ @click="store.setDepartment(dept)"
139
+ >
140
+ {{ dept }}
141
+ </button>
142
+ </div>
143
+ </div>
144
+
145
+ <div class="table-container">
146
+ <table class="employee-table">
147
+ <thead>
148
+ <tr>
149
+ <th>ID</th>
150
+ <th>Name</th>
151
+ <th>Department</th>
152
+ <th>Experience</th>
153
+ <th>Base Salary</th>
154
+ <th>Bonus</th>
155
+ <th>Tax</th>
156
+ <th>Net Salary</th>
157
+ <th>Actions</th>
158
+ </tr>
159
+ </thead>
160
+ <tbody>
161
+ <tr v-if="store.filteredEmployees.length === 0">
162
+ <td colspan="9" class="no-records">No employees found in this department.</td>
163
+ </tr>
164
+ <tr v-for="emp in store.filteredEmployees" :key="emp.id">
165
+ <td>{{ emp.id }}</td>
166
+ <td>{{ emp.name }}</td>
167
+ <td>{{ emp.department }}</td>
168
+ <td>{{ emp.experience }} yrs</td>
169
+ <td>${{ formatCurrency(emp.baseSalary) }}</td>
170
+ <td>${{ formatCurrency(store.calculateBonus(emp)) }}</td>
171
+ <td>${{ formatCurrency(store.calculateTax(emp)) }}</td>
172
+ <td>${{ formatCurrency(store.calculateNetSalary(emp)) }}</td>
173
+ <td>
174
+ <button
175
+ type="button"
176
+ class="btn btn-danger"
177
+ @click="store.deleteEmployee(emp.id)"
178
+ >
179
+ Delete
180
+ </button>
181
+ </td>
182
+ </tr>
183
+ </tbody>
184
+ </table>
185
+ </div>
186
+ </section>
187
+ </div>
188
+ </template>
189
+
190
+ <style>
191
+ * {
192
+ box-sizing: border-box;
193
+ margin: 0;
194
+ padding: 0;
195
+ }
196
+
197
+ body {
198
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
199
+ background-color: #f7f9fc;
200
+ color: #333333;
201
+ padding: 24px 16px;
202
+ }
203
+
204
+ .container {
205
+ max-width: 1000px;
206
+ margin: 0 auto;
207
+ }
208
+
209
+ .header {
210
+ margin-bottom: 24px;
211
+ }
212
+
213
+ .header h1 {
214
+ font-size: 28px;
215
+ color: #1a202c;
216
+ }
217
+
218
+ .summary-cards {
219
+ display: grid;
220
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
221
+ gap: 16px;
222
+ margin-bottom: 24px;
223
+ }
224
+
225
+ .card {
226
+ background-color: #ffffff;
227
+ border: 1px solid #e2e8f0;
228
+ border-radius: 8px;
229
+ padding: 16px;
230
+ }
231
+
232
+ .card h3 {
233
+ font-size: 14px;
234
+ font-weight: 500;
235
+ color: #718096;
236
+ margin-bottom: 8px;
237
+ }
238
+
239
+ .card-value {
240
+ font-size: 24px;
241
+ font-weight: 700;
242
+ color: #2b6cb0;
243
+ }
244
+
245
+ .section {
246
+ background-color: #ffffff;
247
+ border: 1px solid #e2e8f0;
248
+ border-radius: 8px;
249
+ padding: 20px;
250
+ margin-bottom: 24px;
251
+ }
252
+
253
+ .section h2 {
254
+ font-size: 18px;
255
+ color: #2d3748;
256
+ margin-bottom: 16px;
257
+ }
258
+
259
+ .employee-form {
260
+ display: grid;
261
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
262
+ gap: 12px;
263
+ align-items: flex-end;
264
+ }
265
+
266
+ .form-group {
267
+ display: flex;
268
+ flex-direction: column;
269
+ }
270
+
271
+ .form-group label {
272
+ font-size: 13px;
273
+ font-weight: 600;
274
+ color: #4a5568;
275
+ margin-bottom: 6px;
276
+ }
277
+
278
+ .form-group input,
279
+ .form-group select {
280
+ padding: 8px 10px;
281
+ font-size: 14px;
282
+ border: 1px solid #cbd5e0;
283
+ border-radius: 4px;
284
+ background-color: #ffffff;
285
+ outline: none;
286
+ }
287
+
288
+ .form-group input:focus,
289
+ .form-group select:focus {
290
+ border-color: #3182ce;
291
+ }
292
+
293
+ .form-actions {
294
+ display: flex;
295
+ }
296
+
297
+ .btn {
298
+ padding: 8px 16px;
299
+ font-size: 14px;
300
+ font-weight: 500;
301
+ border-radius: 4px;
302
+ border: none;
303
+ cursor: pointer;
304
+ }
305
+
306
+ .btn-primary {
307
+ background-color: #3182ce;
308
+ color: #ffffff;
309
+ width: 100%;
310
+ height: 38px;
311
+ }
312
+
313
+ .btn-primary:hover {
314
+ background-color: #2b6cb0;
315
+ }
316
+
317
+ .btn-danger {
318
+ background-color: #e53e3e;
319
+ color: #ffffff;
320
+ padding: 4px 10px;
321
+ font-size: 12px;
322
+ }
323
+
324
+ .btn-danger:hover {
325
+ background-color: #c53030;
326
+ }
327
+
328
+ .tabs-header {
329
+ display: flex;
330
+ flex-wrap: wrap;
331
+ justify-content: space-between;
332
+ align-items: center;
333
+ gap: 12px;
334
+ margin-bottom: 16px;
335
+ }
336
+
337
+ .filter-tabs {
338
+ display: flex;
339
+ flex-wrap: wrap;
340
+ gap: 8px;
341
+ }
342
+
343
+ .tab-btn {
344
+ padding: 6px 14px;
345
+ font-size: 13px;
346
+ border: 1px solid #cbd5e0;
347
+ background-color: #edf2f7;
348
+ color: #4a5568;
349
+ border-radius: 4px;
350
+ cursor: pointer;
351
+ }
352
+
353
+ .tab-btn.active {
354
+ background-color: #3182ce;
355
+ color: #ffffff;
356
+ border-color: #3182ce;
357
+ font-weight: 600;
358
+ }
359
+
360
+ .table-container {
361
+ overflow-x: auto;
362
+ }
363
+
364
+ .employee-table {
365
+ width: 100%;
366
+ border-collapse: collapse;
367
+ text-align: left;
368
+ }
369
+
370
+ .employee-table th,
371
+ .employee-table td {
372
+ padding: 10px 12px;
373
+ border-bottom: 1px solid #e2e8f0;
374
+ font-size: 14px;
375
+ }
376
+
377
+ .employee-table th {
378
+ background-color: #f7fafc;
379
+ color: #4a5568;
380
+ font-weight: 600;
381
+ }
382
+
383
+ .no-records {
384
+ text-align: center;
385
+ color: #a0aec0;
386
+ padding: 24px;
387
+ }
388
+ </style>
@@ -0,0 +1,5 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+ import App from './App.vue'
4
+
5
+ createApp(App).use(createPinia()).mount('#app')
@@ -0,0 +1,54 @@
1
+ import { defineStore } from 'pinia'
2
+
3
+ export const useEmployeeStore = defineStore('employee', {
4
+ state: () => ({
5
+ employees: [
6
+ { id: 1, name: 'Alice Smith', department: 'Engineering', experience: 4, baseSalary: 60000 },
7
+ { id: 2, name: 'Bob Jones', department: 'HR', experience: 2, baseSalary: 45000 },
8
+ { id: 3, name: 'Charlie Brown', department: 'Marketing', experience: 5, baseSalary: 52000 },
9
+ { id: 4, name: 'Diana Prince', department: 'Finance', experience: 1, baseSalary: 58000 }
10
+ ],
11
+ departments: ['All', 'Engineering', 'HR', 'Marketing', 'Finance'],
12
+ selectedDept: 'All'
13
+ }),
14
+ getters: {
15
+ filteredEmployees(state) {
16
+ if (state.selectedDept === 'All') {
17
+ return state.employees
18
+ }
19
+ return state.employees.filter(emp => emp.department === state.selectedDept)
20
+ },
21
+ totalEmployees(state) {
22
+ return state.employees.length
23
+ },
24
+ totalPayroll() {
25
+ return this.employees.reduce((sum, emp) => sum + this.calculateNetSalary(emp), 0)
26
+ },
27
+ averageSalary() {
28
+ if (this.totalEmployees === 0) return 0
29
+ return this.totalPayroll / this.totalEmployees
30
+ }
31
+ },
32
+ actions: {
33
+ calculateBonus(emp) {
34
+ return emp.experience >= 3 ? emp.baseSalary * 0.10 : emp.baseSalary * 0.05
35
+ },
36
+ calculateTax(emp) {
37
+ return emp.baseSalary * 0.12
38
+ },
39
+ calculateNetSalary(emp) {
40
+ const bonus = this.calculateBonus(emp)
41
+ const tax = this.calculateTax(emp)
42
+ return emp.baseSalary + bonus - tax
43
+ },
44
+ addEmployee(emp) {
45
+ this.employees.push(emp)
46
+ },
47
+ deleteEmployee(id) {
48
+ this.employees = this.employees.filter(emp => emp.id !== id)
49
+ },
50
+ setDepartment(dept) {
51
+ this.selectedDept = dept
52
+ }
53
+ }
54
+ })
@@ -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>Food Ordering App</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-food-ordering",
3
+ "version": "0.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
+ "pinia": "^4.1.0",
13
+ "vue": "^3.5.44",
14
+ "vue-router": "^5.4.0"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-vue": "^5.2.1",
18
+ "vite": "^6.2.0"
19
+ }
20
+ }
@@ -0,0 +1,109 @@
1
+ <script setup>
2
+ import { useFoodStore } from './stores/foodStore'
3
+
4
+ const store = useFoodStore()
5
+ </script>
6
+
7
+ <template>
8
+ <div class="app-layout">
9
+ <header class="app-header">
10
+ <div class="header-inner">
11
+ <h1 class="logo">Food Ordering App</h1>
12
+ <nav class="nav-links">
13
+ <router-link to="/" class="nav-item">Menu</router-link>
14
+ <router-link to="/cart" class="nav-item cart-link">
15
+ Cart
16
+ <span v-if="store.cartCount > 0" class="cart-badge">{{ store.cartCount }}</span>
17
+ </router-link>
18
+ </nav>
19
+ </div>
20
+ </header>
21
+
22
+ <main class="main-content">
23
+ <router-view />
24
+ </main>
25
+ </div>
26
+ </template>
27
+
28
+ <style>
29
+ * {
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ body {
34
+ margin: 0;
35
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
36
+ color: #333;
37
+ background-color: #f4f6f9;
38
+ }
39
+ </style>
40
+
41
+ <style scoped>
42
+ .app-layout {
43
+ min-height: 100vh;
44
+ display: flex;
45
+ flex-direction: column;
46
+ }
47
+
48
+ .app-header {
49
+ background-color: #ffffff;
50
+ border-bottom: 1px solid #e0e0e0;
51
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
52
+ }
53
+
54
+ .header-inner {
55
+ max-width: 1000px;
56
+ margin: 0 auto;
57
+ padding: 1rem 1.5rem;
58
+ display: flex;
59
+ justify-content: space-between;
60
+ align-items: center;
61
+ }
62
+
63
+ .logo {
64
+ margin: 0;
65
+ font-size: 1.4rem;
66
+ color: #222;
67
+ }
68
+
69
+ .nav-links {
70
+ display: flex;
71
+ gap: 1.5rem;
72
+ align-items: center;
73
+ }
74
+
75
+ .nav-item {
76
+ text-decoration: none;
77
+ color: #555;
78
+ font-weight: 500;
79
+ position: relative;
80
+ display: inline-flex;
81
+ align-items: center;
82
+ }
83
+
84
+ .nav-item.router-link-exact-active {
85
+ color: #007bff;
86
+ }
87
+
88
+ .cart-badge {
89
+ background-color: #dc3545;
90
+ color: #fff;
91
+ border-radius: 50%;
92
+ font-size: 0.75rem;
93
+ min-width: 18px;
94
+ height: 18px;
95
+ display: inline-flex;
96
+ align-items: center;
97
+ justify-content: center;
98
+ padding: 0 4px;
99
+ margin-left: 0.4rem;
100
+ }
101
+
102
+ .main-content {
103
+ max-width: 1000px;
104
+ width: 100%;
105
+ margin: 0 auto;
106
+ padding: 1.5rem;
107
+ flex: 1;
108
+ }
109
+ </style>
@@ -0,0 +1,6 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+ import App from './App.vue'
4
+ import router from './router'
5
+
6
+ createApp(App).use(createPinia()).use(router).mount('#app')
@@ -0,0 +1,17 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+ import MenuView from '../views/MenuView.vue'
3
+ import OrderCartView from '../views/OrderCartView.vue'
4
+ import OrderSuccessView from '../views/OrderSuccessView.vue'
5
+
6
+ const routes = [
7
+ { path: '/', name: 'Menu', component: MenuView },
8
+ { path: '/cart', name: 'OrderCart', component: OrderCartView },
9
+ { path: '/order-success', name: 'OrderSuccess', component: OrderSuccessView }
10
+ ]
11
+
12
+ const router = createRouter({
13
+ history: createWebHistory(),
14
+ routes
15
+ })
16
+
17
+ export default router
@@ -0,0 +1,65 @@
1
+ import { defineStore } from 'pinia'
2
+
3
+ export const useFoodStore = defineStore('food', {
4
+ state: () => ({
5
+ categories: ['All', 'Starters', 'Main Course', 'Beverages', 'Desserts'],
6
+ menuItems: [
7
+ { id: 1, name: 'Spring Rolls', category: 'Starters', price: 5.99, description: 'Crispy vegetable rolls served with sweet chili sauce' },
8
+ { id: 2, name: 'Garlic Bread', category: 'Starters', price: 4.49, description: 'Toasted baguette slices with garlic butter and herbs' },
9
+ { id: 3, name: 'Grilled Chicken Burger', category: 'Main Course', price: 11.99, description: 'Juicy chicken breast with lettuce, tomato, and special sauce' },
10
+ { id: 4, name: 'Pasta Primavera', category: 'Main Course', price: 13.50, description: 'Fettuccine tossed with fresh garden vegetables and parmesan' },
11
+ { id: 5, name: 'Margherita Pizza', category: 'Main Course', price: 12.99, description: 'Classic cheese and tomato pizza with basil leaves' },
12
+ { id: 6, name: 'Iced Lemon Tea', category: 'Beverages', price: 3.25, description: 'Refreshing brewed tea with lemon and mint' },
13
+ { id: 7, name: 'Berry Smoothie', category: 'Beverages', price: 4.95, description: 'Blended mixed berries with yogurt and honey' },
14
+ { id: 8, name: 'Chocolate Lava Cake', category: 'Desserts', price: 6.50, description: 'Warm molten chocolate cake with vanilla ice cream' }
15
+ ],
16
+ cart: []
17
+ }),
18
+ getters: {
19
+ cartCount: (state) => state.cart.reduce((sum, item) => sum + item.quantity, 0),
20
+ cartSubtotal: (state) => Number(state.cart.reduce((sum, item) => sum + item.price * item.quantity, 0).toFixed(2)),
21
+ taxAmount: (state) => Number((state.cart.reduce((sum, item) => sum + item.price * item.quantity, 0) * 0.05).toFixed(2)),
22
+ totalBill: (state) => {
23
+ const subtotal = state.cart.reduce((sum, item) => sum + item.price * item.quantity, 0)
24
+ const tax = subtotal * 0.05
25
+ return Number((subtotal + tax).toFixed(2))
26
+ }
27
+ },
28
+ actions: {
29
+ addToCart(item) {
30
+ const existing = this.cart.find((i) => i.id === item.id)
31
+ if (existing) {
32
+ existing.quantity += 1
33
+ } else {
34
+ this.cart.push({
35
+ id: item.id,
36
+ name: item.name,
37
+ price: item.price,
38
+ quantity: 1
39
+ })
40
+ }
41
+ },
42
+ incrementQuantity(id) {
43
+ const item = this.cart.find((i) => i.id === id)
44
+ if (item) {
45
+ item.quantity += 1
46
+ }
47
+ },
48
+ decrementQuantity(id) {
49
+ const item = this.cart.find((i) => i.id === id)
50
+ if (item) {
51
+ if (item.quantity > 1) {
52
+ item.quantity -= 1
53
+ } else {
54
+ this.removeFromCart(id)
55
+ }
56
+ }
57
+ },
58
+ removeFromCart(id) {
59
+ this.cart = this.cart.filter((i) => i.id !== id)
60
+ },
61
+ clearCart() {
62
+ this.cart = []
63
+ }
64
+ }
65
+ })