· 6 years ago · Apr 12, 2019, 08:28 AM
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <link rel="stylesheet" type="text/css" href="styles.css">
9 <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900" rel="stylesheet">
10 <title>Shopping Cards</title>
11 <link rel="shortcut icon" href="favicon.png">
12</head>
13
14<body>
15
16 <section><!-- #region [rgba(0, 150, 136, 0.1)] -->
17 <header>
18 <div class="container-header">
19 <div class="brand" onclick="resetData()">
20 <svg class="brand-img" version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg"
21 xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100"
22 xml:space="preserve">
23 <g>
24 <path class="c1" d="M95.7,17l-32-7.9c-3-0.7-6.1,1.1-6.9,4.1L41.2,76.1c-0.7,3,1.1,6.1,4.1,6.9l32,7.9c3,0.7,6.1-1.1,6.9-4.1
25 l15.6-62.9C100.6,20.8,98.7,17.7,95.7,17z" />
26 <path class="c2" d="M35.5,74.5l15.6-62.9c0-0.2,0.1-0.4,0.1-0.5l-8-2c-3-0.7-6.1,1.1-6.9,4.1L20.7,76.1c-0.7,3,1.1,6.1,4.1,6.9
27 l14.3,3.6C35.9,83.6,34.4,79.1,35.5,74.5z" />
28 <path class="c3" d="M14.9,74.5l15.6-62.9c0-0.2,0.1-0.4,0.1-0.5l-8-2c-3-0.7-6.1,1.1-6.9,4.1L0.2,76.1c-0.7,3,1.1,6.1,4.1,6.9
29 l14.3,3.6C15.4,83.6,13.8,79.1,14.9,74.5z" />
30 </g>
31 </svg>
32 <span class="brand-text">Shopping Cards</span>
33 </div>
34 <div class="user-msg">
35 <span class="user-msg-text">Welcome Back!</span>
36 </div>
37 <div class="profile-info">
38 <div class="avatar-container"></div>
39 <div class="usr-info">
40 <span class="profile-name">Ivo Mladenov</span>
41 <span class="profile-email">ivo@mycompany.com</span>
42 </div>
43 </div>
44 </div>
45 </header>
46 </section><!-- #endregion -->
47
48 <section id="section-filter"><!-- #region [rgba(255, 255, 255, 0.04)] -->
49 <div class="container-filter">
50 <div class="filter-block">
51 <div class="filter-caption">
52 <form autocomplete="off" onsubmit="preventDefault()">
53 <span>Percentage</span>
54 </div>
55 <ul class="filter-cbox-container">
56 <li class="filter-cbox">
57 <label class="cbox-label" for="percent-5">
58 <input type="checkbox" id="percent-5" name="percentage" onclick="loadPercent(); applyFilter();"></input>
59 <div class="checkmark"></div>
60 5
61 </label>
62 </li>
63 <li class="filter-cbox">
64 <label class="cbox-label" for="percent-10">
65 <input type="checkbox" id="percent-10" name="percentage" onclick="loadPercent(); applyFilter();"></input>
66 <div class="checkmark"></div>
67 10
68 </label>
69 </li>
70 <li class="filter-cbox">
71 <label class="cbox-label" for="percent-20">
72 <input type="checkbox" id="percent-20" name="percentage" onclick="loadPercent(); applyFilter();"></input>
73 <div class="checkmark"></div>
74 20
75 </label>
76 </li>
77 <li class="filter-cbox">
78 <label class="cbox-label" for="percent-30">
79 <input type="checkbox" id="percent-30" name="percentage" onclick="loadPercent(); applyFilter();"></input>
80 <div class="checkmark"></div>
81 30
82 </label>
83 </li>
84 </ul>
85 </div>
86 <div class="filter-block">
87 <div class="filter-caption">
88 <span>Category</span>
89 </div>
90 <ul class="filter-cbox-container">
91 <li class="filter-cbox">
92 <label class="cbox-label" for="cosmetics">
93 <input type="checkbox" id="cosmetics" name="category" onclick="loadCategory(); applyFilter();"></input>
94 <div class="checkmark"></div>
95 Cosmetics
96 </label>
97 </li>
98 <li class="filter-cbox">
99 <label class="cbox-label" for="books">
100 <input type="checkbox" id="books" name="category" onclick="loadCategory(); applyFilter();"></input>
101 <div class="checkmark"></div>
102 Books
103 </label>
104 </li>
105 <li class="filter-cbox">
106 <label class="cbox-label" for="accessories">
107 <input type="checkbox" id="accessories" name="category" onclick="loadCategory(); applyFilter();"></input>
108 <div class="checkmark"></div>
109 Accessories
110 </label>
111 </li>
112 <li class="filter-cbox">
113 <label class="cbox-label" for="services">
114 <input type="checkbox" id="services" name="category" onclick="loadCategory(); applyFilter();"></input>
115 <div class="checkmark"></div>
116 Services
117 </label>
118 </li>
119 </ul>
120 </div>
121 <div class="filter-block">
122 <div class="filter-caption">
123 <span>Expiration Date</span>
124 </div>
125 <div class="filter-date-container">
126 <div class="filter-date">
127 <label for="from">from:</label>
128 <input type="date" id="from" name="date" oninput="loadDates(); applyFilter();">
129 </div>
130 <div class="filter-date">
131 <label for="from">to:</label>
132 <input type="date" id="to" name="date" oninput="loadDates(); applyFilter();">
133 </div>
134 </div>
135 </div>
136 <div class="filter-block">
137 <div class="filter-caption">
138 <span>Search</span>
139 </div>
140 <div class="filter-search-container">
141 <input id="search-box" type="text" placeholder="Name, City, or #" name="search" onkeyup="loadTerm(); applyFilter();">
142 </form>
143 </div>
144 </div>
145 </div>
146 </section><!-- #endregion -->
147
148 <section>
149 <div class="new-record" onclick="insertRow();"><span>+</span></div>
150 <div class="container-body">
151 <div class="container-sort"><!-- #region [rgba(43, 15, 15, 1)] -->
152 <div class="sort-word" onclick="sortData(this.firstChild, 'name')"><span>Name</span></div>
153 <div class="sort-word" onclick="sortData(this.firstChild, 'city')"><span>City</span></div>
154 <div class="sort-word" onclick="sortData(this.firstChild, 'category')"><span>Category</span></div>
155 <div class="sort-word" onclick="sortData(this.firstChild, 'accu')"><span>Acc.</span></div>
156 <div class="sort-word" onclick="sortData(this.firstChild, 'discount')"><span>Discount</span></div>
157 <div class="sort-word" onclick="sortData(this.firstChild, 'expiry')"><span>Expiry Date</span></div>
158 <div class="sort-word" onclick="sortData(this.firstChild, 'num')"><span>Card Number #</span></div>
159 <div>Modify</div>
160 </div><!-- #endregion -->
161
162 <div class="container-records"><!-- #region [rgba(0, 0, 0, 0.6)] -->
163
164 </div><!-- #endregion -->
165 </div>
166 </section>
167
168 <script src="script.js"></script>
169</body>
170
171</html>
172
173/* @import url('https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900'); */
174
175body {
176 font-family: 'Roboto', sans-serif;
177 margin: 0;
178 padding: 0;
179 background-color: #f1f1f1;
180}
181
182/* HEADER SECTION --------------------- */
183
184header {
185 background-color: #009688;
186 height: 100px;
187 color: white;
188 display: flex;
189 font-weight: 100;
190 min-width: 1240px;
191}
192
193.container-header {
194 width: 100%;
195 padding: 0 60px 0 60px;
196 display: flex;
197 justify-content: space-between;
198 margin: auto;
199}
200
201.brand {
202 display: flex;
203 align-items: center;
204 opacity: 0.8;
205 background-color: #00000063;
206 border-radius: 5px;
207 padding: 0 18px 0 18px;
208 text-transform: uppercase;
209 font-weight: 600;
210 font-size: 0.8rem;
211 cursor: pointer;
212}
213
214.brand-img {
215 height: 30px;
216 margin-right: 10px;
217}
218
219.brand-img .c1 {
220 fill: #46ec91;
221}
222
223.brand-img .c2 {
224 fill: #0dafa7;
225}
226
227.brand-img .c3 {
228 fill: #0081a1;
229}
230
231.brand-text {
232 opacity: 0.8;
233}
234
235@keyframes fade-in-out {
236 0% {
237 transform: scale(0.85);
238 opacity: 0;
239 }
240 10% {
241 transform: scale(0.92);
242 opacity: 1;
243 }
244 90% {
245 transform: scale(1);
246 opacity: 1;
247 }
248 100% {
249 transform: scale(1.1);
250 opacity: 0;
251 }
252}
253
254.user-msg {
255 transform: scale(0.7);
256 opacity: 0;
257 display: flex;
258 align-items: center;
259 animation: 3s ease-out 1s 1 fade-in-out;
260 animation-fill-mode: forwards;
261}
262
263.user-msg-text {
264 font-size: 1.3rem;
265 color: #eeeeee;
266}
267
268.profile-info {
269 display: flex;
270 align-items: center;
271}
272
273.profile-name {
274 font-size: 0.9rem;
275 font-weight: 500;
276}
277
278.profile-email {
279 font-size: 0.8rem;
280 opacity: 0.7;
281}
282
283.avatar-container {
284 width: 40px;
285 height: 40px;
286 border-radius: 50%;
287 background-color: #000000;
288 opacity: 0.26;
289 margin-right: 20px;
290}
291
292.usr-info {
293 display: flex;
294 flex-direction: column;
295}
296
297/* FILTER SECTION --------------------- */
298
299#section-filter {
300 background-color: #e3e3e3;
301 width: 100%;
302 min-width: 1240px;
303 position: relative;
304}
305
306#section-filter:after {
307 content: "";
308 position: absolute;
309 width: 100%;
310 bottom: 5px;
311 z-index: -1;
312 box-shadow: 0px 0px 10px 2px #555555;
313}
314
315.container-filter {
316 margin: auto;
317 display: flex;
318 justify-content: center;
319 align-content: center;
320 width: 80%;
321 min-width: 1240px;
322 height: 100px;
323 padding: 10px 0 10px 0;
324}
325
326.filter-block {
327 height: 100%;
328 position: relative;
329 display: flex;
330 flex-direction: column;
331 font-size: 0.9rem;
332 font-weight: 500;
333 padding: 0 20px 0 20px;
334}
335
336.filter-block:nth-child(1)::before, .filter-block::after {
337 content: '';
338 background-color: #000000;
339 opacity: 0.12;
340 height: 70%;
341 width: 1px;
342 border-radius: 0.5px;
343 display: flex;
344 flex-direction: column;
345 position: absolute;
346 top: 50%;
347 right: 0;
348 transform: translateY(-50%);
349}
350
351.filter-block:nth-child(1)::before {
352 right: initial;
353 left: 0;
354}
355
356.filter-caption {
357 color: #009688;
358 box-sizing: border-box;
359 display: flex;
360 align-items: center;
361 justify-content: center;
362 /* border: 1px dashed rgb(234, 0, 255); */
363 height: 40px;
364}
365
366.filter-cbox-container {
367 list-style-type: none;
368 margin-block-start: 0;
369 margin-block-end: 0;
370 padding-inline-start: 0;
371 display: flex;
372 justify-content: space-between;
373 align-items: center;
374 /* box-sizing: border-box;
375 border: 1px dashed rgb(0, 185, 15); */
376 flex-grow: 1;
377 color: #909090;
378 font-weight: 200;
379}
380
381/* .filter-cbox-container input[type=checkbox]:checked + label{
382 color: #009688;
383} */
384
385.filter-cbox {
386 /* box-sizing: border-box;
387 border: 1px dashed rgba(0, 0, 0, 0.281); */
388 padding: 0 7px 0 7px;
389 display: flex;
390}
391
392/* CUSTOM CHECKBOX INSANITY #region [rgba(0, 150, 136, 0.1)]------------ */
393
394/* The label container */
395
396.cbox-label {
397 display: flex;
398 align-items: center;
399 position: relative;
400 cursor: pointer;
401 -webkit-user-select: none;
402 -moz-user-select: none;
403 -ms-user-select: none;
404 user-select: none;
405}
406
407/* Hide the browser's default checkbox */
408
409.cbox-label input {
410 position: absolute;
411 opacity: 0;
412 cursor: pointer;
413 height: 0;
414 width: 0;
415}
416
417/* Create a custom checkbox */
418
419.checkmark {
420 height: 18px;
421 width: 18px;
422 margin-right: 6px;
423 border-radius: 3px;
424 border: 2px solid #808080;
425 box-sizing: border-box;
426 transition: all 0.2s;
427}
428
429/* On mouse-over, add a grey background color */
430
431.cbox-label:hover input~.checkmark {
432 background-color: #ccc;
433}
434
435/* When the checkbox is checked, add a blue background */
436
437.cbox-label input:checked~.checkmark {
438 background-color: #009688;
439 border: none;
440}
441
442/* Create the checkmark/indicator (hidden when not checked) */
443
444.checkmark:after {
445 content: "";
446 position: absolute;
447 display: none;
448}
449
450/* Show the checkmark when checked */
451
452.cbox-label input:checked~.checkmark:after {
453 display: block;
454}
455
456/* Style the checkmark/indicator */
457
458.cbox-label .checkmark:after {
459 left: 6px;
460 top: 2px;
461 width: 5px;
462 height: 9px;
463 border: solid white;
464 border-width: 0 2px 2px 0;
465 -webkit-transform: rotate(45deg);
466 -ms-transform: rotate(45deg);
467 transform: rotate(45deg);
468}
469
470/* END OF CUSTOM CHECKBOX INSANITY #endregion ----*/
471
472.filter-date-container {
473 display: flex;
474}
475
476.filter-date-container input[type="date"] {
477 background: #e3e3e300;
478 border: none;
479 font-family: sans-serif;
480 font-size: 0.9rem;
481 text-transform: uppercase;
482 color: #009688;
483}
484
485.filter-date label {
486 display: block;
487 color: #909090;
488 font-size: 0.8em;
489 font-weight: 500;
490}
491
492.filter-search-container input {
493 border: none;
494 background: #e3e3e300;
495 border-radius: 3px;
496 border-bottom: 1px solid #c7c7c7;
497 font-size: 1rem;
498 height: 35px;
499 transition: background 0.2s, border 0.2s;
500 padding-left: 10px;
501 font-weight: 100;
502}
503
504.filter-search-container input::placeholder {
505 color: #acacac;
506}
507
508.filter-search-container input:hover {
509 background: #d6d6d6;
510 border-bottom: 1px solid #7c7c7c;
511}
512
513.filter-search-container input:focus {
514 outline: none;
515 background: #f0f0f0;
516 border-bottom: 2px solid #009688;
517}
518
519.filter-search-container input:focus::placeholder {
520 color: #c2c2c2;
521}
522
523/* BODY CONTAINER -------------------- */
524
525.new-record {
526 position: fixed;
527 bottom: 3%;
528 right: 1%;
529 height: 50px;
530 width: 50px;
531 border-radius: 50%;
532 box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
533 display: flex;
534 justify-content: center;
535 align-items: center;
536 background-color: #009687;
537 transition: background-color 0.2s, box-shadow 0.6s;
538 cursor: pointer;
539}
540
541.new-record::before {
542 content: 'New Record';
543 position: absolute;
544 left: -100%;
545 font-size: 0.9rem;
546 font-weight: 500;
547 color: #009688;
548 opacity: 0;
549 transition: all 0.3s;
550}
551
552.new-record.new-record span {
553 font-size: 1.1rem;
554 font-weight: 500;
555 color: white;
556 transition: all 0.5s;
557 display: inline-block;
558}
559
560.new-record:hover {
561 box-shadow: 0px 8px 8px rgba(0, 0, 0, 0.24), 0px 0px 8px rgba(0, 0, 0, 0.12);
562}
563
564.new-record:hover span {
565 transform: rotate(90deg);
566}
567
568.new-record:hover::before {
569 opacity: 1;
570 transform: translateX(-50%);
571}
572
573.container-body {
574 box-sizing: border-box;
575 width: 90%;
576 height: 800px;
577 min-width: 1240px;
578 margin: 0 auto 0 auto;
579 /* border: 1px dashed #000; */
580}
581
582.container-sort {
583 margin-top: 65px;
584 margin-bottom: 18px;
585 display: grid;
586 grid-template-columns: 4fr 2fr 2fr 2fr 2fr 3fr 3fr 2fr;
587 padding: 0 25px 0 25px;
588 /* padding sides have to match .record-row - line 436 */
589 font-weight: 500;
590 font-size: 0.9em;
591 color: #a3e4dd;
592 background-color: #009688;
593 border-radius: 4px;
594}
595
596.container-sort > div {
597 padding: 10px 0 10px 10px;
598 cursor: pointer;
599 margin-left: -10px;
600 margin-right: 10px;
601 transition: background-color 0.2s;
602}
603
604.container-sort > div:hover {
605 background-color: #04aa99;
606 color: white;
607}
608
609.container-sort > div:last-child:hover {
610 background-color: #009688;
611 cursor: initial;
612 color: inherit;
613}
614
615.sort-word-active {
616 color: white;
617 position: relative;
618}
619
620.sort-word-active::after {
621 content: '';
622 position: absolute;
623 top: 43%;
624 transform: translateY(-50%) rotate(45deg);
625 right: -15px;
626 width: 0;
627 height: 0;
628 border-bottom: 7px solid white;
629 border-left: 7px solid transparent;
630}
631
632.sort-word-inverted::after {
633 transform: translateY(0) rotate(225deg);
634}
635
636.record-row {
637 display: grid;
638 grid-template-columns: 4fr 2fr 2fr 2fr 2fr 3fr 3fr 2fr;
639 padding: 0 25px 0 25px;
640 /* padding sides have to match .container-sort - line 400 */
641 background: #ffffff;
642 border-radius: 3px;
643 border: 1px solid rgba(0, 0, 0, 0.068);
644 box-sizing: border-box;
645 font-size: 0.9rem;
646 color: #4e4e4e;
647 cursor: pointer;
648 transition: all 0.1s;
649}
650
651/* .modify {
652 justify-content: center;
653} */
654
655.record-row-active {
656 border: none;
657 padding: 12px 25px 12px 25px;
658 margin: 20px 0 20px 0;
659 box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.24), 0px 0px 2px rgba(0, 0, 0, 0.12);
660 font-size: 1.1rem;
661 font-weight: 300;
662 color: black;
663}
664
665.record-row-active .modify {
666 justify-content: flex-start;
667}
668
669.record-field {
670 display: flex;
671 height: 35px;
672 align-items: center;
673}
674
675.button {
676 color: #009688;
677 border-radius: 3px;
678 font-weight: 500;
679 font-size: 0.9rem;
680 height: 70%;
681 display: flex;
682 opacity: 0;
683 align-items: center;
684 justify-content: center;
685 text-transform: uppercase;
686}
687
688.record-row:hover .button {
689 opacity: 1;
690 transition: opacity 0.3s;
691}
692
693.record-row-active .button {
694 transition-delay: 0.3s;
695 transition: opacity 0.3s;
696 opacity: 1;
697 height: 93%;
698 color: white;
699 box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.24), 0px 0px 2px rgba(0, 0, 0, 0.12);
700}
701
702.record-row-active .button-edit {
703 width: 65px;
704 background-color: #009688;
705 margin-right: 5px;
706 text-transform: capitalize;
707}
708
709.record-row-active .button-del {
710 width: 35px;
711 background-color: #BF736E;
712}
713
714.record-row:hover {
715 background-color: #f4f8ff;
716}
717
718.button-edit {
719 margin-right: 10px;
720 padding-top: 1px;
721}
722
723.button-del {
724 width: 30px;
725}
726
727.bin-icon {
728 width: 50%;
729}
730
731.bin-icon .st0 {
732 fill: #BF736E;
733}
734
735.record-row-active .st0 {
736 fill: #f5f5f5;
737}
738
739/* EDIT MODE --------------------------------- */
740.row-edit {
741 background-color: #97a5a4; /* has to match .row-edit:hover on line 583 */
742 display: flex !important;
743 transition: background-color 0.15s;
744}
745
746.row-edit form {
747 display: grid;
748 grid-template-columns: 4fr 2fr 2fr 2fr 2fr 3fr 3fr 2fr;
749 margin: 0 -10px 0 -10px;
750}
751.record-field input {
752 height: 100%;
753 border: none;
754 background-color: #ffffff;
755 border-radius: 3px;
756 font-size: 0.9rem;
757 padding: 10px;
758 box-sizing: border-box;
759 margin: 0;
760 width: 90%;
761 font-size: 1.1rem;
762 transition: background-color 0.15s;
763}
764
765.record-field input::placeholder {
766 color: #b8b8b8;
767}
768
769.record-field input:focus {
770 background-color: #faefdd;
771 outline: none;
772}
773
774.invalid {
775 background-color: #facec3 !important;
776}
777
778.row-edit:hover {
779 background-color: #97a5a4; /* has to match .row-edit on line 553 */
780}
781
782.row-edit .button-edit {
783 background-color: #c7eed9;
784 color: #3e4d4b;
785}
786
787.row-edit select {
788 height: 100%;
789 width: 90%;
790 font-size: 1em;
791 padding-left: 10px;
792 position: relative;
793 display: flex;
794 align-content: center;
795 /*roundness*/
796 border: 0;
797 -webkit-appearance: none;
798 border-radius: 3px;
799 -webkit-border-radius: 3px;
800 /*background*/
801 background-color: white;
802 background-image: url('../img/select-arrows.svg');
803 background-repeat: no-repeat;
804 background-position: 98% 50%;
805 background-size: 14px;
806}
807
808.row-edit select:focus {
809 background-color: #faefdd;
810}
811
812#edit-accu {
813 background-color: white;
814 border-radius: 3px;
815 padding-left: 10px;
816 margin-right: 10px;
817}
818
819.row-edit input[type='date'] {
820 font-size: 1em;
821 font-family: 'Roboto', sans-serif;
822 font-weight: inherit;
823}
824
825.edit-number {
826 display: block;
827}
828
829.row-edit .record-field.card-num {
830 border-radius: 3px;
831 padding-left: 10px;
832 width: 80%;
833 cursor: initial;
834 color: #ffffff;
835 background-color: hsl(176, 7%, 55%);
836 box-sizing: border-box;
837 border: 1px solid #afb4b4;
838}
839
840
841// GLOBAL VARIABLES ************************************************************
842// 0. RECORD DATA
843var recordData;
844// 1. FILTERS
845// 1.1 Filters - Percent Checkboxes
846var checkBox_5 = document.getElementById('percent-5');
847var checkBox_10 = document.getElementById('percent-10');
848var checkBox_20 = document.getElementById('percent-20');
849var checkBox_30 = document.getElementById('percent-30');
850
851// 1.2 Filters - Category Checkboxes
852var cosmetics = document.getElementById('cosmetics')
853var books = document.getElementById('books')
854var accessories = document.getElementById('accessories')
855var services = document.getElementById('services')
856
857// 1.3 Filters - Date Inputs
858var date_from = document.getElementById('from');
859var date_to = document.getElementById('to');
860
861// 1.4 Filters - Search Box
862var search_box = document.getElementById('search-box');
863
864// 2. RECORDS
865var record_cont = document.querySelector('.container-records');
866var row_list = document.getElementsByClassName('record-row');
867
868// RECORD OBJECT CONSTRUCTOR ***************************************************
869var myDateFormat = {
870 day: '2-digit',
871 month: 'long',
872 year: 'numeric'
873}
874
875var Record = function (name, city, category, accu, discount, expiry, num) {
876 this.name = name;
877 this.city = city;
878 this.category = category;
879 this.accu = accu;
880 this.discount = discount;
881 this.expiry = expiry.toLocaleDateString('en-GB', myDateFormat);
882 this.num = num;
883}
884
885// INITIALIZE DATABASE *********************************************************
886// 0. Enable creation of localStorage a entry
887var createDB = function() {
888 var georgi = new Record('Georgi Karapeev', 'Sofia', 'Books', 'No', 5, new Date(2019, 3, 5), 2005050419);
889 var georgi_2 = new Record('Georgi Karapeev', 'Sofia', 'Cosmetics', 'Yes', 20, new Date(2019, 11, 7), 2120071220);
890 var marko = new Record('Marko Popovic', 'Niš', 'Cosmetics', 'Yes', 30, new Date(2019, 4, 5), 1130050519);
891 var marko_2 = new Record('Marko Popovic', 'Niš', 'Books', 'Yes', 5, new Date(2022, 8, 17), 2105170922);
892 var vlada = new Record('Vladan Petrovic', 'Niš', 'Services', 'No', 10, new Date(2019, 5, 5), 4010050619);
893 var vlada_2 = new Record('Vladan Petrovic', 'Niš', 'Services', 'Yes', 20, new Date(2020, 0, 1), 3120010120);
894 var tsvyatko = new Record('Tsvyatko Ivanov', 'Plovdiv', 'Accessories', 'Yes', 20, new Date(2019, 6, 5), 3120050719);
895 var tsvyatko_2 = new Record('Tsvyatko Ivanov', 'Plovdiv', 'Books', 'No', 30, new Date(2018, 1, 26), 2030260218);
896 var ani = new Record('Anita Andreeva', 'Plovdiv', 'Books', 'No', 10, new Date(2018, 1, 10), 2010100218);
897 var ani_2 = new Record('Anita Andreeva', 'Plovdiv', 'Cosmetics', 'No', 30, new Date(2016, 1, 28), 1030280216);
898 var toni = new Record('Anton Cholakov', 'Plovdiv', 'Services', 'Yes', 10, new Date(2018, 3, 14), 4110140418);
899 var toni_2 = new Record('Anton Cholakov', 'Plovdiv', 'Accessories', 'Yes', 20, new Date(2007, 1, 07), 3120070207);
900 var peter = new Record('Peter Yochev', 'Plovdiv', 'Books', 'No', 30, new Date(2076, 6, 13), 2030130776);
901 var peter_2 = new Record('Peter Yochev', 'Plovdiv', 'Services', 'No', 5, new Date(2019, 8, 21), 4005210919);
902 recordData = [georgi, georgi_2, marko, marko_2, vlada, vlada_2, tsvyatko, tsvyatko_2, ani, ani_2, toni, toni_2, peter, peter_2];
903
904 localStorage.discountCards = JSON.stringify(recordData);
905}
906// 1. Check if 'discountCards' exists in localStorage
907if (localStorage.discountCards) {
908 recordData = JSON.parse(localStorage.discountCards);
909// 2. If not, generate it
910} else {
911 createDB();
912 showList(recordData);
913}
914// 3. Reset function
915var resetData = function() {
916 localStorage.removeItem('discountCards');
917 createDB();
918 showList(recordData);
919}
920
921// GENERATE HTML ELEMENTS FROM THE DATA ****************************************
922function showList(array) {
923
924 // Clear any content before populating with new
925 record_cont.innerHTML = '';
926
927 for (let i = array.length - 1; i >= 0; i--) {
928 let rowData = [];
929
930 for (field in array[i]) {
931 rowData.push(recordData[i][field]);
932 }
933
934 let html = `<div class="record-row" onclick="setActive(this);">
935 <div class="record-field name">${rowData[0]}</div>
936 <div class="record-field city">${rowData[1]}</div>
937 <div class="record-field category">${rowData[2]}</div>
938 <div class="record-field accumulation">${rowData[3]}</div>
939 <div class="record-field d-percent">${rowData[4]}%</div>
940 <div class="record-field exp-date">${rowData[5]}</div>
941 <div class="record-field card-num">${rowData[6]}</div>
942 <div class="record-field modify">
943 <div class="button button-edit" onclick="editRow(this);">Edit</div>
944 <div class="button button-del" onclick="deleteRow(this);">
945 <svg version="1.1" class="bin-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
946 viewBox="0 0 32 32" xml:space="preserve">
947 <style type="text/css">
948 .st0{fill:#F5F5F5;}
949 </style>
950 <g id="trash">
951 <path class="st0" d="M30,6.8C29.9,5.2,28.6,4,27,4h-3V3l0,0c0-1.7-1.3-3-3-3H11C9.3,0,8,1.3,8,3l0,0v1H5C3.4,4,2.1,5.2,2,6.8l0,0V8
952 v1c0,1.1,0.9,2,2,2l0,0v17c0,2.2,1.8,4,4,4h16c2.2,0,4-1.8,4-4V11l0,0c1.1,0,2-0.9,2-2V8V6.8L30,6.8z M10,3c0-0.6,0.4-1,1-1h10
953 c0.6,0,1,0.4,1,1v1H10V3z M26,28c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V11h20V28z M28,8v1H4V8V7c0-0.6,0.4-1,1-1h22c0.6,0,1,0.4,1,1
954 V8z"/>
955 </g>
956 </svg>
957 </div>
958 </div>
959 </div>`;
960
961 // Insert the HTML
962 record_cont.insertAdjacentHTML('afterbegin', html);
963
964 }
965}
966
967showList(recordData);
968
969// CREATE RECORD ***************************************************************
970function insertRow() {
971
972 // Remove the active state from any other active elements
973 removeActive();
974 // Define the HTML content of the new row
975 var newRow = `<div class="record-row row-edit record-row-active" onclick="setActive(this);">
976 <form name="edit-form" onsubmit="preventDefault();"><div class="record-field name"><input type="text" value="" name="name" onkeyup="validate_n(this)" placeholder="Name and surname"></div>
977 <div class="record-field city"><input type="text" name="city" value="" onkeyup="validate_n(this)" placeholder="City"></div>
978 <div class="record-field category">
979 <select onchange="refreshNum(this)">
980 <option value="Cosmetics">Cosmetics</option>
981 <option value="Books">Books</option>
982 <option value="Accessories">Accessories</option>
983 <option value="Services">Services</option>
984 </select>
985 </div>
986 <div id="edit-accu" class="record-field accumulation">
987 <label class="cbox-label" for="accumulation">
988 <input type="checkbox" id="accumulation" name="accumulation" checked onclick="refreshNum(this.parentNode)"></input>
989 <div class="checkmark"></div>
990 Accu.
991 </label>
992 </div>
993 <div class="record-field d-percent">
994 <select onchange="refreshNum(this)">
995 <option value="5">5%</option>
996 <option value="10">10%</option>
997 <option value="20">20%</option>
998 <option value="30">30%</option>
999 </select>
1000 </div>
1001 <div class="record-field exp-date">
1002 <input type="date" value="" onchange="refreshNum(this)">
1003 </div>
1004 <div class="record-field card-num"><span class="edit-number"></span></div>
1005 <div class="record-field modify">
1006 <div class="button button-edit button-save" onclick="saveRow(this, true);">OK</div>
1007 <div class="button button-del" onclick="deleteRow(this);">
1008 <svg version="1.1" class="bin-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
1009 viewBox="0 0 32 32" xml:space="preserve">
1010 <style type="text/css">
1011 .st0{fill:#F5F5F5;}
1012 </style>
1013 <g id="trash">
1014 <path class="st0" d="M30,6.8C29.9,5.2,28.6,4,27,4h-3V3l0,0c0-1.7-1.3-3-3-3H11C9.3,0,8,1.3,8,3l0,0v1H5C3.4,4,2.1,5.2,2,6.8l0,0V8
1015 v1c0,1.1,0.9,2,2,2l0,0v17c0,2.2,1.8,4,4,4h16c2.2,0,4-1.8,4-4V11l0,0c1.1,0,2-0.9,2-2V8V6.8L30,6.8z M10,3c0-0.6,0.4-1,1-1h10
1016 c0.6,0,1,0.4,1,1v1H10V3z M26,28c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V11h20V28z M28,8v1H4V8V7c0-0.6,0.4-1,1-1h22c0.6,0,1,0.4,1,1
1017 V8z"/>
1018 </g>
1019 </svg>
1020 </div>
1021 </div>
1022 </form>
1023 </div>`;
1024
1025 // Insert the HTML
1026 record_cont.insertAdjacentHTML('afterbegin', newRow);
1027
1028 // Place the cursor inside
1029 document.getElementsByName('name')[0].focus();
1030
1031 enableSaveOnEnter();
1032
1033}
1034
1035// DELETE RECORD ***************************************************************
1036function deleteRow(button) {
1037
1038 let confirmed = confirm('Confirm record deletion?');
1039 if (confirmed) {
1040 let row = button.parentNode.parentNode;
1041 // Because .children doesn't return an array as such. It returns a 'collection'.
1042 // slice() is then borrowed from the array prototype in order to cast
1043 // that collection into a regular array object.
1044 let rows = Array.prototype.slice.call(record_cont.children);
1045
1046 // Updating the database
1047 // First, update the recordData
1048 recordData.splice(rows.indexOf(row), 1);
1049 // Second, update the localStorage
1050 localStorage.discountCards = JSON.stringify(recordData);
1051
1052 //Finally, remove it from the HTML
1053 row.parentNode.removeChild(row);
1054 }
1055}
1056
1057// VALIDATE NAME AND CITY ******************************************************
1058let validate_n = function(field) {
1059 let pattern = /([\s]{2,}|[$&+,:;=?@#|'<>.^*()%!0-9\{\}\[\]\\\/\t]|^[\s*$]|^$|\s$)/;
1060
1061 if (field.value.match(pattern)) {
1062 field.classList.add('invalid');
1063 return false;
1064 } else {
1065 field.classList.remove('invalid');
1066 return true;
1067 }
1068}
1069
1070// EDIT RECORD *****************************************************************
1071function editRow(button) {
1072
1073 let row = button.parentNode.parentNode;
1074 let rows = Array.prototype.slice.call(record_cont.children);
1075 let thisRecord = recordData[rows.indexOf(row)]; // making an object which is identical to the database entry with the same index as the index of this row in the table. I'll need to actually generate unique record ID's if I don't do this, but I'm not ready to go there yet! :D
1076
1077 // A way to generate UTC dates
1078 function createDateAsUTC(date) {
1079 return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
1080 }
1081
1082 // Finding which values to select for the 'category' and 'percent' <select> elements
1083 // 1. Arrays to hold the 'selected' state of all 4 options for both elenents - initially all are blank
1084 let options_cat = ['', '', '', ''];
1085 let options_perc = ['', '', '', ''];
1086 // 2. Adding 'selected' state to the correct 'category' <select> element
1087 switch (thisRecord.category) {
1088 case 'Cosmetics':
1089 options_cat[0] = 'selected';
1090 break;
1091
1092 case 'Books':
1093 options_cat[1] = 'selected';
1094 break;
1095
1096 case 'Accessories':
1097 options_cat[2] = 'selected';
1098 break;
1099
1100 case 'Services':
1101 options_cat[3] = 'selected';
1102 break;
1103
1104 default:
1105 break;
1106 }
1107 // 3. Adding 'selected' state to the correct 'percent' <select> element
1108 switch (thisRecord.discount) {
1109 case 5:
1110 options_perc[0] = 'selected';
1111 break;
1112
1113 case 10:
1114 options_perc[1] = 'selected';
1115 break;
1116
1117 case 20:
1118 options_perc[2] = 'selected';
1119 break;
1120
1121 case 30:
1122 options_perc[3] = 'selected';
1123 break;
1124
1125 default:
1126 break;
1127 }
1128
1129 // Setting the correct 'accumulation' checkbox value
1130 let checkbox_state;
1131 thisRecord.accu === 'Yes' ? checkbox_state = 'checked' : checkbox_state = '';
1132
1133 // Generate the HTML
1134 let editForm = `<form name="edit-form" onsubmit="event.preventDefault();"><div class="record-field name"><input type="text" name="name" value="${thisRecord.name}" onkeyup="validate_n(this)" placeholder="Name and surname"></div>
1135 <div class="record-field city"><input type="text" name="city" value="${thisRecord.city}" onkeyup="validate_n(this)" placeholder="City"></div>
1136 <div class="record-field category">
1137 <select onchange="refreshNum(this)">
1138 <option value="Cosmetics" ${options_cat[0]}>Cosmetics</option>
1139 <option value="Books" ${options_cat[1]}>Books</option>
1140 <option value="Accessories" ${options_cat[2]}>Accessories</option>
1141 <option value="Services" ${options_cat[3]}>Services</option>
1142 </select>
1143 </div>
1144 <div id="edit-accu" class="record-field accumulation">
1145 <label class="cbox-label" for="accumulation">
1146 <input type="checkbox" id="accumulation" name="accumulation" ${checkbox_state} onclick="refreshNum(this.parentNode)"></input>
1147 <div class="checkmark"></div>
1148 Accu.
1149 </label>
1150 </div>
1151 <div class="record-field d-percent">
1152 <select onchange="refreshNum(this)">
1153 <option value="5" ${options_perc[0]}>5%</option>
1154 <option value="10" ${options_perc[1]}>10%</option>
1155 <option value="20" ${options_perc[2]}>20%</option>
1156 <option value="30" ${options_perc[3]}>30%</option>
1157 </select>
1158 </div>
1159 <div class="record-field exp-date">
1160 <input type="date" value="${createDateAsUTC(new Date(thisRecord.expiry)).toISOString().slice(0,10)}" onchange="refreshNum(this)">
1161 </div>
1162 <div class="record-field card-num"><span class="edit-number">${thisRecord.num}</span></div>
1163 <div class="record-field modify">
1164 <div class="button button-edit button-save" onclick="saveRow(this);">OK</div>
1165 <div class="button button-del" onclick="deleteRow(this);">
1166 <svg version="1.1" class="bin-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
1167 viewBox="0 0 32 32" xml:space="preserve">
1168 <style type="text/css">
1169 .st0{fill:#F5F5F5;}
1170 </style>
1171 <g id="trash">
1172 <path class="st0" d="M30,6.8C29.9,5.2,28.6,4,27,4h-3V3l0,0c0-1.7-1.3-3-3-3H11C9.3,0,8,1.3,8,3l0,0v1H5C3.4,4,2.1,5.2,2,6.8l0,0V8
1173 v1c0,1.1,0.9,2,2,2l0,0v17c0,2.2,1.8,4,4,4h16c2.2,0,4-1.8,4-4V11l0,0c1.1,0,2-0.9,2-2V8V6.8L30,6.8z M10,3c0-0.6,0.4-1,1-1h10
1174 c0.6,0,1,0.4,1,1v1H10V3z M26,28c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V11h20V28z M28,8v1H4V8V7c0-0.6,0.4-1,1-1h22c0.6,0,1,0.4,1,1
1175 V8z"/>
1176 </g>
1177 </svg>
1178 </div>
1179 </div>
1180 </form>`;
1181
1182 row.innerHTML = editForm;
1183
1184 row.classList.add("row-edit");
1185
1186 document.getElementsByName('name')[0].focus();
1187
1188 enableSaveOnEnter();
1189}
1190
1191// Enable saveRow on Enter
1192function enableSaveOnEnter() {
1193 let edit_fields = document.getElementsByName('edit-form')[0].getElementsByTagName('input');
1194
1195 for (let i = 0; i < edit_fields.length; i++) {
1196 edit_fields[i].addEventListener("keyup", function (event) {
1197 event.preventDefault();
1198 if (event.keyCode == 13) {
1199 document.getElementsByClassName("button-save")[0].click();
1200 }
1201 })
1202 }
1203}
1204
1205// GET ROW VALUES **************************************************************
1206function getRowValues(row) {
1207 let rowValues = [];
1208
1209 for (let i = 0; i < row.children[0].children.length - 1; i++) { // length minus 1, because the last one is the card number, which we will generate later
1210 rowValues.push(row.children[0].children[i].children[0].value);
1211
1212 // For the checkbox, gotta dig 1 element deeper and get the checkbox state
1213 if (i === 3) {
1214 let value = row.children[0].children[3].children[0].children[0].checked;
1215 // And overwrite the 'undefined' value which will have inevitably been written to newValues[3]
1216 rowValues[3] = value ? 'Yes' : 'No';
1217 }
1218
1219 // For the percent field, cast the string from the element value to a number
1220 if (i === 4 ) {
1221 rowValues[4] = parseInt(row.children[0].children[4].children[0].value);
1222 }
1223 }
1224
1225 return rowValues;
1226}
1227
1228// GENERATE CARD NUMBER ********************************************************
1229function generateCardNum(values) {
1230 let cardNum = '';
1231 // 1. Category
1232 switch (values[2]) {
1233 case 'Cosmetics':
1234 cardNum += 1;
1235 break;
1236 case 'Books':
1237 cardNum += 2;
1238 break;
1239 case 'Accessories':
1240 cardNum += 3;
1241 break;
1242 case 'Services':
1243 cardNum += 4;
1244 break;
1245 default:
1246 break;
1247 }
1248 // 2. Accumulation
1249 values[3] === 'Yes' ? cardNum += 1 : cardNum += 0;
1250 // 3. Percentage
1251 // 3.1 Padding
1252 if (values[4] < 10) {
1253 cardNum += 0;
1254 }
1255 // 3.2 The percent ammount
1256 cardNum += values[4];
1257 // 4. Expiry Date
1258 cardNum += values[5].slice(8, 10); // Add the day
1259 cardNum += values[5].slice(5, 7); // Add the month
1260 cardNum += values[5].slice(2, 4); // Add the year
1261 return cardNum;
1262}
1263
1264// REFRESH CARD NUMBER *********************************************************
1265function refreshNum(field) {
1266 let row = field.parentNode.parentNode.parentNode;
1267 let rowValues = getRowValues(row);
1268 let cardNumberField = row.children[0].children[6];
1269
1270 cardNumberField.children[0].innerHTML = generateCardNum(rowValues);
1271}
1272
1273// SAVE RECORD *****************************************************************
1274function saveRow(button, isNew) {
1275 let form = button.parentNode.parentNode;
1276 let rows = Array.prototype.slice.call(record_cont.children);
1277
1278 // Check if the data was valid
1279 let nameField = form.children[0].children[0];
1280 let cityField = form.children[1].children[0];
1281 let dateField = form.children[5].children[0];
1282
1283 if (!(validate_n(nameField) && validate_n(cityField) && (dateField.value !== ''))) {
1284 alert('Please fill all fields correctly before saving.');
1285 return;
1286 }
1287
1288 // Write the new values to an array
1289 let newValues = getRowValues(form.parentNode);
1290
1291 // Format the date properly
1292 let dateArr = newValues[5].split('-');
1293 dateArr[1] = parseInt(dateArr[1]) - 1;
1294 let tempDate = new Date(...dateArr);
1295 let theDate = tempDate.toLocaleDateString('en-GB', myDateFormat);
1296
1297 // Write the values of newValues to an object
1298 let newRecord = {
1299 'name': newValues[0],
1300 'city': newValues[1],
1301 'category': newValues[2],
1302 'accu': newValues[3],
1303 'discount': newValues[4],
1304 'expiry': theDate,
1305 'num': generateCardNum(newValues)
1306 }
1307
1308 // If it's a new record, insert it at the fron of recordData
1309 if (isNew) {
1310 recordData.unshift(newRecord);
1311 localStorage.discountCards = JSON.stringify(recordData);
1312 } else {
1313 // Else overwrite the corresponding index of recordData
1314 recordData[rows.indexOf(form.parentNode)] = newRecord;
1315 // And update localStorage to match
1316 localStorage.discountCards = JSON.stringify(recordData);
1317 }
1318
1319 form.parentNode.classList.remove("row-edit");
1320
1321 // Finally, update the HTML with the new data and revert to standard view
1322 form.parentNode.innerHTML = `<div class="record-field name">${newValues[0]}</div>
1323 <div class="record-field city">${newValues[1]}</div>
1324 <div class="record-field category">${newValues[2]}</div>
1325 <div class="record-field accumulation">${newValues[3]}</div>
1326 <div class="record-field d-percent">${newValues[4]}%</div>
1327 <div class="record-field exp-date">${theDate}</div>
1328 <div class="record-field card-num">${generateCardNum(newValues)}</div>
1329 <div class="record-field modify">
1330 <div class="button button-edit" onclick="editRow(this);">Edit</div>
1331 <div class="button button-del" onclick="deleteRow(this);">
1332 <svg version="1.1" class="bin-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
1333 viewBox="0 0 32 32" xml:space="preserve">
1334 <style type="text/css">
1335 .st0{fill:#F5F5F5;}
1336 </style>
1337 <g id="trash">
1338 <path class="st0" d="M30,6.8C29.9,5.2,28.6,4,27,4h-3V3l0,0c0-1.7-1.3-3-3-3H11C9.3,0,8,1.3,8,3l0,0v1H5C3.4,4,2.1,5.2,2,6.8l0,0V8
1339 v1c0,1.1,0.9,2,2,2l0,0v17c0,2.2,1.8,4,4,4h16c2.2,0,4-1.8,4-4V11l0,0c1.1,0,2-0.9,2-2V8V6.8L30,6.8z M10,3c0-0.6,0.4-1,1-1h10
1340 c0.6,0,1,0.4,1,1v1H10V3z M26,28c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V11h20V28z M28,8v1H4V8V7c0-0.6,0.4-1,1-1h22c0.6,0,1,0.4,1,1
1341 V8z"/>
1342 </g>
1343 </svg>
1344 </div>
1345 </div>`;
1346}
1347
1348// ROW ACTIVE STATE ************************************************************
1349function removeActive() {
1350 let active_element = document.querySelector('.record-row-active');
1351
1352 let i = 0;
1353
1354 while (active_element && i < row_list.length) {
1355 if (!(row_list[i].classList.contains('row-edit'))) {
1356 row_list[i].classList.remove('record-row-active');
1357 }
1358
1359 active_element = document.querySelector('.record-row-active');
1360
1361 i++;
1362 }
1363}
1364
1365function setActive(row) {
1366 // Remove the 'active' state from any row that may currently have it
1367 removeActive();
1368 // Add the active state to this row
1369 row.classList.add('record-row-active');
1370}
1371
1372// Remove active state by clicking anywhere other than .record-row or .new-record
1373document.addEventListener('click', function () {
1374 if ((event.target.closest('.record-row')) || (event.target.closest('.new-record')) || (event.target.closest('.button-edit'))) {
1375 return;
1376 } else {
1377 removeActive();
1378 }
1379});
1380
1381// FILTER **********************************************************************
1382// An object that holds the current state of ALL filters
1383var filterCriteria = {
1384 'percentage': [false, false, false, false],
1385 'category': [false, false, false, false],
1386 'expiry': [],
1387 'term': ''
1388}
1389
1390// Triggered whenever a percent checkbox is changed
1391function loadPercent() {
1392
1393 let show_5 = checkBox_5.checked;
1394 let show_10 = checkBox_10.checked;
1395 let show_20 = checkBox_20.checked;
1396 let show_30 = checkBox_30.checked;
1397
1398 filterCriteria.percentage[0] = show_5;
1399 filterCriteria.percentage[1] = show_10;
1400 filterCriteria.percentage[2] = show_20;
1401 filterCriteria.percentage[3] = show_30;
1402}
1403
1404// Triggered whenever a percent checkbox is changed
1405function loadCategory() {
1406
1407 let showCosmetics = cosmetics.checked;
1408 let showBooks = books.checked;
1409 let showAccessories = accessories.checked;
1410 let showServices = services.checked;
1411
1412 filterCriteria.category[0] = showCosmetics;
1413 filterCriteria.category[1] = showBooks;
1414 filterCriteria.category[2] = showAccessories;
1415 filterCriteria.category[3] = showServices;
1416}
1417
1418// Triggered whenever a date input is changed
1419function loadDates() {
1420 let from_raw = new Date(date_from.value);
1421 let to_raw = new Date(date_to.value);
1422
1423 // let from = from_raw.toLocaleDateString('en-GB', myDateFormat);
1424 // let to = to_raw.toLocaleDateString('en-GB', myDateFormat);
1425
1426 filterCriteria.expiry[0] = from_raw;
1427 filterCriteria.expiry[1] = to_raw;
1428}
1429
1430// Triggered whenever a letter is typed in/ deleted from the search box
1431function loadTerm() {
1432 let searchTerm = search_box.value.toLowerCase();
1433 filterCriteria.term = searchTerm;
1434}
1435
1436// Triggered on EVERY change of state of EVERY filter option
1437function applyFilter() {
1438
1439 for (let i = 0; i < recordData.length; i++) {
1440 let row = recordData[i];
1441 let show = true;
1442
1443 // DISCOUNT PERCENTAGE
1444 // 0. If there is a checked box, perform the check, otherwise ignore it
1445 if (filterCriteria.percentage.some(state => state === true)) {
1446
1447 // 1. Determine how many percent this entry has
1448 // 2. For each case, see if this percentage should be visible and if not - set "show" to false
1449 switch (row.discount) {
1450 case 5:
1451 show = filterCriteria.percentage[0];
1452 break;
1453
1454 case 10:
1455 show = filterCriteria.percentage[1];
1456 break;
1457
1458 case 20:
1459 show = filterCriteria.percentage[2];
1460 break;
1461
1462 case 30:
1463 show = filterCriteria.percentage[3];
1464 break;
1465
1466 default:
1467 break;
1468 }
1469 }
1470
1471 // CATEGORY
1472 // 0. If there is a checked box, perform the check, otherwise ignore it
1473 if (filterCriteria.category.some(state => state === true)) {
1474
1475 // 1. Determine what category this row has
1476 // 2. For each case, see if this category should be visible and if not - set "show" to false
1477 switch (row.category.toLowerCase()) {
1478 case 'cosmetics':
1479 if (filterCriteria.category[0] === false) {
1480 show = false;
1481 }
1482 break;
1483
1484 case 'books':
1485 if (filterCriteria.category[1] === false) {
1486 show = false;
1487 }
1488 break;
1489
1490 case 'accessories':
1491 if (filterCriteria.category[2] === false) {
1492 show = false;
1493 }
1494 break;
1495
1496 case 'services':
1497 if (filterCriteria.category[3] === false) {
1498 show = false;
1499 }
1500 break;
1501
1502 default:
1503 break;
1504 }
1505 }
1506
1507 // DATE
1508 // 1. Obtain a date object with the date of this row
1509 let date = new Date(row.expiry);
1510
1511 // 2. Check if the expiry date in the filterCriteria object is valid
1512 if (!isNaN(filterCriteria.expiry[0])) {
1513 // 3. Check if the date on this row satisfies the criteria
1514 if (date <= filterCriteria.expiry[0]) {
1515 show = false;
1516 }
1517 }
1518 // Repeat the same steps 2. and 3. for the second date
1519 if (!isNaN(filterCriteria.expiry[1])) {
1520 if (date >= filterCriteria.expiry[1]) {
1521 show = false;
1522 }
1523 }
1524
1525 // SEARCH TERM
1526
1527 let name = row.name.toLowerCase();
1528 let city = row.city.toLowerCase();
1529 let number = row.num.toString();
1530
1531 let matchName = name.indexOf(filterCriteria.term) > -1;
1532 let matchCity = city.indexOf(filterCriteria.term) > -1;
1533 let matchNumber = number.indexOf(filterCriteria.term) > -1;
1534
1535 if (!(matchName || matchCity || matchNumber)) {
1536 show = false;
1537 }
1538
1539 // FINALLY, if ANY of the filters excluded the row, set its "display" to "none", otherwise set it to "grid"
1540 if (show) {
1541 row_list[i].style.display = 'grid';
1542 } else {
1543 row_list[i].style.display = 'none';
1544 }
1545 }
1546}
1547
1548// SORT ************************************************************************
1549
1550// Restoring the original order of the data. Called after each sort.
1551function restoreOrder() {
1552 recordData = JSON.parse(localStorage.discountCards);
1553}
1554
1555// Initialize sort direction counters
1556let sortCounters = {
1557 name: 0,
1558 city: 0,
1559 category: 0,
1560 accu: 0,
1561 discount: 0,
1562 expiry: 0,
1563 num: 0
1564}
1565
1566// The sort function
1567let sortData = function(colTitle, criteria) {
1568
1569 // 1. Reset other sort-direction counters to zero
1570
1571 // 1.1 List all the counters
1572 let allCounters = ['name', 'city', 'category', 'accu', 'discount', 'expiry', 'num'];
1573
1574 // 1.2 Remove the one we're sorting by and obtain a list of the irrelevant counters
1575 let otherCounters = allCounters.filter(cName => cName !== criteria);
1576
1577 // 1.3 Set them all to zero
1578 for (let k = 0; k < otherCounters.length; k++) {
1579 sortCounters[otherCounters[k]] = 0;
1580 }
1581
1582 // 2. Increment the counter
1583 sortCounters[criteria]++;
1584
1585 // 3. Loop the counter if needed
1586 if (sortCounters[criteria] === 3) {
1587 sortCounters[criteria] = 0;
1588 }
1589
1590 // 4. Display sort indication via CSS classes
1591 // 4.1 Remove active class from all column titles
1592 let allTitles = colTitle.parentNode.parentNode.children;
1593
1594 for (let c = 0; c < allTitles.length - 1; c++) {
1595 allTitles[c].firstChild.classList.remove('sort-word-active');
1596 allTitles[c].firstChild.classList.remove('sort-word-inverted');
1597 }
1598
1599 // 4.2 Add the class to the row being sorted, based on counter index
1600 if (sortCounters[criteria] !== 0) {
1601 colTitle.classList.add('sort-word-active');
1602
1603 //4.3
1604 if (sortCounters[criteria] === 2) {
1605 colTitle.classList.add('sort-word-inverted');
1606 }
1607 }
1608
1609 // 5. Define the sort direction based on counter value
1610 let direction = (sortCounters[criteria] === 2) ? -1 : 1;
1611
1612 // 6. Define how the objects will be sorted
1613 function compare(a, b) {
1614
1615 // 6.1 If it's a date field, cast the strings into Date objects
1616 if (criteria === 'expiry') {
1617 let date_a = new Date(a[criteria]);
1618 let date_b = new Date(b[criteria]);
1619
1620 if (date_a < date_b) {
1621 return -1 * direction;
1622 }
1623
1624 if (date_a > date_b) {
1625 return 1 * direction;
1626 }
1627
1628 } else {
1629 // 6.2 Else, just take the values as they are
1630 if (a[criteria] < b[criteria]) {
1631 return -1 * direction;
1632 }
1633
1634 if (a[criteria] > b[criteria]) {
1635 return 1 * direction;
1636 }
1637 }
1638
1639 return 0;
1640 }
1641
1642 // 7. If counter isn't ZERO, perform the sort
1643 if (sortCounters[criteria] !== 0) {
1644 recordData.sort(compare);
1645
1646 // 7.1 Display the sorted result
1647 showList(recordData);
1648
1649 // 7.2 Apply any active filters
1650 applyFilter();
1651
1652 } else {
1653 // 8.1 Else, restore the recordData to its original order
1654 restoreOrder();
1655
1656 // 8.2 Display the UNsorted result
1657 showList(recordData);
1658
1659 // 8.3 Apply any active filters
1660 applyFilter();
1661 }
1662}
1663
1664// Sort by name on startup so that a sort indicator is visible
1665sortData(document.querySelector('.container-sort').children[0].children[0], 'name');