├── .editorconfig ├── .github └── workflows │ └── main.yml ├── README.md ├── css └── style.css ├── data └── sample.json ├── favicon.ico ├── img ├── beef-burger.png ├── choco-glaze-donut-peanut.png ├── choco-glaze-donut.png ├── cinnamon-roll.png ├── coffee-latte.png ├── croissant.png ├── ice-chocolate.png ├── ice-tea.png ├── matcha-latte.png ├── receipt-logo.png ├── red-glaze-donut.png ├── sandwich.png └── sawarma.png ├── index.html ├── js └── script.js └── sound ├── beep-29.mp3 └── button-21.mp3 /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{js,css,html}] 4 | indent_size = 2 5 | indent_style = space 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | end_of_line = lf -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 🛎️ 11 | uses: actions/checkout@v2.3.1 12 | - name: Deploy 🚀 13 | uses: JamesIves/github-pages-deploy-action@4.1.1 14 | with: 15 | branch: gh-pages # The branch the action should deploy to. 16 | folder: "." # The folder the action should deploy. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TAILWIND POS 2 | ============ 3 | 4 | ![tailwind-pos-cover](https://user-images.githubusercontent.com/6297931/114289085-6599fc00-9a9f-11eb-810e-b32f1096ecdc.png) 5 | 6 | Tailwind POS is a POS (Point of Sales) application built with [Tailwind.css](https://tailwindcss.com) and [Alpine.js](https://github.com/alpinejs/alpine/). I create this application in order to exercise myself to build web application in "traditional" way, without any module bundler/build tools such as webpack, rollup, postcss, etc. So yes, it load tailwind.css and alpine.js via CDN. 7 | 8 | You can view the demo at [https://www.emsifa.com/tailwind-pos/](https://www.emsifa.com/tailwind-pos/). 9 | 10 | ## Attribution 11 | 12 | Sample food photos in this application are downloaded from freepik.com accounts listed below: 13 | 14 | * [topntp26](https://www.freepik.com/topntp26) 15 | * [mrsiraphol](https://www.freepik.com/mrsiraphol) 16 | * [jcomp](https://www.freepik.com/jcomp) 17 | * [timolina](https://www.freepik.com/timolina) 18 | * [Racool_studio](https://www.freepik.com/Racool_studio) 19 | * [8photo](https://www.freepik.com/8photo) 20 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /* width */ 2 | ::-webkit-scrollbar { 3 | width: 5px; 4 | } 5 | 6 | /* Track */ 7 | ::-webkit-scrollbar-track { 8 | background: #cfd8dc; 9 | border-radius: 5px; 10 | } 11 | 12 | /* Handle */ 13 | ::-webkit-scrollbar-thumb { 14 | background: #b0bec5; 15 | border-radius: 5px; 16 | } 17 | 18 | /* Handle on hover */ 19 | ::-webkit-scrollbar-thumb:hover { 20 | background: #90a4ae; 21 | } 22 | 23 | .bg-cyan-50, .hover\:bg-cyan-50:hover { 24 | background-color: #e0f7fa; 25 | } 26 | .bg-cyan-100, .hover\:bg-cyan-100:hover { 27 | background-color: #b2ebf2; 28 | } 29 | .bg-cyan-200, .hover\:bg-cyan-200:hover { 30 | background-color: #80deea; 31 | } 32 | .bg-cyan-300, .hover\:bg-cyan-300:hover { 33 | background-color: #4dd0e1; 34 | } 35 | .bg-cyan-400, .hover\:bg-cyan-400:hover { 36 | background-color: #26c6da; 37 | } 38 | .bg-cyan-500, .hover\:bg-cyan-500:hover { 39 | background-color: #00bcd4; 40 | } 41 | .bg-cyan-600, .hover\:bg-cyan-600:hover { 42 | background-color: #00acc1; 43 | } 44 | .bg-cyan-700, .hover\:bg-cyan-700:hover { 45 | background-color: #0097a7; 46 | } 47 | .bg-cyan-800, .hover\:bg-cyan-800:hover { 48 | background-color: #00838f; 49 | } 50 | .bg-cyan-900, .hover\:bg-cyan-900:hover { 51 | background-color: #006064; 52 | } 53 | 54 | 55 | .text-cyan-50, .hover\:text-cyan-50:hover { 56 | color: #e0f7fa; 57 | } 58 | .text-cyan-100, .hover\:text-cyan-100:hover { 59 | color: #b2ebf2; 60 | } 61 | .text-cyan-200, .hover\:text-cyan-200:hover { 62 | color: #80deea; 63 | } 64 | .text-cyan-300, .hover\:text-cyan-300:hover { 65 | color: #4dd0e1; 66 | } 67 | .text-cyan-400, .hover\:text-cyan-400:hover { 68 | color: #26c6da; 69 | } 70 | .text-cyan-500, .hover\:text-cyan-500:hover { 71 | color: #00bcd4; 72 | } 73 | .text-cyan-600, .hover\:text-cyan-600:hover { 74 | color: #00acc1; 75 | } 76 | .text-cyan-700, .hover\:text-cyan-700:hover { 77 | color: #0097a7; 78 | } 79 | .text-cyan-800, .hover\:text-cyan-800:hover { 80 | color: #00838f; 81 | } 82 | .text-cyan-900, .hover\:text-cyan-900:hover { 83 | color: #006064; 84 | } 85 | 86 | .bg-blue-gray-50, .hover\:bg-blue-gray-50:hover { 87 | background-color: #eceff1; 88 | } 89 | .bg-blue-gray-100, .hover\:bg-blue-gray-100:hover { 90 | background-color: #cfd8dc; 91 | } 92 | .bg-blue-gray-200, .hover\:bg-blue-gray-200:hover { 93 | background-color: #b0bec5; 94 | } 95 | .bg-blue-gray-300, .hover\:bg-blue-gray-300:hover { 96 | background-color: #90a4ae; 97 | } 98 | .bg-blue-gray-400, .hover\:bg-blue-gray-400:hover { 99 | background-color: #78909c; 100 | } 101 | .bg-blue-gray-500, .hover\:bg-blue-gray-500:hover { 102 | background-color: #607d8b; 103 | } 104 | .bg-blue-gray-600, .hover\:bg-blue-gray-600:hover { 105 | background-color: #546e7a; 106 | } 107 | .bg-blue-gray-700, .hover\:bg-blue-gray-700:hover { 108 | background-color: #455a64; 109 | } 110 | .bg-blue-gray-800, .hover\:bg-blue-gray-800:hover { 111 | background-color: #37474f; 112 | } 113 | .bg-blue-gray-900, .hover\:bg-blue-gray-900:hover { 114 | background-color: #263238; 115 | } 116 | 117 | .text-blue-gray-50, .hover\:text-blue-gray-50:hover { 118 | color: #eceff1; 119 | } 120 | .text-blue-gray-100, .hover\:text-blue-gray-100:hover { 121 | color: #cfd8dc; 122 | } 123 | .text-blue-gray-200, .hover\:text-blue-gray-200:hover { 124 | color: #b0bec5; 125 | } 126 | .text-blue-gray-300, .hover\:text-blue-gray-300:hover { 127 | color: #90a4ae; 128 | } 129 | .text-blue-gray-400, .hover\:text-blue-gray-400:hover { 130 | color: #78909c; 131 | } 132 | .text-blue-gray-500, .hover\:text-blue-gray-500:hover { 133 | color: #607d8b; 134 | } 135 | .text-blue-gray-600, .hover\:text-blue-gray-600:hover { 136 | color: #546e7a; 137 | } 138 | .text-blue-gray-700, .hover\:text-blue-gray-700:hover { 139 | color: #455a64; 140 | } 141 | .text-blue-gray-800, .hover\:text-blue-gray-800:hover { 142 | color: #37474f; 143 | } 144 | .text-blue-gray-900, .hover\:text-blue-gray-900:hover { 145 | color: #263238; 146 | } 147 | .bg-teal-50, .hover\:bg-teal-50:hover { 148 | background-color: #e0f2f1; 149 | } 150 | .bg-teal-100, .hover\:bg-teal-100:hover { 151 | background-color: #b2dfdb; 152 | } 153 | .bg-teal-200, .hover\:bg-teal-200:hover { 154 | background-color: #80cbc4; 155 | } 156 | .bg-teal-300, .hover\:bg-teal-300:hover { 157 | background-color: #4db6ac; 158 | } 159 | .bg-teal-400, .hover\:bg-teal-400:hover { 160 | background-color: #26a69a; 161 | } 162 | .bg-teal-500, .hover\:bg-teal-500:hover { 163 | background-color: #009688; 164 | } 165 | .bg-teal-600, .hover\:bg-teal-600:hover { 166 | background-color: #00897b; 167 | } 168 | .bg-teal-700, .hover\:bg-teal-700:hover { 169 | background-color: #00796b; 170 | } 171 | .bg-teal-800, .hover\:bg-teal-800:hover { 172 | background-color: #00695c; 173 | } 174 | .bg-teal-900, .hover\:bg-teal-900:hover { 175 | background-color: #004d40; 176 | } 177 | 178 | .text-teal-50, .hover\:text-teal-50:hover { 179 | color: #e0f2f1; 180 | } 181 | .text-teal-100, .hover\:text-teal-100:hover { 182 | color: #b2dfdb; 183 | } 184 | .text-teal-200, .hover\:text-teal-200:hover { 185 | color: #80cbc4; 186 | } 187 | .text-teal-300, .hover\:text-teal-300:hover { 188 | color: #4db6ac; 189 | } 190 | .text-teal-400, .hover\:text-teal-400:hover { 191 | color: #26a69a; 192 | } 193 | .text-teal-500, .hover\:text-teal-500:hover { 194 | color: #009688; 195 | } 196 | .text-teal-600, .hover\:text-teal-600:hover { 197 | color: #00897b; 198 | } 199 | .text-teal-700, .hover\:text-teal-700:hover { 200 | color: #00796b; 201 | } 202 | .text-teal-800, .hover\:text-teal-800:hover { 203 | color: #00695c; 204 | } 205 | .text-teal-900, .hover\:text-teal-900:hover { 206 | color: #004d40; 207 | } 208 | 209 | .nowrap { 210 | white-space: nowrap; 211 | } 212 | 213 | .glass { 214 | background-color: rgba(100, 120, 130, .6); 215 | backdrop-filter: blur(10px); 216 | } 217 | 218 | table td { 219 | vertical-align: top; 220 | } 221 | 222 | #receipt-content { 223 | max-height: 70vh; 224 | } 225 | 226 | @media print { 227 | .hide-print { 228 | display: none !important; 229 | } 230 | .print-area { 231 | display: block; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /data/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Beef Burger", 6 | "price": 45000, 7 | "image": "img/beef-burger.png", 8 | "options": null 9 | }, 10 | { 11 | "id": 2, 12 | "name": "Sandwich", 13 | "price": 32000, 14 | "image": "img/sandwich.png", 15 | "options": null 16 | }, 17 | { 18 | "id": 3, 19 | "name": "Sawarma", 20 | "price": 30000, 21 | "image": "img/sawarma.png", 22 | "options": null 23 | }, 24 | { 25 | "id": 4, 26 | "name": "Croissant", 27 | "price": 16000, 28 | "image": "img/croissant.png", 29 | "options": null 30 | }, 31 | { 32 | "id": 5, 33 | "name": "Cinnamon Roll", 34 | "price": 20000, 35 | "image": "img/cinnamon-roll.png", 36 | "options": null 37 | }, 38 | { 39 | "id": 6, 40 | "name": "Choco Glaze Donut with Peanut", 41 | "price": 10000, 42 | "image": "img/choco-glaze-donut-peanut.png", 43 | "options": null 44 | }, 45 | { 46 | "id": 7, 47 | "name": "Choco Glazed Donut", 48 | "price": 10000, 49 | "image": "img/choco-glaze-donut.png", 50 | "options": null 51 | }, 52 | { 53 | "id": 8, 54 | "name": "Red Glazed Donut", 55 | "price": 10000, 56 | "image": "img/red-glaze-donut.png", 57 | "options": null 58 | }, 59 | { 60 | "id": 9, 61 | "name": "Iced Coffee Latte", 62 | "price": 25000, 63 | "image": "img/coffee-latte.png", 64 | "options": null 65 | }, 66 | { 67 | "id": 10, 68 | "name": "Iced Chocolate", 69 | "price": 20000, 70 | "image": "img/ice-chocolate.png", 71 | "options": null 72 | }, 73 | { 74 | "id": 11, 75 | "name": "Iced Tea", 76 | "price": 15000, 77 | "image": "img/ice-tea.png", 78 | "options": null 79 | }, 80 | { 81 | "id": 12, 82 | "name": "Iced Matcha Latte", 83 | "price": 22000, 84 | "image": "img/matcha-latte.png", 85 | "options": null 86 | } 87 | ] 88 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/favicon.ico -------------------------------------------------------------------------------- /img/beef-burger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/beef-burger.png -------------------------------------------------------------------------------- /img/choco-glaze-donut-peanut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/choco-glaze-donut-peanut.png -------------------------------------------------------------------------------- /img/choco-glaze-donut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/choco-glaze-donut.png -------------------------------------------------------------------------------- /img/cinnamon-roll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/cinnamon-roll.png -------------------------------------------------------------------------------- /img/coffee-latte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/coffee-latte.png -------------------------------------------------------------------------------- /img/croissant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/croissant.png -------------------------------------------------------------------------------- /img/ice-chocolate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/ice-chocolate.png -------------------------------------------------------------------------------- /img/ice-tea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/ice-tea.png -------------------------------------------------------------------------------- /img/matcha-latte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/matcha-latte.png -------------------------------------------------------------------------------- /img/receipt-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/receipt-logo.png -------------------------------------------------------------------------------- /img/red-glaze-donut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/red-glaze-donut.png -------------------------------------------------------------------------------- /img/sandwich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/sandwich.png -------------------------------------------------------------------------------- /img/sawarma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/img/sawarma.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tailwind POS 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 96 |
97 | 98 | 99 |
100 | 101 |
102 |
103 |
104 | 105 | 106 | 107 |
108 | 114 |
115 |
116 |
117 |
121 |
122 | 123 | 124 | 125 |

126 | YOU DON'T HAVE 127 |
128 | ANY PRODUCTS TO SHOW 129 |

130 |
131 |
132 |
136 |
137 | 138 | 139 | 140 |

141 | EMPTY SEARCH RESULT 142 |
143 | "" 144 |

145 |
146 |
147 |
148 | 162 |
163 |
164 |
165 |
166 | 167 | 168 | 169 |
170 |
171 | 172 |
173 | 174 | 175 | 176 |

177 | CART EMPTY 178 |

179 |
180 | 181 | 182 |
183 |
184 |
185 | 186 | 187 | 188 | 189 |
190 |
191 |
192 | 193 | 198 |
199 |
200 | 201 |
202 | 226 |
227 |
228 | 229 | 230 | 231 |
232 |
233 |
TOTAL
234 |
235 |
236 |
237 |
238 |
CASH
239 |
240 |
Rp
241 | 242 |
243 |
244 |
245 |
246 | 249 |
250 |
251 |
255 |
CHANGE
256 |
259 |
260 |
261 |
265 |
268 |
269 |
270 |
274 | 275 | 276 | 277 |
278 | 289 |
290 | 291 |
292 |
293 | 294 |
295 | 296 | 297 |
298 |
299 |
300 | 301 | 302 | 303 |

FIRST TIME?

304 |
305 |
306 | 312 | 318 |
319 |
320 |
321 | 322 | 323 |
327 |
337 |
347 |
348 |
349 | Tailwind POS 350 |

TAILWIND POS

351 |

CABANG KONOHA SELATAN

352 |
353 |
354 |
No:
355 |
356 |
357 |
358 |
359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 381 | 382 |
#ItemQtySubtotal
383 |
384 |
385 |
386 |
387 |
TOTAL
388 |
389 |
390 |
391 |
PAY AMOUNT
392 |
393 |
394 |
395 |
396 |
CHANGE
397 |
398 |
399 |
400 |
401 |
402 | 403 |
404 |
405 |
406 |
407 | 408 | 409 | 410 | 411 | 412 | -------------------------------------------------------------------------------- /js/script.js: -------------------------------------------------------------------------------- 1 | async function loadDatabase() { 2 | const db = await idb.openDB("tailwind_store", 1, { 3 | upgrade(db, oldVersion, newVersion, transaction) { 4 | db.createObjectStore("products", { 5 | keyPath: "id", 6 | autoIncrement: true, 7 | }); 8 | db.createObjectStore("sales", { 9 | keyPath: "id", 10 | autoIncrement: true, 11 | }); 12 | }, 13 | }); 14 | 15 | return { 16 | db, 17 | getProducts: async () => await db.getAll("products"), 18 | addProduct: async (product) => await db.add("products", product), 19 | editProduct: async (product) => 20 | await db.put("products", product.id, product), 21 | deleteProduct: async (product) => await db.delete("products", product.id), 22 | }; 23 | } 24 | 25 | function initApp() { 26 | const app = { 27 | db: null, 28 | time: null, 29 | firstTime: localStorage.getItem("first_time") === null, 30 | activeMenu: 'pos', 31 | loadingSampleData: false, 32 | moneys: [2000, 5000, 10000, 20000, 50000, 100000], 33 | products: [], 34 | keyword: "", 35 | cart: [], 36 | cash: 0, 37 | change: 0, 38 | isShowModalReceipt: false, 39 | receiptNo: null, 40 | receiptDate: null, 41 | async initDatabase() { 42 | this.db = await loadDatabase(); 43 | this.loadProducts(); 44 | }, 45 | async loadProducts() { 46 | this.products = await this.db.getProducts(); 47 | console.log("products loaded", this.products); 48 | }, 49 | async startWithSampleData() { 50 | const response = await fetch("data/sample.json"); 51 | const data = await response.json(); 52 | this.products = data.products; 53 | for (let product of data.products) { 54 | await this.db.addProduct(product); 55 | } 56 | 57 | this.setFirstTime(false); 58 | }, 59 | startBlank() { 60 | this.setFirstTime(false); 61 | }, 62 | setFirstTime(firstTime) { 63 | this.firstTime = firstTime; 64 | if (firstTime) { 65 | localStorage.removeItem("first_time"); 66 | } else { 67 | localStorage.setItem("first_time", new Date().getTime()); 68 | } 69 | }, 70 | filteredProducts() { 71 | const rg = this.keyword ? new RegExp(this.keyword, "gi") : null; 72 | return this.products.filter((p) => !rg || p.name.match(rg)); 73 | }, 74 | addToCart(product) { 75 | const index = this.findCartIndex(product); 76 | if (index === -1) { 77 | this.cart.push({ 78 | productId: product.id, 79 | image: product.image, 80 | name: product.name, 81 | price: product.price, 82 | option: product.option, 83 | qty: 1, 84 | }); 85 | } else { 86 | this.cart[index].qty += 1; 87 | } 88 | this.beep(); 89 | this.updateChange(); 90 | }, 91 | findCartIndex(product) { 92 | return this.cart.findIndex((p) => p.productId === product.id); 93 | }, 94 | addQty(item, qty) { 95 | const index = this.cart.findIndex((i) => i.productId === item.productId); 96 | if (index === -1) { 97 | return; 98 | } 99 | const afterAdd = item.qty + qty; 100 | if (afterAdd === 0) { 101 | this.cart.splice(index, 1); 102 | this.clearSound(); 103 | } else { 104 | this.cart[index].qty = afterAdd; 105 | this.beep(); 106 | } 107 | this.updateChange(); 108 | }, 109 | addCash(amount) { 110 | this.cash = (this.cash || 0) + amount; 111 | this.updateChange(); 112 | this.beep(); 113 | }, 114 | getItemsCount() { 115 | return this.cart.reduce((count, item) => count + item.qty, 0); 116 | }, 117 | updateChange() { 118 | this.change = this.cash - this.getTotalPrice(); 119 | }, 120 | updateCash(value) { 121 | this.cash = parseFloat(value.replace(/[^0-9]+/g, "")); 122 | this.updateChange(); 123 | }, 124 | getTotalPrice() { 125 | return this.cart.reduce( 126 | (total, item) => total + item.qty * item.price, 127 | 0 128 | ); 129 | }, 130 | submitable() { 131 | return this.change >= 0 && this.cart.length > 0; 132 | }, 133 | submit() { 134 | const time = new Date(); 135 | this.isShowModalReceipt = true; 136 | this.receiptNo = `TWPOS-KS-${Math.round(time.getTime() / 1000)}`; 137 | this.receiptDate = this.dateFormat(time); 138 | }, 139 | closeModalReceipt() { 140 | this.isShowModalReceipt = false; 141 | }, 142 | dateFormat(date) { 143 | const formatter = new Intl.DateTimeFormat('id', { dateStyle: 'short', timeStyle: 'short'}); 144 | return formatter.format(date); 145 | }, 146 | numberFormat(number) { 147 | return (number || "") 148 | .toString() 149 | .replace(/^0|\./g, "") 150 | .replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1."); 151 | }, 152 | priceFormat(number) { 153 | return number ? `Rp. ${this.numberFormat(number)}` : `Rp. 0`; 154 | }, 155 | clear() { 156 | this.cash = 0; 157 | this.cart = []; 158 | this.receiptNo = null; 159 | this.receiptDate = null; 160 | this.updateChange(); 161 | this.clearSound(); 162 | }, 163 | beep() { 164 | this.playSound("sound/beep-29.mp3"); 165 | }, 166 | clearSound() { 167 | this.playSound("sound/button-21.mp3"); 168 | }, 169 | playSound(src) { 170 | const sound = new Audio(); 171 | sound.src = src; 172 | sound.play(); 173 | sound.onended = () => delete(sound); 174 | }, 175 | printAndProceed() { 176 | const receiptContent = document.getElementById('receipt-content'); 177 | const titleBefore = document.title; 178 | const printArea = document.getElementById('print-area'); 179 | 180 | printArea.innerHTML = receiptContent.innerHTML; 181 | document.title = this.receiptNo; 182 | 183 | window.print(); 184 | this.isShowModalReceipt = false; 185 | 186 | printArea.innerHTML = ''; 187 | document.title = titleBefore; 188 | 189 | // TODO save sale data to database 190 | 191 | this.clear(); 192 | } 193 | }; 194 | 195 | return app; 196 | } 197 | -------------------------------------------------------------------------------- /sound/beep-29.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/sound/beep-29.mp3 -------------------------------------------------------------------------------- /sound/button-21.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emsifa/tailwind-pos/fda7c6d89404a16395dd4441d1a8791a53980271/sound/button-21.mp3 --------------------------------------------------------------------------------