Kobus Botha před 5 roky
rodič
revize
5334dc4da3

binární
public/img/icons/area.png Zobrazit soubor


binární
public/img/icons/garage.png Zobrazit soubor


+ 206
- 1
src/components/admin/misc/carousel.vue Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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>

+ 17
- 9
src/components/home/carouselSection.vue Zobrazit soubor

@@ -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>

+ 14
- 4
src/components/property/propertyCard.vue Zobrazit soubor

@@ -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>

+ 37
- 55
src/components/property/propertyCreate.vue Zobrazit soubor

@@ -69,11 +69,9 @@
69 69
                     v-model="property.propertyTypeId"
70 70
                   >
71 71
                     <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>
72
+                    <option v-for="item in propertyTypes" :value="item.id" :key="item.id">{{
73
+                      item.description
74
+                    }}</option>
77 75
                   </select>
78 76
                 </div>
79 77
               </div>
@@ -152,11 +150,9 @@
152 150
                     v-model="property.provinceId"
153 151
                   >
154 152
                     <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>
153
+                    <option v-for="province in provinces" :value="province.id" :key="province.id">{{
154
+                      province.description
155
+                    }}</option>
160 156
                   </select>
161 157
                 </div>
162 158
               </div>
@@ -175,11 +171,9 @@
175 171
                     v-model="property.cityId"
176 172
                   >
177 173
                     <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>
174
+                    <option v-for="city in cities" :value="city.id" :key="city.id">{{
175
+                      city.description
176
+                    }}</option>
183 177
                   </select>
184 178
                 </div>
185 179
               </div>
@@ -197,11 +191,9 @@
197 191
                     @change="getPostalCode"
198 192
                   >
199 193
                     <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>
194
+                    <option v-for="suburb in suburbs" :value="suburb.id" :key="suburb.id">{{
195
+                      suburb.description
196
+                    }}</option>
205 197
                   </select>
206 198
                 </div>
207 199
               </div>
@@ -263,12 +255,15 @@
263 255
                 <label for="Property Description">Description</label>
264 256
                 <vue-editor v-model="property.description" :editor-toolbar="customToolbar" />
265 257
                 <br />
266
-                <p>* A listing fee of R380 including VAT is payable to list your Property on the Uni-Vate website</p>
258
+                <p>
259
+                  * A listing fee of R380 including VAT is payable to list your Property on the
260
+                  Uni-Vate website
261
+                </p>
267 262
               </div>
268 263
             </div>
269 264
             <div class="form-group row" />
270 265
             <UserField
271
-              v-if="propertyType === 'Residential' & propertyOverviewFields.length > 0"
266
+              v-if="(propertyType === 'Residential') & (propertyOverviewFields.length > 0)"
272 267
               :fields="propertyOverviewFields[0].fields"
273 268
               @UpdateUserDefinedFields="UpdateUserDefinedFields"
274 269
               :id="1"
@@ -348,7 +343,9 @@
348 343
               @click="SubmitData()"
349 344
               class="btn btn-b-n"
350 345
               style="width: 85px; height:40px;"
351
-            >Save</button>
346
+            >
347
+              Save
348
+            </button>
352 349
             <div v-if="wait" id="preloader"></div>
353 350
           </form>
354 351
         </div>
@@ -360,7 +357,6 @@
360 357
 <script>
361 358
 import { mapState, mapActions } from 'vuex';
362 359
 import { VueEditor } from 'vue2-editor';
363
-import { setTimeout } from 'timers';
364 360
 import UserField from './propertyUserField.vue';
365 361
 import ImageLoad from './propertyImage.vue';
366 362
 
@@ -384,16 +380,12 @@ export default {
384 380
       customToolbar: [
385 381
         [{ header: [false, 1, 2, 3, 4, 5, 6] }],
386 382
         ['bold', 'italic', 'underline', 'strike'],
387
-        [
388
-          { align: '' },
389
-          { align: 'center' },
390
-          { align: 'right' },
391
-          { align: 'justify' },
392
-        ],
383
+        [{ align: '' }, { align: 'center' }, { align: 'right' }, { align: 'justify' }],
393 384
         [{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
394 385
         [{ script: 'sub' }, { script: 'super' }],
395 386
         [{ indent: '-1' }, { indent: '+1' }],
396 387
       ],
388
+      error: '',
397 389
     };
398 390
   },
399 391
   methods: {
@@ -433,47 +425,37 @@ export default {
433 425
       }
434 426
       this.property.propertyUserFields = this.propertyFieldValues;
435 427
 
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
-      );
428
+      if (this.user) {
429
+        this.property.userId = this.user.id;
430
+      }
431
+
432
+      this.saveProperty(this.property)
433
+        .then((fulfilled) => {
434
+          this.$router.push(`/property/property/${fulfilled.data.id}`);
435
+        })
436
+        .catch((error) => {
437
+          console.log(error.message);
438
+        });
448 439
     },
449 440
     Close() {
450 441
       this.$router.push('/property/admin/list/my');
451 442
     },
452 443
     ProvinceSelected(item) {
453 444
       if (item.target.options.selectedIndex > 0) {
454
-        this.selectedProvince = this.provinces[
455
-          item.target.options.selectedIndex - 1
456
-        ].description;
445
+        this.selectedProvince = this.provinces[item.target.options.selectedIndex - 1].description;
457 446
         this.getCities(Object.assign({}, { province: this.selectedProvince }));
458 447
       }
459 448
     },
460 449
     CitySelected(item) {
461 450
       if (item.target.options.selectedIndex > 0) {
462
-        this.selectedCity = this.cities[
463
-          item.target.options.selectedIndex - 1
464
-        ].description;
451
+        this.selectedCity = this.cities[item.target.options.selectedIndex - 1].description;
465 452
         this.getSuburbs(
466
-          Object.assign(
467
-            {},
468
-            { province: this.selectedProvince, city: this.selectedCity },
469
-          ),
453
+          Object.assign({}, { province: this.selectedProvince, city: this.selectedCity }),
470 454
         );
471 455
       }
472 456
     },
473 457
     getPostalCode(item) {
474
-      this.property.addressLine3 = this.suburbs[
475
-        item.target.options.selectedIndex - 1
476
-      ].postalCode;
458
+      this.property.addressLine3 = this.suburbs[item.target.options.selectedIndex - 1].postalCode;
477 459
     },
478 460
     loadedImages(values) {
479 461
       this.images = values;

+ 8
- 3
src/components/property/propertyFieldEditor.vue Zobrazit soubor

@@ -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>
@@ -50,10 +55,10 @@
50 55
           <option value="yes">Yes</option>
51 56
           <option value="no">No</option>
52 57
         </select>
53
-        <span @click="UpdateValue()" class="input-group-text" style="color: #60CBEB">
58
+        <span @click="UpdateValue()" class="input-group-text spanCursor" style="color: #60CBEB">
54 59
           <eva-icon name="checkmark-outline" fill="#60CBEB"></eva-icon>
55 60
         </span>
56
-        <span @click="Close()" class="input-group-text" style="color: #60CBEB">
61
+        <span @click="Close()" class="input-group-text spanCursor" style="color: #60CBEB">
57 62
           <eva-icon name="close-outline" fill="#60CBEB"></eva-icon>
58 63
         </span>
59 64
       </div>

+ 15
- 3
src/components/property/propertyImage.vue Zobrazit soubor

@@ -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,8 +18,14 @@
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 31
         <span class="input-group-text" align="center" @click="removeImage(key)">
@@ -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 Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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>

+ 9
- 24
src/components/property/propertySearchFields.vue Zobrazit soubor

@@ -29,10 +29,9 @@
29 29
           </div>
30 30
           <select class="form-control" v-model="resType" @change="PropertyTypeSelected">
31 31
             <option>All</option>
32
-            <option
33
-              v-for="(propertyType, i) in propertyTypesRes"
34
-              :key="i"
35
-            >{{ propertyType.description }}</option>
32
+            <option v-for="(propertyType, i) in propertyTypesRes" :key="i">{{
33
+              propertyType.description
34
+            }}</option>
36 35
           </select>
37 36
           <div class="input-group-append" @click="clearResType">
38 37
             <span class="input-group-text cursor-pointer" style="color: #60CBEB">
@@ -51,10 +50,9 @@
51 50
           </div>
52 51
           <select class="form-control" v-model="comType" @change="PropertyTypeSelected">
53 52
             <option>All</option>
54
-            <option
55
-              v-for="(propertyType, i) in propertyTypesCom"
56
-              :key="i"
57
-            >{{ propertyType.description }}</option>
53
+            <option v-for="(propertyType, i) in propertyTypesCom" :key="i">{{
54
+              propertyType.description
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">
@@ -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,20 +212,13 @@ 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') {
228
-        this.getCities(
229
-          Object.assign({}, { province: this.propertySearch.province }),
230
-        );
220
+        this.getCities(Object.assign({}, { province: this.propertySearch.province }));
231 221
       }
232
-      this.$emit('updateSearch', this.propertySearch);
233 222
     },
234 223
     CitySelected(item) {
235 224
       if (item.target.value !== 'All') {
@@ -243,10 +232,6 @@ export default {
243 232
           ),
244 233
         );
245 234
       }
246
-      this.$emit('updateSearch', this.propertySearch);
247
-    },
248
-    SuburbSeleted() {
249
-      this.$emit('updateSearch', this.propertySearch);
250 235
     },
251 236
   },
252 237
 };

+ 127
- 147
src/components/property/propertySearchPage.vue Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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>

+ 5
- 13
src/components/property/propertyeditPage.vue Zobrazit soubor

@@ -300,7 +300,7 @@
300 300
                 />
301 301
                 <span
302 302
                   v-if="!img.isDeleted && mayEdit"
303
-                  class="input-group-text"
303
+                  class="input-group-text spanCursor"
304 304
                   align="center"
305 305
                   @click="DeleteImage(img)"
306 306
                 >
@@ -535,20 +535,12 @@ export default {
535 535
       this.updateProperty({
536 536
         property: this.property,
537 537
         images: this.newPropertyImages,
538
+      }).then(() => {
539
+        this.$router.push('/properties');
538 540
       });
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 541
     },
550 542
     Close() {
551
-      this.$router.push('/property/admin/list/my');
543
+      this.$router.push('/properties');
552 544
     },
553 545
     loadedImages(values) {
554 546
       this.images = values;
@@ -625,7 +617,7 @@ export default {
625 617
 </script>
626 618
 
627 619
 <style>
628
-span {
620
+.spanCursor {
629 621
   cursor: pointer;
630 622
 }
631 623
 .opacity {

+ 81
- 0
src/components/shared/autoComplete.vue Zobrazit soubor

@@ -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>

+ 338
- 0
src/components/shared/gallerySlideShow.vue Zobrazit soubor

@@ -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>

+ 2
- 2
src/components/shared/navBar.vue Zobrazit soubor

@@ -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>

+ 11
- 27
src/components/shared/searchTab.vue Zobrazit soubor

@@ -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
   },

+ 230
- 223
src/router/index.js Zobrazit soubor

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

+ 25
- 0
src/store/modules/misc/carousel.js Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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 Zobrazit soubor

@@ -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
 };

Načítá se…
Zrušit
Uložit