Parcourir la source

#13382 modify dynamic rate

vbea il y a 2 ans
Parent
commit
c1a28ea743

+ 45 - 3
Strides-Admin/src/router/SiteRouter.js

@@ -23,7 +23,7 @@ export default {
     },
     {
       path: '/site-management/dynamic-rate-configuration',
-      component: () => import('@/views/site/DynamicRates'),
+      component: () => import('@/views/rate-dynamic/index'),
       hidden: true,
       name: 'dynamic-rate-configuration',
       meta: {
@@ -33,6 +33,18 @@ export default {
         activeIcon: 'sidebar-submenu-item-active',
       }
     },
+    {
+      path: '/site-management/rate-configuration',
+      component: () => import('@/views/rate-base/index'),
+      hidden: true,
+      name: 'base-rate-configuration',
+      meta: {
+        breadcrumb: true,
+        title: 'Rate Configuration',
+        icon: 'sidebar-submenu-item',
+        activeIcon: 'sidebar-submenu-item-active',
+      }
+    },
     {
       path: '/site-management/edit/:id',
       component: () => import('@/views/site/detail'),
@@ -71,7 +83,7 @@ export default {
     },
     {
       path: '/site-management/dynamic-rate-add',
-      component: () => import('@/views/site/RateDetail'),
+      component: () => import('@/views/rate-dynamic/detail'),
       hidden: true,
       name: 'dynamic-rate-add',
       meta: {
@@ -86,7 +98,7 @@ export default {
     },
     {
       path: '/site-management/dynamic-rate-update/:id',
-      component: () => import('@/views/site/RateDetail'),
+      component: () => import('@/views/rate-dynamic/detail'),
       hidden: true,
       name: 'dynamic-rate-update',
       meta: {
@@ -98,6 +110,36 @@ export default {
         },
         activeMenu: '/site-management/dynamic-rate-configuration'
       }
+    },
+    {
+      path: '/site-management/rate-add',
+      component: () => import('@/views/rate-base/detail'),
+      hidden: true,
+      name: 'base-rate-add',
+      meta: {
+        breadcrumb: true,
+        title: 'Create',
+        parent: {
+          title: 'Rate Configuration',
+          path: "/site-management/rate-configuration"
+        },
+        activeMenu: '/site-management/rate-configuration'
+      }
+    },
+    {
+      path: '/site-management/rate-update/:id',
+      component: () => import('@/views/rate-base/detail'),
+      hidden: true,
+      name: 'base-rate-update',
+      meta: {
+        breadcrumb: true,
+        title: 'Edit',
+        parent: {
+          title: 'Rate Configuration',
+          path: "/site-management/rate-configuration"
+        },
+        activeMenu: '/site-management/rate-configuration'
+      }
     }
   ]
 }

+ 528 - 0
Strides-Admin/src/views/rate-base/detail.vue

@@ -0,0 +1,528 @@
+<template>
+  <div class="container">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="150px"
+      label-position="top">
+      <div class="content">
+        <div class="section-title">Rate Config</div>
+        <div class="flexcr">
+          <el-form-item
+            label="Name:"
+            prop="rateName"
+            class="add-input">
+            <el-input
+              v-model="form.rateName"/>
+          </el-form-item>
+          <el-form-item
+            label="Country:"
+            prop="countryCode"
+            class="add-input">
+            <el-select
+              v-model="form.countryCode">
+              <el-option
+                v-for="item in options.country"
+                :key="item.name"
+                :label="item.name"
+                :value="item.value" />
+            </el-select>
+          </el-form-item>
+          <!-- <el-form-item
+            label="Rate Type:"
+            prop="rateType"
+            class="add-input">
+            <el-input
+              v-model="form.rateType"/>
+          </el-form-item> -->
+        </div>
+        <div class="flexcr" v-if="false">
+          <label class="el-form-item__label">Repeat:</label>
+          <div class="repeat-view">
+            (&nbsp;
+            <div
+              class="link-type"
+              v-for="(item, index) in options.shortcut"
+              :key="index"
+              @click="handleShortcut(item)">
+              <span>{{item.name}}</span>
+            </div>
+            &nbsp;)
+            <el-tooltip
+              effect="dark"
+              content="This is an items for quickly selecting the repeats"
+              placement="right">
+              <i class="el-icon-question icon-help"></i>
+            </el-tooltip>
+          </div>
+        </div>
+        <div style="margin-bottom: 10px;" v-if="false">
+          <el-checkbox-group
+            v-model="form.repeats">
+            <el-checkbox-button
+              v-for="(item, index) in options.repeat"
+              :label="item.value"
+              :key="index">
+              {{item.name}}
+            </el-checkbox-button>
+          </el-checkbox-group>
+        </div>
+        <div class="flexcr" v-if="false">
+          <el-form-item
+            label="All Day:"
+            class="add-input">
+            <el-switch
+              v-model="form.allDay"
+              @change="changeAllday"
+              disabled/>
+          </el-form-item>
+          <template v-if="form.allDay">
+            <el-form-item
+              label="Start Time:"
+              class="add-input">
+              <el-input disabled/>
+            </el-form-item>
+            <el-form-item
+              label="End Time:"
+              class="add-input">
+              <el-input disabled/>
+            </el-form-item>
+          </template>
+          <template v-else>
+            <el-form-item
+              label="Start Time:"
+              class="add-input"
+              prop="startTime">
+              <el-time-picker
+                v-model="form.startTime"
+                format="HH:mm"
+                value-format="HH:mm"
+                clearable/>
+            </el-form-item>
+            <el-form-item
+              label="End Time:"
+              class="add-input"
+              prop="endTime">
+              <el-time-picker
+                v-model="form.endTime"
+                format="HH:mm"
+                value-format="HH:mm"
+                clearable/>
+            </el-form-item>
+          </template>
+        </div>
+      </div>
+      <div class="content">
+        <div class="section-title flexcr">
+          CHARGE SITE RATE
+          <div class="section-sub-title">(Currency Used: {{currencyData[form.countryCode]}})</div>
+        </div>
+        <charge-rate
+          v-model="ratesForm.chargeRates"/>
+      </div>
+      <div class="content" v-if="false">
+        <div class="section-title flexcr">
+          SPECIAL CHARGE RATE
+          <div class="section-sub-title">(Currency Used: {{currencyData[form.countryCode]}})</div>
+        </div>
+        <charge-rate
+          isSpecial
+          v-model="ratesForm.specialChargeRates"/>
+      </div>
+      <div class="content flexcr">
+        <div class="buttons">
+          <el-button
+            @click="onBack"
+            type="primary"
+            class="cancel-button">
+            Cancel
+          </el-button>
+          <el-button
+            @click="onClickSave"
+            type="primary"
+            :loading="loadingSave">
+            &nbsp;Save&nbsp;
+          </el-button>
+        </div>
+        <div class="update-by" v-if="isEdit">
+          <span
+            class="add-text"
+            :title='"CREATED BY " + form.createdBy + " ON " + form.createdOn'>
+            LAST UPDATED BY {{form.updatedBy}} TIMESTAMP: {{form.updatedOn}}
+          </span>
+        </div>
+      </div>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import site from '../../http/api/site'
+import api from '../../http/api/rates'
+import settings from '../../settings.js'
+import ChargeRate from '../site/components/ChargeRate'
+export default {
+  data() {
+    return {
+      loading: false,
+      loadingSave: false,
+      isEdit: false,
+      form: {
+        dynamicRateId: "",
+        rateName: "",
+        countryCode: settings.defaultCountry,
+        repeats: [],
+        allDay: true,
+        startTime: "",
+        endTime: "",
+        rates: [],
+        specialRates: []
+      },
+      options: {
+        country: [],
+        repeat: [],
+        shortcut: [{
+          name: "Daily",
+          value: [],
+          all: true
+        },{
+          name: "Weekday",
+          value: ["Mon","Tue","Wed","Thu","Fri"]
+        },{
+          name: "Weekend",
+          value: ["Sat","Sun"]
+        }, {
+          name: "None",
+          value: []
+        }]
+      },
+      currencyData: {
+        SG: "S$"
+      },
+      ratesForm: {
+        chargeRates: [{
+          rate: '',
+          rateType: '',
+          chargeTypePk: '',
+          dynamicRateItemId: ''
+        }],
+        specialChargeRates: [{
+          rate: '',
+          rateType: '',
+          chargeTypePk: '',
+          groupPk: ''
+        }]
+      },
+      rules: {
+        rateName: {
+          required: true,
+          trigger: "blur",
+          message: "Please input rate name"
+        },
+        startTime: {
+          required: true,
+          trigger: "change",
+          message: "Please select start time"
+        },
+        endTime: {
+          required: true,
+          trigger: "change",
+          message: "Please select end time"
+        }
+      },
+      
+    }
+  },
+  components: {ChargeRate},
+  created() {
+    this.loading = true;
+    this.getCountryOptions();
+    this.getRepeatOptions();
+    if (this.$route.params.id) {
+      this.isEdit = true;
+      this.getRateDetail();
+    }
+  },
+  methods: {
+    onBack() {
+      this.$nextTick(() => {
+        this.$router.replace({
+          path: "/site-management/dynamic-rate-configuration"
+        })
+      })
+    },
+    getCountryOptions() {
+      site.getCountryList().then(res => {
+        if (res.data) {
+          this.options.country = res.data
+          const sign = {}
+          res.data.forEach(item => {
+            sign[item.value] = item.currencySymbol
+          })
+          this.currencyData = sign;
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.loading = false;
+      })
+    },
+    getRepeatOptions() {
+      api.getRepeatOptions().then(res => {
+        if (res.data) {
+          this.options.repeat = res.data;
+          this.handleShortcut(this.options.shortcut[0]);
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    getRateDetail() {
+      this.loading = true;
+      api.getDynamicRateInfo({
+        dynamicRateId: this.$route.params.id
+      }).then(res => {
+        if (res.data) {
+          this.form = res.data;
+          if (res.data.rates && res.data.rates.length) {
+            this.ratesForm.chargeRates = res.data.rates;
+          }
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    handleShortcut(shortcut) {
+      const select = []
+      if (shortcut.all) {
+        this.options.repeat.forEach(item => {
+          select.push(item.value)
+        })
+      } else {
+        select.push(...shortcut.value)
+      }
+      this.form.repeats = select;
+    },
+    changeAllday(all) {
+      if (all) {
+        this.form.startTime = "";
+        this.form.endTime = "";
+        this.$refs.form.clearValidate()
+      }
+    },
+    onClickSave() {
+      this.$refs.form.validate(result => {
+        if (result) {
+          if (this.form.repeats.length == 0) {
+            this.$message({
+              message: "Please select at least one repeat day",
+              type: 'error',
+              duration: 3000,
+            })
+            return;
+          }
+          const rates = [];
+          this.ratesForm.chargeRates.forEach(item => {
+            if (item.rate) {
+              rates.push(item);
+            }
+          })
+          this.ratesForm.specialChargeRates.forEach(item => {
+            if (item.rate) {
+              rates.push(item);
+            }
+          })
+          if (rates.length == 0) {
+            this.$message({
+              message: "Please add at least one site rate",
+              type: 'error',
+              duration: 3000,
+            })
+            return;
+          }
+          this.form.rates = rates;
+          this.loadingSave = true;
+          this.isEdit ? this.updateRateData() : this.addRateData();
+        }
+      });
+    },
+    addRateData() {
+      api.addDynamicRate(this.form).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Successfully added"
+        });
+        this.onBack();
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        });
+      }).finally(() => {
+        this.loadingSave = false;
+      });
+    },
+    updateRateData() {
+      api.updateDynamicRate(this.form).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Successfully updated"
+        });
+        this.onBack();
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        });
+      }).finally(() => {
+        this.loadingSave = false;
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  @import '../../styles/variables.scss';
+  .container {
+    width: 100%;
+    padding: 20px 60px;
+    min-height: $mainAppMinHeight;
+    background-color: #F0F5FC;
+  }
+  .content {
+    margin: 0 8px 16px;
+    padding: 15px 80px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  
+  .section-title {
+    color: #333;
+    margin-top: 20px;
+    margin-bottom: 30px;
+    font-size: 15px;
+    user-select: none;
+    line-height: 24px;
+    font-weight: bold;
+    font-family: sans-serif;
+    text-transform: uppercase;
+  }
+  
+  .section-sub-title {
+    font-size: 14px;
+    padding-left: 5px;
+    font-weight: normal;
+  }
+  
+  .add-text {
+    width: 100%;
+    min-width: 100px;
+    max-width: 300px;
+  }
+  .add-text ::v-deep .el-textarea__inner {
+    font-family: sans-serif;
+  }
+  .add-input {
+    width: 100%;
+    min-width: 100px;
+    max-width: 250px;
+    margin-right: 10px;
+    ::v-deep .el-input,
+    ::v-deep .el-select {
+      width: 100%;
+    }
+  }
+  
+  .icon-help {
+    color: #999;
+    font-size: 15px;
+    cursor: pointer;
+  }
+  
+  .form-photo {
+    flex: 1;
+    ::v-deep .el-form-item__label {
+      padding: 12px;
+      line-height: 16px;
+    }
+    .photo-uploader {
+      margin-right: 10px;
+      .uploader-image {
+        width: 180px;
+        height: 120px;
+        text-align: left;
+      }
+      ::v-deep img {
+        object-fit: cover;
+      }
+      .avatar-uploader-icon {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        font-size: 28px;
+        color: #8c939d;
+        width: 120px;
+        height: 120px;
+        line-height: 120px;
+        text-align: center;
+      }
+    }
+  }
+  .repeat-view {
+    display: flex;
+    font-size: 14px;
+    font-weight: bold;
+    align-items: center;
+    padding: 0 10px 10px;
+    .link-type + .link-type {
+      margin-left: 5px;
+      &::before {
+        color: #333;
+        content: "|";
+        font-size: 15px;
+        font-weight: normal;
+        padding-right: 5px;
+      }
+    }
+  }
+  .hr {
+    height: 2px;
+    margin: 10px -40px;
+    background-color: #F0F5FC;
+  }
+  .buttons {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+  @media screen and (max-width: 1200px) {
+    .add-input {
+      min-width: 80px;
+      max-width: 200px;
+    }
+  }
+  @media screen and (max-width: 500px) {
+    .container {
+      padding: 0px;
+    }
+    .content {
+      padding: 15px 30px;
+    }
+    .add-input {
+      max-width: unset;
+    }
+  }
+</style>

+ 225 - 0
Strides-Admin/src/views/rate-base/index.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <div class="filter-view">
+        <el-input
+          class="filter-input"
+          v-model="filters.pageVo.criteria"
+          placeholder="Search by Rate Name"
+          prefix-icon="el-icon-search"
+          clearable/>
+        <div>
+          <el-button
+            @click="onClickSearch"
+            icon="el-icon-search"
+            type="primary">
+            Search
+          </el-button>
+        </div>
+        <div
+          class="filter-flex-button"
+          v-if="!$route.meta.onlyView">
+          <el-button
+            icon="el-icon-plus"
+            type="primary"
+            @click="onClickAdd">
+            Create Rate
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <el-table
+      v-loading="loading"
+      :data="table.list">
+      <el-table-column
+        align="center"
+        label="Rate ID"
+        prop="userPk"
+        min-width="120">
+        <template slot-scope="{row}" >
+          <span
+            class="link-type"
+            @click="onClickEdit(row)"
+            v-if="!$route.meta.onlyView">
+            {{ row.dynamicRateId }}
+          </span>
+          <span v-else>{{ row.dynamicRateId }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="Rate Name"
+        prop="rateName"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="No. of Sites Configured"
+        prop="assignedSiteCount"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Status"
+        prop="dataStatus"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Update Time"
+        prop="updateTime"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Action"
+        min-width="70"
+        v-if="!$route.meta.onlyView">
+        <template v-slot="{ row }">
+          <el-dropdown
+            class="action-dropdown"
+            @command="(v) => handleCommand(v, row)">
+            <i class="el-icon-more icon-action"></i>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item
+                command="assignRates">
+                Assign Sites
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickEdit">
+                Edit
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickDelete">
+                Delete
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="right">
+      <pagination
+        v-show="table.total > 0"
+        :total="table.total"
+        :page.sync="filters.pageNo"
+        :limit.sync="filters.pageSize"
+        @pagination="getTableData" />
+    </div>
+    <DialogAssignment
+      :visible="assign.visible"
+      :title="'ASSIGN SITES (RATE NAME: ' + assign.item.rateName + ')'"
+      :rate="assign.item"
+      @hide="assignRates"/>
+  </div>
+</template>
+
+<script>
+import TableAction from '@/components/TableAction.vue'
+import Pagination from '@/components/Pagination'
+import DialogAssignment from '@/components/DialogAssignment'
+import api from '@/http/api/rates'
+export default {
+  components: { Pagination, TableAction, DialogAssignment },
+  data() {
+    return {
+      loading: false,
+      filters: {
+        pageNo: 1,
+        pageSize: 10,
+        pageVo: {
+          criteria: ""
+        }
+      },
+      table: {
+        list: [],
+        total: 0
+      },
+      assign: {
+        item: {},
+        visible: false
+      }
+    }
+  },
+  created() {
+    this.onClickSearch()
+  },
+  methods: {
+    onClickSearch() {
+      this.filters.pageNo = 1
+      this.getTableData()
+    },
+    getTableData() {
+      this.loading = true;
+      api.getRatePages(this.filters).then(res => {
+        if (res.data && res.total) {
+          this.table.total = res.total;
+          this.table.list = res.data;
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.table.total = 0;
+        this.table.list = [];
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    handleCommand(cb, item) {
+      this[cb](item)
+    },
+    onClickAdd() {
+      this.$router.push({
+        path: "/site-management/rate-add"
+      })
+    },
+    onClickEdit(row) {
+      this.$router.push({
+        path: "/site-management/rate-update/" + row.dynamicRateId
+      })
+    },
+    onClickDelete(row) {
+      this.$confirm('Are you sure you want to delete this rate?', 'Delete', {
+        confirmButtonText: 'OK',
+        cancelButtonText: 'Cancel',
+        type: 'warning'
+      }).then(res => {
+        this.deleteDynamicRate(row.dynamicRateId);
+      })
+    },
+    assignRates(row) {
+      if (row) {
+        this.assign.item = row;
+        this.assign.visible = true;
+      } else {
+        this.assign.item = {};
+        this.assign.visible = false;
+        this.getTableData();
+      }
+    },
+    deleteDynamicRate(id) {
+      this.loading = true;
+      api.deleteDynamicRate({
+        dynamicRateId: id
+      }).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Delete success."
+        })
+        this.getTableData()
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.loading = false;
+      })
+    }
+  }
+}
+</script>
+
+<style>
+  .filter-input {
+    min-width: 100px;
+    max-width: 300px;
+  }
+</style>

+ 526 - 0
Strides-Admin/src/views/rate-dynamic/detail.vue

@@ -0,0 +1,526 @@
+<template>
+  <div class="container">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="150px"
+      label-position="top">
+      <div class="content">
+        <div class="section-title">Rate Config</div>
+        <div class="flexcr">
+          <el-form-item
+            label="Name:"
+            prop="rateName"
+            class="add-input">
+            <el-input
+              v-model="form.rateName"/>
+          </el-form-item>
+          <el-form-item
+            label="Country:"
+            prop="countryCode"
+            class="add-input">
+            <el-select
+              v-model="form.countryCode">
+              <el-option
+                v-for="item in options.country"
+                :key="item.name"
+                :label="item.name"
+                :value="item.value" />
+            </el-select>
+          </el-form-item>
+          <!-- <el-form-item
+            label="Rate Type:"
+            prop="rateType"
+            class="add-input">
+            <el-input
+              v-model="form.rateType"/>
+          </el-form-item> -->
+        </div>
+        <div class="flexcr">
+          <label class="el-form-item__label">Repeat:</label>
+          <div class="repeat-view">
+            (&nbsp;
+            <div
+              class="link-type"
+              v-for="(item, index) in options.shortcut"
+              :key="index"
+              @click="handleShortcut(item)">
+              <span>{{item.name}}</span>
+            </div>
+            &nbsp;)
+            <el-tooltip
+              effect="dark"
+              content="This is an items for quickly selecting the repeats"
+              placement="right">
+              <i class="el-icon-question icon-help"></i>
+            </el-tooltip>
+          </div>
+        </div>
+        <div style="margin-bottom: 10px;">
+          <el-checkbox-group
+            v-model="form.repeats">
+            <el-checkbox-button
+              v-for="(item, index) in options.repeat"
+              :label="item.value"
+              :key="index">
+              {{item.name}}
+            </el-checkbox-button>
+          </el-checkbox-group>
+        </div>
+        <div class="flexcr">
+          <el-form-item
+            label="All Day:"
+            class="add-input">
+            <el-switch
+              v-model="form.allDay"
+              @change="changeAllday"/>
+          </el-form-item>
+          <template v-if="form.allDay">
+            <el-form-item
+              label="Start Time:"
+              class="add-input">
+              <el-input disabled/>
+            </el-form-item>
+            <el-form-item
+              label="End Time:"
+              class="add-input">
+              <el-input disabled/>
+            </el-form-item>
+          </template>
+          <template v-else>
+            <el-form-item
+              label="Start Time:"
+              class="add-input"
+              prop="startTime">
+              <el-time-picker
+                v-model="form.startTime"
+                format="HH:mm"
+                value-format="HH:mm"
+                clearable/>
+            </el-form-item>
+            <el-form-item
+              label="End Time:"
+              class="add-input"
+              prop="endTime">
+              <el-time-picker
+                v-model="form.endTime"
+                format="HH:mm"
+                value-format="HH:mm"
+                clearable/>
+            </el-form-item>
+          </template>
+        </div>
+      </div>
+      <div class="content">
+        <div class="section-title flexcr">
+          CHARGE SITE RATE
+          <div class="section-sub-title">(Currency Used: {{currencyData[form.countryCode]}})</div>
+        </div>
+        <charge-rate
+          v-model="ratesForm.chargeRates"/>
+      </div>
+      <div class="content" v-if="false">
+        <div class="section-title flexcr">
+          SPECIAL CHARGE RATE
+          <div class="section-sub-title">(Currency Used: {{currencyData[form.countryCode]}})</div>
+        </div>
+        <charge-rate
+          isSpecial
+          v-model="ratesForm.specialChargeRates"/>
+      </div>
+      <div class="content flexcr">
+        <div class="buttons">
+          <el-button
+            @click="onBack"
+            type="primary"
+            class="cancel-button">
+            Cancel
+          </el-button>
+          <el-button
+            @click="onClickSave"
+            type="primary"
+            :loading="loadingSave">
+            &nbsp;Save&nbsp;
+          </el-button>
+        </div>
+        <div class="update-by" v-if="isEdit">
+          <span
+            class="add-text"
+            :title='"CREATED BY " + form.createdBy + " ON " + form.createdOn'>
+            LAST UPDATED BY {{form.updatedBy}} TIMESTAMP: {{form.updatedOn}}
+          </span>
+        </div>
+      </div>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import site from '../../http/api/site'
+import api from '../../http/api/rates'
+import settings from '../../settings.js'
+import ChargeRate from '../site/components/ChargeRate'
+export default {
+  data() {
+    return {
+      loading: false,
+      loadingSave: false,
+      isEdit: false,
+      form: {
+        dynamicRateId: "",
+        rateName: "",
+        countryCode: settings.defaultCountry,
+        repeats: [],
+        allDay: false,
+        startTime: "",
+        endTime: "",
+        rates: [],
+        specialRates: []
+      },
+      options: {
+        country: [],
+        repeat: [],
+        shortcut: [{
+          name: "Daily",
+          value: [],
+          all: true
+        },{
+          name: "Weekday",
+          value: ["Mon","Tue","Wed","Thu","Fri"]
+        },{
+          name: "Weekend",
+          value: ["Sat","Sun"]
+        }, {
+          name: "None",
+          value: []
+        }]
+      },
+      currencyData: {
+        SG: "S$"
+      },
+      ratesForm: {
+        chargeRates: [{
+          rate: '',
+          rateType: '',
+          chargeTypePk: '',
+          dynamicRateItemId: ''
+        }],
+        specialChargeRates: [{
+          rate: '',
+          rateType: '',
+          chargeTypePk: '',
+          groupPk: ''
+        }]
+      },
+      rules: {
+        rateName: {
+          required: true,
+          trigger: "blur",
+          message: "Please input rate name"
+        },
+        startTime: {
+          required: true,
+          trigger: "change",
+          message: "Please select start time"
+        },
+        endTime: {
+          required: true,
+          trigger: "change",
+          message: "Please select end time"
+        }
+      },
+      
+    }
+  },
+  components: {ChargeRate},
+  created() {
+    this.loading = true;
+    this.getCountryOptions();
+    this.getRepeatOptions();
+    if (this.$route.params.id) {
+      this.isEdit = true;
+      this.getRateDetail();
+    }
+  },
+  methods: {
+    onBack() {
+      this.$nextTick(() => {
+        this.$router.replace({
+          path: "/site-management/dynamic-rate-configuration"
+        })
+      })
+    },
+    getCountryOptions() {
+      site.getCountryList().then(res => {
+        if (res.data) {
+          this.options.country = res.data
+          const sign = {}
+          res.data.forEach(item => {
+            sign[item.value] = item.currencySymbol
+          })
+          this.currencyData = sign;
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.loading = false;
+      })
+    },
+    getRepeatOptions() {
+      api.getRepeatOptions().then(res => {
+        if (res.data) {
+          this.options.repeat = res.data
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    getRateDetail() {
+      this.loading = true;
+      api.getDynamicRateInfo({
+        dynamicRateId: this.$route.params.id
+      }).then(res => {
+        if (res.data) {
+          this.form = res.data;
+          if (res.data.rates && res.data.rates.length) {
+            this.ratesForm.chargeRates = res.data.rates;
+          }
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    handleShortcut(shortcut) {
+      const select = []
+      if (shortcut.all) {
+        this.options.repeat.forEach(item => {
+          select.push(item.value)
+        })
+      } else {
+        select.push(...shortcut.value)
+      }
+      this.form.repeats = select;
+    },
+    changeAllday(all) {
+      if (all) {
+        this.form.startTime = "";
+        this.form.endTime = "";
+        this.$refs.form.clearValidate()
+      }
+    },
+    onClickSave() {
+      this.$refs.form.validate(result => {
+        if (result) {
+          if (this.form.repeats.length == 0) {
+            this.$message({
+              message: "Please select at least one repeat day",
+              type: 'error',
+              duration: 3000,
+            })
+            return;
+          }
+          const rates = [];
+          this.ratesForm.chargeRates.forEach(item => {
+            if (item.rate) {
+              rates.push(item);
+            }
+          })
+          this.ratesForm.specialChargeRates.forEach(item => {
+            if (item.rate) {
+              rates.push(item);
+            }
+          })
+          if (rates.length == 0) {
+            this.$message({
+              message: "Please add at least one site rate",
+              type: 'error',
+              duration: 3000,
+            })
+            return;
+          }
+          this.form.rates = rates;
+          this.loadingSave = true;
+          this.isEdit ? this.updateRateData() : this.addRateData();
+        }
+      });
+    },
+    addRateData() {
+      api.addDynamicRate(this.form).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Successfully added"
+        });
+        this.onBack();
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        });
+      }).finally(() => {
+        this.loadingSave = false;
+      });
+    },
+    updateRateData() {
+      api.updateDynamicRate(this.form).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Successfully updated"
+        });
+        this.onBack();
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        });
+      }).finally(() => {
+        this.loadingSave = false;
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  @import '../../styles/variables.scss';
+  .container {
+    width: 100%;
+    padding: 20px 60px;
+    min-height: $mainAppMinHeight;
+    background-color: #F0F5FC;
+  }
+  .content {
+    margin: 0 8px 16px;
+    padding: 15px 80px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  
+  .section-title {
+    color: #333;
+    margin-top: 20px;
+    margin-bottom: 30px;
+    font-size: 15px;
+    user-select: none;
+    line-height: 24px;
+    font-weight: bold;
+    font-family: sans-serif;
+    text-transform: uppercase;
+  }
+  
+  .section-sub-title {
+    font-size: 14px;
+    padding-left: 5px;
+    font-weight: normal;
+  }
+  
+  .add-text {
+    width: 100%;
+    min-width: 100px;
+    max-width: 300px;
+  }
+  .add-text ::v-deep .el-textarea__inner {
+    font-family: sans-serif;
+  }
+  .add-input {
+    width: 100%;
+    min-width: 100px;
+    max-width: 250px;
+    margin-right: 10px;
+    ::v-deep .el-input,
+    ::v-deep .el-select {
+      width: 100%;
+    }
+  }
+  
+  .icon-help {
+    color: #999;
+    font-size: 15px;
+    cursor: pointer;
+  }
+  
+  .form-photo {
+    flex: 1;
+    ::v-deep .el-form-item__label {
+      padding: 12px;
+      line-height: 16px;
+    }
+    .photo-uploader {
+      margin-right: 10px;
+      .uploader-image {
+        width: 180px;
+        height: 120px;
+        text-align: left;
+      }
+      ::v-deep img {
+        object-fit: cover;
+      }
+      .avatar-uploader-icon {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        font-size: 28px;
+        color: #8c939d;
+        width: 120px;
+        height: 120px;
+        line-height: 120px;
+        text-align: center;
+      }
+    }
+  }
+  .repeat-view {
+    display: flex;
+    font-size: 14px;
+    font-weight: bold;
+    align-items: center;
+    padding: 0 10px 10px;
+    .link-type + .link-type {
+      margin-left: 5px;
+      &::before {
+        color: #333;
+        content: "|";
+        font-size: 15px;
+        font-weight: normal;
+        padding-right: 5px;
+      }
+    }
+  }
+  .hr {
+    height: 2px;
+    margin: 10px -40px;
+    background-color: #F0F5FC;
+  }
+  .buttons {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+  @media screen and (max-width: 1200px) {
+    .add-input {
+      min-width: 80px;
+      max-width: 200px;
+    }
+  }
+  @media screen and (max-width: 500px) {
+    .container {
+      padding: 0px;
+    }
+    .content {
+      padding: 15px 30px;
+    }
+    .add-input {
+      max-width: unset;
+    }
+  }
+</style>

+ 225 - 0
Strides-Admin/src/views/rate-dynamic/index.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <div class="filter-view">
+        <el-input
+          class="filter-input"
+          v-model="filters.pageVo.criteria"
+          placeholder="Search by Rate Name"
+          prefix-icon="el-icon-search"
+          clearable/>
+        <div>
+          <el-button
+            @click="onClickSearch"
+            icon="el-icon-search"
+            type="primary">
+            Search
+          </el-button>
+        </div>
+        <div
+          class="filter-flex-button"
+          v-if="!$route.meta.onlyView">
+          <el-button
+            icon="el-icon-plus"
+            type="primary"
+            @click="onClickAdd">
+            Create Rate
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <el-table
+      v-loading="loading"
+      :data="table.list">
+      <el-table-column
+        align="center"
+        label="Rate ID"
+        prop="userPk"
+        min-width="120">
+        <template slot-scope="{row}" >
+          <span
+            class="link-type"
+            @click="onClickEdit(row)"
+            v-if="!$route.meta.onlyView">
+            {{ row.dynamicRateId }}
+          </span>
+          <span v-else>{{ row.dynamicRateId }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="Rate Name"
+        prop="rateName"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="No. of Sites Configured"
+        prop="assignedSiteCount"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Status"
+        prop="dataStatus"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Update Time"
+        prop="updateTime"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Action"
+        min-width="70"
+        v-if="!$route.meta.onlyView">
+        <template v-slot="{ row }">
+          <el-dropdown
+            class="action-dropdown"
+            @command="(v) => handleCommand(v, row)">
+            <i class="el-icon-more icon-action"></i>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item
+                command="assignRates">
+                Assign Sites
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickEdit">
+                Edit
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickDelete">
+                Delete
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="right">
+      <pagination
+        v-show="table.total > 0"
+        :total="table.total"
+        :page.sync="filters.pageNo"
+        :limit.sync="filters.pageSize"
+        @pagination="getTableData" />
+    </div>
+    <DialogAssignment
+      :visible="assign.visible"
+      :title="'ASSIGN SITES (RATE NAME: ' + assign.item.rateName + ')'"
+      :rate="assign.item"
+      @hide="assignRates"/>
+  </div>
+</template>
+
+<script>
+import TableAction from '@/components/TableAction.vue'
+import Pagination from '@/components/Pagination'
+import DialogAssignment from '@/components/DialogAssignment'
+import api from '@/http/api/rates'
+export default {
+  components: { Pagination, TableAction, DialogAssignment },
+  data() {
+    return {
+      loading: false,
+      filters: {
+        pageNo: 1,
+        pageSize: 10,
+        pageVo: {
+          criteria: ""
+        }
+      },
+      table: {
+        list: [],
+        total: 0
+      },
+      assign: {
+        item: {},
+        visible: false
+      }
+    }
+  },
+  created() {
+    this.onClickSearch()
+  },
+  methods: {
+    onClickSearch() {
+      this.filters.pageNo = 1
+      this.getTableData()
+    },
+    getTableData() {
+      this.loading = true;
+      api.getRatePages(this.filters).then(res => {
+        if (res.data && res.total) {
+          this.table.total = res.total;
+          this.table.list = res.data;
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.table.total = 0;
+        this.table.list = [];
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    handleCommand(cb, item) {
+      this[cb](item)
+    },
+    onClickAdd() {
+      this.$router.push({
+        path: "/site-management/dynamic-rate-add"
+      })
+    },
+    onClickEdit(row) {
+      this.$router.push({
+        path: "/site-management/dynamic-rate-update/" + row.dynamicRateId
+      })
+    },
+    onClickDelete(row) {
+      this.$confirm('Are you sure you want to delete this rate?', 'Delete', {
+        confirmButtonText: 'OK',
+        cancelButtonText: 'Cancel',
+        type: 'warning'
+      }).then(res => {
+        this.deleteDynamicRate(row.dynamicRateId);
+      })
+    },
+    assignRates(row) {
+      if (row) {
+        this.assign.item = row;
+        this.assign.visible = true;
+      } else {
+        this.assign.item = {};
+        this.assign.visible = false;
+        this.getTableData();
+      }
+    },
+    deleteDynamicRate(id) {
+      this.loading = true;
+      api.deleteDynamicRate({
+        dynamicRateId: id
+      }).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Delete success."
+        })
+        this.getTableData()
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.loading = false;
+      })
+    }
+  }
+}
+</script>
+
+<style>
+  .filter-input {
+    min-width: 100px;
+    max-width: 300px;
+  }
+</style>