Lene Scholtz il y a 5 ans
Parent
révision
6d061a034e
45 fichiers modifiés avec 2112 ajouts et 925 suppressions
  1. BIN
      public/img/icons/area.png
  2. BIN
      public/img/icons/garage.png
  3. BIN
      public/img/sort-down.png
  4. BIN
      public/img/sort-up.png
  5. 3
    0
      src/App.vue
  6. 206
    1
      src/components/admin/misc/carousel.vue
  7. 1
    16
      src/components/admin/misc/carouselList.vue
  8. 101
    0
      src/components/admin/misc/carouselSearch.vue
  9. 5
    16
      src/components/admin/status/statusPage.vue
  10. 8
    4
      src/components/admin/status/tenderWeekAdminPage.vue
  11. 23
    11
      src/components/admin/status/timeshareAdminPage.vue
  12. 7
    3
      src/components/communication/templateDetail.vue
  13. 1
    0
      src/components/communication/templateInnerItem.vue
  14. 10
    6
      src/components/communication/templatePage.vue
  15. 17
    9
      src/components/home/carouselSection.vue
  16. 4
    0
      src/components/processFlow/makeOffer.vue
  17. 14
    4
      src/components/property/propertyCard.vue
  18. 107
    60
      src/components/property/propertyCreate.vue
  19. 12
    7
      src/components/property/propertyFieldEditor.vue
  20. 16
    4
      src/components/property/propertyImage.vue
  21. 33
    84
      src/components/property/propertyList.vue
  22. 61
    36
      src/components/property/propertyPage.vue
  23. 16
    29
      src/components/property/propertySearchFields.vue
  24. 127
    147
      src/components/property/propertySearchPage.vue
  25. 83
    0
      src/components/property/propertySearchResults.vue
  26. 168
    199
      src/components/property/propertyeditPage.vue
  27. 30
    0
      src/components/shared/alertPage.vue
  28. 81
    0
      src/components/shared/autoComplete.vue
  29. 118
    0
      src/components/shared/fieldEditor.vue
  30. 338
    0
      src/components/shared/gallerySlideShow.vue
  31. 51
    11
      src/components/shared/listView.vue
  32. 8
    6
      src/components/shared/navBar.vue
  33. 11
    27
      src/components/shared/searchTab.vue
  34. 13
    3
      src/components/timeshare/buy/buyPage.vue
  35. 38
    10
      src/components/timeshare/myWeeksPage.vue
  36. 9
    0
      src/main.js
  37. 231
    218
      src/router/index.js
  38. 4
    0
      src/store/index.js
  39. 21
    0
      src/store/modules/misc/alert.js
  40. 25
    0
      src/store/modules/misc/carousel.js
  41. 17
    4
      src/store/modules/property/property.js
  42. 11
    8
      src/store/modules/property/propertyEdit.js
  43. 41
    0
      src/store/modules/property/propertySearch.js
  44. 20
    2
      src/store/modules/timeshare/myWeeks.js
  45. 22
    0
      src/store/modules/timeshare/tenderWeeks.js

BIN
public/img/icons/area.png Voir le fichier


BIN
public/img/icons/garage.png Voir le fichier


BIN
public/img/sort-down.png Voir le fichier


BIN
public/img/sort-up.png Voir le fichier


+ 3
- 0
src/App.vue Voir le fichier

@@ -33,6 +33,9 @@ export default {
33 33
 </script>
34 34
 
35 35
 <style>
36
+button:hover {
37
+  cursor: pointer;
38
+},
36 39
 hr {
37 40
   background-color: #60cbeb;
38 41
 }

+ 206
- 1
src/components/admin/misc/carousel.vue Voir le fichier

@@ -1 +1,206 @@
1
-<!-- Work in Progress -->
1
+<template>
2
+  <div>
3
+    <div class="container">
4
+      <div class="container">
5
+        <div class="row">
6
+          <div class="col-md-12 col-lg-8">
7
+            <div class="title-box-d">
8
+              <br />
9
+              <h1 class="title-d" style="text-align:left; font-size: 250%">Carousel Item</h1>
10
+            </div>
11
+            <br />
12
+          </div>
13
+        </div>
14
+      </div>
15
+      <div class="container col-md-12" style="text-align:left">
16
+        <div class="form-goup row">
17
+          <div class="col-md-4">
18
+            <label>Type</label>
19
+            <div class="input-group-prepend">
20
+              <select class="form-control" v-model="selectedType">
21
+                <option value="Timeshare">Timeshare</option>
22
+                <option value="Property">Property</option>
23
+              </select>
24
+              <button
25
+                type="button"
26
+                class="input-group-text fa fa-search"
27
+                style="color: #60CBEB"
28
+                data-toggle="modal"
29
+                data-target="#myModal"
30
+              ></button>
31
+              <!-- Modal content-->
32
+              <div id="myModal" class="modal fade" role="dialog">
33
+                <div class="modal-dialog modal-lg">
34
+                  <div class="modal-content">
35
+                    <div class="modal-header">
36
+                      <button type="button" class="close" data-dismiss="modal"></button>
37
+                    </div>
38
+                    <div padding-left="20px">
39
+                      <Search :name="selectedType" @onSelected="onSelected" />
40
+                    </div>
41
+                    <div class="modal-footer">
42
+                      <button
43
+                        type="button"
44
+                        class="btn btn-b-n"
45
+                        style="width: 150px; height:40px;"
46
+                        data-dismiss="modal"
47
+                      >Close</button>
48
+                    </div>
49
+                  </div>
50
+                </div>
51
+              </div>
52
+              <!-- Modal content END-->
53
+            </div>
54
+          </div>
55
+        </div>
56
+        <div class="form-goup row">
57
+          <div class="col-md-4">
58
+            <br />
59
+            <label>Header</label>
60
+            <input class="form-control" type="text" v-model="carousel.header" />
61
+          </div>
62
+        </div>
63
+        <div class="form-goup row">
64
+          <div class="col-md-4">
65
+            <br />
66
+            <label>Description</label>
67
+            <p v-if="selectedType === 'Property'" v-html="propDescription" />
68
+            <ul v-if="selectedType === 'Timeshare' && bedrooms" class="list">
69
+              <li class="d-flex justify-content-between">
70
+                <strong>
71
+                  <i class="fa fa-bed"></i>
72
+                  &nbsp&nbsp&nbspBedrooms:
73
+                </strong>
74
+                <span>{{ bedrooms }}</span>
75
+              </li>
76
+              <li class="d-flex justify-content-between">
77
+                <strong>
78
+                  <i class="fa fa-users"></i>&nbsp&nbsp&nbspSleeps:
79
+                </strong>
80
+                <span>{{ sleeps }}</span>
81
+              </li>
82
+              <li class="d-flex justify-content-between">
83
+                <strong>
84
+                  <i class="fa fa-calendar"></i>&nbsp&nbsp&nbspCheck in:
85
+                </strong>
86
+                <span>{{ arrival }}</span>
87
+              </li>
88
+              <li class="d-flex justify-content-between">
89
+                <strong>
90
+                  <i class="fa fa-calendar"></i>&nbsp&nbsp&nbspCheck out:
91
+                </strong>
92
+                <span>{{ departure }}</span>
93
+              </li>
94
+            </ul>
95
+          </div>
96
+        </div>
97
+        <div class="form-group row">
98
+          <div class="col-md-2">
99
+            <label>Image</label>
100
+          </div>
101
+        </div>
102
+        <div v-if="selectedType === 'Timeshare'">
103
+          <div>
104
+            <Images :allowMultiple="false" :loadedImages="loadedImages" />
105
+          </div>
106
+        </div>
107
+        <div class="form-group row" v-if="selectedType === 'Property'">
108
+          <div class="col-md-2" v-for="(img, i) in propertyImages" :key="i">
109
+            <input type="radio" name="image" @click="setImage(img)" />
110
+            <label for="checkbox" style="margin: 10px;">Set</label>
111
+            <br />
112
+            <img :src="img.image" style="height:200px; width:150px; object-fit: cover;" />
113
+          </div>
114
+        </div>
115
+        <div class="form-group row">
116
+          <button
117
+            type="button"
118
+            @click="SaveData()"
119
+            class="btn btn-b-n"
120
+            style="width: 85px; height:40px;"
121
+          >Save</button>
122
+          <button
123
+            type="button"
124
+            @click="Close()"
125
+            class="btn btn-b-n"
126
+            style="width: 85px; height:40px;"
127
+          >Close</button>
128
+        </div>
129
+      </div>
130
+    </div>
131
+  </div>
132
+</template>
133
+
134
+<script>
135
+import { mapState, mapActions } from 'vuex';
136
+import Search from './carouselSearch.vue';
137
+import Images from '../../property/propertyImage.vue';
138
+
139
+export default {
140
+  name: 'CarouselItem',
141
+  components: {
142
+    Search,
143
+    Images,
144
+  },
145
+  data() {
146
+    return {
147
+      selectedType: 'Timeshare',
148
+      propDescription: '',
149
+      bedrooms: '',
150
+      sleeps: '',
151
+      arrival: Date,
152
+      departure: Date,
153
+      images: [],
154
+    };
155
+  },
156
+  computed: {
157
+    ...mapState('carousel', ['carousel']),
158
+    ...mapState('property', ['propertyImages']),
159
+  },
160
+  mounted() {
161
+    this.getCarouselItem(this.$route.params.id);
162
+  },
163
+  methods: {
164
+    ...mapActions('carousel', ['saveCarouselItem', 'getCarouselItem']),
165
+    ...mapActions('property', ['getSavedPropertyImages']),
166
+    onSelected(item) {
167
+      if (this.selectedType === 'Timeshare') {
168
+        this.carousel.header = item.resort.resortName;
169
+        this.bedrooms = item.bedrooms;
170
+        this.sleeps = item.maxSleep;
171
+        this.arrival = item.arrivalDate;
172
+        this.departure = item.departureDate;
173
+        this.carousel.timeshareID = item.id;
174
+      } else {
175
+        this.propertyImages = [];
176
+        this.carousel.header = item.name;
177
+        this.propDescription = item.carouselDescription;
178
+        this.carousel.propertyID = item.id;
179
+        this.getSavedPropertyImages(item.id);
180
+      }
181
+    },
182
+    setImage(img) {
183
+      this.carousel.image = img.image;
184
+    },
185
+    loadedImages(values) {
186
+      this.images = values;
187
+    },
188
+    Close() {
189
+      this.$router.push('/carousel');
190
+    },
191
+    SaveData() {
192
+      if (this.selectedType === 'Timeshare') {
193
+        // eslint-disable-next-line no-plusplus
194
+        for (let i = 0; i < this.images.length; i++) {
195
+          this.carousel.image = this.images[i];
196
+        }
197
+      }
198
+      if (this.carousel.id === 0) {
199
+        this.saveCarouselItem(this.carousel);
200
+        this.$router.push('/carousel');
201
+      }
202
+      // Else update
203
+    },
204
+  },
205
+};
206
+</script>

+ 1
- 16
src/components/admin/misc/carouselList.vue Voir le fichier

@@ -2,23 +2,17 @@
2 2
   <!-- eslint-disable max-len -->
3 3
   <div>
4 4
     <div class="container">
5
-      <!-- <section class="intro-single"> -->
6 5
       <div class="container">
7
-        <br />
8
-        <br />
9 6
         <div class="row">
10 7
           <div class="col-md-12 col-lg-8">
11
-            <!-- <div class="title-single-box"> -->
12
-            <!-- <h1 class="title-single">Property Types</h1> -->
13
-            <!-- </div> -->
14 8
             <div class="title-box-d">
9
+              <br />
15 10
               <h1 class="title-d" style="text-align:left; font-size: 250%">Carousel Items</h1>
16 11
             </div>
17 12
             <br />
18 13
           </div>
19 14
         </div>
20 15
       </div>
21
-      <!-- </section> -->
22 16
     </div>
23 17
     <div class="container">
24 18
       <button type="button" @click="New()" class="btn btn-b-n" style="width: 85px; height:40px;">New</button>
@@ -31,7 +25,6 @@
31 25
             <th>Header</th>
32 26
             <th>Type</th>
33 27
             <th></th>
34
-            <th></th>
35 28
           </tr>
36 29
         </thead>
37 30
         <tbody>
@@ -42,14 +35,6 @@
42 35
             <td>{{ item.header }}</td>
43 36
             <td v-if="item.propertyId">Property</td>
44 37
             <td v-else>Timeshare Week</td>
45
-            <td>
46
-              <button
47
-                type="button"
48
-                @click="Edit(item.id)"
49
-                class="btn"
50
-                style="margin:2px; color: #60CBEB"
51
-              >Edit</button>
52
-            </td>
53 38
             <td>
54 39
               <button
55 40
                 type="button"

+ 101
- 0
src/components/admin/misc/carouselSearch.vue Voir le fichier

@@ -0,0 +1,101 @@
1
+<template>
2
+  <div>
3
+    <div class="container">
4
+      <div class="container">
5
+        <div class="row">
6
+          <div class="col-md-12 col-lg-8">
7
+            <div class="title-box-d">
8
+              <br />
9
+              <h1 class="title-d" style="text-align:left; font-size: 250%">Search {{ name }}</h1>
10
+            </div>
11
+          </div>
12
+        </div>
13
+        <div class="row">
14
+          <div class="container">
15
+            <ListView
16
+              v-if="name === 'Timeshare'"
17
+              :items="items"
18
+              :showNew="false"
19
+              @onRowClick="onRowClick"
20
+            />
21
+            <ListView v-else :items="properties" :showNew="false" @onRowClick="onRowClick" />
22
+          </div>
23
+        </div>
24
+      </div>
25
+    </div>
26
+  </div>
27
+</template>
28
+
29
+<script>
30
+import { mapState, mapActions } from 'vuex';
31
+import ListView from '../../shared/listView.vue';
32
+import Log from '../../../assets/Log';
33
+
34
+export default {
35
+  name: 'CarouselSearch',
36
+  props: {
37
+    name: String,
38
+  },
39
+  components: {
40
+    ListView,
41
+  },
42
+  data() {
43
+    return {
44
+      user: Log.getUser(),
45
+    };
46
+  },
47
+  mounted() {
48
+    if (this.name === 'Timeshare') {
49
+      this.getItems(this.user.id);
50
+    } else {
51
+      this.getProperties(
52
+        Object.assign(
53
+          {},
54
+          {
55
+            propertyType: 'Admin',
56
+            user: this.user.id,
57
+          },
58
+        ),
59
+      );
60
+    }
61
+  },
62
+  computed: {
63
+    ...mapState('myWeeks', ['items']),
64
+    ...mapState('propertyList', ['properties']),
65
+    // eslint-disable-next-line vue/return-in-computed-property
66
+    nameChanged() {
67
+      if (this.name === 'Timeshare') {
68
+        this.getItems(this.user.id);
69
+      } else {
70
+        this.getProperties(
71
+          Object.assign(
72
+            {},
73
+            {
74
+              propertyType: 'Admin',
75
+              user: this.user.id,
76
+            },
77
+          ),
78
+        );
79
+      }
80
+    },
81
+  },
82
+  methods: {
83
+    ...mapActions('myWeeks', ['getItems']),
84
+    ...mapActions('propertyList', ['getProperties']),
85
+    onRowClick(item) {
86
+      if (this.name === 'Timeshare') {
87
+        const week = this.items[item];
88
+        this.$emit('onSelected', week);
89
+      } else {
90
+        const prop = this.properties[item];
91
+        this.$emit('onSelected', prop);
92
+      }
93
+    },
94
+  },
95
+  watch: {
96
+    nameChanged() {
97
+      return null;
98
+    },
99
+  },
100
+};
101
+</script>

+ 5
- 16
src/components/admin/status/statusPage.vue Voir le fichier

@@ -20,30 +20,19 @@
20 20
     </div>
21 21
     <!-- </section> -->
22 22
     <div class="container">
23
-      <table class="table table-bordered">
24
-        <thead>
25
-          <tr>
26
-            <th>Id</th>
27
-            <th>Code</th>
28
-            <th>Description</th>
29
-          </tr>
30
-        </thead>
31
-        <tbody>
32
-          <tr v-for="(item, i) in statusList" :key="i">
33
-            <td>{{item.id}}</td>
34
-            <td>{{item.code}}</td>
35
-            <td>{{item.description}}</td>
36
-          </tr>
37
-        </tbody>
38
-      </table>
23
+      <ListView :items="statusList" :columnCount="4" :compact="false" :showNew="false" />
39 24
     </div>
40 25
   </div>
41 26
 </template>
42 27
 <script>
43 28
 import { mapState, mapActions } from 'vuex';
29
+import ListView from '../../shared/listView.vue';
44 30
 
45 31
 export default {
46 32
   name: 'StatusList',
33
+  components: {
34
+    ListView,
35
+  },
47 36
   created() {
48 37
     this.getStatusList();
49 38
   },

+ 8
- 4
src/components/admin/status/tenderWeekAdminPage.vue Voir le fichier

@@ -17,7 +17,7 @@
17 17
       </div>
18 18
     </div>
19 19
     <div id="table" class="col-xs-12 table-responsive">
20
-      <datatable :columns="columns" :data="rows"></datatable>
20
+      <ListView :items="items" :showNew="false"></listView>
21 21
     </div>
22 22
     <br />
23 23
   </div>
@@ -26,17 +26,21 @@
26 26
 </template>
27 27
 <script>
28 28
 import { mapState, mapActions } from 'vuex';
29
+import ListView from '../../shared/listView.vue';
29 30
 
30 31
 export default {
31 32
   name: 'TenderWeekAdmin',
32 33
   created() {
33
-    this.gettenderWeekdmin();
34
+    this.getItems();
35
+  },
36
+  components: {
37
+    ListView
34 38
   },
35 39
   computed: {
36
-    ...mapState('status', ['tenderWeekAdmin']),
40
+    ...mapState('tenderWeek', ['items']),
37 41
   },
38 42
   methods: {
39
-    ...mapActions('status', ['gettenderWeekAdmin']),
43
+    ...mapActions('tenderWeek', ['getItems']),
40 44
   },
41 45
 };
42 46
 </script>

+ 23
- 11
src/components/admin/status/timeshareAdminPage.vue Voir le fichier

@@ -1,9 +1,7 @@
1
+/* eslint-disable no-restricted-syntax */
2
+/* eslint-disable guard-for-in */
1 3
 <template>
2 4
   <div>
3
-    <br />
4
-    <br />
5
-    <br />
6
-    <br />
7 5
     <br />
8 6
     <div class="row">
9 7
       <div class="offset-1 col-md-3">
@@ -11,25 +9,25 @@
11 9
           type="button"
12 10
           :class="{'form-control btn btn-primary ': (1===1), 'my-disable': (!selectedItems || selectedItems.length === 0)}"
13 11
           value="Verify Week(s)"
14
-          :disabled="selectedItems.length > 0"
12
+          :disabled="!(selectedItems.length > 0)"
13
+          @click="onVerify()"
15 14
         >{{ButtonMessage}}</button>
16 15
         <div>{{Message}}</div>
17 16
       </div>
18 17
       <div class="offset-1 col-md-10">
19 18
         <ListView
20
-          :items="items"
21
-          :allowMultipleSelect="true"
19
+          :items="getNeedsVerify"
22 20
           @onRowClick="onRowClick"
23 21
           @onClearSelected="onClearSelected"
24
-          :columnsCount="15"
25 22
           :title="'Pending Weeks'"
23
+          :showNew="false"
26 24
         />
27 25
       </div>
28 26
     </div>
29 27
   </div>
30 28
 </template>
31 29
 <script>
32
-import { mapState, mapActions } from 'vuex';
30
+import { mapState, mapActions, mapGetters } from 'vuex';
33 31
 import Log from '../../../assets/Log';
34 32
 import ListView from '../../shared/listView.vue';
35 33
 
@@ -46,7 +44,6 @@ export default {
46 44
     };
47 45
   },
48 46
   mounted() {
49
-    console.log(this.user.id);
50 47
     this.getItems(this.user.id);
51 48
   },
52 49
   computed: {
@@ -81,9 +78,24 @@ export default {
81 78
       return msg;
82 79
     },
83 80
     ...mapState('myWeeks', ['items']),
81
+    ...mapGetters({ getNeedsVerify: 'myWeeks/getNeedsVerify' }),
84 82
   },
85 83
   methods: {
86
-    ...mapActions('myWeeks', ['getItems']),
84
+    ...mapActions('myWeeks', ['getItems', 'verifyWeek']),
85
+    ...mapActions('alert', ['success']),
86
+    onVerify() {
87
+      if (this.selectedItems.length > 0) {
88
+        for (const i in this.selectedItems) {
89
+          const selectedIndex = this.selectedItems[i];
90
+          const item = this.items[selectedIndex];
91
+          if (item) {
92
+            this.verifyWeek(item.id);
93
+          }
94
+        }
95
+        this.success('Week verified!');
96
+        this.$router.push('/shared/alert');
97
+      }
98
+    },
87 99
     onRowClick(items) {
88 100
       this.selectedItems = items;
89 101
     },

+ 7
- 3
src/components/communication/templateDetail.vue Voir le fichier

@@ -170,7 +170,7 @@
170 170
         </div>
171 171
         <div class="col-md-12">
172 172
           <ListView
173
-            :items="item.placeHolders"
173
+            :items="PlaceHolders"
174 174
             :hideSearch="true"
175 175
             :showNew="false"
176 176
             :deleteable="CanEdit"
@@ -200,6 +200,7 @@ export default {
200 200
   },
201 201
   props: {
202 202
     item: {},
203
+    placeHolders: [],
203 204
     editable: {
204 205
       default: false,
205 206
     },
@@ -214,6 +215,9 @@ export default {
214 215
     ItemList() {
215 216
       return this.item.placeHolders;
216 217
     },
218
+    PlaceHolders(){
219
+      return this.placeHolders;
220
+    }
217 221
   },
218 222
   methods: {
219 223
     ...mapActions('template', ['addItem', 'editItem']),
@@ -230,7 +234,6 @@ export default {
230 234
         // add.
231 235
         this.addItem(this.item);
232 236
       }
233
-      console.log(JSON.stringify(this.item));
234 237
       this.$emit('onClose');
235 238
     },
236 239
     onItemDelete(item) {
@@ -250,7 +253,8 @@ export default {
250 253
         boundToClassDisplay: item.class.name,
251 254
         boundToClass: item.class.fullName,
252 255
       });
253
-      this.item.placeHolders = myList;
256
+      this.placeHolders = myList;
257
+      this.item.placeHolders = this.placeHolders;
254 258
     },
255 259
   },
256 260
 };

+ 1
- 0
src/components/communication/templateInnerItem.vue Voir le fichier

@@ -58,6 +58,7 @@ export default {
58 58
       this.getProperties(this.item.class);
59 59
     },
60 60
     onItemAdd() {
61
+      this.message = undefined;
61 62
       let msg = '';
62 63
       if (this.item.name === undefined) {
63 64
         msg += 'Please give a name...';

+ 10
- 6
src/components/communication/templatePage.vue Voir le fichier

@@ -16,7 +16,7 @@
16 16
         <ListView :items="list" @onRowClick="onRowClick" @onNew="onNew" :showNew="true" />
17 17
       </div>
18 18
       <div class="col-md-6" v-if="showDetailView">
19
-        <DetailView :item="detailItem" @onClose="onDetailClose" :editable="clickedNew" />
19
+        <DetailView :placeHolders="detailItem ? detailItem.placeHolders : []" :item="detailItem" @onClose="onDetailClose" :editable="clickedNew" />
20 20
       </div>
21 21
     </div>
22 22
 
@@ -56,11 +56,15 @@ export default {
56 56
   },
57 57
   methods: {
58 58
     ...mapActions('template', ['getList']),
59
-    onRowClick(item) {
60
-      if (this.detailItem !== item) {
61
-        this.detailItem = item;
62
-      } else this.detailItem = undefined;
63
-      this.clickedNew = false;
59
+    onRowClick(items) {
60
+      if (items.length > 0) {
61
+        const i = items[0];
62
+        const item = this.list[i];
63
+        if (this.detailItem !== item) {
64
+          this.detailItem = item;
65
+        } else this.detailItem = undefined;
66
+        this.clickedNew = false;
67
+      }
64 68
     },
65 69
     onNew() {
66 70
       this.detailItem = {};

+ 17
- 9
src/components/home/carouselSection.vue Voir le fichier

@@ -22,28 +22,36 @@
22 22
               <div class="col-lg-8">
23 23
                 <div
24 24
                   class="price-a"
25
-                  style="opacity:0.95; border: white solid 3px; border-radius: 15px; background-color: white;"
25
+                  style="opacity:0.7; border: white solid 3px; border-radius: 15px; background-color: white;"
26 26
                 >
27 27
                   <h1 class="intro-title mb-4">{{ car.header }}</h1>
28
-                  <div v-if="car.isProperty">
29
-                    <p class="color-b" v-html="car.address" />
28
+                  <div class="summary-list" v-if="car.isProperty">
29
+                    <p v-html="car.address" />
30 30
                   </div>
31
-                  <div v-else>
32
-                    <ul class="list color-b">
31
+                  <div class="summary-list" v-else>
32
+                    <ul class="list">
33 33
                       <li class="d-flex justify-content-between">
34
-                        <strong>Bedrooms:</strong>
34
+                        <strong>
35
+                          <i class="fa fa-bed"></i>&nbsp&nbsp&nbspBedrooms:
36
+                        </strong>
35 37
                         <span>{{ car.bedrooms }}</span>
36 38
                       </li>
37 39
                       <li class="d-flex justify-content-between">
38
-                        <strong>Sleeps:</strong>
40
+                        <strong>
41
+                          <i class="fa fa-users"></i>&nbsp&nbsp&nbspSleeps:
42
+                        </strong>
39 43
                         <span>{{ car.sleeps }}</span>
40 44
                       </li>
41 45
                       <li class="d-flex justify-content-between">
42
-                        <strong>Check in:</strong>
46
+                        <strong>
47
+                          <i class="fa fa-calendar"></i>&nbsp&nbsp&nbspCheck in:
48
+                        </strong>
43 49
                         <span>{{ car.arrival | toDate }}</span>
44 50
                       </li>
45 51
                       <li class="d-flex justify-content-between">
46
-                        <strong>Check out:</strong>
52
+                        <strong>
53
+                          <i class="fa fa-calendar"></i>&nbsp&nbsp&nbspCheck out:
54
+                        </strong>
47 55
                         <span>{{ car.departure | toDate}}</span>
48 56
                       </li>
49 57
                     </ul>

+ 4
- 0
src/components/processFlow/makeOffer.vue Voir le fichier

@@ -196,6 +196,10 @@ export default {
196 196
       if (this.isProperty) {
197 197
         this.bidItem.propertyId = this.item.id;
198 198
       } else {
199
+        this.bidItem.timeshareWeek = this.item;
200
+        this.bidItem.timeshareWeek.id = this.item.resort.id;
201
+        this.bidItem.timeshareWeek.resortCode = this.item.resort.resortCode;
202
+        this.bidItem.timeshareWeek.resortName = this.item.resort.resortName;
199 203
         this.bidItem.timeshareWeekId = this.item.id;
200 204
       }
201 205
       this.saveBid(this.bidItem);

+ 14
- 4
src/components/property/propertyCard.vue Voir le fichier

@@ -71,19 +71,29 @@
71 71
                 <div class="card-footer-a" v-if="currentProperty.showFooter">
72 72
                   <ul class="card-info d-flex justify-content-around">
73 73
                     <li v-if="currentProperty.area !== null">
74
-                      <h4 class="card-info-title">Area</h4>
74
+                      <h4 class="card-info-title">
75
+                        <img src="../../../public/img/icons/area.png" height="16px" width="16px" />
76
+                        <!-- <i class="fa fa-ruler-combined"></i> -->
77
+                      </h4>
75 78
                       <span v-html="currentProperty.area"></span>
76 79
                     </li>
77 80
                     <li v-if="currentProperty.beds !== null">
78
-                      <h4 class="card-info-title">Beds</h4>
81
+                      <h4 class="card-info-title">
82
+                        <i class="fa fa-bed"></i>
83
+                      </h4>
79 84
                       <span>{{ currentProperty.beds }}</span>
80 85
                     </li>
81 86
                     <li v-if="currentProperty.baths !== null">
82
-                      <h4 class="card-info-title">Baths</h4>
87
+                      <h4 class="card-info-title">
88
+                        <i class="fa fa-bath"></i>
89
+                      </h4>
83 90
                       <span>{{ currentProperty.baths }}</span>
84 91
                     </li>
85 92
                     <li v-if="currentProperty.garages !== null">
86
-                      <h4 class="card-info-title">Garages</h4>
93
+                      <h4 class="card-info-title">
94
+                        <!-- <i class="fa fa-warehouse"></i> -->
95
+                        <img src="../../../public/img/icons/garage.png" height="16px" width="16px" />
96
+                      </h4>
87 97
                       <span>{{ currentProperty.garages }}</span>
88 98
                     </li>
89 99
                   </ul>

+ 107
- 60
src/components/property/propertyCreate.vue Voir le fichier

@@ -46,7 +46,7 @@
46 46
                     class="form-control"
47 47
                     name="propertyUsageType"
48 48
                     id="propertyUsageType"
49
-                    v-model="property.propertyUsageType"
49
+                    v-model="propertyType"
50 50
                     @change="TypeChanged"
51 51
                   >
52 52
                     <option value="Residential">Residential</option>
@@ -55,9 +55,10 @@
55 55
                 </div>
56 56
               </div>
57 57
             </div>
58
+            <br />
58 59
             <div class="form-group row">
59 60
               <div class="col-md-4">
60
-                <label>Property Type</label>
61
+                <label>Property Type *</label>
61 62
                 <div class="input-group-prepend">
62 63
                   <span class="input-group-text" style="color: #60CBEB">
63 64
                     <b>T</b>
@@ -67,15 +68,17 @@
67 68
                     name="propertyType"
68 69
                     id="propertyType"
69 70
                     v-model="property.propertyTypeId"
71
+                    @change="PropertyTypeSelected"
70 72
                   >
71 73
                     <option value="0">Please select type</option>
72
-                    <option
73
-                      v-for="item in propertyTypes"
74
-                      :value="item.id"
75
-                      :key="item.id"
76
-                    >{{ item.description }}</option>
74
+                    <option v-for="item in propertyTypes" :value="item.id" :key="item.id">{{
75
+                      item.description
76
+                    }}</option>
77 77
                   </select>
78 78
                 </div>
79
+                <div v-if="showPropertyTypeError">
80
+                  <p class="alert myError">Please select a type</p>
81
+                </div>
79 82
               </div>
80 83
               <div v-if="propertyType === 'Commercial'" class="col-md-4">
81 84
                 <label>Property Name</label>
@@ -139,7 +142,7 @@
139 142
                 </div>
140 143
               </div>
141 144
               <div class="col-md-6" style="margin-bottom: 1em">
142
-                <label>Province</label>
145
+                <label>Province *</label>
143 146
                 <div class="input-group-prepend">
144 147
                   <span class="input-group-text">
145 148
                     <eva-icon name="home-outline" fill="#60CBEB"></eva-icon>
@@ -152,17 +155,18 @@
152 155
                     v-model="property.provinceId"
153 156
                   >
154 157
                     <option value="0">Please select province</option>
155
-                    <option
156
-                      v-for="province in provinces"
157
-                      :value="province.id"
158
-                      :key="province.id"
159
-                    >{{ province.description }}</option>
158
+                    <option v-for="province in provinces" :value="province.id" :key="province.id">{{
159
+                      province.description
160
+                    }}</option>
160 161
                   </select>
161 162
                 </div>
163
+                <div v-if="showProvinceError">
164
+                  <p class="alert myError">Please select a Province</p>
165
+                </div>
162 166
               </div>
163 167
 
164 168
               <div class="col-md-6" style="margin-bottom: 1em">
165
-                <label>City</label>
169
+                <label>City *</label>
166 170
                 <div class="input-group-prepend">
167 171
                   <span class="input-group-text">
168 172
                     <eva-icon name="home-outline" fill="#60CBEB"></eva-icon>
@@ -175,16 +179,17 @@
175 179
                     v-model="property.cityId"
176 180
                   >
177 181
                     <option value="0">Please select city</option>
178
-                    <option
179
-                      v-for="city in cities"
180
-                      :value="city.id"
181
-                      :key="city.id"
182
-                    >{{ city.description }}</option>
182
+                    <option v-for="city in cities" :value="city.id" :key="city.id">{{
183
+                      city.description
184
+                    }}</option>
183 185
                   </select>
184 186
                 </div>
187
+                <div v-if="showCityError">
188
+                  <p class="alert myError">Please select a City</p>
189
+                </div>
185 190
               </div>
186 191
               <div class="col-md-6" style="margin-bottom: 1em">
187
-                <label>Suburb</label>
192
+                <label>Suburb *</label>
188 193
                 <div class="input-group-prepend">
189 194
                   <span class="input-group-text">
190 195
                     <eva-icon name="home-outline" fill="#60CBEB"></eva-icon>
@@ -197,13 +202,14 @@
197 202
                     @change="getPostalCode"
198 203
                   >
199 204
                     <option value="0">Please select suburb</option>
200
-                    <option
201
-                      v-for="suburb in suburbs"
202
-                      :value="suburb.id"
203
-                      :key="suburb.id"
204
-                    >{{ suburb.description }}</option>
205
+                    <option v-for="suburb in suburbs" :value="suburb.id" :key="suburb.id">{{
206
+                      suburb.description
207
+                    }}</option>
205 208
                   </select>
206 209
                 </div>
210
+                <div v-if="showSuburbError">
211
+                  <p class="alert myError">Please select a Suburb</p>
212
+                </div>
207 213
               </div>
208 214
               <div class="col-md-6" style="margin-bottom: 1em">
209 215
                 <label>Postal Code</label>
@@ -263,12 +269,15 @@
263 269
                 <label for="Property Description">Description</label>
264 270
                 <vue-editor v-model="property.description" :editor-toolbar="customToolbar" />
265 271
                 <br />
266
-                <p>* A listing fee of R380 including VAT is payable to list your Property on the Uni-Vate website</p>
272
+                <p>
273
+                  * A listing fee of R380 including VAT is payable to list your Property on the
274
+                  Uni-Vate website
275
+                </p>
267 276
               </div>
268 277
             </div>
269 278
             <div class="form-group row" />
270 279
             <UserField
271
-              v-if="propertyType === 'Residential' & propertyOverviewFields.length > 0"
280
+              v-if="(propertyType === 'Residential') & (propertyOverviewFields.length > 0)"
272 281
               :fields="propertyOverviewFields[0].fields"
273 282
               @UpdateUserDefinedFields="UpdateUserDefinedFields"
274 283
               :id="1"
@@ -348,7 +357,16 @@
348 357
               @click="SubmitData()"
349 358
               class="btn btn-b-n"
350 359
               style="width: 85px; height:40px;"
351
-            >Save</button>
360
+            >
361
+              Save
362
+            </button>
363
+            <div
364
+              v-if="showPropertyTypeError || showProvinceError || showCityError || showSuburbError"
365
+            >
366
+              <p class="alert myError">
367
+                Missing fields. Please fill in all required fields. Marked with *
368
+              </p>
369
+            </div>
352 370
             <div v-if="wait" id="preloader"></div>
353 371
           </form>
354 372
         </div>
@@ -360,7 +378,6 @@
360 378
 <script>
361 379
 import { mapState, mapActions } from 'vuex';
362 380
 import { VueEditor } from 'vue2-editor';
363
-import { setTimeout } from 'timers';
364 381
 import UserField from './propertyUserField.vue';
365 382
 import ImageLoad from './propertyImage.vue';
366 383
 
@@ -384,16 +401,16 @@ export default {
384 401
       customToolbar: [
385 402
         [{ header: [false, 1, 2, 3, 4, 5, 6] }],
386 403
         ['bold', 'italic', 'underline', 'strike'],
387
-        [
388
-          { align: '' },
389
-          { align: 'center' },
390
-          { align: 'right' },
391
-          { align: 'justify' },
392
-        ],
404
+        [{ align: '' }, { align: 'center' }, { align: 'right' }, { align: 'justify' }],
393 405
         [{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
394 406
         [{ script: 'sub' }, { script: 'super' }],
395 407
         [{ indent: '-1' }, { indent: '+1' }],
396 408
       ],
409
+      error: '',
410
+      showPropertyTypeError: false,
411
+      showProvinceError: false,
412
+      showCityError: false,
413
+      showSuburbError: false,
397 414
     };
398 415
   },
399 416
   methods: {
@@ -416,6 +433,28 @@ export default {
416 433
       this.propertyType = this.property.propertyUsageType;
417 434
     },
418 435
     SubmitData() {
436
+      if (this.property.propertyTypeId === 0) {
437
+        this.showPropertyTypeError = true;
438
+      }
439
+      if (this.property.provinceId === 0) {
440
+        this.showProvinceError = true;
441
+      }
442
+      if (this.property.cityId === 0) {
443
+        this.showCityError = true;
444
+      }
445
+      if (this.property.suburbId === 0) {
446
+        this.showSuburbError = true;
447
+      }
448
+
449
+      if (
450
+        this.showPropertyTypeError
451
+        || this.showProvinceError
452
+        || this.showCityError
453
+        || this.showSuburbError
454
+      ) {
455
+        return;
456
+      }
457
+
419 458
       this.wait = true;
420 459
       if (this.salesType === 'Sale') {
421 460
         this.property.isSale = true;
@@ -433,47 +472,55 @@ export default {
433 472
       }
434 473
       this.property.propertyUserFields = this.propertyFieldValues;
435 474
 
436
-      this.property.userId = this.user.id;
437
-      this.saveProperty(this.property);
438
-      setTimeout(
439
-        () => this.$router.push({
440
-            path: '/property/Search',
441
-            query: {
442
-              salesType: this.salesType,
443
-              propertyUsageType: this.propertyType,
444
-            },
445
-          }),
446
-        3000,
447
-      );
475
+      if (this.user) {
476
+        this.property.userId = this.user.id;
477
+      }
478
+
479
+      this.saveProperty(this.property)
480
+        .then((fulfilled) => {
481
+          this.$router.push(`/property/property/${fulfilled.data.id}`);
482
+        })
483
+        .catch((error) => {
484
+          console.log(error.message);
485
+        });
448 486
     },
449 487
     Close() {
450 488
       this.$router.push('/property/admin/list/my');
451 489
     },
490
+    PropertyTypeSelected(item) {
491
+      if (item.target.options.selectedIndex > 0) {
492
+        this.showPropertyTypeError = false;
493
+      } else {
494
+        this.showPropertyTypeError = true;
495
+      }
496
+    },
452 497
     ProvinceSelected(item) {
453 498
       if (item.target.options.selectedIndex > 0) {
454
-        this.selectedProvince = this.provinces[
455
-          item.target.options.selectedIndex - 1
456
-        ].description;
499
+        this.selectedProvince = this.provinces[item.target.options.selectedIndex - 1].description;
457 500
         this.getCities(Object.assign({}, { province: this.selectedProvince }));
501
+        this.showProvinceError = false;
502
+      } else {
503
+        this.showProvinceError = true;
458 504
       }
459 505
     },
460 506
     CitySelected(item) {
461 507
       if (item.target.options.selectedIndex > 0) {
462
-        this.selectedCity = this.cities[
463
-          item.target.options.selectedIndex - 1
464
-        ].description;
508
+        this.selectedCity = this.cities[item.target.options.selectedIndex - 1].description;
465 509
         this.getSuburbs(
466
-          Object.assign(
467
-            {},
468
-            { province: this.selectedProvince, city: this.selectedCity },
469
-          ),
510
+          Object.assign({}, { province: this.selectedProvince, city: this.selectedCity }),
470 511
         );
512
+        this.showCityError = false;
513
+      } else {
514
+        this.showCityError = true;
471 515
       }
472 516
     },
473 517
     getPostalCode(item) {
474
-      this.property.addressLine3 = this.suburbs[
475
-        item.target.options.selectedIndex - 1
476
-      ].postalCode;
518
+      this.property.addressLine3 = this.suburbs[item.target.options.selectedIndex - 1].postalCode;
519
+      if (item.target.options.selectedIndex > 0) {
520
+        this.showSuburbError = false;
521
+      } else {
522
+        this.showSuburbError = true;
523
+      }
477 524
     },
478 525
     loadedImages(values) {
479 526
       this.images = values;

+ 12
- 7
src/components/property/propertyFieldEditor.vue Voir le fichier

@@ -11,7 +11,12 @@
11 11
           v-model="display"
12 12
           disabled
13 13
         />
14
-        <span v-if="mayEdit" @click="EditClick()" class="input-group-text" style="color: #60CBEB">
14
+        <span
15
+          v-if="mayEdit"
16
+          @click="EditClick()"
17
+          class="input-group-text spanCursor"
18
+          style="color: #60CBEB"
19
+        >
15 20
           <eva-icon name="edit-outline" fill="#60CBEB"></eva-icon>
16 21
         </span>
17 22
       </div>
@@ -42,7 +47,9 @@
42 47
           @change="SelectorSelected"
43 48
         >
44 49
           <option value="0"></option>
45
-          <option v-for="item in arrayObject" :value="item.id" :key="item.id">{{ item.description }}</option>
50
+          <option v-for="item in arrayObject" :value="item.id" :key="item.id">{{
51
+            item.description
52
+          }}</option>
46 53
         </select>
47 54
         <!-- yesno -->
48 55
         <select v-if="editType === 'yesno'" class="form-control" @change="YesNoSelected">
@@ -50,10 +57,10 @@
50 57
           <option value="yes">Yes</option>
51 58
           <option value="no">No</option>
52 59
         </select>
53
-        <span @click="UpdateValue()" class="input-group-text" style="color: #60CBEB">
60
+        <span @click="UpdateValue()" class="input-group-text spanCursor" style="color: #60CBEB">
54 61
           <eva-icon name="checkmark-outline" fill="#60CBEB"></eva-icon>
55 62
         </span>
56
-        <span @click="Close()" class="input-group-text" style="color: #60CBEB">
63
+        <span @click="Close()" class="input-group-text spanCursor" style="color: #60CBEB">
57 64
           <eva-icon name="close-outline" fill="#60CBEB"></eva-icon>
58 65
         </span>
59 66
       </div>
@@ -94,9 +101,7 @@ export default {
94 101
     },
95 102
     SelectorSelected(item) {
96 103
       if (item.target.options.selectedIndex > 0) {
97
-        this.updatedDisplay = this.arrayObject[
98
-          item.target.options.selectedIndex - 1
99
-        ].description;
104
+        this.updatedDisplay = this.arrayObject[item.target.options.selectedIndex - 1].description;
100 105
       }
101 106
     },
102 107
     YesNoSelected(item) {

+ 16
- 4
src/components/property/propertyImage.vue Voir le fichier

@@ -10,7 +10,7 @@
10 10
           style="width: 0px;height: 0px;overflow: hidden;"
11 11
           name="images[]"
12 12
           @change="imagesAdd"
13
-          multiple
13
+          :multiple="allowMultiple"
14 14
           :disabled="!mayEdit"
15 15
         />
16 16
       </label>
@@ -18,11 +18,17 @@
18 18
     <br />
19 19
     <div class="form-group row">
20 20
       <div v-for="(img, i) in image" class="col-md-2" :key="i">
21
-        <input type="checkbox" id="checkbox" v-model="imagesDefault[i]" @change="updateList(i)" />
22
-        <label for="checkbox" style="margin: 10px;">Main Image</label>
21
+        <input
22
+          v-if="allowMultiple"
23
+          type="checkbox"
24
+          id="checkbox"
25
+          v-model="imagesDefault[i]"
26
+          @change="updateList(i)"
27
+        />
28
+        <label v-if="allowMultiple" for="checkbox" style="margin: 10px;">Main Image</label>
23 29
         <img :src="img" style="height:200px; width:150px; object-fit: cover;" />
24 30
         <br />
25
-        <span class="input-group-text" align="center" @click="removeImage(key)">
31
+        <span class="input-group-text" align="center" style="width:150px" @click="removeImage(key)">
26 32
           <eva-icon name="trash-2-outline" fill="#60CBEB"></eva-icon>Delete
27 33
         </span>
28 34
       </div>
@@ -36,6 +42,7 @@ export default {
36 42
   props: {
37 43
     loadedImages: Function,
38 44
     mayEdit: { type: Boolean, default: () => true },
45
+    allowMultiple: { type: Boolean, default: () => true },
39 46
   },
40 47
   data() {
41 48
     return {
@@ -54,6 +61,11 @@ export default {
54 61
   // },
55 62
   methods: {
56 63
     imagesAdd(e) {
64
+      if (!this.allowMultiple) {
65
+        this.images = {};
66
+        this.image = [];
67
+      }
68
+
57 69
       const files = e.target.files || e.dataTransfer.files;
58 70
 
59 71
       this.images = [];

+ 33
- 84
src/components/property/propertyList.vue Voir le fichier

@@ -2,75 +2,27 @@
2 2
   <!-- eslint-disable max-len -->
3 3
   <div>
4 4
     <div class="container">
5
-      <!-- <section class="intro-single"> -->
6 5
       <div class="container">
7
-        <br />
8
-        <br />
9 6
         <br />
10 7
         <br />
11 8
         <div class="row">
12 9
           <div class="col-md-12 col-lg-8">
13 10
             <div class="title-box-d">
14
-              <h1
15
-                v-if="showAdmin"
16
-                class="title-d"
17
-                style="text-align:left; font-size: 250%"
18
-              >Admin Properties</h1>
19
-              <h1 v-else class="title-d" style="text-align:left; font-size: 250%">My Properties</h1>
11
+              <h1 class="title-d" style="text-align:left; font-size: 250%">Properties</h1>
20 12
             </div>
21 13
           </div>
22 14
         </div>
23 15
       </div>
24
-      <!-- </section> -->
25 16
     </div>
26 17
     <div class="container">
27
-      <table class="table table-bordered">
28
-        <thead>
29
-          <tr>
30
-            <th>Name</th>
31
-            <th>Property ID</th>
32
-            <th>Size</th>
33
-            <th>Price</th>
34
-            <th>Usage Type</th>
35
-            <th>Type</th>
36
-            <th>Sale Type</th>
37
-            <th>Publish</th>
38
-            <th>Status</th>
39
-            <th></th>
40
-            <th></th>
41
-          </tr>
42
-        </thead>
43
-        <tbody>
44
-          <tr v-for="(item, i) in properties" :key="i">
45
-            <td>{{ item.name }}</td>
46
-            <td>{{ item.id }}</td>
47
-            <td v-html="item.size" />
48
-            <td>{{ item.price }}</td>
49
-            <td>{{ item.usageType }}</td>
50
-            <td>{{ item.type }}</td>
51
-            <td>{{ item.saleType }}</td>
52
-            <!-- <td>{{ item.publish }}</td> -->
53
-            <td></td>
54
-            <td>{{ item.status }}</td>
55
-            <td>
56
-              <button
57
-                type="button"
58
-                @click="Edit(item)"
59
-                class="btn"
60
-                style="margin:2px; color: #60CBEB"
61
-              >Edit</button>
62
-            </td>
63
-            <td>
64
-              <button
65
-                type="button"
66
-                @click="Delete(item)"
67
-                class="btn btn-b-n"
68
-                style="width: 85px; height:40px;"
69
-              >Delete</button>
70
-            </td>
71
-          </tr>
72
-        </tbody>
73
-      </table>
18
+      <listView
19
+        :items="properties"
20
+        :showNew="false"
21
+        :editable="true"
22
+        :deleteable="true"
23
+        @onEdit="Edit"
24
+        @onDelete="Delete"
25
+      />
74 26
     </div>
75 27
     <br />
76 28
   </div>
@@ -78,13 +30,17 @@
78 30
 
79 31
 <script>
80 32
 import { mapState, mapActions } from 'vuex';
33
+import listView from '../shared/listView.vue';
81 34
 
82 35
 export default {
83 36
   name: 'PropertyList',
37
+  components: {
38
+    listView,
39
+  },
84 40
   data() {
85 41
     return {
86 42
       propertyType: '',
87
-      showAdmin: false,
43
+      role: 'MY',
88 44
     };
89 45
   },
90 46
   methods: {
@@ -101,45 +57,38 @@ export default {
101 57
     },
102 58
   },
103 59
   mounted() {
104
-    if (this.$route.params.by === 'my') {
105
-      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
106
-      this.showAdmin = false;
107
-    } else {
108
-      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
109
-      this.showAdmin = true;
60
+    if (this.user.role === 'Super Admin') {
61
+      this.role = 'SUPERADMIN';
62
+    }
63
+    if (this.user.role === 'Agency') {
64
+      this.user.role = 'ADMIN';
110 65
     }
111
-
112
-    // this.user = 28; // user id
113 66
 
114 67
     this.getProperties(
115
-      Object.assign(
116
-        {},
117
-        {
118
-          propertyType: this.showAdmin ? 'Admin' : 'My',
119
-          user: this.user.id,
120
-        },
121
-      ),
68
+      Object.assign({
69
+        propertyType: this.role,
70
+        user: this.user.id,
71
+      }),
122 72
     );
123 73
   },
124 74
   computed: {
125 75
     ...mapState('propertyList', ['properties']),
126 76
     ...mapState('authentication', ['user']),
127 77
     UserChanged() {
128
-      if (this.$route.params.by === 'my') {
78
+      if (this.user.role === 'Super Admin') {
129 79
         // eslint-disable-next-line vue/no-side-effects-in-computed-properties
130
-        this.showAdmin = false;
131
-      } else {
80
+        this.role = 'SUPERADMIN';
81
+      }
82
+      if (this.user.role === 'Agency') {
132 83
         // eslint-disable-next-line vue/no-side-effects-in-computed-properties
133
-        this.showAdmin = true;
84
+        this.user.role = 'ADMIN';
134 85
       }
86
+
135 87
       this.getProperties(
136
-        Object.assign(
137
-          {},
138
-          {
139
-            propertyType: this.showAdmin ? 'Admin' : 'My',
140
-            user: this.user,
141
-          },
142
-        ),
88
+        Object.assign({
89
+          propertyType: this.role,
90
+          user: this.user.id,
91
+        }),
143 92
       );
144 93
       return this.user;
145 94
     },

+ 61
- 36
src/components/property/propertyPage.vue Voir le fichier

@@ -2,17 +2,13 @@
2 2
   <!-- eslint-disable max-len -->
3 3
   <div v-if="property">
4 4
     <div class="container">
5
-      <br />
6
-      <br />
7
-      <br />
8 5
       <br />
9 6
       <div class="row">
10 7
         <div class="col-md-12 col-lg-8">
11 8
           <div class="title-box-d">
12
-            <h1
13
-              class="title-d"
14
-              style="text-align:left; font-size: 250%"
15
-            >{{ property.shortDescription }}</h1>
9
+            <h1 class="title-d" style="text-align:left; font-size: 250%">
10
+              {{ property.shortDescription }}
11
+            </h1>
16 12
           </div>
17 13
         </div>
18 14
       </div>
@@ -23,13 +19,20 @@
23 19
     <section class="property-single nav-arrow-b">
24 20
       <div class="container">
25 21
         <div class="row">
26
-          <lightBox
27
-            :thumbnails="propertyImages"
28
-            :largeImages="propertyImages"
29
-            :caption="false"
30
-            class="lightBox"
31
-          />
22
+          <div id="app" class="scrolling-wrapper">
23
+            <div class="card">
24
+              <img
25
+                class="image"
26
+                v-for="(image, i) in propertyImages"
27
+                :src="image"
28
+                @click="index = i"
29
+                :key="i"
30
+              />
31
+            </div>
32
+            <gallery :images="propertyImages" :index="index" @close="index = null"></gallery>
33
+          </div>
32 34
         </div>
35
+        <br />
33 36
         <div class="row">
34 37
           <div class="col-sm-12">
35 38
             <div class="row justify-content-between">
@@ -72,7 +75,7 @@
72 75
 
73 76
                     <div class="card-title-c align-self-center">
74 77
                       <h5 class="title-c">R{{ formatPrice(property.price) }}</h5>
75
-                      <h6 v-if="property.pricePer">Per {{property.pricePer}}</h6>
78
+                      <h6 v-if="property.pricePer">Per {{ property.pricePer }}</h6>
76 79
                     </div>
77 80
                   </div>
78 81
                   <!-- </div> -->
@@ -93,16 +96,23 @@
93 96
                       </li>
94 97
                       <li class="d-flex justify-content-between">
95 98
                         <strong>Status:</strong>
96
-                        <span
97
-                          v-if="property.status"
98
-                        >{{ property.status.code }} - {{ property.status.description }}</span>
99
+                        <span v-if="property.status"
100
+                          >{{ property.status.code }} - {{ property.status.description }}</span
101
+                        >
99 102
                       </li>
100 103
                       <li class="d-flex justify-content-between">
101 104
                         <strong>Address:</strong>
102 105
                         <span
103 106
                           style="text-align:right"
104 107
                           v-if="property"
105
-                          v-html="formatAddress(property.addressLine1) + formatAddress(property.addressLine2) + formatAddress(property.addressLine3) + formatAddress(property.suburb ? property.suburb.description : '') + formatAddress(property.city ? property.city.description : '') + formatAddress(property.province ? property.province.description : '') "
108
+                          v-html="
109
+                            formatAddress(property.addressLine1) +
110
+                              formatAddress(property.addressLine2) +
111
+                              formatAddress(property.addressLine3) +
112
+                              formatAddress(property.suburb ? property.suburb.description : '') +
113
+                              formatAddress(property.city ? property.city.description : '') +
114
+                              formatAddress(property.province ? property.province.description : '')
115
+                          "
106 116
                         ></span>
107 117
                       </li>
108 118
                     </ul>
@@ -114,7 +124,9 @@
114 124
                     class="btn btn-b-n"
115 125
                     data-toggle="modal"
116 126
                     data-target="#myModal"
117
-                  >Make an Offer</button>
127
+                  >
128
+                    Make an Offer
129
+                  </button>
118 130
                   <div id="myModal" class="modal fade" role="dialog">
119 131
                     <div class="modal-dialog modal-lg">
120 132
                       <!-- Modal content-->
@@ -127,7 +139,12 @@
127 139
                             name="MakeOffer"
128 140
                             :isMakeOffer="true"
129 141
                             :isProperty="true"
130
-                            :item="{id: property.id, shortDescription: property.shortDescription, description: property.description, sellPrice: property.price}"
142
+                            :item="{
143
+                              id: property.id,
144
+                              shortDescription: property.shortDescription,
145
+                              description: property.description,
146
+                              sellPrice: property.price
147
+                            }"
131 148
                           />
132 149
                         </div>
133 150
                       </div>
@@ -252,17 +269,19 @@
252 269
 
253 270
 <script>
254 271
 import { mapState, mapActions } from 'vuex';
255
-import lightBox from '../shared/lightBoxGallery.vue';
256 272
 import makeOffer from '../processFlow/makeOffer.vue';
273
+import gallery from '../shared/gallerySlideShow.vue';
257 274
 
258 275
 export default {
259 276
   name: 'property',
260 277
   components: {
261
-    lightBox,
262 278
     makeOffer,
279
+    gallery,
263 280
   },
264 281
   data() {
265
-    return {};
282
+    return {
283
+      index: null,
284
+    };
266 285
   },
267 286
   mounted() {
268 287
     this.getProperty(this.$route.params.id);
@@ -272,11 +291,7 @@ export default {
272 291
     ...mapState('property', ['property', 'propertyImages']),
273 292
   },
274 293
   methods: {
275
-    ...mapActions('property', [
276
-      'getProperty',
277
-      'getPropertyImages',
278
-      'clearPropertyImages',
279
-    ]),
294
+    ...mapActions('property', ['getProperty', 'getPropertyImages', 'clearPropertyImages']),
280 295
     formatPrice(value) {
281 296
       const val = (value / 1).toFixed(2);
282 297
       return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
@@ -294,14 +309,24 @@ export default {
294 309
 };
295 310
 </script>
296 311
 
297
-<style lang ="scss">
298
-.light-box {
299
-  &__thumbnail {
300
-    margin: 20px;
301
-    width: 200px;
302
-  }
312
+<style lang="scss">
313
+.image {
314
+  width: 150px;
315
+  height: 150px;
316
+  background-size: cover;
317
+  cursor: pointer;
318
+  margin: 5px;
319
+  border-radius: 3px;
320
+  border: 1px solid lightgray;
321
+  object-fit: contain;
303 322
 }
304
-img {
305
-  max-width: 100%;
323
+.scrolling-wrapper {
324
+  overflow-x: scroll;
325
+  overflow-y: hidden;
326
+  white-space: nowrap;
327
+
328
+  .card {
329
+    display: inline-block;
330
+  }
306 331
 }
307 332
 </style>

+ 16
- 29
src/components/property/propertySearchFields.vue Voir le fichier

@@ -9,11 +9,7 @@
9 9
               <b>F</b>
10 10
             </span>
11 11
           </div>
12
-          <select
13
-            class="form-control"
14
-            v-model="propertySearch.salesType"
15
-            @change="salesTypeSelected"
16
-          >
12
+          <select class="form-control" v-model="propertySearch.salesType">
17 13
             <option value="Sale">Sale</option>
18 14
             <option value="Rent">Rent</option>
19 15
           </select>
@@ -29,10 +25,11 @@
29 25
           </div>
30 26
           <select class="form-control" v-model="resType" @change="PropertyTypeSelected">
31 27
             <option>All</option>
32
-            <option
33
-              v-for="(propertyType, i) in propertyTypesRes"
34
-              :key="i"
35
-            >{{ propertyType.description }}</option>
28
+            <option v-for="(propertyType, i) in propertyTypesRes" :key="i">
29
+              {{
30
+              propertyType.description
31
+              }}
32
+            </option>
36 33
           </select>
37 34
           <div class="input-group-append" @click="clearResType">
38 35
             <span class="input-group-text cursor-pointer" style="color: #60CBEB">
@@ -51,10 +48,11 @@
51 48
           </div>
52 49
           <select class="form-control" v-model="comType" @change="PropertyTypeSelected">
53 50
             <option>All</option>
54
-            <option
55
-              v-for="(propertyType, i) in propertyTypesCom"
56
-              :key="i"
57
-            >{{ propertyType.description }}</option>
51
+            <option v-for="(propertyType, i) in propertyTypesCom" :key="i">
52
+              {{
53
+              propertyType.description
54
+              }}
55
+            </option>
58 56
           </select>
59 57
           <div class="input-group-append" @click="clearComType">
60 58
             <span class="input-group-text cursor-pointer" style="color: #60CBEB">
@@ -71,7 +69,7 @@
71 69
               <b>P</b>
72 70
             </span>
73 71
           </div>
74
-          <select class="form-control" @change="ProvinceSelected" v-model="propertySearch.province">
72
+          <select class="form-control" v-model="propertySearch.province">
75 73
             <option>All</option>
76 74
             <option v-for="(province, i) in provinces" :key="i">{{ province.description }}</option>
77 75
           </select>
@@ -92,7 +90,7 @@
92 90
               <b>C</b>
93 91
             </span>
94 92
           </div>
95
-          <select class="form-control" @change="CitySelected" v-model="propertySearch.city">
93
+          <select class="form-control" v-model="propertySearch.city">
96 94
             <option>All</option>
97 95
             <option v-for="(city, i) in cities" :key="i">{{ city.description }}</option>
98 96
           </select>
@@ -113,7 +111,7 @@
113 111
               <b>S</b>
114 112
             </span>
115 113
           </div>
116
-          <select class="form-control" v-model="propertySearch.suburb" @change="SuburbSeleted">
114
+          <select class="form-control" v-model="propertySearch.suburb">
117 115
             <option>All</option>
118 116
             <option v-for="(suburb, i) in suburbs" :key="i">{{ suburb.description }}</option>
119 117
           </select>
@@ -139,8 +137,7 @@
139 137
               <input
140 138
                 class="form-control"
141 139
                 type="number"
142
-                step="any"
143
-                name="minPrice"
140
+                setp="any"
144 141
                 v-model="propertySearch.minPrice"
145 142
               />
146 143
               <div class="input-group-append" @click="clearFilter('minPrice')">
@@ -161,8 +158,7 @@
161 158
               <input
162 159
                 class="form-control"
163 160
                 type="number"
164
-                step="any"
165
-                name="maxPrice"
161
+                setp="any"
166 162
                 v-model="propertySearch.maxPrice"
167 163
               />
168 164
               <div class="input-group-append" @click="clearFilter('maxPrice')">
@@ -216,12 +212,8 @@ export default {
216 212
       this.clearFilter('propertyType');
217 213
       this.comType = 'All';
218 214
     },
219
-    salesTypeSelected() {
220
-      this.$emit('updateSearch', this.propertySearch);
221
-    },
222 215
     PropertyTypeSelected(item) {
223 216
       this.propertySearch.propertyType = item.target.value;
224
-      this.$emit('updateSearch', this.propertySearch);
225 217
     },
226 218
     ProvinceSelected(item) {
227 219
       if (item.target.value !== 'All') {
@@ -229,7 +221,6 @@ export default {
229 221
           Object.assign({}, { province: this.propertySearch.province }),
230 222
         );
231 223
       }
232
-      this.$emit('updateSearch', this.propertySearch);
233 224
     },
234 225
     CitySelected(item) {
235 226
       if (item.target.value !== 'All') {
@@ -243,10 +234,6 @@ export default {
243 234
           ),
244 235
         );
245 236
       }
246
-      this.$emit('updateSearch', this.propertySearch);
247
-    },
248
-    SuburbSeleted() {
249
-      this.$emit('updateSearch', this.propertySearch);
250 237
     },
251 238
   },
252 239
 };

+ 127
- 147
src/components/property/propertySearchPage.vue Voir le fichier

@@ -2,48 +2,107 @@
2 2
   <!-- eslint-disable max-len -->
3 3
   <div class="container">
4 4
     <br />
5
-    <div class="row">
6
-      <div class="col-md-2 offset-4">
7
-        <button type="button" @click="SetType('Residential')" class="btn btn-b-n">Residential</button>
8
-      </div>
9
-      <div class="col-md-2">
10
-        <button type="button" @click="SetType('Commercial')" class="btn btn-b-n">Commercial</button>
11
-      </div>
12
-    </div>
13
-    <div class="container col-md-12" v-if="propertySearch.propertyUsageType === 'Residential'">
5
+    <div class="container col-md-12">
14 6
       <div class="col-sm-12">
15 7
         <div class="about-img-box">
16 8
           <img
9
+            v-if="propertyUsageType === 'Residential'"
17 10
             src="img/Pretoria-South-Africa.jpg"
18 11
             alt="Property Listing"
19 12
             class="img-fluid"
20 13
             style="width:800px;height:400px; border-radius:10px"
21 14
           />
15
+          <img
16
+            v-else
17
+            src="img/Johannesburg-south-africa-1.jpg"
18
+            alt="Property Listing"
19
+            class="img-fluid"
20
+            style="width:800px;height:400px; border-radius:10px"
21
+          />
22 22
         </div>
23 23
         <div class="sinse-box" style="opacity:0.7; border: white solid 3px; border-radius: 15px">
24 24
           <h3 class="sinse-title">Property Listing</h3>
25 25
         </div>
26 26
       </div>
27 27
       <br />
28
+      <div class="row">
29
+        <div class="col-md-2 offset-4">
30
+          <button type="button" @click="SetType('Residential')" class="btn btn-b-n">
31
+            Residential
32
+          </button>
33
+        </div>
34
+        <div class="col-md-2">
35
+          <button type="button" @click="SetType('Commercial')" class="btn btn-b-n">
36
+            Commercial
37
+          </button>
38
+        </div>
39
+      </div>
40
+      <br />
41
+      <div class="col-md-10 offset-1">
42
+        <ul class="nav nav-pills-a nav-pills mb-3 section-t3" id="pills-tab" role="tablist">
43
+          <li class="nav-item">
44
+            <a
45
+              class="nav-link active"
46
+              id="pills-video-tab"
47
+              data-toggle="pill"
48
+              href="#pills-video"
49
+              role="tab"
50
+              aria-controls="pills-video"
51
+              aria-selected="true"
52
+              v-on:click="SetSalesType('Sale')"
53
+              >For Sale</a
54
+            >
55
+          </li>
56
+          <li class="nav-item">
57
+            <a
58
+              class="nav-link"
59
+              id="pills-plans-tab"
60
+              data-toggle="pill"
61
+              href="#pills-plans"
62
+              role="tab"
63
+              aria-controls="pills-plans"
64
+              aria-selected="false"
65
+              v-on:click="SetSalesType('Rent')"
66
+              >To Rent</a
67
+            >
68
+          </li>
69
+        </ul>
70
+      </div>
71
+      <br />
72
+      <div class="row">
73
+        <autoComplete class="col-md-11 offset-1" :items="suburbList" @selection="SelectedFilter" />
74
+      </div>
75
+      <br />
76
+      <div class="row">
77
+        <div class="col-md-10 offset-1">
78
+          <button
79
+            type="button"
80
+            class="input-group-text fa fa-search"
81
+            style="color: #60CBEB"
82
+            @click="SearchClick"
83
+          >
84
+            Search
85
+          </button>
86
+        </div>
87
+      </div>
28 88
       <div class="row">
29 89
         <div class="col-md-12">
30
-          <h1 class="my-4">About Residential Properties</h1>
90
+          <h1 class="my-4" v-if="propertyUsageType === 'Residential'">
91
+            About Residential Properties
92
+          </h1>
93
+          <h1 class="my-4" v-else>About Commercial Properties</h1>
31 94
         </div>
32
-        <div class="container col-md-6 text-left">
95
+        <div class="container col-md-6 text-left" v-if="propertyUsageType === 'Residential'">
33 96
           <p>
34
-            Uni-Vate Properties understands the necessity in property-seekers
35
-            to find that perfect fit;
36
-            a home that meets, and exceeds the individual's needs. That is why,
37
-            our dedicated team sources
38
-            a range of property types, whether that home is a rental option near
39
-            a University for a student
40
-            just starting out, or a family home fit for entertaining.
97
+            Uni-Vate Properties understands the necessity in property-seekers to find that perfect
98
+            fit; a home that meets, and exceeds the individual's needs. That is why, our dedicated
99
+            team sources a range of property types, whether that home is a rental option near a
100
+            University for a student just starting out, or a family home fit for entertaining.
41 101
           </p>
42 102
           <p>
43
-            Looking to sell your residential property, instead? Uni-Vate
44
-            Properties prides itself on
45
-            professionalism and the right expertise, to help you secure the
46
-            best possible deal for your home.
103
+            Looking to sell your residential property, instead? Uni-Vate Properties prides itself on
104
+            professionalism and the right expertise, to help you secure the best possible deal for
105
+            your home.
47 106
           </p>
48 107
           <p>
49 108
             Wish to RENT your property?
@@ -54,61 +113,19 @@
54 113
             <router-link to="/property/new/Residential/Sale">Click Here</router-link>
55 114
           </p>
56 115
         </div>
57
-        <div class="col-md-4">
116
+        <div class="container col-md-6 text-left" v-else>
58 117
           <p>
59
-            <img
60
-              class="img-fluid"
61
-              src="./../../../public/img/residential.jpg"
62
-              alt="About Resale"
63
-              style="border-radius:10px"
64
-            />
65
-          </p>
66
-        </div>
67
-      </div>
68
-      <div>
69
-        <propertyCard v-if="properties.length > 0" name="propertyholder" :properties="properties" />
70
-        <div v-if="properties.length === 0">
71
-          <img src="../../../public/img/no-homes.png" />
72
-          <br />
73
-          <br />
74
-          <p>Sorry no listing where found matching your search</p>
75
-        </div>
76
-      </div>
77
-      <br />
78
-    </div>
79
-
80
-    <div class="container col-md-12" v-if="propertySearch.propertyUsageType === 'Commercial'">
81
-      <div class="col-sm-12">
82
-        <div class="about-img-box">
83
-          <img
84
-            src="img/Johannesburg-south-africa-1.jpg"
85
-            alt="Property Listing"
86
-            class="img-fluid"
87
-            style="width:800px;height:400px; border-radius:10px"
88
-          />
89
-        </div>
90
-        <div class="sinse-box" style="opacity:0.7; border: white solid 3px; border-radius: 15px">
91
-          <h3 class="sinse-title">Property Listing</h3>
92
-        </div>
93
-      </div>
94
-      <br />
95
-      <div class="row">
96
-        <div class="col-md-12">
97
-          <h1 class="my-4">About Commercial Properties</h1>
98
-        </div>
99
-        <div class="container col-md-6 text-left">
100
-          <p>
101
-            Commercial properties are characteristically any larger properties that
102
-            generate profit through leasing or rental activities. These properties
103
-            are typically used to conduct business and provide companies with cut-and-dry
104
-            leasing agreements, which keep their involvement in the maintenance of the property
105
-            to a minimum, so they may focus on the growth of their company.
118
+            Commercial properties are characteristically any larger properties that generate profit
119
+            through leasing or rental activities. These properties are typically used to conduct
120
+            business and provide companies with cut-and-dry leasing agreements, which keep their
121
+            involvement in the maintenance of the property to a minimum, so they may focus on the
122
+            growth of their company.
106 123
           </p>
107 124
           <p>
108 125
             Uni-Vate Properties seeks out professional, clean spaces for such companies and acts as
109 126
             mediator between the landlord/-lady and the tenants. Uni-Vate Properties provides
110
-            value-adding service to clients and conducts business with a high standard
111
-            and integrity.
127
+            value-adding service to clients and conducts business with a high standard and
128
+            integrity.
112 129
           </p>
113 130
           <p>
114 131
             Wish to RENT your property?
@@ -122,6 +139,14 @@
122 139
         <div class="col-md-4">
123 140
           <p>
124 141
             <img
142
+              v-if="propertyUsageType === 'Residential'"
143
+              class="img-fluid"
144
+              src="./../../../public/img/residential.jpg"
145
+              alt="About Resale"
146
+              style="border-radius:10px"
147
+            />
148
+            <img
149
+              v-else
125 150
               class="img-fluid"
126 151
               src="./../../../public/img/commercial.jpg"
127 152
               alt="About Resale"
@@ -130,102 +155,57 @@
130 155
           </p>
131 156
         </div>
132 157
       </div>
133
-      <div>
134
-        <propertyCard v-if="properties.length > 0" name="propertyholder" :properties="properties" />
135
-        <div v-if="properties.length === 0">
136
-          <img src="../../../public/img/no-homes.png" />
137
-          <br />
138
-          <br />
139
-          <p>Sorry no listing where found matching your search</p>
140
-        </div>
141
-      </div>
142 158
       <br />
143 159
     </div>
144 160
   </div>
145 161
 </template>
146 162
 <script>
147 163
 import { mapState, mapActions } from 'vuex';
148
-import propertyCard from './propertyCard.vue';
164
+import autoComplete from '../shared/autoComplete.vue';
149 165
 
150 166
 export default {
151 167
   name: 'propertysearch',
152 168
   components: {
153
-    propertyCard,
169
+    autoComplete,
154 170
   },
155 171
   data() {
156 172
     return {
157
-      propertySearch: {
158
-        userName: '',
159
-        salesType: 'All',
160
-        propertyUsageType: 'All',
161
-        propertyType: 'All',
162
-        province: 'All',
163
-        city: 'All',
164
-        suburb: 'All',
165
-        minPrice: 0,
166
-        maxPrice: 0,
167
-      },
173
+      propertyUsageType: 'Residential',
174
+      salesType: 'Sale',
175
+      searchText: '',
168 176
     };
169 177
   },
170
-  mounted() {
171
-    if (typeof this.propertySearch.propertyUsageType === 'undefined') {
172
-      this.propertySearch.propertyUsageType = 'Residential';
173
-    }
178
+  computed: {
179
+    ...mapState('propertySearch', ['suburbs', 'suburbList', 'propertySearch']),
174 180
   },
175 181
   methods: {
176
-    ...mapActions('propertySearch', ['searchProperties']),
182
+    ...mapActions('propertySearch', ['getSuburbs', 'applyFilter']),
177 183
     SetType(item) {
178
-      this.propertySearch.propertyUsageType = item;
184
+      this.propertyUsageType = item;
179 185
     },
180
-  },
181
-  computed: {
182
-    ...mapState('propertySearch', ['properties']),
183
-    ...mapState('authentication', ['username']),
184
-    ParamsChanged() {
185
-      if (Object.keys(this.$route.query).length === 0) {
186
-        if (this.propertySearch.propertyUsageType === 'All') {
187
-          // eslint-disable-next-line vue/no-side-effects-in-computed-properties
188
-          this.propertySearch.propertyUsageType = 'Residential';
189
-          // eslint-disable-next-line vue/no-side-effects-in-computed-properties
190
-          this.propertySearch.keyword = 'All';
191
-          // eslint-disable-next-line vue/no-side-effects-in-computed-properties
192
-          this.propertySearch.salesType = 'All';
193
-        }
194
-      }
195
-      if (Object.keys(this.$route.query).length > 0) {
196
-        if (Object.keys(this.$route.query).length > 2) {
197
-          // eslint-disable-next-line vue/no-side-effects-in-computed-properties
198
-          this.propertySearch = this.$route.query;
199
-        } else {
200
-          if (this.$route.query.salesType) {
201
-            // eslint-disable-next-line vue/no-side-effects-in-computed-properties
202
-            this.propertySearch.salesType = this.$route.query.salesType;
203
-          }
204
-          if (this.$route.query.propertyUsageType) {
205
-            // eslint-disable-next-line vue/no-side-effects-in-computed-properties
206
-            this.propertySearch.propertyUsageType = this.$route.query.propertyUsageType;
207
-          }
208
-        }
209
-      }
210
-      if (this.propertySearch.keyword === '') {
211
-        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
212
-        this.propertySearch.keyword = 'All';
213
-      }
214
-      if (this.username) {
215
-        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
216
-        this.propertySearch.userName = this.username;
217
-      } else {
218
-        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
219
-        this.propertySearch.userName = 'Unknown';
220
-      }
221
-      this.searchProperties(this.propertySearch);
222
-      return null;
186
+    SearchClick() {
187
+      const item = this.suburbs.find(s => s.display === this.searchText);
188
+      console.log(JSON.stringify(item));
189
+      this.propertySearch.province = item.province;
190
+      this.propertySearch.city = item.city;
191
+      this.propertySearch.suburb = item.suburb;
192
+      this.propertySearch.propertyUsageType = this.propertyUsageType;
193
+      this.propertySearch.salesType = this.salesType;
194
+
195
+      this.$router.push('/property/propertySearch/results');
223 196
     },
224
-  },
225
-  watch: {
226
-    ParamsChanged() {
227
-      return null;
197
+    Filter() {
198
+      this.applyFilter(this.searchText);
228 199
     },
200
+    SetSalesType(value) {
201
+      this.salesType = value;
202
+    },
203
+    SelectedFilter(item) {
204
+      this.searchText = item;
205
+    },
206
+  },
207
+  mounted() {
208
+    this.getSuburbs();
229 209
   },
230 210
 };
231 211
 </script>

+ 83
- 0
src/components/property/propertySearchResults.vue Voir le fichier

@@ -0,0 +1,83 @@
1
+<template>
2
+  <!-- eslint-disable max-len -->
3
+  <div class="container">
4
+    <br />
5
+    <div class="container col-md-12">
6
+      <div class="col-sm-12">
7
+        <div class="about-img-box">
8
+          <img
9
+            v-if="propertySearch.propertyUsageType === 'Residential'"
10
+            src="img/Pretoria-South-Africa.jpg"
11
+            alt="Property Listing"
12
+            class="img-fluid"
13
+            style="width:800px;height:400px; border-radius:10px"
14
+          />
15
+          <img
16
+            v-else
17
+            src="img/Johannesburg-south-africa-1.jpg"
18
+            alt="Property Listing"
19
+            class="img-fluid"
20
+            style="width:800px;height:400px; border-radius:10px"
21
+          />
22
+        </div>
23
+        <div class="sinse-box" style="opacity:0.7; border: white solid 3px; border-radius: 15px">
24
+          <h3 class="sinse-title">Property Listing</h3>
25
+        </div>
26
+      </div>
27
+      <br />
28
+      <div>
29
+        <propertyCard v-if="properties.length > 0" name="propertyholder" :properties="properties" />
30
+        <div v-if="properties.length === 0">
31
+          <img src="../../../public/img/no-homes.png" />
32
+          <br />
33
+          <br />
34
+          <p>Sorry no listing where found matching your search</p>
35
+        </div>
36
+      </div>
37
+      <br />
38
+    </div>
39
+  </div>
40
+</template>
41
+<script>
42
+import { mapState, mapActions } from 'vuex';
43
+import propertyCard from './propertyCard.vue';
44
+
45
+export default {
46
+  name: 'propertysearch',
47
+  components: {
48
+    propertyCard,
49
+  },
50
+  data() {
51
+    return {};
52
+  },
53
+  mounted() {
54
+    if (typeof this.propertySearch.propertyUsageType === 'undefined') {
55
+      this.propertySearch.propertyUsageType = 'Residential';
56
+    }
57
+    this.searchProperties(this.propertySearch);
58
+  },
59
+  methods: {
60
+    ...mapActions('propertySearch', ['searchProperties']),
61
+    SetType(item) {
62
+      this.propertySearch.propertyUsageType = item;
63
+    },
64
+  },
65
+  computed: {
66
+    ...mapState('propertySearch', ['properties', 'propertySearch']),
67
+    ...mapState('authentication', ['username']),
68
+    ParamsChanged() {
69
+      if (typeof this.propertySearch.propertyUsageType === 'undefined') {
70
+        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
71
+        this.propertySearch.propertyUsageType = 'Residential';
72
+      }
73
+      this.searchProperties(this.propertySearch);
74
+      return null;
75
+    },
76
+  },
77
+  watch: {
78
+    ParamsChanged() {
79
+      return null;
80
+    },
81
+  },
82
+};
83
+</script>

+ 168
- 199
src/components/property/propertyeditPage.vue Voir le fichier

@@ -35,13 +35,15 @@
35 35
       <div class="row mb-3">
36 36
         <div class="container col-md-10" style="text-align:left">
37 37
           <form id="mainForm">
38
+            <div class="form-goup row">
39
+              <div class="col-md-4">
40
+                <field :type="'datetime'" :mayEdit="true" />
41
+              </div>
42
+            </div>
38 43
             <div class="form-goup row">
39 44
               <div class="col-md-4">
40 45
                 <label>Usage Type</label>
41 46
                 <div class="input-group-prepend">
42
-                  <!-- <span class="input-group-text" style="color: #60CBEB">
43
-                    <b>U</b>
44
-                  </span>-->
45 47
                   <input
46 48
                     class="form-control"
47 49
                     type="text"
@@ -53,6 +55,7 @@
53 55
                 </div>
54 56
               </div>
55 57
             </div>
58
+            <br />
56 59
             <div class="form-group row">
57 60
               <div class="col-md-4">
58 61
                 <label>Sale Type</label>
@@ -64,106 +67,113 @@
64 67
                   :mayEdit="mayEdit"
65 68
                   @UpdateValue="UpdateValue"
66 69
                 />
70
+                <div v-if="showPropertyTypeError">
71
+                  <p class="alert myError">Please select a Sales Type</p>
72
+                </div>
67 73
               </div>
68 74
             </div>
69 75
             <div class="form-group row">
70 76
               <div class="col-md-4">
71
-                <label>Property Type</label>
72
-                <propField
73
-                  :display="property.propertyType ? property.propertyType.description : ''"
74
-                  :editType="'selector'"
75
-                  :arrayObject="propertyTypes"
76
-                  :propertyName="'propertyType'"
77
+                <label>Property Type *</label>
78
+                <field
79
+                  :type="'select'"
80
+                  :selectOptions="propertyTypes"
81
+                  :selectValue="'id'"
82
+                  :selectText="'description'"
83
+                  v-model="property.propertyType"
77 84
                   :mayEdit="mayEdit"
78
-                  @UpdateValue="UpdateValue"
79 85
                 />
80 86
               </div>
81 87
               <div v-if="propertyType === 'Commercial'" class="col-md-4">
82 88
                 <label>Property Name</label>
83
-                <propField
84
-                  :display="property.propertyName"
85
-                  :editType="'text'"
86
-                  :propertyName="'propertyName'"
87
-                  :mayEdit="mayEdit"
88
-                  @UpdateValue="UpdateValue"
89
-                />
89
+                <field :type="'text'" v-model="property.propertyName" :mayEdit="mayEdit" />
90 90
               </div>
91 91
               <div v-if="propertyType === 'Commercial'" class="col-md-4">
92 92
                 <label>Unit</label>
93
-                <propField
94
-                  :label="'Property Type'"
95
-                  :display="property.unit"
96
-                  :editType="'text'"
97
-                  :propertyName="'unit'"
98
-                  :mayEdit="mayEdit"
99
-                  @UpdateValue="UpdateValue"
100
-                />
93
+                <field :type="'text'" v-model="property.unit" :mayEdit="mayEdit" />
101 94
               </div>
102 95
             </div>
103 96
             <div class="form-group row">
104 97
               <div class="col-md-6" style="margin-bottom: 1em">
105 98
                 <label>Street Number</label>
106
-                <propField
107
-                  :display="property.addressLine1"
108
-                  :editType="'text'"
109
-                  :propertyName="'addressLine1'"
110
-                  :mayEdit="mayEdit"
111
-                  @UpdateValue="UpdateValue"
112
-                />
99
+                <field :type="'text'" v-model="property.addressLine1" :mayEdit="mayEdit" />
113 100
               </div>
114 101
               <div class="col-md-6" style="margin-bottom: 1em">
115 102
                 <label>Street Name</label>
116
-                <propField
117
-                  :display="property.addressLine2"
118
-                  :editType="'text'"
119
-                  :propertyName="'addressLine2'"
120
-                  :mayEdit="mayEdit"
121
-                  @UpdateValue="UpdateValue"
122
-                />
103
+                <field :type="'text'" v-model="property.addressLine2" :mayEdit="mayEdit" />
123 104
               </div>
124 105
               <div class="col-md-6" style="margin-bottom: 1em">
125
-                <label>Province</label>
126
-                <propField
106
+                <label>Province *</label>
107
+                <!-- <propField
127 108
                   :display="property.province ? property.province.description : ''"
128 109
                   :editType="'selector'"
129 110
                   :arrayObject="provinces"
130 111
                   :propertyName="'province'"
131 112
                   :mayEdit="mayEdit"
132 113
                   @UpdateValue="UpdateValue"
114
+                />-->
115
+                <field
116
+                  :type="'select'"
117
+                  :selectOptions="provinces"
118
+                  :selectValue="'id'"
119
+                  :selectText="'description'"
120
+                  v-model="property.province"
121
+                  :mayEdit="mayEdit"
122
+                  @change="provChanged"
133 123
                 />
124
+                <div v-if="showProvinceError">
125
+                  <p class="alert myError">Please select a Province</p>
126
+                </div>
134 127
               </div>
135
-
136 128
               <div class="col-md-6" style="margin-bottom: 1em">
137
-                <label>City</label>
138
-                <propField
129
+                <label>City *</label>
130
+                <!-- <propField
139 131
                   :display="property.city ? property.city.description : ''"
140 132
                   :editType="'selector'"
141 133
                   :arrayObject="cities"
142 134
                   :propertyName="'city'"
143 135
                   :mayEdit="mayEdit"
144 136
                   @UpdateValue="UpdateValue"
137
+                />-->
138
+                <field
139
+                  :type="'select'"
140
+                  :selectOptions="cities"
141
+                  :selectValue="'id'"
142
+                  :selectText="'description'"
143
+                  v-model="property.city"
144
+                  :mayEdit="mayEdit"
145
+                  @change="cityChanged"
145 146
                 />
147
+                <div v-if="showCityError">
148
+                  <p class="alert myError">Please select a City</p>
149
+                </div>
146 150
               </div>
147 151
               <div class="col-md-6" style="margin-bottom: 1em">
148
-                <label>Suburb</label>
149
-                <propField
152
+                <label>Suburb *</label>
153
+                <!-- <propField
150 154
                   :display="property.suburb ? property.suburb.description : ''"
151 155
                   :editType="'selector'"
152 156
                   :arrayObject="suburbs"
153 157
                   :propertyName="'suburb'"
154 158
                   :mayEdit="mayEdit"
155 159
                   @UpdateValue="UpdateValue"
160
+                />-->
161
+                <field
162
+                  :type="'select'"
163
+                  :selectOptions="suburbs"
164
+                  :selectValue="'id'"
165
+                  :selectText="'description'"
166
+                  v-model="property.suburb"
167
+                  :mayEdit="mayEdit"
168
+                  @change="suburbChanged"
156 169
                 />
170
+                <div v-if="showSuburbError">
171
+                  <p class="alert myError">Please select a suburb</p>
172
+                </div>
157 173
               </div>
158 174
               <div class="col-md-6" style="margin-bottom: 1em">
159 175
                 <label>Postal Code</label>
160
-                <propField
161
-                  :display="property.addressLine3"
162
-                  :editType="'text'"
163
-                  :propertyName="'addressLine3'"
164
-                  :mayEdit="mayEdit"
165
-                  @UpdateValue="UpdateValue"
166
-                />
176
+                <field :type="'text'" v-model="property.addressLine3" :mayEdit="mayEdit" />
167 177
               </div>
168 178
             </div>
169 179
 
@@ -171,23 +181,30 @@
171 181
               <div class="col-md-6">
172 182
                 <label v-if="salesType === 'Rental'">Rental Price</label>
173 183
                 <label v-if="salesType !== 'Rental'">Sales Price</label>
174
-                <propField
184
+                <field :type="'number'" v-model="property.price" :mayEdit="mayEdit" />
185
+                <!-- <propField
175 186
                   :display="String(property.price)"
176 187
                   :editType="'number'"
177 188
                   :propertyName="'price'"
178 189
                   :mayEdit="mayEdit"
179 190
                   @UpdateValue="UpdateValue"
180
-                />
191
+                />-->
181 192
               </div>
182 193
               <div class="col-md-6" v-if="salesType === 'Rental'">
183 194
                 <label>Per</label>
184
-                <propField
195
+                <!-- <propField
185 196
                   :display="property.pricePer"
186 197
                   :editType="'selector'"
187 198
                   :propertyName="'pricePer'"
188 199
                   :arrayObject="pricePerArr"
189 200
                   :mayEdit="mayEdit"
190 201
                   @UpdateValue="UpdateValue"
202
+                />-->
203
+                <field
204
+                  :type="'selectlist'"
205
+                  :selectOptions="pricePerArr"
206
+                  v-model="property.pricePer"
207
+                  :mayEdit="mayEdit"
191 208
                 />
192 209
               </div>
193 210
             </div>
@@ -196,13 +213,16 @@
196 213
                 <label for="Property Description">Description</label>
197 214
                 <vue-editor v-model="property.description" :editor-toolbar="customToolbar" />
198 215
                 <br />
199
-                <p>* A listing fee of R380 including VAT is payable to list your Property on the Uni-Vate website</p>
216
+                <p>
217
+                  * A listing fee of R380 including VAT is payable to list your Property on the
218
+                  Uni-Vate website
219
+                </p>
200 220
               </div>
201 221
             </div>
202 222
             <div class="form-group row" />
203 223
             <div
204 224
               class="col-md-6"
205
-              v-if="propertyType === 'Residential' & propertyOverviewFields.length > 0"
225
+              v-if="(propertyType === 'Residential') & (propertyOverviewFields.length > 0)"
206 226
             >
207 227
               <div v-for="(item, i) in propertyOverviewFields[0].fields" :key="item.id">
208 228
                 <label>{{ item.name }}</label>
@@ -243,6 +263,7 @@
243 263
                   @UpdateValue="UpdateValue"
244 264
                 />
245 265
               </div>
266
+              <br />
246 267
             </div>
247 268
             <div class="form-group row" />
248 269
             <div class="row">
@@ -255,25 +276,13 @@
255 276
             <div class="form-group row">
256 277
               <div class="col-md-12">
257 278
                 <label>Virtual Tour (URL)</label>
258
-                <propField
259
-                  :display="property.virtualTour"
260
-                  :editType="'text'"
261
-                  :propertyName="'virtualTour'"
262
-                  :mayEdit="mayEdit"
263
-                  @UpdateValue="UpdateValue"
264
-                />
279
+                <field :type="'text'" v-model="property.virtualTour" :mayEdit="mayEdit" />
265 280
               </div>
266 281
             </div>
267 282
             <div class="form-group row">
268 283
               <div class="col-md-12">
269 284
                 <label>Video (URL)</label>
270
-                <propField
271
-                  :display="property.video"
272
-                  :editType="'text'"
273
-                  :propertyName="'video'"
274
-                  :mayEdit="mayEdit"
275
-                  @UpdateValue="UpdateValue"
276
-                />
285
+                <field :type="'text'" v-model="property.video" :mayEdit="mayEdit" />
277 286
               </div>
278 287
             </div>
279 288
             <div class="form-group row">
@@ -300,9 +309,10 @@
300 309
                 />
301 310
                 <span
302 311
                   v-if="!img.isDeleted && mayEdit"
303
-                  class="input-group-text"
312
+                  class="input-group-text spanCursor"
304 313
                   align="center"
305 314
                   @click="DeleteImage(img)"
315
+                  style="width:150px"
306 316
                 >
307 317
                   <eva-icon name="trash-2-outline" fill="#60CBEB"></eva-icon>Delete
308 318
                 </span>
@@ -321,13 +331,27 @@
321 331
               class="btn btn-b-n"
322 332
               style="width: 85px; height:40px;"
323 333
               :disabled="!mayEdit"
324
-            >Save</button>
334
+            >
335
+              Save
336
+            </button>
325 337
             <button
326 338
               type="button"
327 339
               @click="Close()"
328 340
               class="btn btn-b-n"
329 341
               style="width: 85px; height:40px;"
330
-            >Close</button>
342
+              :disabled="
343
+                showPropertyTypeError || showProvinceError || showCityError || showSuburbError
344
+              "
345
+            >
346
+              Close
347
+            </button>
348
+            <div
349
+              v-if="showPropertyTypeError || showProvinceError || showCityError || showSuburbError"
350
+            >
351
+              <p class="alert myError">
352
+                Missing fields. Please fill in all required fields. Marked with *
353
+              </p>
354
+            </div>
331 355
             <div v-if="wait" id="preloader"></div>
332 356
           </form>
333 357
         </div>
@@ -339,9 +363,9 @@
339 363
 <script>
340 364
 import { mapState, mapActions } from 'vuex';
341 365
 import { VueEditor } from 'vue2-editor';
342
-import { setTimeout } from 'timers';
343 366
 import ImageLoad from './propertyImage.vue';
344 367
 import propField from './propertyFieldEditor.vue';
368
+import field from '../shared/fieldEditor.vue';
345 369
 
346 370
 export default {
347 371
   name: 'PropertyEdit',
@@ -349,6 +373,7 @@ export default {
349 373
     ImageLoad,
350 374
     VueEditor,
351 375
     propField,
376
+    field,
352 377
   },
353 378
   data() {
354 379
     return {
@@ -361,12 +386,7 @@ export default {
361 386
       customToolbar: [
362 387
         [{ header: [false, 1, 2, 3, 4, 5, 6] }],
363 388
         ['bold', 'italic', 'underline', 'strike'],
364
-        [
365
-          { align: '' },
366
-          { align: 'center' },
367
-          { align: 'right' },
368
-          { align: 'justify' },
369
-        ],
389
+        [{ align: '' }, { align: 'center' }, { align: 'right' }, { align: 'justify' }],
370 390
         [{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
371 391
         [{ script: 'sub' }, { script: 'super' }],
372 392
         [{ indent: '-1' }, { indent: '+1' }],
@@ -375,10 +395,12 @@ export default {
375 395
         { id: 1, description: 'Sale' },
376 396
         { id: 2, description: 'Rental' },
377 397
       ],
378
-      pricePerArr: [
379
-        { id: 'Month', description: 'Month' },
380
-        { id: 'Day', description: 'Day' },
381
-      ],
398
+      pricePerArr: ['Month', 'Day'],
399
+      yesno: ['Yes', 'No'],
400
+      showPropertyTypeError: false,
401
+      showProvinceError: false,
402
+      showCityError: false,
403
+      showSuburbError: false,
382 404
     };
383 405
   },
384 406
   methods: {
@@ -389,19 +411,45 @@ export default {
389 411
       'getListsForPropertyEdit',
390 412
     ]),
391 413
     ...mapActions('property', ['getPropertyTypes']),
392
-    ...mapActions('propertyEdit', [
393
-      'getSavedPropertyData',
394
-      'updateProperty',
395
-      'mayEditProperty',
396
-    ]),
414
+    ...mapActions('propertyEdit', ['getSavedPropertyData', 'updateProperty', 'mayEditProperty']),
415
+    provChanged(item) {
416
+      this.getCities(Object.assign({}, { province: item.description }));
417
+      this.property.city = null;
418
+      this.property.suburb = null;
419
+      this.property.addressLine3 = '';
420
+      this.showProvinceError = false;
421
+      this.showCityError = true;
422
+      this.showSuburbError = true;
423
+    },
424
+    cityChanged(item) {
425
+      this.getSuburbs(
426
+        Object.assign(
427
+          {},
428
+          {
429
+            province: this.property.province.description,
430
+            city: item.description,
431
+          },
432
+        ),
433
+      );
434
+      this.property.suburb = null;
435
+      this.property.addressLine3 = '';
436
+      this.showCityError = false;
437
+      this.showSuburbError = true;
438
+    },
439
+    suburbChanged(item) {
440
+      const newSuburb = this.suburbs.find(p => p.description === item.description);
441
+      this.property.addressLine3 = newSuburb.postalCode;
442
+      this.showSuburbError = false;
443
+    },
444
+    salesTypeChanged(item) {
445
+      console.log(item);
446
+    },
397 447
     UpdateValue(item) {
398 448
       if (item.isUDF) {
399 449
         if (item.isPropOverview) {
400
-          this.propertyOverviewFields[0].fields[item.arrayIndex].value =            item.value;
450
+          this.propertyOverviewFields[0].fields[item.arrayIndex].value = item.value;
401 451
         } else if (item.isPropOverview === false) {
402
-          this.propertyFields[item.arrayIndex].fields[
403
-            item.arrayItemIndex
404
-          ].value = item.value;
452
+          this.propertyFields[item.arrayIndex].fields[item.arrayItemIndex].value = item.value;
405 453
         }
406 454
       } else if (!item.isUDF) {
407 455
         if (item.fieldName) {
@@ -412,96 +460,6 @@ export default {
412 460
             } else isSaleValue = false;
413 461
             this.property.isSale = isSaleValue;
414 462
           }
415
-          if (item.fieldName === 'propertyType') {
416
-            this.property.propertyType = this.propertyTypes.find(
417
-              pt => pt.description === item.value,
418
-            );
419
-            this.property.propertyTypeId = this.property.propertyType.id;
420
-          }
421
-          if (item.fieldName === 'propertyName') {
422
-            this.property.propertyName = item.value;
423
-          }
424
-          if (item.fieldName === 'unit') {
425
-            this.property.unit = item.value;
426
-          }
427
-          if (item.fieldName === 'addressLine1') {
428
-            this.property.addressLine1 = item.value;
429
-          }
430
-          if (item.fieldName === 'addressLine2') {
431
-            this.property.addressLine2 = item.value;
432
-          }
433
-          if (item.fieldName === 'addressLine3') {
434
-            this.property.addressLine3 = item.value;
435
-          }
436
-          if (item.fieldName === 'province') {
437
-            if (item.value !== '') {
438
-              this.property.province = this.provinces.find(
439
-                p => p.description === item.value,
440
-              );
441
-              this.property.provinceId = this.property.province.id;
442
-              this.getCities(Object.assign({}, { province: item.value }));
443
-              this.property.city = null;
444
-              this.property.suburb = null;
445
-              this.property.addressLine3 = '';
446
-            } else {
447
-              this.property.province = null;
448
-              this.property.city = null;
449
-              this.property.suburb = null;
450
-              this.property.addressLine3 = '';
451
-              this.cities = [];
452
-              this.suburbs = [];
453
-            }
454
-          }
455
-          if (item.fieldName === 'city') {
456
-            if (item.value !== '') {
457
-              const newCity = this.cities.find(
458
-                p => p.description === item.value,
459
-              );
460
-              this.property.city = newCity;
461
-              this.property.cityId = newCity.id;
462
-              this.getSuburbs(
463
-                Object.assign(
464
-                  {},
465
-                  {
466
-                    province: this.property.province.description,
467
-                    city: item.value,
468
-                  },
469
-                ),
470
-              );
471
-              this.property.suburb = null;
472
-              this.property.addressLine3 = '';
473
-            } else {
474
-              this.property.city = null;
475
-              this.property.suburb = null;
476
-              this.property.addressLine3 = '';
477
-              this.suburbs = [];
478
-            }
479
-          }
480
-          if (item.fieldName === 'suburb') {
481
-            if (item.value !== '') {
482
-              const newSuburb = this.suburbs.find(
483
-                p => p.description === item.value,
484
-              );
485
-              this.property.suburb = newSuburb;
486
-              this.property.suburbId = newSuburb.id;
487
-              this.property.addressLine3 = this.property.suburb.postalCode;
488
-            } else {
489
-              this.property.suburb = null;
490
-              this.property.addressLine3 = '';
491
-            }
492
-          }
493
-          if (item.fieldName === 'price') {
494
-            this.property.price = item.value;
495
-          }
496
-          if (item.fieldName === 'pricePer') {
497
-            this.property.pricePer = item.value;
498
-          }
499
-          if (item.fieldName === 'virtualTour') {
500
-            this.property.virtualTour = item.value;
501
-          }
502
-          if (item.fieldName === 'video') {
503
-            this.property.video = item.value;
504
-          }
505 463
         }
506 464
       }
507 465
     },
@@ -509,8 +467,28 @@ export default {
509 467
       this.propertyType = this.property.propertyUsageType;
510 468
     },
511 469
     SubmitData() {
512
-      this.wait = true;
470
+      if (this.property.propertyTypeId === 0) {
471
+        this.showPropertyTypeError = true;
472
+      }
473
+      if (this.property.province === null) {
474
+        this.showProvinceError = true;
475
+      }
476
+      if (this.property.city === null) {
477
+        this.showCityError = true;
478
+      }
479
+      if (this.property.suburb === null) {
480
+        this.showSuburbError = true;
481
+      }
513 482
 
483
+      if (
484
+        this.showPropertyTypeError
485
+        || this.showProvinceError
486
+        || this.showCityError
487
+        || this.showSuburbError
488
+      ) {
489
+        return;
490
+      }
491
+      this.wait = true;
514 492
       this.newPropertyImages.propertyId = this.property.id;
515 493
       this.newPropertyImages.Images = [];
516 494
       // eslint-disable-next-line no-plusplus
@@ -524,7 +502,6 @@ export default {
524 502
           isDefault: setAsDefault,
525 503
         });
526 504
       }
527
-
528 505
       this.property.propertyImages = this.propertyImages;
529 506
       // eslint-disable-next-line no-plusplus
530 507
       for (let i = 0; i < this.property.propertyImages.length; i++) {
@@ -535,20 +512,12 @@ export default {
535 512
       this.updateProperty({
536 513
         property: this.property,
537 514
         images: this.newPropertyImages,
515
+      }).then(() => {
516
+        this.$router.push('/properties');
538 517
       });
539
-
540
-      // console.log(JSON.stringify(this.newPropertyImages));
541
-
542
-      // Need to change to promis.
543
-      setTimeout(
544
-        () => this.$router.push({
545
-            path: '/property/admin/list/my',
546
-          }),
547
-        5000,
548
-      );
549 518
     },
550 519
     Close() {
551
-      this.$router.push('/property/admin/list/my');
520
+      this.$router.push('/properties');
552 521
     },
553 522
     loadedImages(values) {
554 523
       this.images = values;
@@ -625,7 +594,7 @@ export default {
625 594
 </script>
626 595
 
627 596
 <style>
628
-span {
597
+.spanCursor {
629 598
   cursor: pointer;
630 599
 }
631 600
 .opacity {

+ 30
- 0
src/components/shared/alertPage.vue Voir le fichier

@@ -0,0 +1,30 @@
1
+<template>
2
+  <div>
3
+    <br />
4
+    <Alert :text="message" :type="type" />
5
+    <div class="row">
6
+      <div class="offset-3 col-md-2">
7
+        <button type="button" class="form-control btn btn-primary" @click="goBack()">Back</button>
8
+      </div>
9
+    </div>
10
+  </div>
11
+</template>
12
+
13
+<script>
14
+import { mapState } from 'vuex';
15
+import Alert from './alert.vue';
16
+
17
+export default {
18
+  components: {
19
+    Alert,
20
+  },
21
+  computed: {
22
+    ...mapState('alert', ['message', 'type']),
23
+  },
24
+  methods: {
25
+    goBack() {
26
+      this.$router.go(-1);
27
+    },
28
+  },
29
+};
30
+</script>

+ 81
- 0
src/components/shared/autoComplete.vue Voir le fichier

@@ -0,0 +1,81 @@
1
+<template>
2
+  <div class="autocomplete">
3
+    <input type="text" v-model="search" @input="onChange" class="form-control" />
4
+    <ul v-show="isOpen" class="autocomplete-results">
5
+      <li
6
+        v-for="(result, i) in results"
7
+        :key="i"
8
+        class="autocomplete-result"
9
+        @click="setResult(result)"
10
+      >
11
+        {{ result }}
12
+      </li>
13
+    </ul>
14
+  </div>
15
+</template>
16
+
17
+<script>
18
+export default {
19
+  props: {
20
+    items: {
21
+      type: Array,
22
+      required: false,
23
+      default: () => [],
24
+    },
25
+  },
26
+  data() {
27
+    return {
28
+      search: '',
29
+      results: [],
30
+      isOpen: false,
31
+    };
32
+  },
33
+  methods: {
34
+    onChange() {
35
+      if (this.search.length >= 3) {
36
+        this.isOpen = true;
37
+        this.filterResults();
38
+      } else {
39
+        this.isOpen = false;
40
+      }
41
+    },
42
+    filterResults() {
43
+      this.results = this.items.filter(
44
+        item => item.toLowerCase().indexOf(this.search.toLowerCase()) > -1,
45
+      );
46
+    },
47
+    setResult(result) {
48
+      this.search = result;
49
+      this.isOpen = false;
50
+      this.$emit('selection', this.search);
51
+    },
52
+  },
53
+};
54
+</script>
55
+
56
+<style>
57
+.autocomplete {
58
+  position: relative;
59
+  width: 130px;
60
+}
61
+
62
+.autocomplete-results {
63
+  padding: 0;
64
+  margin: 0;
65
+  border: 1px solid #eeeeee;
66
+  height: 120px;
67
+  overflow: auto;
68
+}
69
+
70
+.autocomplete-result {
71
+  list-style: none;
72
+  text-align: left;
73
+  padding: 4px 2px;
74
+  cursor: pointer;
75
+}
76
+
77
+.autocomplete-result:hover {
78
+  background-color: #60cbeb;
79
+  color: white;
80
+}
81
+</style>

+ 118
- 0
src/components/shared/fieldEditor.vue Voir le fichier

@@ -0,0 +1,118 @@
1
+<template>
2
+  <div>
3
+    <div v-if="!edit" class="input-group-prepend">
4
+      <input class="form-control" v-model="myDisplay" disabled />
5
+      <span
6
+        v-if="mayEdit"
7
+        @click="EditClick()"
8
+        class="input-group-text spanCursor"
9
+        style="color: #60CBEB"
10
+      >
11
+        <eva-icon name="edit-outline" fill="#60CBEB"></eva-icon>
12
+      </span>
13
+    </div>
14
+    <div v-if="edit" class="input-group-prepend">
15
+      <input v-if="type === 'text'" class="form-control" v-model="value" />
16
+      <input v-if="type === 'number'" type="number" class="form-control" v-model="value" />
17
+      <select v-if="type === 'select'" class="form-control" @change="selectionClick">
18
+        <option>Please select</option>
19
+        <option
20
+          v-for="option in selectOptions"
21
+          :value="option[selectValue]"
22
+          :key="option[selectValue]"
23
+          >{{ option[selectText] }}</option
24
+        >
25
+      </select>
26
+      <select v-if="type === 'selectlist'" class="form-control" v-model="value">
27
+        <option v-for="item in selectOptions" :value="item" :key="item">{{ item }}</option>
28
+      </select>
29
+      <!-- <Datetime class="form-control" v-if="type === 'datetime'" v-model="value" /> -->
30
+      <span
31
+        v-if="edit"
32
+        @click="UpdateValue()"
33
+        class="input-group-text spanCursor"
34
+        style="color: #60CBEB"
35
+      >
36
+        <eva-icon name="checkmark-outline" fill="#60CBEB"></eva-icon>
37
+      </span>
38
+      <span v-if="edit" @click="Close()" class="input-group-text spanCursor" style="color: #60CBEB">
39
+        <eva-icon name="close-outline" fill="#60CBEB"></eva-icon>
40
+      </span>
41
+    </div>
42
+  </div>
43
+</template>
44
+
45
+<script>
46
+export default {
47
+  props: ['value', 'type', 'mayEdit', 'selectOptions', 'selectValue', 'selectText', 'display'],
48
+  data() {
49
+    return {
50
+      edit: false,
51
+      myDisplay: '',
52
+    };
53
+  },
54
+  methods: {
55
+    UpdateValue() {
56
+      this.edit = false;
57
+      if (this.type !== 'select' && this.value) {
58
+        this.$emit('input', this.value);
59
+        this.$emit('change', this.value);
60
+      }
61
+    },
62
+    EditClick() {
63
+      this.edit = true;
64
+    },
65
+    Close() {
66
+      this.edit = false;
67
+    },
68
+    selectionClick(item) {
69
+      if (item.target.options.selectedIndex > 0) {
70
+        if (!this.value) {
71
+          this.myDisplay = this.selectOptions[item.target.options.selectedIndex - 1][
72
+            this.selectText
73
+          ];
74
+        }
75
+        if (this.selectOptions[item.target.options.selectedIndex - 1]) {
76
+          this.$emit('input', this.selectOptions[item.target.options.selectedIndex - 1]);
77
+          this.$emit('change', this.selectOptions[item.target.options.selectedIndex - 1]);
78
+        }
79
+      }
80
+    },
81
+  },
82
+  mounted() {
83
+    if (this.value) {
84
+      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
85
+      this.myDisplay = this.value;
86
+    }
87
+    if (this.value && this.selectText) {
88
+      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
89
+      this.myDisplay = this.value[this.selectText];
90
+    }
91
+    if (this.display) {
92
+      this.myDisplay = this.display;
93
+    }
94
+  },
95
+  computed: {
96
+    // eslint-disable-next-line vue/return-in-computed-property
97
+    checkValue() {
98
+      if (this.value) {
99
+        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
100
+        this.myDisplay = this.value;
101
+      }
102
+      if (this.value && this.selectText) {
103
+        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
104
+        this.myDisplay = this.value[this.selectText];
105
+      }
106
+      if (this.display) {
107
+        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
108
+        this.myDisplay = this.display;
109
+      }
110
+    },
111
+  },
112
+  watch: {
113
+    checkValue() {
114
+      return null;
115
+    },
116
+  },
117
+};
118
+</script>

+ 338
- 0
src/components/shared/gallerySlideShow.vue Voir le fichier

@@ -0,0 +1,338 @@
1
+// Source: https://github.com/KitchenStories/vue-gallery-slideshow
2
+<template>
3
+  <transition name="modal">
4
+    <div v-if="imgIndex !== null" class="vgs" @click="close">
5
+      <button type="button" class="vgs__close" @click="close">
6
+        &times;
7
+      </button>
8
+      <button v-if="isMultiple" type="button" class="vgs__prev" @click.stop="onPrev">
9
+        &lsaquo;
10
+      </button>
11
+      <div v-if="images" class="vgs__container" @click.stop="onNext">
12
+        <img class="vgs__container__img" :src="imageUrl" :alt="alt" @click.stop="onNext" />
13
+        <slot></slot>
14
+      </div>
15
+      <button v-if="isMultiple" type="button" class="vgs__next" @click.stop="onNext">
16
+        &rsaquo;
17
+      </button>
18
+      <div v-if="isMultiple" ref="gallery" class="vgs__gallery">
19
+        <div v-if="images" class="vgs__gallery__title">
20
+          {{ imgIndex + 1 }} / {{ images.length }}
21
+        </div>
22
+        <div
23
+          v-if="images"
24
+          class="vgs__gallery__container"
25
+          :style="{ transform: 'translate(' + galleryXPos + 'px, 0)' }"
26
+        >
27
+          <img
28
+            v-for="(img, i) in images"
29
+            :key="i"
30
+            class="vgs__gallery__container__img"
31
+            :src="typeof img === 'string' ? img : img.url"
32
+            :class="{ 'vgs__gallery__container__img--active': i === imgIndex }"
33
+            :alt="typeof img === 'string' ? '' : img.alt"
34
+            @click.stop="onClickThumb(img, i)"
35
+          />
36
+        </div>
37
+      </div>
38
+    </div>
39
+  </transition>
40
+</template>
41
+
42
+<script>
43
+export default {
44
+  props: {
45
+    images: {
46
+      type: Array,
47
+      required: true,
48
+    },
49
+    index: {
50
+      type: Number,
51
+      required: false,
52
+      default: null,
53
+    },
54
+  },
55
+  data() {
56
+    return {
57
+      imgIndex: this.index,
58
+      image: null,
59
+      galleryXPos: 0,
60
+      thumbnailWidth: 120,
61
+    };
62
+  },
63
+  computed: {
64
+    imageUrl() {
65
+      const img = this.images[this.imgIndex];
66
+      if (typeof img === 'string') {
67
+        return img;
68
+      }
69
+      return img.url;
70
+    },
71
+    alt() {
72
+      const img = this.images[this.imgIndex];
73
+      if (typeof img === 'object') {
74
+        return img.alt;
75
+      }
76
+
77
+      return '';
78
+    },
79
+    isMultiple() {
80
+      return this.images.length > 1;
81
+    },
82
+  },
83
+  watch: {
84
+    index(val, prev) {
85
+      this.imgIndex = val;
86
+
87
+      // updateThumbails when popup
88
+      if (prev == null && val != null) {
89
+        this.$nextTick(() => {
90
+          this.updateThumbails();
91
+        });
92
+      }
93
+    },
94
+  },
95
+  mounted() {
96
+    window.addEventListener('keydown', (e) => {
97
+      if (e.keyCode === 37) {
98
+        this.onPrev();
99
+      } else if (e.keyCode === 39) {
100
+        this.onNext();
101
+      } else if (e.keyCode === 27) {
102
+        this.close();
103
+      }
104
+    });
105
+  },
106
+  methods: {
107
+    close() {
108
+      this.imgIndex = null;
109
+      this.$emit('close');
110
+    },
111
+    onPrev() {
112
+      if (this.imgIndex === null) return;
113
+      if (this.imgIndex > 0) {
114
+        this.imgIndex--;
115
+      } else {
116
+        this.imgIndex = this.images.length - 1;
117
+      }
118
+      this.updateThumbails();
119
+    },
120
+    onNext() {
121
+      if (this.imgIndex === null) return;
122
+      if (this.imgIndex < this.images.length - 1) {
123
+        this.imgIndex++;
124
+      } else {
125
+        this.imgIndex = 0;
126
+      }
127
+      this.updateThumbails();
128
+    },
129
+    onClickThumb(image, index) {
130
+      this.imgIndex = index;
131
+      this.updateThumbails();
132
+    },
133
+    updateThumbails() {
134
+      if (!this.$refs.gallery) {
135
+        return;
136
+      }
137
+
138
+      const galleryWidth = this.$refs.gallery.clientWidth;
139
+      const currThumbsWidth = this.imgIndex * this.thumbnailWidth;
140
+      const maxThumbsWidth = this.images.length * this.thumbnailWidth;
141
+      const centerPos = Math.floor(galleryWidth / (this.thumbnailWidth * 2)) * this.thumbnailWidth;
142
+
143
+      // Prevent scrolling of images if not needed
144
+      if (maxThumbsWidth < galleryWidth) {
145
+        return;
146
+      }
147
+
148
+      if (currThumbsWidth < centerPos) {
149
+        this.galleryXPos = 0;
150
+      } else if (
151
+        currThumbsWidth
152
+        > this.images.length * this.thumbnailWidth - galleryWidth + centerPos
153
+      ) {
154
+        this.galleryXPos = -(this.images.length * this.thumbnailWidth - galleryWidth - 20);
155
+      } else {
156
+        this.galleryXPos = -(this.imgIndex * this.thumbnailWidth) + centerPos;
157
+      }
158
+    },
159
+  },
160
+};
161
+</script>
162
+
163
+<style lang="scss">
164
+$black-alpha-80: rgba(0, 0, 0, 0.8);
165
+$black: #000;
166
+$white: #fff;
167
+$radius-medium: 8px;
168
+$radius-large: 12px;
169
+// Breakpoints
170
+$screen-xs: 480px;
171
+$screen-sm: 768px;
172
+$screen-md: 992px;
173
+$screen-lg: 1200px;
174
+// So media queries don't overlap when required, provide a maximum
175
+$screen-xs-max: ($screen-sm - 1);
176
+$screen-sm-max: ($screen-md - 1);
177
+$screen-md-max: ($screen-lg - 1);
178
+@mixin respond-to($media) {
179
+  @if $media==xs {
180
+    @media (max-width: $screen-xs-max) {
181
+      @content;
182
+    }
183
+  } @else if $media==sm {
184
+    @media (min-width: $screen-sm) and (max-width: $screen-sm-max) {
185
+      @content;
186
+    }
187
+  } @else if $media==md {
188
+    @media (min-width: $screen-md) and (max-width: $screen-md-max) {
189
+      @content;
190
+    }
191
+  } @else if $media==lg {
192
+    @media (min-width: $screen-lg) {
193
+      @content;
194
+    }
195
+  }
196
+}
197
+
198
+@mixin modal-base() {
199
+  transition: opacity 0.2s ease;
200
+  position: fixed;
201
+  z-index: 9998;
202
+}
203
+
204
+@mixin modal-mask() {
205
+  @include modal-base();
206
+  top: 0;
207
+  left: 0;
208
+  width: 100%;
209
+  min-height: 100%;
210
+  height: 100vh;
211
+  background-color: $black-alpha-80;
212
+  display: table;
213
+}
214
+
215
+.vgs {
216
+  @include modal-mask();
217
+  &__close {
218
+    color: #fff;
219
+    position: absolute;
220
+    top: 0;
221
+    right: 0;
222
+    background-color: transparent;
223
+    border: none;
224
+    font-size: 25px;
225
+    width: 50px;
226
+    height: 50px;
227
+    cursor: pointer;
228
+    z-index: 999;
229
+    &:focus {
230
+      outline: 0;
231
+    }
232
+  }
233
+  &__prev,
234
+  &__next {
235
+    position: absolute;
236
+    top: 50%;
237
+    margin-top: -25px;
238
+    width: 50px;
239
+    height: 50px;
240
+    z-index: 999;
241
+    cursor: pointer;
242
+    font-size: 40px;
243
+    color: #fff;
244
+    background-color: transparent;
245
+    border: none;
246
+    &:focus {
247
+      outline: 0;
248
+    }
249
+  }
250
+  &__prev {
251
+    left: 0;
252
+  }
253
+  &__next {
254
+    right: 0;
255
+  }
256
+  &__container {
257
+    position: absolute;
258
+    overflow: hidden;
259
+    cursor: pointer;
260
+    overflow: hidden;
261
+    max-width: 100vh;
262
+    margin: 0.5rem auto 0;
263
+    left: 0.5rem;
264
+    right: 0.5rem;
265
+    height: 60vh;
266
+    border-radius: $radius-large;
267
+    background-color: $black;
268
+    @include respond-to(xs) {
269
+      width: 100%;
270
+      max-width: 100%;
271
+      top: 50%;
272
+      margin-top: -140px;
273
+      left: 0;
274
+      right: 0;
275
+      border-radius: 0;
276
+      height: 280px;
277
+    }
278
+
279
+    &__img {
280
+      width: 100%;
281
+      height: 100%;
282
+      object-fit: contain;
283
+    }
284
+  }
285
+}
286
+
287
+.vgs__gallery {
288
+  @include respond-to(xs) {
289
+    display: none;
290
+  }
291
+  overflow-x: hidden;
292
+  overflow-y: hidden;
293
+  position: absolute;
294
+  bottom: 10px;
295
+  margin: auto;
296
+  max-width: 100vh;
297
+  white-space: nowrap;
298
+  left: 0.5rem;
299
+  right: 0.5rem;
300
+  &__title {
301
+    color: $white;
302
+    margin-bottom: 0.5rem;
303
+  }
304
+  &__container {
305
+    overflow: visible;
306
+    display: block;
307
+    height: 250px;
308
+    white-space: nowrap;
309
+    transition: all 200ms ease-in-out;
310
+    width: 100%;
311
+    &__img {
312
+      width: 250px;
313
+      height: 250px;
314
+      object-fit: cover;
315
+      display: inline-block;
316
+      float: none;
317
+      margin-right: 20px;
318
+      cursor: pointer;
319
+      opacity: 0.6;
320
+      border-radius: $radius-medium;
321
+    }
322
+    &__img--active {
323
+      width: 100px;
324
+      display: inline-block;
325
+      float: none;
326
+      opacity: 1;
327
+    }
328
+  }
329
+}
330
+
331
+.modal-enter {
332
+  opacity: 0;
333
+}
334
+
335
+.modal-leave-active {
336
+  opacity: 0;
337
+}
338
+</style>

+ 51
- 11
src/components/shared/listView.vue Voir le fichier

@@ -5,7 +5,7 @@
5 5
     <div style="height:5px"></div>
6 6
     <div class="d-flex justify-content-between table-title">
7 7
       <div class="p-2" v-if="!hideSearch">
8
-        <input v-model="searchItem" class="form-control" :placeholder="currentPage" />
8
+        <input v-model="searchItem" class="form-control" placeholder="Search...." />
9 9
       </div>
10 10
       <div class="p-2" v-if="title">
11 11
         <h2>{{title}}</h2>
@@ -14,12 +14,12 @@
14 14
         <div class="d-flex flex-row">
15 15
           <div class="p2" v-if="selectedItems.length > 0">
16 16
             <div
17
-              class="btn btn-primary myBackground btn-width"
17
+              class="btn btn-primary myBackground btn-width cursor-pointer"
18 18
               @click="onClearSelected()"
19 19
             >Clear Selected</div>
20 20
           </div>
21 21
           <div class="p2" v-if="showNew">
22
-            <div class="btn btn-primary myBackground btn-width" @click="onNew()">New</div>
22
+            <div class="btn btn-primary myBackground btn-width cursor-pointer" @click="onNew()">New</div>
23 23
           </div>
24 24
         </div>
25 25
       </div>
@@ -28,12 +28,33 @@
28 28
     <div v-if="items && items.length > 0" class="table-responsive">
29 29
       <table
30 30
         id="table"
31
-        :class="{'table table-bordered table-hover': (1 === 1), 'table-sm': compact}"
31
+        :class="{'table table-hover': (1 === 1), 'table-sm': compact, 'table-bordered': bordered, 'table-striped': striped}"
32 32
       >
33
-        <thead class="my-table table-header">
33
+        <thead>
34 34
           <tr class="dnd-moved">
35 35
             <th v-for="(column, c) in Columns" :key="c">
36
-              <div @click="sortBy(column)">{{ column }}</div>
36
+              <div
37
+                @click="sortBy(column)"
38
+                @mouseover="hover = c"
39
+                @mouseleave="hover = -1"
40
+                :class="{ active: hover === c }"
41
+              >
42
+                <div class="d-flex bd-highlight">
43
+                  <div class="p-2 w-100 bd-highlight">{{ column | toProper }}</div>
44
+                  <div class="p-2 flex-shrink-1 bd-highlight">
45
+                    <img
46
+                      src="../../../public/img/sort-up.png"
47
+                      height="8px;"
48
+                      v-if="sortKey === column && reverse"
49
+                    />
50
+                    <img
51
+                      src="../../../public/img/sort-down.png"
52
+                      height="8px;"
53
+                      v-if="sortKey === column && !reverse"
54
+                    />
55
+                  </div>
56
+                </div>
57
+              </div>
37 58
             </th>
38 59
             <th v-if="editable"></th>
39 60
             <th v-if="deleteable"></th>
@@ -60,7 +81,9 @@
60 81
         </tbody>
61 82
       </table>
62 83
       <div class="d-flex justify-content-between" v-if="showPager">
63
-        <div class="p-1">{{ currentPage + ' / ' + PageCount }}</div>
84
+        <div
85
+          class="p-1"
86
+        >{{ currentPage + ' / ' + PageCount + (!hideItemCount ? ' - (' + items.length + ' items)' : '')}}</div>
64 87
         <div class="p-1">
65 88
           <BasePagination
66 89
             :currentPage="currentPage"
@@ -136,7 +159,7 @@ export default {
136 159
     deleteable: {
137 160
       default: false,
138 161
     },
139
-    columnsCount: {
162
+    columnCount: {
140 163
       default: 6,
141 164
     },
142 165
     showPager: {
@@ -148,16 +171,29 @@ export default {
148 171
     sortKey: {
149 172
       default: 'id',
150 173
     },
174
+    hideItemCount: {
175
+      default: false,
176
+    },
177
+
178
+    currentPage: {
179
+      default: 1,
180
+    },
181
+    bordered: {
182
+      default: true,
183
+    },
184
+    striped: {
185
+      default: true,
186
+    },
151 187
   },
152 188
   data() {
153 189
     return {
190
+      hover: -1,
154 191
       selectedItems: [],
155 192
       showControl: false,
156 193
       reverse: false,
157 194
       searchItem: '',
195
+      visibleItemsPerPageCount: 20,
158 196
       itemsPerPageList: ItemsPerPageList,
159
-      visibleItemsPerPageCount: 0,
160
-      currentPage: 1,
161 197
     };
162 198
   },
163 199
   methods: {
@@ -257,7 +293,7 @@ export default {
257 293
               !list.includes(Object.keys(item)[o])
258 294
               && !Array.isArray(Object.values(item)[o])
259 295
             ) {
260
-              if (list.length < this.columnsCount) {
296
+              if (list.length < this.columnCount) {
261 297
                 list.push(Object.keys(item)[o]);
262 298
               }
263 299
             }
@@ -297,6 +333,10 @@ th[draggable] a {
297 333
   text-decoration: none;
298 334
   color: #333333;
299 335
 }
336
+.active {
337
+  background-color: rgba(255, 255, 255, 0.5);
338
+  cursor: pointer;
339
+}
300 340
 .table > tbody > tr > td {
301 341
   vertical-align: middle;
302 342
 }

+ 8
- 6
src/components/shared/navBar.vue Voir le fichier

@@ -91,11 +91,11 @@
91 91
                   class="dropdown-item cursor-pointer"
92 92
                   @click="routerGoTo('/property/new/Rental')"
93 93
                 >To Rent</a>
94
-                <hr />
94
+                <hr v-if="isLoggedIn" />
95 95
                 <a
96 96
                   v-if="isLoggedIn"
97 97
                   class="dropdown-item cursor-pointer"
98
-                  @click="routerGoTo('/property/admin/list/my')"
98
+                  @click="routerGoTo('/properties')"
99 99
                 >My Properties</a>
100 100
               </div>
101 101
             </li>
@@ -214,6 +214,7 @@
214 214
 
215 215
 <script>
216 216
 import { mapState, mapActions } from 'vuex';
217
+import Log from '../../assets/Log';
217 218
 
218 219
 export default {
219 220
   computed: {
@@ -224,11 +225,12 @@ export default {
224 225
       'person',
225 226
       'token',
226 227
     ]),
227
-    NAME() {
228
-      return this.person.name;
229
-    },
230 228
     isLoggedIn() {
231
-      return this.token !== null && this.token !== undefined;
229
+      console.log(Log.isLoggedIn());
230
+      return Log.isLoggedIn();
231
+    },
232
+    NAME() {
233
+      return Log.getPerson().name;
232 234
     },
233 235
     // eslint-disable-next-line vue/return-in-computed-property
234 236
     Logout() {

+ 11
- 27
src/components/shared/searchTab.vue Voir le fichier

@@ -38,7 +38,8 @@
38 38
                   aria-controls="pills-video"
39 39
                   aria-selected="true"
40 40
                   v-on:click="updateType('Timeshare')"
41
-                >Timeshare</a>
41
+                  >Timeshare</a
42
+                >
42 43
               </li>
43 44
               <li class="nav-item">
44 45
                 <a
@@ -50,7 +51,8 @@
50 51
                   aria-controls="pills-plans"
51 52
                   aria-selected="false"
52 53
                   v-on:click="updateType('Residential')"
53
-                >Residential</a>
54
+                  >Residential</a
55
+                >
54 56
               </li>
55 57
               <li class="nav-item">
56 58
                 <a
@@ -62,7 +64,8 @@
62 64
                   aria-controls="pills-map"
63 65
                   aria-selected="false"
64 66
                   v-on:click="updateType('Commercial')"
65
-                >Commercial</a>
67
+                  >Commercial</a
68
+                >
66 69
               </li>
67 70
             </ul>
68 71
             <div class="tab-content" id="pills-tabContent">
@@ -80,7 +83,7 @@
80 83
                 role="tabpanel"
81 84
                 aria-labelledby="pills-plans-tab"
82 85
               >
83
-                <propertySearch propertyType="Residential" @updateSearch="updateSearch" />
86
+                <propertySearch propertyType="Residential" />
84 87
               </div>
85 88
               <div
86 89
                 class="tab-pane fade"
@@ -88,7 +91,7 @@
88 91
                 role="tabpanel"
89 92
                 aria-labelledby="pills-map-tab"
90 93
               >
91
-                <propertySearch propertyType="Commercial" @updateSearch="updateSearch" />
94
+                <propertySearch propertyType="Commercial" />
92 95
               </div>
93 96
             </div>
94 97
           </div>
@@ -115,41 +118,22 @@ export default {
115 118
     return {
116 119
       selectedPropertyType: 'timeshare',
117 120
       keyword: '',
118
-      propertySearch: {
119
-        keyword: '',
120
-        userName: '',
121
-        salesType: 'Sale',
122
-        propertyUsageType: 'All',
123
-        propertyType: 'All',
124
-        province: 'All',
125
-        city: 'All',
126
-        suburb: 'All',
127
-        minPrice: 0,
128
-        maxPrice: 0,
129
-      },
130 121
     };
131 122
   },
132 123
   computed: {
133 124
     ...mapState('weekList', ['filter']),
125
+    ...mapState('propertySearch', ['propertySearch']),
134 126
   },
135 127
   methods: {
136 128
     updateType(item) {
137 129
       this.selectedPropertyType = item;
138 130
     },
139
-    updateSearch(item) {
140
-      this.propertySearch = item;
141
-      this.propertySearch.propertyUsageType = this.selectedPropertyType;
142
-      this.propertySearch.keyword = this.filter.keyword;
143
-    },
144 131
     Search() {
145 132
       if (this.selectedPropertyType === 'timeshare') {
146 133
         this.$router.push('/timesharesearch');
147 134
       } else {
148
-        // this.$router.push('/property/search');
149
-        this.$router.push({
150
-          path: '/property/search',
151
-          query: this.propertySearch,
152
-        });
135
+        this.propertySearch.propertyUsageType = this.selectedPropertyType;
136
+        this.$router.push('/property/propertySearch/results');
153 137
       }
154 138
     },
155 139
   },

+ 13
- 3
src/components/timeshare/buy/buyPage.vue Voir le fichier

@@ -35,9 +35,9 @@
35 35
           </div>
36 36
           <hr />
37 37
           <div class="row mb-4">
38
-            <div class="container col-md-4">
39
-              <div class="accordion" id="accordionExample">
40
-                <div class="card" v-for="(region, r) in availRegion" :key="r">
38
+            <div class="container col-md-4" v-if="availRegion">
39
+              <div class="accordion" id="accordionExample" v-if="availRegion.length > 0">
40
+                <div class="card darker-border" v-for="(region, r) in availRegion" :key="r">
41 41
                   <a
42 42
                     class="mb-0 color-text-a"
43 43
                     data-toggle="collapse"
@@ -77,6 +77,9 @@
77 77
                     </div>
78 78
                   </div>
79 79
                 </div>
80
+                  </div>
81
+                  <div v-else>
82
+                    <Alert :text="'No items found...'" :type="'INFO'" />
80 83
               </div>
81 84
             </div>
82 85
             <div class="col-md-8">
@@ -110,9 +113,13 @@
110 113
 </template>
111 114
 <script>
112 115
 import { mapState, mapActions } from 'vuex';
116
+import Alert from '../../shared/alert.vue';
113 117
 
114 118
 export default {
115 119
   name: 'TimeshareToBuy',
120
+  components:{
121
+    Alert
122
+  },
116 123
   data() {
117 124
     return {
118 125
       myMap: 'SouthAfrica',
@@ -166,4 +173,7 @@ export default {
166 173
   border-radius: 5px;
167 174
   cursor: pointer;
168 175
 }
176
+.darker-border {
177
+  border-color: #A5A5A5;
178
+}
169 179
 </style>

+ 38
- 10
src/components/timeshare/myWeeksPage.vue Voir le fichier

@@ -1,16 +1,24 @@
1 1
 <template>
2
-  <div>
3
-    <br />
4
-    <br />
5
-    <br />
6
-    <br />
7
-    <br />
8
-    <div class="row">
9
-      <div class="offset-1 col-md-10">
10
-        <ListView :items="items" :editable="true" @onEdit="onEdit" />
11
-      </div>
2
+<div>
3
+  <br />
4
+  <div class="row">
5
+    <div class="offset-1 col-md-3">
6
+      {{items[selectedItems]}}
7
+      <button 
8
+        type="button"
9
+        :class="{'form-control btn btn-primary ': (1===1), 'my-disable': (!(selectedItems.length == 1) || !items[selectedItems] || items[selectedItems].status.code !== 'A2')}"
10
+        value="Publish Week"
11
+        :disabled="!(items.length == 1)"
12
+      >
13
+        Publish selected week
14
+      </button>
15
+    </div>
16
+    <div class="offset-1 col-md-10">
17
+      <ListView :items="items" :editable="true" @onNew="onNew" @onRowClick="onRowClick"
18
+          @onClearSelected="onClearSelected"  />
12 19
     </div>
13 20
   </div>
21
+</div>
14 22
 </template>
15 23
 <script>
16 24
 import { mapState, mapActions } from 'vuex';
@@ -24,6 +32,7 @@ export default {
24 32
   data() {
25 33
     return {
26 34
       user: Log.getUser(),
35
+      selectedItems: [],
27 36
     };
28 37
   },
29 38
   mounted() {
@@ -38,6 +47,25 @@ export default {
38 47
     onEdit(item) {
39 48
       this.$router.push(`/timeshare/${item.id}`);
40 49
     },
50
+    onNew() {
51
+      this.$router.push('/timeshare/sell');
52
+    },
53
+    onRowClick(items) {
54
+      this.selectedItems = items;
55
+    },
56
+    onClearSelected() {
57
+      this.selectedItems = [];
58
+    },
41 59
   },
42 60
 };
43 61
 </script>
62
+<style scoped>
63
+.my-disable {
64
+  background-color: silver;
65
+  border-color: silver;
66
+}
67
+.my-disable:hover {
68
+  background-color: lightgray;
69
+  border-color: lightgray;
70
+}
71
+</style>

+ 9
- 0
src/main.js Voir le fichier

@@ -38,6 +38,15 @@ Vue.filter('toCurrency', (value) => {
38 38
   return `R ${formatter.format(value)}`;
39 39
 });
40 40
 
41
+Vue.filter('toProper', (value) => {
42
+  if (typeof value !== 'string') {
43
+    console.log(typeof value);
44
+    return value;
45
+  }
46
+  value = value.replace(/([a-z])([A-Z])/g, '$1 $2');
47
+  return value.charAt(0).toUpperCase() + value.slice(1);
48
+});
49
+
41 50
 
42 51
 Vue.filter('toDate', value => value.substring(0, value.length > 9 ? 10 : value.length));
43 52
 

+ 231
- 218
src/router/index.js Voir le fichier

@@ -50,6 +50,8 @@ import searchLog from '../components/admin/logs/SearchLogs.vue';
50 50
 import TemplatePage from '../components/communication/templatePage.vue';
51 51
 import CarouselList from '../components/admin/misc/carouselList.vue';
52 52
 import CarouselDetail from '../components/admin/misc/carousel.vue';
53
+import AlertPage from '../components/shared/alertPage.vue';
54
+import PropertySearchResults from '../components/property/propertySearchResults.vue';
53 55
 
54 56
 Vue.use(Router);
55 57
 
@@ -61,223 +63,234 @@ export default new Router({
61 63
       y: 0,
62 64
     };
63 65
   },
64
-  routes: [{
65
-    path: '/',
66
-    name: 'Home',
67
-    component: HomePage,
68
-  },
69
-  {
70
-    path: '/about/us',
71
-    name: 'aboutus',
72
-    component: AboutUs,
73
-  },
74
-  {
75
-    path: '/about/timeshare',
76
-    name: 'abouttimeshare',
77
-    component: AboutTimeshare,
78
-  },
79
-  {
80
-    path: '/communication/template',
81
-    name: 'template',
82
-    component: TemplatePage,
83
-  },
84
-  {
85
-    path: '/timeshare/sell',
86
-    name: 'TimeshareSell',
87
-    component: TimeshareSell,
88
-  },
89
-  {
90
-    path: '/timeshare/buy',
91
-    name: 'TimeshareBuy',
92
-    component: TimeshareBuy,
93
-  },
94
-  {
95
-    path: '/timeshare/faq',
96
-    name: 'TimeshareFAQ',
97
-    component: TimeshareFAQ,
98
-  },
99
-  {
100
-    path: '/timeshare/myWeeks',
101
-    name: 'MyWeeks',
102
-    component: MyWeeksPage,
103
-  },
104
-  {
105
-    path: '/user/login',
106
-    name: 'Login',
107
-    component: Login,
108
-  },
109
-  {
110
-    path: '/user/updateProfileInfo',
111
-    name: 'UpdateInfo',
112
-    component: UpdateInfo,
113
-  },
114
-  {
115
-    path: '/user/register',
116
-    name: 'PrivateIndividual',
117
-    component: PrivateIndividual,
118
-  },
119
-  {
120
-    path: '/user/registeragency',
121
-    name: 'Agency',
122
-    component: Agency,
123
-  },
124
-  {
125
-    path: '/property/property/:id',
126
-    name: 'PropertyPage',
127
-    component: PropertyPage,
128
-  },
129
-  {
130
-    path: '/property/:propertyUsageType/search',
131
-    name: 'PropertySearch',
132
-    component: PropertySearch,
133
-  },
134
-  {
135
-    path: '/property/search',
136
-    name: 'PropertySearchTab',
137
-    component: PropertySearch,
138
-  },
139
-  {
140
-    path: '/property/new/:saleType',
141
-    name: 'PropertyNew',
142
-    component: PropertyCreate,
143
-  },
144
-  {
145
-    path: '/property/new/:propertyUsageType/:saleType',
146
-    name: 'PropertyNewFromSearch',
147
-    component: PropertyCreate,
148
-  },
149
-  {
150
-    path: '/property/edit',
151
-    name: 'PropertyEdit',
152
-    component: PropertyEdit,
153
-  },
154
-  {
155
-    path: '/property/admin/list/:by',
156
-    name: 'PropertyListAdmin',
157
-    component: PropertyList,
158
-  },
159
-  {
160
-    path: '/propertyTypes/list',
161
-    name: 'PropertyTypeList',
162
-    component: PropertyTypeList,
163
-  },
164
-  {
165
-    path: '/propertyType/new',
166
-    name: 'PropertyTypeNew',
167
-    component: PropertyType,
168
-  },
169
-  {
170
-    path: '/propertyType/:id',
171
-    name: 'PropertyTypeEdit',
172
-    component: PropertyType,
173
-  },
174
-  {
175
-    path: '/userDefinedGroups/list',
176
-    name: 'UserDefinedGroupsList',
177
-    component: UserDefinedGroups,
178
-  },
179
-  {
180
-    path: '/userDefinedGroups/userDefinedGroup/:id',
181
-    name: 'UserDefinedGroupEdit',
182
-    component: UserDefinedGroup,
183
-  },
184
-  {
185
-    path: '/userDefinedGroups/userDefinedGroup',
186
-    name: 'UserDefinedGroupNew',
187
-    component: UserDefinedGroup,
188
-  },
189
-  {
190
-    path: '/status/list',
191
-    name: 'StatusList',
192
-    component: Status,
193
-  },
194
-  {
195
-    path: '/status/timeshareAdmin',
196
-    name: 'TimeshareAdmin',
197
-    component: timeshareAdminPage,
198
-  },
199
-  {
200
-    path: '/status/tenderWeekAdmin',
201
-    name: 'TenderWeekAdmin',
202
-    component: tenderWeekAdminPage,
203
-  },
204
-  {
205
-    path: '/status/userManagementPage',
206
-    name: 'userManagementPage',
207
-    component: userManagementPage,
208
-  },
209
-  {
210
-    path: '/status/agentUserManagementPage',
211
-    name: 'agentManagementPage',
212
-    component: agentManagementPage,
213
-  },
214
-  {
215
-    path: '/status/changeLogPage',
216
-    name: 'changeLogPage',
217
-    component: changeLogPage,
218
-  },
219
-  {
220
-    path: '/unitConfiguration/list',
221
-    name: 'UnitConfiguration',
222
-    component: UnitConfiguration,
223
-  },
224
-  {
225
-    path: '/contactus',
226
-    name: 'ContactUs',
227
-    component: ContactUs,
228
-  },
229
-  {
230
-    path: '/privacypolicy',
231
-    name: 'PrivacyPolicy',
232
-    component: PrivacyPolicy,
233
-  },
234
-  {
235
-    path: '/resort/:resortCode',
236
-    name: 'ResortPage',
237
-    component: ResortPage,
238
-    props: true,
239
-  },
240
-  {
241
-    path: '/resort/:resortCode/:unitNumber',
242
-    name: 'UnitPage',
243
-    component: UnitPage,
244
-    props: true,
245
-  },
246
-  {
247
-    path: '/timeshare/:weekId',
248
-    name: 'TimeshareSell',
249
-    component: TimeshareSell,
250
-    props: true,
251
-  },
252
-  {
253
-    path: '/MakeOffer',
254
-    name: 'MakeOffer',
255
-    component: MakeOffer,
256
-  },
257
-  {
258
-    path: '/Offers',
259
-    name: 'Offers',
260
-    component: Offer,
261
-  },
262
-  {
263
-    path: '/timesharesearch',
264
-    name: 'TimeshareSearch',
265
-    component: TimeshareSearch,
266
-  },
267
-  {
268
-    path: '/searchLog',
269
-    name: 'SearchLog',
270
-    component: searchLog,
271
-  },
272
-  {
273
-    path: '/carousel',
274
-    name: 'carousel',
275
-    component: CarouselList,
276
-  },
277
-  {
278
-    path: '/carousel/details/:id',
279
-    name: 'CarouselDetails',
280
-    component: CarouselDetail,
281
-  },
66
+  routes: [
67
+    {
68
+      path: '/',
69
+      name: 'Home',
70
+      component: HomePage,
71
+    },
72
+    {
73
+      path: '/shared/alert',
74
+      name: 'AlertPage',
75
+      component: AlertPage,
76
+    },
77
+    {
78
+      path: '/about/us',
79
+      name: 'aboutus',
80
+      component: AboutUs,
81
+    },
82
+    {
83
+      path: '/about/timeshare',
84
+      name: 'abouttimeshare',
85
+      component: AboutTimeshare,
86
+    },
87
+    {
88
+      path: '/communication/template',
89
+      name: 'template',
90
+      component: TemplatePage,
91
+    },
92
+    {
93
+      path: '/timeshare/sell',
94
+      name: 'TimeshareSell',
95
+      component: TimeshareSell,
96
+    },
97
+    {
98
+      path: '/timeshare/buy',
99
+      name: 'TimeshareBuy',
100
+      component: TimeshareBuy,
101
+    },
102
+    {
103
+      path: '/timeshare/faq',
104
+      name: 'TimeshareFAQ',
105
+      component: TimeshareFAQ,
106
+    },
107
+    {
108
+      path: '/timeshare/myWeeks',
109
+      name: 'MyWeeks',
110
+      component: MyWeeksPage,
111
+    },
112
+    {
113
+      path: '/user/login',
114
+      name: 'Login',
115
+      component: Login,
116
+    },
117
+    {
118
+      path: '/user/updateProfileInfo',
119
+      name: 'UpdateInfo',
120
+      component: UpdateInfo,
121
+    },
122
+    {
123
+      path: '/user/register',
124
+      name: 'PrivateIndividual',
125
+      component: PrivateIndividual,
126
+    },
127
+    {
128
+      path: '/user/registeragency',
129
+      name: 'Agency',
130
+      component: Agency,
131
+    },
132
+    {
133
+      path: '/property/property/:id',
134
+      name: 'PropertyPage',
135
+      component: PropertyPage,
136
+    },
137
+    {
138
+      path: '/property/:propertyUsageType/search',
139
+      name: 'PropertySearch',
140
+      component: PropertySearch,
141
+    },
142
+    {
143
+      path: '/property/search',
144
+      name: 'PropertySearchTab',
145
+      component: PropertySearch,
146
+    },
147
+    {
148
+      path: '/property/new/:saleType',
149
+      name: 'PropertyNew',
150
+      component: PropertyCreate,
151
+    },
152
+    {
153
+      path: '/property/new/:propertyUsageType/:saleType',
154
+      name: 'PropertyNewFromSearch',
155
+      component: PropertyCreate,
156
+    },
157
+    {
158
+      path: '/property/edit',
159
+      name: 'PropertyEdit',
160
+      component: PropertyEdit,
161
+    },
162
+    {
163
+      path: '/properties',
164
+      name: 'PropertyListAdmin',
165
+      component: PropertyList,
166
+    },
167
+    {
168
+      path: '/propertyTypes/list',
169
+      name: 'PropertyTypeList',
170
+      component: PropertyTypeList,
171
+    },
172
+    {
173
+      path: '/propertyType/new',
174
+      name: 'PropertyTypeNew',
175
+      component: PropertyType,
176
+    },
177
+    {
178
+      path: '/propertyType/:id',
179
+      name: 'PropertyTypeEdit',
180
+      component: PropertyType,
181
+    },
182
+    {
183
+      path: '/userDefinedGroups/list',
184
+      name: 'UserDefinedGroupsList',
185
+      component: UserDefinedGroups,
186
+    },
187
+    {
188
+      path: '/userDefinedGroups/userDefinedGroup/:id',
189
+      name: 'UserDefinedGroupEdit',
190
+      component: UserDefinedGroup,
191
+    },
192
+    {
193
+      path: '/userDefinedGroups/userDefinedGroup',
194
+      name: 'UserDefinedGroupNew',
195
+      component: UserDefinedGroup,
196
+    },
197
+    {
198
+      path: '/status/list',
199
+      name: 'StatusList',
200
+      component: Status,
201
+    },
202
+    {
203
+      path: '/status/timeshareAdmin',
204
+      name: 'TimeshareAdmin',
205
+      component: timeshareAdminPage,
206
+    },
207
+    {
208
+      path: '/status/tenderWeekAdmin',
209
+      name: 'TenderWeekAdmin',
210
+      component: tenderWeekAdminPage,
211
+    },
212
+    {
213
+      path: '/status/userManagementPage',
214
+      name: 'userManagementPage',
215
+      component: userManagementPage,
216
+    },
217
+    {
218
+      path: '/status/agentUserManagementPage',
219
+      name: 'agentManagementPage',
220
+      component: agentManagementPage,
221
+    },
222
+    {
223
+      path: '/status/changeLogPage',
224
+      name: 'changeLogPage',
225
+      component: changeLogPage,
226
+    },
227
+    {
228
+      path: '/unitConfiguration/list',
229
+      name: 'UnitConfiguration',
230
+      component: UnitConfiguration,
231
+    },
232
+    {
233
+      path: '/contactus',
234
+      name: 'ContactUs',
235
+      component: ContactUs,
236
+    },
237
+    {
238
+      path: '/privacypolicy',
239
+      name: 'PrivacyPolicy',
240
+      component: PrivacyPolicy,
241
+    },
242
+    {
243
+      path: '/resort/:resortCode',
244
+      name: 'ResortPage',
245
+      component: ResortPage,
246
+      props: true,
247
+    },
248
+    {
249
+      path: '/resort/:resortCode/:unitNumber',
250
+      name: 'UnitPage',
251
+      component: UnitPage,
252
+      props: true,
253
+    },
254
+    {
255
+      path: '/timeshare/:weekId',
256
+      name: 'TimeshareSellEdit',
257
+      component: TimeshareSell,
258
+      props: true,
259
+    },
260
+    {
261
+      path: '/MakeOffer',
262
+      name: 'MakeOffer',
263
+      component: MakeOffer,
264
+    },
265
+    {
266
+      path: '/Offers',
267
+      name: 'Offers',
268
+      component: Offer,
269
+    },
270
+    {
271
+      path: '/timesharesearch',
272
+      name: 'TimeshareSearch',
273
+      component: TimeshareSearch,
274
+    },
275
+    {
276
+      path: '/searchLog',
277
+      name: 'SearchLog',
278
+      component: searchLog,
279
+    },
280
+    {
281
+      path: '/carousel',
282
+      name: 'carousel',
283
+      component: CarouselList,
284
+    },
285
+    {
286
+      path: '/carousel/details/:id',
287
+      name: 'CarouselDetails',
288
+      component: CarouselDetail,
289
+    },
290
+    {
291
+      path: '/property/propertySearch/results',
292
+      name: 'PropertySearchResults',
293
+      component: PropertySearchResults,
294
+    },
282 295
   ],
283 296
 });

+ 4
- 0
src/store/index.js Voir le fichier

@@ -25,6 +25,8 @@ import Info from './modules/communication/info';
25 25
 import PropertyEdit from './modules/property/propertyEdit';
26 26
 import Carousel from './modules/misc/carousel';
27 27
 import Individual from './modules/user/individual';
28
+import Alert from './modules/misc/alert';
29
+import TenderWeek from './modules/timeshare/tenderWeeks';
28 30
 
29 31
 Vue.use(Vuex);
30 32
 /* eslint no-param-reassign: ["error", { "props": false }] */
@@ -54,5 +56,7 @@ export default new Vuex.Store({
54 56
     propertyEdit: PropertyEdit,
55 57
     carousel: Carousel,
56 58
     individual: Individual,
59
+    alert: Alert,
60
+    tenderWeek: TenderWeek
57 61
   },
58 62
 });

+ 21
- 0
src/store/modules/misc/alert.js Voir le fichier

@@ -0,0 +1,21 @@
1
+export default {
2
+  namespaced: true,
3
+  state: {
4
+    type: undefined,
5
+    message: undefined,
6
+  },
7
+  mutations: {
8
+    setSuccess(state, msg) {
9
+      state.type = 'SUCCESS';
10
+      state.message = msg;
11
+    },
12
+  },
13
+  getters: {},
14
+  actions: {
15
+    success({
16
+      commit,
17
+    }, msg) {
18
+      commit('setSuccess', msg);
19
+    },
20
+  },
21
+};

+ 25
- 0
src/store/modules/misc/carousel.js Voir le fichier

@@ -4,23 +4,48 @@ export default {
4 4
   namespaced: true,
5 5
   state: {
6 6
     carouselList: [],
7
+    carousel: {
8
+      id: 0,
9
+      propertyID: 0,
10
+      timeshareID: 0,
11
+      header: '',
12
+      image: '',
13
+    },
7 14
   },
8 15
   mutations: {
16
+    setCarouselItem(state, item) {
17
+      state.carousel = item;
18
+    },
9 19
     setCarouselList(state, items) {
10 20
       state.carouselList = items;
11 21
     },
22
+    addToCarouselList(state, item) {
23
+      state.carouselList.push(item);
24
+    },
12 25
     removeCarousel(state, id) {
13 26
       state.carouselList.pop(state.carouselList.find(p => p.id === id));
14 27
     },
15 28
   },
16 29
   getters: {},
17 30
   actions: {
31
+    getCarouselItem({ commit }, id) {
32
+      axios
33
+        .get(`/api/Carousel/${id}`)
34
+        .then(result => commit('setCarouselItem', result.data))
35
+        .catch(console.error);
36
+    },
18 37
     getCarouselList({ commit }) {
19 38
       axios
20 39
         .get('/api/Carousel')
21 40
         .then(result => commit('setCarouselList', result.data))
22 41
         .catch(console.error);
23 42
     },
43
+    saveCarouselItem({ commit }, item) {
44
+      axios
45
+        .post('/api/Carousel', item)
46
+        .then(result => commit('addToCarouselList', result.data))
47
+        .catch(console.error);
48
+    },
24 49
     deleteCarousel({ commit }, id) {
25 50
       axios
26 51
         .delete(`/api/Carousel/${id}`)

+ 17
- 4
src/store/modules/property/property.js Voir le fichier

@@ -148,10 +148,17 @@ export default {
148 148
         .catch(console.error);
149 149
     },
150 150
     saveProperty({ commit }, item) {
151
-      axios
152
-        .post('/api/Property', item)
153
-        .then(result => commit('updateCurrentProperty', result.data))
154
-        .catch(console.error);
151
+      return new Promise((resolve, reject) => {
152
+        axios
153
+          .post('/api/Property', item)
154
+          .then((resp) => {
155
+            commit('updateCurrentProperty', resp);
156
+            resolve(resp);
157
+          })
158
+          .catch(() => {
159
+            reject(console.error);
160
+          });
161
+      });
155 162
     },
156 163
     clearProperty({ commit }) {
157 164
       commit('clearProperty');
@@ -183,5 +190,11 @@ export default {
183 190
         .then(response => commit('setPropertyFields', response.data))
184 191
         .catch(console.error);
185 192
     },
193
+    getSavedPropertyImages({ commit }, id) {
194
+      axios
195
+        .get(`/api/PropertyImage/GetProperySavedImages/${id}`)
196
+        .then(result => commit('setPropertyImages', result.data))
197
+        .catch(console.error);
198
+    },
186 199
   },
187 200
 };

+ 11
- 8
src/store/modules/property/propertyEdit.js Voir le fichier

@@ -123,15 +123,18 @@ export default {
123 123
         .catch(console.error);
124 124
     },
125 125
     updateProperty({ commit }, item) {
126
-      axios
127
-        .post('/api/PropertyImage', item.images)
128
-        .then(commit('ClearNewImages'))
129
-        .catch(console.error);
126
+      return new Promise((resolve) => {
127
+        axios
128
+          .post('/api/PropertyImage', item.images)
129
+          .then(commit('ClearNewImages'))
130
+          .catch(console.error);
130 131
 
131
-      axios
132
-        .put('/api/Property', item.property)
133
-        .then(commit('setProperty', item.property))
134
-        .catch(console.error);
132
+        axios
133
+          .put('/api/Property', item.property)
134
+          .then(commit('setProperty', item.property))
135
+          .catch(console.error);
136
+        resolve(true);
137
+      });
135 138
     },
136 139
     mayEditProperty({ commit }, id) {
137 140
       axios

+ 41
- 0
src/store/modules/property/propertySearch.js Voir le fichier

@@ -19,6 +19,9 @@ export default {
19 19
     },
20 20
     properties: [],
21 21
     latestProperties: [],
22
+    suburbs: [],
23
+    searchText: '',
24
+    suburbList: [],
22 25
   },
23 26
   mutations: {
24 27
     updateSearch(state, propertySearch) {
@@ -34,6 +37,26 @@ export default {
34 37
     onClearFilter(state, filter) {
35 38
       state.propertySearch[filter] = 'All';
36 39
     },
40
+    setSuburbs(state, items) {
41
+      state.suburbList = [];
42
+      state.suburbs = items;
43
+      // eslint-disable-next-line no-plusplus
44
+      for (let i = 0; i < state.suburbs.length; i++) {
45
+        state.suburbList.push(state.suburbs[i].display);
46
+      }
47
+    },
48
+    setFilter(state, value) {
49
+      state.searchText = value;
50
+    },
51
+  },
52
+  getters: {
53
+    filterSuburbs: (state) => {
54
+      let subs = state.suburbs;
55
+      if (state.searchText) {
56
+        subs = _.filter(subs, s => s.display.contains(state.searchText));
57
+      }
58
+      return subs;
59
+    },
37 60
   },
38 61
   actions: {
39 62
     clearFilter({ commit }, filter) {
@@ -60,6 +83,15 @@ export default {
60 83
       if (item.userName === '') {
61 84
         item.userName = 'Unknown';
62 85
       }
86
+      if (item.suburb === '') {
87
+        item.suburb = 'All';
88
+      }
89
+      if (item.city === '') {
90
+        item.city = 'All';
91
+      }
92
+      if (item.province === '') {
93
+        item.province = 'All';
94
+      }
63 95
       axios
64 96
         .get(
65 97
           `/api/Property/Search/${item.userName}/${item.keyword}/${item.salesType}/${item.propertyUsageType}/${item.propertyType}/${item.province}/${item.city}/${item.suburb}/${item.minPrice}/${item.maxPrice}`,
@@ -73,5 +105,14 @@ export default {
73 105
         .then(response => commit('setLatestProperties', response.data))
74 106
         .catch(console.error);
75 107
     },
108
+    getSuburbs({ commit }) {
109
+      axios
110
+        .get('/api/suburb/GetSearchList')
111
+        .then(response => commit('setSuburbs', response.data))
112
+        .catch(console.error);
113
+    },
114
+    applyFilter({ commit }, value) {
115
+      commit('setFilter', { value });
116
+    },
76 117
   },
77 118
 };

+ 20
- 2
src/store/modules/timeshare/myWeeks.js Voir le fichier

@@ -1,9 +1,10 @@
1
+/* eslint-disable no-restricted-syntax */
2
+/* eslint-disable guard-for-in */
1 3
 import axios from 'axios';
2 4
 
3 5
 export default {
4 6
   namespaced: true,
5 7
   state: {
6
-    test: 'kobus',
7 8
     items: [],
8 9
   },
9 10
   mutations: {
@@ -11,7 +12,11 @@ export default {
11 12
       state.items = list;
12 13
     },
13 14
   },
14
-  getters: {},
15
+  getters: {
16
+    getNeedsVerify(state) {
17
+      return _.filter(state.items, x => x.status && x.status.code === 'A1');
18
+    },
19
+  },
15 20
   actions: {
16 21
     getItems({
17 22
       commit,
@@ -21,5 +26,18 @@ export default {
21 26
           .error,
22 27
       );
23 28
     },
29
+    verifyWeek({
30
+      commit,
31
+    }, id) {
32
+      try {
33
+        axios.post(`/api/timeshareweek/verifyweek/${id}`).catch(console.error);
34
+      } catch (err) {
35
+        console.log(err);
36
+      }
37
+    },
38
+    publishWeek(id) {
39
+      axios.post(`/api/timeshareweek/publishweek/${id}`).catch(console.error);
40
+    }
41
+
24 42
   },
25 43
 };

+ 22
- 0
src/store/modules/timeshare/tenderWeeks.js Voir le fichier

@@ -0,0 +1,22 @@
1
+import axios from 'axios';
2
+
3
+export default {
4
+  namespaced: true,
5
+  state: {
6
+    items: [],
7
+  },
8
+  mutations: {
9
+    setItems(state, list) {
10
+      state.items = list;
11
+    },
12
+  },
13
+  getters: {},
14
+  actions: {
15
+    getItems({ commit }) {
16
+      axios
17
+        .get('/api/timeshareweek/getTenderWeeks')
18
+        .then(result => commit('setItems', result.data))
19
+        .catch(console.error);
20
+    },
21
+  },
22
+};

Chargement…
Annuler
Enregistrer