Added dockerfile
This commit is contained in:
parent
e4868deb16
commit
62858f3aba
|
|
@ -0,0 +1,33 @@
|
|||
# Use a Node.js image for the build stage
|
||||
FROM node:14 AS build
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package.json and package-lock.json (if available)
|
||||
COPY package.json ./
|
||||
# COPY package-lock.json ./ # Uncomment if you have a package-lock.json
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the application code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Use the busybox image for the final stage
|
||||
FROM busybox:latest
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the built assets from the build stage
|
||||
COPY --from=build /app/dist ./
|
||||
|
||||
# Expose the port the app runs on
|
||||
EXPOSE 80
|
||||
|
||||
# Command to serve the static files
|
||||
CMD ["httpd", "-f", "-p", "80"]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -256,6 +256,13 @@ body {
|
|||
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* Overwriting 's Original Styles */
|
||||
/******************************************************************************/
|
||||
// .ant-table td { white-space: nowrap; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
<template>
|
||||
<b-modal id="modal-show-plan" v-bind:centered="true" size="lg">
|
||||
<!-- Header -->
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<h2 class="text-primary m-0">Plan's Information</h2>
|
||||
<img style="cursor: pointer;" src="@/assets/svg/cross.svg" alt="close-icon" v-on:click="close()">
|
||||
</template>
|
||||
|
||||
<!-- Body -->
|
||||
<template v-if="data" v-slot:default class="d-flex flex-column" style="">
|
||||
<b-row class="mb-2">
|
||||
<b-col cols="12">
|
||||
<h3>Plan: {{ data.title }}</h3>
|
||||
</b-col>
|
||||
<b-col cols="12">
|
||||
<span class="info-key">Description:</span>
|
||||
<span class="info-value">{{ data.description }}</span>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row style="row-gap: 1rem;">
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Start Date:</span>
|
||||
<span class="info-value">{{ GetDate(data.from_date) }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">End Date:</span>
|
||||
<span class="info-value">{{ GetDate(data.to_date) }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Months:</span>
|
||||
<span class="info-value">{{ data.months }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">User Limit:</span>
|
||||
<span class="info-value">{{ data.attributes["user limit"] }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Storage Limit:</span>
|
||||
<span class="info-value">{{ FormatBytes(data.storage_limit) }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Is Active:</span>
|
||||
<span class="info-value">
|
||||
<svg v-if="data.is_active" style="width: 24px; height: 24px;" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
<svg v-else style="width: 24px; height: 24px;" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
|
||||
<!-- Footer -->
|
||||
<template v-slot:modal-footer="{ hide }">
|
||||
<a class="text-primary" v-on:click="hide()">Close</a>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BForm, BButton, BRow, BCol } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import { SeparateNumberByThousands, FormatBytes } from '@/modules/core'
|
||||
|
||||
export default {
|
||||
name: 'AddUserModalComponent',
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
BForm,
|
||||
BButton,
|
||||
BRow,
|
||||
BCol,
|
||||
},
|
||||
|
||||
directives: {
|
||||
Ripple,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
FormatBytes(Bytes)
|
||||
{
|
||||
return FormatBytes(Bytes)
|
||||
},
|
||||
|
||||
GetDate(DateTimeString)
|
||||
{
|
||||
return new Date(DateTimeString).toDateString()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.info-key {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
.info-value {
|
||||
margin-inline-start: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,32 +1,31 @@
|
|||
<template>
|
||||
<b-sidebar
|
||||
id="user-filters--sidebar"
|
||||
bg-variant="white"
|
||||
v-model="IsOpen_D"
|
||||
title="Filters"
|
||||
v-bind:width="SidebarWidth"
|
||||
body-class="p-2"
|
||||
shadow
|
||||
backdrop
|
||||
right
|
||||
>
|
||||
<transition>
|
||||
<!-- No Filters -->
|
||||
<!-- <p v-if="Filters_D.length === 0" class="text-center text-dark" style="font-size: 1.125rem; font-weight: 500;">
|
||||
No filters are specified.
|
||||
</p> -->
|
||||
<!-- ❗ For the documentation, go to the end of the file. -->
|
||||
|
||||
<b-form
|
||||
ref="filters"
|
||||
id="filters-repeater-form"
|
||||
v-bind:style="{ height: trHeight }"
|
||||
v-on:submit.prevent="Repeat()"
|
||||
>
|
||||
<b-row
|
||||
ref="row"
|
||||
v-for="(Filter, Index) in Filters_D"
|
||||
v-bind:key="Index"
|
||||
>
|
||||
<template>
|
||||
<b-sidebar id="user-filters--sidebar"
|
||||
bg-variant="white"
|
||||
v-model="IsOpen_D"
|
||||
title="Filters"
|
||||
v-bind:width="SidebarWidth"
|
||||
body-class="p-2"
|
||||
shadow
|
||||
backdrop
|
||||
right>
|
||||
<!-- No Filters -->
|
||||
<!-- commented out because animations are buggy and don't work properly -->
|
||||
<!-- <p v-if="Filters_D.length === 0"
|
||||
class="text-center text-secondary"
|
||||
style="font-size: 1.125rem; font-weight: 500;">
|
||||
You have not filtered anything.
|
||||
</p> -->
|
||||
|
||||
<transition>
|
||||
<b-form ref="filters"
|
||||
id="filters-repeater-form"
|
||||
v-bind:style="{ height: trHeight }"
|
||||
v-on:submit.prevent="Repeat()">
|
||||
<b-row ref="row"
|
||||
v-for="(Filter, Index) in Filters_D"
|
||||
v-bind:key="Index">
|
||||
<b-col lg="3">
|
||||
<b-form-group label="Field">
|
||||
<v-select
|
||||
|
|
@ -38,10 +37,11 @@
|
|||
/>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<b-col lg="3">
|
||||
<b-form-group label="Condition" v-if="Filter.Field.Type !== 'boolean'">
|
||||
<v-select
|
||||
v-bind:options="Conditions_D"
|
||||
v-bind:options="GetConditions(Filter.Field.Value)"
|
||||
label="Title"
|
||||
v-bind:clearable="false"
|
||||
v-bind:value="Filter.Condition"
|
||||
|
|
@ -49,32 +49,47 @@
|
|||
/>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<!-- $COLUMN_VALUES -->
|
||||
<b-col lg="4">
|
||||
<b-form-group label="Value" v-if="Filter.Condition.Value !== 'isnull'">
|
||||
<b-form-checkbox
|
||||
v-if="Filter.Field.Type === 'boolean'"
|
||||
style="user-select: none;"
|
||||
v-model="Filter.Value"
|
||||
v-bind:value="true"
|
||||
v-bind:unchecked-value="false"
|
||||
>
|
||||
<b-form-checkbox v-if="Filter.Field.Type === $TYPES.Bool"
|
||||
style="user-select: none;"
|
||||
v-model="Filter.Value"
|
||||
v-bind:value="true"
|
||||
v-bind:unchecked-value="false">
|
||||
</b-form-checkbox>
|
||||
<b-form-input
|
||||
v-else-if="Filter.Field.Type === 'integer'"
|
||||
type="number"
|
||||
v-model="Filter.Value"
|
||||
/>
|
||||
|
||||
<b-form-input v-else-if="Filter.Field.Type === $TYPES.Int"
|
||||
type="number"
|
||||
v-model="Filter.Value" />
|
||||
|
||||
<v-select v-else-if="Filter.Field.Type === $TYPES.Status"
|
||||
v-bind:options="$STATUS_VALUES"
|
||||
label="Title"
|
||||
v-bind:clearable="false"
|
||||
v-bind:value="Filter.Value"
|
||||
v-on:input="Filter.Value = $event" />
|
||||
|
||||
<b-form-datepicker v-else-if="Filter.Field.Type === $TYPES.Date"
|
||||
v-model="Filter.Value"
|
||||
v-bind:date-format-options="{ day: 'numeric', month: 'long', year: 'numeric' }"
|
||||
today-button
|
||||
close-button
|
||||
label-help=""
|
||||
nav-button-variant="primary" />
|
||||
|
||||
<b-form-input v-else type="text" v-model="Filter.Value" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<!-- Delete Button -->
|
||||
<b-col lg="2" class="mb-1">
|
||||
<b-form-group label=" ">
|
||||
<b-button
|
||||
v-ripple.400="'rgba(234, 84, 85, 0.15)'"
|
||||
variant="outline-danger"
|
||||
class="mt-0 mt-md-2"
|
||||
v-on:click="Remove(Index)"
|
||||
>
|
||||
<b-button v-ripple.400="'rgba(234, 84, 85, 0.15)'"
|
||||
variant="outline-danger"
|
||||
class="mt-0 mt-md-2"
|
||||
v-on:click="Remove(Index)">
|
||||
<feather-icon icon="XIcon" class="mr-25" />
|
||||
<span>Delete</span>
|
||||
</b-button>
|
||||
|
|
@ -130,11 +145,46 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { BSidebar, BRow, BCol, BCard, BButton, BForm, BFormGroup, BFormInput, BFormCheckbox } from 'bootstrap-vue'
|
||||
import { BSidebar, BRow, BCol, BCard, BButton, BForm, BFormGroup, BFormInput, BFormCheckbox, BFormDatepicker } from 'bootstrap-vue'
|
||||
import vSelect from 'vue-select'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import { heightTransition } from '@core/mixins/ui/transition'
|
||||
import { Log } from '@/modules/core'
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CONSTANTS
|
||||
// -----------------------------------------------------------------------------
|
||||
const $CONDITIONS = {
|
||||
// IsNull: { Value: 'isnull', Title: 'Is Empty' },
|
||||
Eq: { Value: 'eq', Title: 'Equals' },
|
||||
NotEq: { Value: 'neq', Title: 'Not Equals' },
|
||||
Gt: { Value: 'gt', Title: 'Greater Than' },
|
||||
GtEq: { Value: 'gte', Title: 'Greater Than or Equals' },
|
||||
Lt: { Value: 'lt', Title: 'Less Than' },
|
||||
LtEq: { Value: 'lte', Title: 'Less Than or Equals' },
|
||||
// Contains: { Value: 'in', Title: 'Contains' },
|
||||
}
|
||||
|
||||
const $TYPES = {
|
||||
Int: 'integer', // @notimplemented
|
||||
Bool: 'boolean',
|
||||
Str: 'string',
|
||||
Percent: 'percent', // @notimplemented
|
||||
Date: 'date', // @notimplemented
|
||||
Time: 'time', // @notimplemented
|
||||
DateTime: 'datetime', // @notimplemented
|
||||
Status: 'enum@status',
|
||||
}
|
||||
|
||||
const $STATUS_VALUES = [
|
||||
{ Value: '1', Title: 'Created' },
|
||||
{ Value: '2', Title: 'Active' },
|
||||
{ Value: '3', Title: 'Inactive' },
|
||||
{ Value: '4', Title: 'Expired' }
|
||||
]
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// VUE INSTANCE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export default {
|
||||
name: 'UserFiltersRepeatingFormComponent',
|
||||
|
|
@ -149,6 +199,7 @@ export default {
|
|||
BFormGroup,
|
||||
BFormInput,
|
||||
BFormCheckbox,
|
||||
BFormDatepicker,
|
||||
vSelect,
|
||||
},
|
||||
|
||||
|
|
@ -162,24 +213,29 @@ export default {
|
|||
return {
|
||||
IsOpen_D: false,
|
||||
Filters_D: [],
|
||||
|
||||
RemainingTableColumns_D: [
|
||||
{ Value: 'first_name', Type: 'string', Title: 'First Name' },
|
||||
{ Value: 'email_address', Type: 'string', Title: 'Email' },
|
||||
{ Value: 'is_main', Type: 'boolean', Title: 'Firm' },
|
||||
{ Value: 'subscription__charge', Type: 'integer', Title: 'Charge' },
|
||||
{ Value: 'total_uploaded_size', Type: 'integer', Title: 'Uploaded Size' },
|
||||
{ Value: 'total_firm_uploaded_size', Type: 'integer', Title: 'Firm Uploaded Size' },
|
||||
],
|
||||
Conditions_D: [
|
||||
{ Value: 'isnull', Title: 'Is Empty' },
|
||||
{ Value: '=', Title: 'Equals' },
|
||||
{ Value: '!=', Title: 'Not Equals' },
|
||||
{ Value: '>', Title: 'Greater Than' },
|
||||
{ Value: '>=', Title: 'Greater Than or Equals' },
|
||||
{ Value: '<', Title: 'Less Than' },
|
||||
{ Value: '<=', Title: 'Less Than or Equals' },
|
||||
{ Value: 'in', Title: 'Contains' },
|
||||
{ Value: 'status', Type: $TYPES.Status, Title: 'Status' },
|
||||
{ Value: 'from_date', Type: $TYPES.Date, Title: 'Start Date' },
|
||||
{ Value: 'to_date', Type: $TYPES.Date, Title: 'End Date' },
|
||||
// { Value: 'first_name', Type: $TYPES.Str, Title: 'First Name' },
|
||||
// { Value: 'email_address', Type: $TYPES.Str, Title: 'Email' },
|
||||
// { Value: 'is_main', Type: $TYPES.Bool, Title: 'Firm' },
|
||||
// { Value: 'subscription__charge', Type: $TYPES.Int, Title: 'Charge' },
|
||||
// { Value: 'total_uploaded_size', Type: 'integer', Title: 'Uploaded Size' },
|
||||
// { Value: 'total_firm_uploaded_size', Type: 'integer', Title: 'Firm Uploaded Size' },
|
||||
],
|
||||
|
||||
// Conditions_D: [
|
||||
// // { Value: 'isnull', Title: 'Is Empty' },
|
||||
// { Value: 'eq', Title: 'Equals' },
|
||||
// { Value: '!=', Title: 'Not Equals' },
|
||||
// { Value: '>', Title: 'Greater Than' },
|
||||
// { Value: '>=', Title: 'Greater Than or Equals' },
|
||||
// { Value: '<', Title: 'Less Than' },
|
||||
// { Value: '<=', Title: 'Less Than or Equals' },
|
||||
// // { Value: 'in', Title: 'Contains' },
|
||||
// ],
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -194,7 +250,7 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
// Initializes the height of the transition
|
||||
// Initializes the height of the transition.
|
||||
InitTrHeight()
|
||||
{
|
||||
this.trSetHeight(null)
|
||||
|
|
@ -203,11 +259,12 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
// Adds a new row to the filters form repeater
|
||||
// Adds a new row to the filters form repeater.
|
||||
Repeat()
|
||||
{
|
||||
const FieldType = this.RemainingTableColumns_D[0].Type
|
||||
const Condition = FieldType === 'boolean' ? this.Conditions_D[1] : this.Conditions_D[0]
|
||||
// const Condition = FieldType === 'boolean' ? $CONDITIONS.Eq : $CONDITIONS.IsNull
|
||||
const Condition = $CONDITIONS.Eq
|
||||
this.Filters_D.push({
|
||||
Field: this.RemainingTableColumns_D[0],
|
||||
Condition,
|
||||
|
|
@ -223,7 +280,7 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
// Deletes a row from the filters form repeater
|
||||
// Deletes a row from the filters form repeater.
|
||||
Remove(Index)
|
||||
{
|
||||
this.RemainingTableColumns_D.push(this.Filters_D[Index].Field)
|
||||
|
|
@ -232,25 +289,39 @@ export default {
|
|||
this.trTrimHeight(this.$refs.row[0].offsetHeight)
|
||||
},
|
||||
|
||||
// Removes all filters
|
||||
// Removes all filters.
|
||||
Clear()
|
||||
{
|
||||
this.Filters_D.forEach(Item => this.RemainingTableColumns_D.push(Item.Field))
|
||||
this.RemainingTableColumns_D.sort((a, b) => a.Title.localeCompare(b.Title))
|
||||
this.Filters_D = []
|
||||
this.InitTrHeight()
|
||||
this.$nextTick(() => {
|
||||
this.trSetHeight(0)
|
||||
})
|
||||
// this.InitTrHeight()
|
||||
},
|
||||
|
||||
// Applies the filters to the table
|
||||
// Applies the filters to the table.
|
||||
ApplyFilters()
|
||||
{
|
||||
this.$emit('submit', this.Filters_D.map(Item => {
|
||||
var Arguments = this.Filters_D.map(Item => {
|
||||
return {
|
||||
field: Item.Field.Value,
|
||||
condition: Item.Condition.Value,
|
||||
value: Item.Value,
|
||||
// Amini Filters:
|
||||
// col: Item.Field.Value,
|
||||
// op: Item.Condition.Value,
|
||||
// val: Item.Value,
|
||||
|
||||
// Gheychisaz Filters:
|
||||
type: Item.Field.Value,
|
||||
opr: Item.Condition.Value,
|
||||
val: (typeof Item.Value === 'object') ? Item.Value.Value : Item.Value,
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
Arguments = Arguments[0]
|
||||
|
||||
|
||||
this.$emit('submit', Arguments)
|
||||
this.IsOpen_D = false
|
||||
// if( this.Query_D.IsMain )
|
||||
// this.table.query.query.push({
|
||||
|
|
@ -265,11 +336,13 @@ export default {
|
|||
{
|
||||
this.RemainingTableColumns_D.push(this.Filters_D[Index].Field)
|
||||
this.Filters_D[Index].Field = Value
|
||||
this.Filters_D[Index].Condition = Value.Type === 'boolean' ? this.Conditions_D[1] : this.Conditions_D[0]
|
||||
// this.Filters_D[Index].Condition = Value.Type === 'boolean' ? $CONDITIONS.Eq : $CONDITIONS.IsNull
|
||||
this.Filters_D[Index].Condition = $CONDITIONS.Eq
|
||||
this.Filters_D[Index].Value =
|
||||
Value.Type === 'boolean' ? false :
|
||||
Value.Type === 'integer' ? 0 :
|
||||
Value.Type === 'string' ? '' : undefined,
|
||||
Value.Type === 'string' ? '' :
|
||||
Value.Type === $TYPES.Status ? $STATUS_VALUES[0] : undefined,
|
||||
this.RemainingTableColumns_D = this.RemainingTableColumns_D.filter(Item => Item.Value !== Value.Value)
|
||||
this.RemainingTableColumns_D.sort((a, b) => a.Title.localeCompare(b.Title))
|
||||
},
|
||||
|
|
@ -283,13 +356,34 @@ export default {
|
|||
this.Filters_D[Index].Value !== undefined ? this.Filters_D[Index].Value :
|
||||
this.Filters_D[Index].Field.Type === 'boolean' ? false :
|
||||
this.Filters_D[Index].Field.Type === 'integer' ? 0 :
|
||||
this.Filters_D[Index].Field.Type === 'string' ? '' : undefined
|
||||
this.Filters_D[Index].Field.Type === 'string' ? '' :
|
||||
this.Filters_D[Index].Field.Type === $TYPES.Status ? $STATUS_VALUES[0] : undefined
|
||||
},
|
||||
|
||||
// Returns the available conditions for the specified column name.
|
||||
GetConditions(ColumnName)
|
||||
{
|
||||
if (ColumnName === 'status')
|
||||
{
|
||||
const ValidConditions = {}
|
||||
Object.keys($CONDITIONS).forEach(Key => {
|
||||
if (Key === 'Eq' || Key === 'NotEq')
|
||||
ValidConditions[Key] = $CONDITIONS[Key]
|
||||
})
|
||||
return Object.values(ValidConditions)
|
||||
}
|
||||
else return Object.values($CONDITIONS)
|
||||
},
|
||||
},
|
||||
|
||||
created()
|
||||
{
|
||||
window.addEventListener('resize', this.InitTrHeight)
|
||||
|
||||
// --- Expose Template Constants ---
|
||||
this.$CONDITIONS = $CONDITIONS
|
||||
this.$TYPES = $TYPES
|
||||
this.$STATUS_VALUES = $STATUS_VALUES
|
||||
},
|
||||
|
||||
mounted()
|
||||
|
|
@ -317,4 +411,28 @@ export default {
|
|||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
DOCUMENTATION:
|
||||
|
||||
INTRODUCTION:
|
||||
The responsibility of this component is to let the user create a custom array of
|
||||
objects, and return it through an event.
|
||||
|
||||
|
||||
LIST OF COLUMNS:
|
||||
To specify the list of columns you can filter, modify the RemainingTableColumns_D
|
||||
reactive variable in the data() section.
|
||||
|
||||
|
||||
ADDING NEW COLUMN TYPES:
|
||||
To add new column types, you must perform two steps:
|
||||
1. first you have to go to "const $TYPES" at the top of the script tag to
|
||||
add the new type.
|
||||
2. then you should go to $COLUMN_VALUES in the template section, and add
|
||||
the appropriate UI component.
|
||||
-->
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<template>
|
||||
<b-modal id="modal-show-user" v-bind:centered="true" size="lg">
|
||||
<!-- Header -->
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<h2 class="text-primary m-0">User's Information</h2>
|
||||
<img style="cursor: pointer;" src="@/assets/svg/cross.svg" alt="close-icon" v-on:click="close()">
|
||||
</template>
|
||||
|
||||
<!-- Body -->
|
||||
<template v-if="data" v-slot:default class="d-flex flex-column" style="">
|
||||
<b-row>
|
||||
<b-col>
|
||||
<img
|
||||
v-if="data.firm_avatar_url"
|
||||
v-bind:src="data.firm_avatar_url"
|
||||
alt="avatar"
|
||||
class="rounded-circle mb-2"
|
||||
style="object-fit: contain; width: 6rem; height: 6rem;"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
src="@/assets/images/default-profile.jpg"
|
||||
alt="avatar"
|
||||
class="rounded-circle mb-2"
|
||||
style="object-fit: contain; width: 6rem; height: 6rem;"
|
||||
>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row style="row-gap: 1rem;">
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">First Name:</span>
|
||||
<span class="info-value">{{ data.first_name }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Last Name:</span>
|
||||
<span class="info-value">{{ data.last_name }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Email:</span>
|
||||
<span class="info-value">{{ data.email_address }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Phone Number:</span>
|
||||
<span class="info-value">{{ data.phone_number }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Total Uploaded Size:</span>
|
||||
<span class="info-value">{{ FormatBytes(data.total_uploaded_size) }}</span>
|
||||
</b-col>
|
||||
<b-col md="6" xl="4">
|
||||
<span class="info-key">Is Firm:</span>
|
||||
<span class="info-value">
|
||||
<svg v-if="data.is_main" style="width: 24px; height: 24px;" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
<svg v-else style="width: 24px; height: 24px;" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
|
||||
<!-- Footer -->
|
||||
<template v-slot:modal-footer="{ hide }">
|
||||
<a class="text-primary" v-on:click="hide()">Close</a>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BForm, BButton, BRow, BCol } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import { SeparateNumberByThousands, FormatBytes } from '@/modules/core'
|
||||
|
||||
export default {
|
||||
name: 'AddUserModalComponent',
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
BForm,
|
||||
BButton,
|
||||
BRow,
|
||||
BCol,
|
||||
},
|
||||
|
||||
directives: {
|
||||
Ripple,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
FormatBytes(Bytes)
|
||||
{
|
||||
return FormatBytes(Bytes)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.info-key {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
.info-value {
|
||||
margin-inline-start: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -7,10 +7,11 @@
|
|||
<!-- </div>-->
|
||||
<ASpin :spinning="loading" :indicator="indicator">
|
||||
<b-row class="mb-4" style="row-gap: 0.75rem;">
|
||||
<b-col md="6">
|
||||
<b-col md="3">
|
||||
<b-input-group v-if="search">
|
||||
<b-form-input
|
||||
id="search"
|
||||
v-model="query.te.s.text"
|
||||
v-bind:placeholder="options.placeholder"
|
||||
>
|
||||
</b-form-input>
|
||||
|
|
@ -21,7 +22,20 @@
|
|||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</b-col>
|
||||
<b-col md="6" class="d-flex align-items-center">
|
||||
|
||||
<!-- Filters -->
|
||||
<b-col md="1">
|
||||
<b-button
|
||||
class="mb-1"
|
||||
variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
v-b-toggle.user-filters--sidebar
|
||||
>
|
||||
Filters
|
||||
</b-button>
|
||||
</b-col>
|
||||
|
||||
<b-col md="8" class="d-flex align-items-center">
|
||||
<a class="left-table-btn" v-on:click="fullScreen">
|
||||
<feather-icon
|
||||
v-bind:icon="getFullIcon"
|
||||
|
|
@ -71,16 +85,15 @@
|
|||
</b-row>
|
||||
|
||||
|
||||
<a-table
|
||||
@change="change"
|
||||
:indentSize="5"
|
||||
:pagination="false"
|
||||
ref="tbl"
|
||||
:locale="locale"
|
||||
:columns="visibleCols"
|
||||
:data-source="data"
|
||||
:row-selection="options.is_row_selection ? { selectedRowKeys: selectedRowKeys, onChange: onChange } : null"
|
||||
>
|
||||
<a-table @change="change"
|
||||
table-layout="auto"
|
||||
:indentSize="5"
|
||||
:pagination="false"
|
||||
ref="tbl"
|
||||
:locale="locale"
|
||||
:columns="visibleCols"
|
||||
:data-source="data"
|
||||
:row-selection="options.is_row_selection ? { selectedRowKeys: selectedRowKeys, onChange: onChange } : null">
|
||||
<template :slot="item" slot-scope="name, record, index" v-for="(item,ind) in customRender">
|
||||
<slot :name="item" :text="name" :record="record" :index="index"></slot>
|
||||
</template>
|
||||
|
|
@ -101,12 +114,12 @@
|
|||
v-if="is_pagenation"
|
||||
show-size-changer
|
||||
:pageSizeOptions="['5','10','20']"
|
||||
:page-size.sync="query.limit"
|
||||
:page-size.sync="query.te.p.s"
|
||||
@change="changePage"
|
||||
@showSizeChange="changePage"
|
||||
:default-current="1"
|
||||
:total="query.total"
|
||||
v-model.sync="query.page"
|
||||
v-model.sync="query.te.p.c"
|
||||
>
|
||||
<template slot="buildOptionText" slot-scope="props">
|
||||
<div dir="rtl">
|
||||
|
|
@ -148,6 +161,8 @@
|
|||
</a-table>
|
||||
</ASpin>
|
||||
</ACard>
|
||||
|
||||
<UserFiltersRepeatingForm v-on:submit="applyFilters($event)"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -161,7 +176,10 @@ import Draggable from 'vuedraggable'
|
|||
import axios from "../../axios";
|
||||
import {openFullscreen, closeFullscreen} from "@/plugins/fullscreen";
|
||||
import NumberXTableFilter from "./NumberXTableFilter";
|
||||
import { BRow, BCol, BInputGroup, BInputGroupAppend, BFormInput, BFormCheckbox, BButton } from 'bootstrap-vue'
|
||||
import { BRow, BCol, BInputGroup, BInputGroupAppend, BFormInput, BFormCheckbox, BButton, VBToggle } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
|
||||
import UserFiltersRepeatingForm from '@/components/ui/UserFiltersRepeatingForm.vue'
|
||||
|
||||
const list = [
|
||||
{
|
||||
|
|
@ -192,6 +210,12 @@ export default {
|
|||
BFormInput,
|
||||
BFormCheckbox,
|
||||
BButton,
|
||||
UserFiltersRepeatingForm,
|
||||
},
|
||||
|
||||
directives: {
|
||||
'b-toggle': VBToggle,
|
||||
Ripple,
|
||||
},
|
||||
|
||||
props: {
|
||||
|
|
@ -199,6 +223,7 @@ export default {
|
|||
is_pagenation: { type: Boolean, default: true },
|
||||
method: { type: String, default: "post" },
|
||||
search: { type: Boolean, default: true },
|
||||
ascending: { type: Boolean, default: true },
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
|
|
@ -217,13 +242,15 @@ export default {
|
|||
testTheme:[],
|
||||
list:[],
|
||||
query: {
|
||||
search: undefined,
|
||||
total: 0,
|
||||
limit: 10,
|
||||
page: 1,
|
||||
query: [],
|
||||
// search: undefined,
|
||||
// total: 0,
|
||||
// limit: 10,
|
||||
// page: 1,
|
||||
te: {
|
||||
f: [],
|
||||
p: { c: 1, s: 10 },
|
||||
s: { cols: null, text: null },
|
||||
o: { field: '', direction: '' },
|
||||
}
|
||||
},
|
||||
locale: {
|
||||
|
|
@ -270,13 +297,13 @@ export default {
|
|||
|
||||
changePage(item) {
|
||||
// this.query.page = item;
|
||||
this.query.te.p.c = item + 1
|
||||
this.query.te.p.c = item
|
||||
this.fetch();
|
||||
this.$emit('changePage')
|
||||
},
|
||||
|
||||
fullScreen() {
|
||||
console.log("this.$refs.tblCard.$el ==> ", this.$refs.tblCard.$el);
|
||||
// console.log("this.$refs.tblCard.$el ==> ", this.$refs.tblCard.$el);
|
||||
if (this.full) {
|
||||
closeFullscreen()
|
||||
this.full = false;
|
||||
|
|
@ -287,14 +314,19 @@ export default {
|
|||
},
|
||||
|
||||
requestData() {
|
||||
return this.query;
|
||||
this.query.te.o.field = 'id'
|
||||
if (this.ascending) this.query.te.o.direction = 'asc'
|
||||
else this.query.te.o.direction = 'desc'
|
||||
return this.query
|
||||
},
|
||||
|
||||
refresh ($event)
|
||||
{
|
||||
this.$emit('refresh')
|
||||
this.query.query = []
|
||||
this.query.search = undefined
|
||||
this.query.te.s.text = ""
|
||||
this.query.te.s.cols = []
|
||||
this.query.te.p.c = 1
|
||||
this.fetch()
|
||||
},
|
||||
|
||||
|
|
@ -303,14 +335,16 @@ export default {
|
|||
try {
|
||||
this.loading = true;
|
||||
if(this.method == "post"){
|
||||
const {data: {rows, query}} = await axios.post(this.model.url, {
|
||||
const {data: {rows, te, total, page}} = await axios.post(this.model.url, {
|
||||
...this.requestData()
|
||||
});
|
||||
this.data = rows;
|
||||
// this.query.page = query.page
|
||||
// this.query.limit = query.limit;
|
||||
// this.query.total = query.total;
|
||||
this.query.total = total;
|
||||
// this.query.max_page = query.max_page;
|
||||
this.query.te = te
|
||||
// this.query.te = Object.assign(te, { p: { c: te.p.c - 1 } })
|
||||
this.$emit('rows',rows)
|
||||
}else{
|
||||
const {data} = await axios.get(this.model.url);
|
||||
|
|
@ -331,9 +365,13 @@ export default {
|
|||
},
|
||||
|
||||
onSearch(value) {
|
||||
this.query.page = 1
|
||||
this.query.search = value
|
||||
this.query.query = [];
|
||||
// Old API
|
||||
// TODO: If not needed, just remove this.
|
||||
// this.query.page = 1
|
||||
// this.query.search = value
|
||||
|
||||
this.query.te.p.c = 1
|
||||
this.query.te.s.cols = ["id"]
|
||||
if (value) {
|
||||
this.fetch();
|
||||
}
|
||||
|
|
@ -341,7 +379,14 @@ export default {
|
|||
|
||||
showColSelector() {
|
||||
this.colSelector = !this.colSelector;
|
||||
}
|
||||
},
|
||||
|
||||
async applyFilters(filters)
|
||||
{
|
||||
console.log()
|
||||
this.query.te.fi = filters
|
||||
this.fetch()
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
"plan_id": "Plan",
|
||||
"priority": "Priority",
|
||||
"contract_id": "Contract Id",
|
||||
"value": ""
|
||||
"value": "",
|
||||
"email": "Email"
|
||||
},
|
||||
"message": {
|
||||
"title": "Card Title",
|
||||
|
|
|
|||
|
|
@ -12,73 +12,73 @@ export default [
|
|||
title: 'Users List',
|
||||
route: 'users-list',
|
||||
},
|
||||
{
|
||||
title: 'Add User',
|
||||
route: 'users-add',
|
||||
},
|
||||
{
|
||||
title: 'User Reports',
|
||||
route: 'users-reports',
|
||||
},
|
||||
{
|
||||
title: 'Deleted Users List',
|
||||
route: 'users-deleted',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Admins',
|
||||
icon: 'Edit2Icon',
|
||||
children: [
|
||||
{
|
||||
title: 'Add Admin',
|
||||
route: 'admins-add',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title : 'Permissions',
|
||||
icon : 'LockIcon' ,
|
||||
route : 'permissions',
|
||||
},
|
||||
{
|
||||
title : 'My Activities',
|
||||
icon : 'ActivityIcon' ,
|
||||
route : 'activities' ,
|
||||
},
|
||||
{
|
||||
title: 'Financial',
|
||||
icon: 'DollarSignIcon',
|
||||
children: [
|
||||
{
|
||||
title: 'Financial List',
|
||||
route: 'financial-list',
|
||||
},
|
||||
{
|
||||
title: 'Financial Reports',
|
||||
route: 'financial-reports',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Refunds',
|
||||
icon: 'RefreshCcwIcon',
|
||||
children: [
|
||||
{
|
||||
title: 'Refunds List',
|
||||
route: 'refunds-list',
|
||||
},
|
||||
{
|
||||
title: 'Add Refund',
|
||||
route: 'refunds-add',
|
||||
},
|
||||
{
|
||||
title: 'Refund Reports',
|
||||
route: 'refunds-reports',
|
||||
},
|
||||
// {
|
||||
// title: 'Add User',
|
||||
// route: 'users-add',
|
||||
// },
|
||||
// {
|
||||
// title: 'User Reports',
|
||||
// route: 'users-reports',
|
||||
// },
|
||||
// {
|
||||
// title: 'Deleted Users List',
|
||||
// route: 'users-deleted',
|
||||
// },
|
||||
],
|
||||
},
|
||||
// {
|
||||
// title: 'Admins',
|
||||
// icon: 'Edit2Icon',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Add Admin',
|
||||
// route: 'admins-add',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// title : 'Permissions',
|
||||
// icon : 'LockIcon' ,
|
||||
// route : 'permissions',
|
||||
// },
|
||||
// {
|
||||
// title : 'My Activities',
|
||||
// icon : 'ActivityIcon' ,
|
||||
// route : 'activities' ,
|
||||
// },
|
||||
// {
|
||||
// title: 'Financial',
|
||||
// icon: 'DollarSignIcon',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Financial List',
|
||||
// route: 'financial-list',
|
||||
// },
|
||||
// {
|
||||
// title: 'Financial Reports',
|
||||
// route: 'financial-reports',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// title: 'Refunds',
|
||||
// icon: 'RefreshCcwIcon',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Refunds List',
|
||||
// route: 'refunds-list',
|
||||
// },
|
||||
// {
|
||||
// title: 'Add Refund',
|
||||
// route: 'refunds-add',
|
||||
// },
|
||||
// {
|
||||
// title: 'Refund Reports',
|
||||
// route: 'refunds-reports',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// title: 'Lawyers',
|
||||
// icon: 'FeatherIcon',
|
||||
// children: [
|
||||
|
|
@ -146,22 +146,26 @@ export default [
|
|||
title: 'Subscription Rules',
|
||||
route: 'subscription-rules',
|
||||
},
|
||||
|
||||
// This was so old (Najjar-era) that was commented out. No need
|
||||
// anymore. Kept for the UI example.
|
||||
// {
|
||||
// title: 'Subscription Subscriptions',
|
||||
// route: 'subscription-subscriptions',
|
||||
// },
|
||||
{
|
||||
title: 'Subscription Transactions',
|
||||
route: 'subscription-transactions',
|
||||
},
|
||||
{
|
||||
title: 'Subscription Payments',
|
||||
route: 'subscription-payments',
|
||||
},
|
||||
{
|
||||
title: 'Subscription Contract Users',
|
||||
route: 'subscription-contractusers',
|
||||
},
|
||||
|
||||
// {
|
||||
// title: 'Subscription Transactions',
|
||||
// route: 'subscription-transactions',
|
||||
// },
|
||||
// {
|
||||
// title: 'Subscription Payments',
|
||||
// route: 'subscription-payments',
|
||||
// },
|
||||
// {
|
||||
// title: 'Subscription Contract Users',
|
||||
// route: 'subscription-contractusers',
|
||||
// },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
<!--
|
||||
DOCUMENTATION:
|
||||
|
||||
|
||||
Description:
|
||||
A modal that prompts for a user/lawyer/law firm's information.
|
||||
|
||||
|
||||
How to use:
|
||||
First, import the component, put it in the template section, and then use
|
||||
VBModal bootstrap-vue component and pass it modal-add-use argument to open the
|
||||
modal.
|
||||
|
||||
|
||||
Example:
|
||||
<b-button v-b-modal:modal-add-user>Add New User</b-button>
|
||||
|
||||
|
||||
Props:
|
||||
Name Type Description
|
||||
------ ------- --------------------------------------------------------------
|
||||
data Object The JSON to initialize the user's data with
|
||||
|
||||
|
||||
Events
|
||||
Name Parameters Description
|
||||
-------- ----------- ----------------------------------------------------------
|
||||
|
||||
-->
|
||||
|
||||
<template>
|
||||
<b-modal id="modal-edit-user" v-bind:centered="true" size="lg" v-on:shown="OnModalShow()">
|
||||
<!-- Header -->
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<h2 class="text-primary m-0">Edit User</h2>
|
||||
<img style="cursor: pointer;" src="@/assets/svg/cross.svg" alt="close-icon" v-on:click="close()">
|
||||
</template>
|
||||
|
||||
<!-- Body -->
|
||||
<template v-slot:default>
|
||||
<validation-observer ref="editUserForm">
|
||||
<b-form class="d-flex flex-column">
|
||||
<b-row>
|
||||
<b-col md="6">
|
||||
<b-form-group label="Payed Amount" label-for="txtPayedAmount">
|
||||
<b-form-input id="txtPayedAmount" v-model="Payload_D.payed_amount" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-group label="Payable Amount" label-for="txtPayableAmount">
|
||||
<b-form-input id="txtPayableAmount" v-model="Payload_D.payable_amount" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-group label="Total Price" label-for="txtTotalPrice">
|
||||
<b-form-input id="txtTotalPrice" v-model="Payload_D.total_price" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-group label="Installments Count" label-for="txtInstallmentsCount">
|
||||
<b-form-input id="txtInstallmentsCount" v-model="Payload_D.installments_count" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<b-col md="6">
|
||||
<b-form-group label="User Usage" label-for="txtUserUsage">
|
||||
<b-form-input id="txtUserUsage" v-model="Payload_D.user_usage" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-group label="User Limit" label-for="txtUserLimit">
|
||||
<b-form-input id="txtUserLimit" v-model="Payload_D.user_limit" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<!-- NOTE: Doesn't makes sense to edit storage usage. It specifies how much of the
|
||||
storage given to the user was used by him/her. Manually editing it introduces buggy
|
||||
behavior to the program. -->
|
||||
<!-- <b-col md="6">
|
||||
<b-form-group label="Storage Usage" label-for="txtStorageUsage">
|
||||
<b-form-input id="txtStorageUsage" v-model="Payload_D.storage_usage" />
|
||||
</b-form-group>
|
||||
</b-col> -->
|
||||
<b-col md="6">
|
||||
<b-form-group label="Storage Limit (MB)" label-for="Storage Limit">
|
||||
<validation-provider
|
||||
v-slot:default="{ errors }"
|
||||
name="Storage Limit"
|
||||
v-bind:rules="{ required, regex: /^\d+(\.\d+)?$/ }">
|
||||
<b-form-input
|
||||
id="Storage Limit"
|
||||
v-model="Payload_D.storage_limit"
|
||||
v-bind:state="errors.length > 0 ? false : null" />
|
||||
<small class="text-danger">{{ errors[0] }}</small>
|
||||
</validation-provider>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<b-col md="6">
|
||||
<b-form-group label="User Type" label-for="dpdUserType">
|
||||
<v-select id="dpdUserType"
|
||||
v-bind:options="TypeOptions_D"
|
||||
label="title"
|
||||
v-model="Payload_D.type"
|
||||
v-bind:clearable="false" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-group label="Status" label-for="dpdStatus">
|
||||
<v-select id="dpdStatus"
|
||||
v-bind:options="StatusOptions_D"
|
||||
label="title"
|
||||
v-model="Payload_D.status"
|
||||
v-bind:clearable="false" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<b-col md="6">
|
||||
<b-form-group label="Start Date" label-for="dpStartDate">
|
||||
<b-form-datepicker
|
||||
id="dpStartDate"
|
||||
v-model="Payload_D.from_date"
|
||||
v-bind:date-format-options="{ day: 'numeric', month: 'long', year: 'numeric' }"
|
||||
today-button
|
||||
close-button
|
||||
label-help=""
|
||||
nav-button-variant="primary"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-group label="End Date" label-for="dpToDate">
|
||||
<b-form-datepicker
|
||||
id="dpToDate"
|
||||
v-model="Payload_D.to_date"
|
||||
v-bind:date-format-options="{ day: 'numeric', month: 'long', year: 'numeric' }"
|
||||
today-button
|
||||
close-button
|
||||
label-help=""
|
||||
nav-button-variant="primary"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
|
||||
<!--
|
||||
<b-col class="my-1" md="5" lg="4">
|
||||
<b-form-group label="Gender">
|
||||
<div class="d-flex" style="column-gap: 1rem;">
|
||||
<b-form-radio value="0" name="gender">Male</b-form-radio>
|
||||
<b-form-radio value="1" name="gender">Female</b-form-radio>
|
||||
</div>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col class="my-1" md="5" lg="4">
|
||||
<b-form-group label="Martial Status">
|
||||
<div class="d-flex" style="column-gap: 1rem;">
|
||||
<b-form-radio value="0" name="martial-status">Married</b-form-radio>
|
||||
<b-form-radio value="1" name="martial-status">Single</b-form-radio>
|
||||
</div>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col class="my-1" md="5" lg="4">
|
||||
<b-form-group label="Status">
|
||||
<div class="d-flex" style="column-gap: 1rem;">
|
||||
<b-form-radio value="0" name="status">Active</b-form-radio>
|
||||
<b-form-radio value="1" name="status">Inactive</b-form-radio>
|
||||
</div>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col class="add-user-form--bottom-row" md="12">
|
||||
<b-form-group id="phone-number" label="Phone Number:">
|
||||
<div style="display: flex; align-items: center; column-gap: 1rem;">
|
||||
<b-form-input type="number" v-bind:max="3" />
|
||||
<span style="min-width: 0.83rem;">- -</span>
|
||||
<b-form-input type="number" v-bind:max="4" />
|
||||
<b-form-input type="number" v-bind:max="4" />
|
||||
<b-form-input type="number" v-bind:max="4" />
|
||||
</div>
|
||||
</b-form-group>
|
||||
<b-form-group v-if="type === 'user'" id="password" label="Password:">
|
||||
<b-form-input />
|
||||
</b-form-group>
|
||||
<b-form-group id="address" label="Address:">
|
||||
<b-form-textarea rows="4" no-resize style="margin-top: 0.2rem;" />
|
||||
</b-form-group>
|
||||
</b-col> -->
|
||||
</b-row>
|
||||
</b-form>
|
||||
</validation-observer>
|
||||
</template>
|
||||
|
||||
<!-- Footer -->
|
||||
<template v-slot:modal-footer="{ hide }">
|
||||
<b-button class="mr-1"
|
||||
variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
v-on:click="OnSubmitButtonClicked(hide)">
|
||||
Update
|
||||
</b-button>
|
||||
or
|
||||
<a class="text-primary" v-on:click="hide()">Cancel</a>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ValidationProvider, ValidationObserver } from 'vee-validate'
|
||||
import { BForm, BButton, BRow, BCol, BFormGroup, BFormInput, BFormDatepicker, BFormRadio, BFormTextarea } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import vSelect from 'vue-select'
|
||||
import axios from '@/axios'
|
||||
import { regex, required } from '@validations'
|
||||
|
||||
export default {
|
||||
name: 'AddUserModalComponent',
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
BForm,
|
||||
BButton,
|
||||
BRow,
|
||||
BCol,
|
||||
BFormGroup,
|
||||
BFormInput,
|
||||
BFormDatepicker,
|
||||
BFormRadio,
|
||||
BFormTextarea,
|
||||
vSelect,
|
||||
},
|
||||
|
||||
directives: {
|
||||
Ripple,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
Payload_D: {
|
||||
payed_amount: '',
|
||||
installments_count: '',
|
||||
from_date: '',
|
||||
user_limit: '',
|
||||
status: '',
|
||||
storage_limit: '',
|
||||
to_date: '',
|
||||
type: '',
|
||||
// storage_usage: '', // NOTE: While possible, don't edit this value.
|
||||
total_price: '',
|
||||
allowed_delay_days: '',
|
||||
user_usage: '',
|
||||
discounted_price: '',
|
||||
payable_amount: '',
|
||||
},
|
||||
UserId_D: -1,
|
||||
StatusOptions_D: [
|
||||
{ value: 1, title: 'Created' },
|
||||
{ value: 2, title: 'Active' },
|
||||
{ value: 3, title: 'Deactivate' },
|
||||
{ value: 4, title: 'Expired' },
|
||||
],
|
||||
TypeOptions_D: [
|
||||
{ value: 1, title: 'From Plans' },
|
||||
{ value: 2, title: 'Manual' },
|
||||
{ value: 3, title: 'Free' },
|
||||
],
|
||||
|
||||
// Validation Rules
|
||||
regex,
|
||||
required
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
OnSubmitButtonClicked(Hide)
|
||||
{
|
||||
this.$refs.editUserForm.validate().then(success => {
|
||||
if (success)
|
||||
{
|
||||
this.$store.commit('app/toggle_full_page_overlay')
|
||||
|
||||
const tmp_payload = Object.assign({}, this.Payload_D)
|
||||
tmp_payload.type = tmp_payload.type.value
|
||||
tmp_payload.status = tmp_payload.status.value
|
||||
tmp_payload.storage_limit = tmp_payload.storage_limit * 1024 * 1024 // Convert from MegaByte to Byte
|
||||
|
||||
axios.put(`admin/api/v1/contract/${this.UserId_D}`, tmp_payload)
|
||||
.then(response => {
|
||||
this.$bvToast.toast('User updated successfully.', {
|
||||
title: 'Success',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: false,
|
||||
variant: 'success',
|
||||
})
|
||||
Hide()
|
||||
this.$emit('updated')
|
||||
})
|
||||
.catch(error => {
|
||||
this.$bvToast.toast('There was an error while updating the user. Please try again later.', {
|
||||
title: 'Failure',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: false,
|
||||
variant: 'danger',
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.$store.commit('app/toggle_full_page_overlay')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
OnModalShow()
|
||||
{
|
||||
this.UserId_D = this.data.id
|
||||
|
||||
this.Payload_D.payed_amount = this.data.payed_amount
|
||||
this.Payload_D.installments_count = this.data.installments_count
|
||||
this.Payload_D.from_date = this.data.from_date
|
||||
this.Payload_D.user_limit = this.data.user_limit
|
||||
this.Payload_D.storage_limit = (this.data.storage_limit / 1024 / 1024).toFixed(4) // Convert from Byte to MegaByte
|
||||
this.Payload_D.to_date = this.data.to_date
|
||||
this.Payload_D.storage_usage = this.data.storage_usage
|
||||
this.Payload_D.total_price = this.data.total_price
|
||||
this.Payload_D.allowed_delay_days = this.data.allowed_delay_days
|
||||
this.Payload_D.user_usage = this.data.user_usage
|
||||
this.Payload_D.discounted_price = this.data.discounted_price
|
||||
this.Payload_D.payable_amount = this.data.payable_amount
|
||||
|
||||
this.Payload_D.type = {
|
||||
value: this.data.type,
|
||||
title: this.TypeOptions_D
|
||||
.reduce((Total, CurrentValue) => (CurrentValue.value === this.data.type) ? CurrentValue.title : Total, '')
|
||||
}
|
||||
|
||||
this.Payload_D.status = {
|
||||
value: this.data.status,
|
||||
title: this.StatusOptions_D
|
||||
.reduce((Total, CurrentValue) => (CurrentValue.value === this.data.status) ? CurrentValue.title : Total, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
<!--
|
||||
Documentation:
|
||||
|
||||
Description:
|
||||
A modal that sends the an specified invoice to one or more emails.
|
||||
|
||||
|
||||
How to use:
|
||||
First, import the component, put it in the template section, and then use
|
||||
VBModal bootstrap-vue component and pass it modal-add-use argument to open the
|
||||
modal.
|
||||
|
||||
|
||||
Example:
|
||||
Opening the modal via HTML:
|
||||
|
||||
<b-button v-b-modal:modal-send-invoice>Add New User</b-button>
|
||||
|
||||
Or opening the modal via code:
|
||||
|
||||
this.$bvModal.show('modal-send-invoice')
|
||||
|
||||
|
||||
Props:
|
||||
Name Type Description
|
||||
------ ------- --------------------------------------------------------------
|
||||
user_id Number The 'id' of the user whose invoice will be sent to the emails
|
||||
|
||||
-->
|
||||
|
||||
<template>
|
||||
<b-modal id="modal-send-invoice"
|
||||
v-model="ModalVisible_D"
|
||||
v-bind:centered="true"
|
||||
size="lg"
|
||||
v-on:shown="OnModalShown()">
|
||||
<!-- Header -->
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<h2 class="text-primary m-0">Send Invoice</h2>
|
||||
<img style="cursor: pointer;" src="@/assets/svg/cross.svg" alt="close-icon" v-on:click="close()">
|
||||
</template>
|
||||
|
||||
<!-- Body -->
|
||||
<template v-slot:default>
|
||||
<transition>
|
||||
<b-form ref="emails"
|
||||
id="emails-repeater-form"
|
||||
class="d-flex flex-column"
|
||||
v-bind:style="{ height: trHeight }">
|
||||
<b-row ref="row"
|
||||
v-for="(Email, Index) in Emails_D"
|
||||
v-bind:key="Index">
|
||||
<b-col md="9">
|
||||
<b-form-group label="Email" label-for="txtFirstName">
|
||||
<b-form-input v-model="Email.Value" />
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col lg="3" class="mb-1">
|
||||
<b-form-group label=" ">
|
||||
<b-button v-ripple.400="'rgba(234, 84, 85, 0.15)'"
|
||||
variant="outline-danger"
|
||||
class="mt-md-1"
|
||||
v-on:click="Remove(Index)"
|
||||
>
|
||||
<feather-icon icon="XIcon" class="mr-25" />
|
||||
<span>Delete</span>
|
||||
</b-button>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<!-- Footer -->
|
||||
<template v-slot:modal-footer="{ hide }">
|
||||
<b-button variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
style="height: 38px;"
|
||||
v-on:click="Clear()">
|
||||
Clear All Emails
|
||||
</b-button>
|
||||
<b-button class="mr-4"
|
||||
variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
style="height: 38px;"
|
||||
v-on:click="Repeat()">
|
||||
Add Email
|
||||
</b-button>
|
||||
<b-button
|
||||
class="mr-1"
|
||||
variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
v-on:click="OnSubmitButtonClicked(hide)"
|
||||
>
|
||||
Send Invoice
|
||||
</b-button>
|
||||
or
|
||||
<a class="text-primary" v-on:click="hide()">Cancel</a>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BForm, BButton, BRow, BCol, BFormGroup, BFormInput } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import { heightTransition } from '@core/mixins/ui/transition'
|
||||
import axios from '@/axios'
|
||||
|
||||
export default {
|
||||
name: 'SendInvoiceModalComponent',
|
||||
|
||||
props: {
|
||||
user_id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
BForm,
|
||||
BButton,
|
||||
BRow,
|
||||
BCol,
|
||||
BFormGroup,
|
||||
BFormInput,
|
||||
},
|
||||
|
||||
directives: {
|
||||
Ripple,
|
||||
},
|
||||
|
||||
mixins: [ heightTransition ],
|
||||
|
||||
data() {
|
||||
return {
|
||||
Emails_D: [{ Value: '' }],
|
||||
|
||||
// This exists so we can check whether the modal is opened or closed.
|
||||
// Do not use to close or open the modal.
|
||||
ModalVisible_D: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// Initializes the height of the transition.
|
||||
InitTrHeight()
|
||||
{
|
||||
if (this.ModalVisible_D)
|
||||
{
|
||||
this.trSetHeight(null)
|
||||
this.$nextTick(() => {
|
||||
this.trSetHeight(this.$refs.emails.scrollHeight)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// Adds a new row to the emails form repeater.
|
||||
Repeat()
|
||||
{
|
||||
this.Emails_D.push({ Value: '' })
|
||||
this.$nextTick(() => {
|
||||
this.trAddHeight(this.$refs.row[0].offsetHeight)
|
||||
})
|
||||
},
|
||||
|
||||
// Deletes a row from the emails form repeater.
|
||||
Remove(Index)
|
||||
{
|
||||
if (this.Emails_D.length === 1)
|
||||
{
|
||||
this.$bvToast.toast('There must be at least one email!', {
|
||||
title: 'Error',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: false,
|
||||
variant: 'danger',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.Emails_D.splice(Index, 1)
|
||||
this.trTrimHeight(this.$refs.row[0].offsetHeight)
|
||||
},
|
||||
|
||||
// Removes all emails.
|
||||
Clear()
|
||||
{
|
||||
this.Emails_D = [{ Value: '' }]
|
||||
this.trSetHeight('86.975')
|
||||
},
|
||||
|
||||
OnSubmitButtonClicked(Hide)
|
||||
{
|
||||
this.$store.commit('app/toggle_full_page_overlay')
|
||||
const data = { to_emails: this.Emails_D.map(item => item.Value) }
|
||||
axios.post(`admin/api/v1/send-invoice/${this.user_id}`, data)
|
||||
.then(response => {
|
||||
this.$bvToast.toast('Invoice was sent successfully.', {
|
||||
title: 'Success',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: false,
|
||||
variant: 'success',
|
||||
})
|
||||
Hide()
|
||||
})
|
||||
.catch(error => {
|
||||
this.$bvToast.toast('There was an error while sending the invoice. Please try again later.', {
|
||||
title: 'Failure',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: false,
|
||||
variant: 'danger',
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.$store.commit('app/toggle_full_page_overlay')
|
||||
})
|
||||
},
|
||||
|
||||
OnModalShown()
|
||||
{
|
||||
this.Clear()
|
||||
},
|
||||
},
|
||||
|
||||
created()
|
||||
{
|
||||
window.addEventListener('resize', this.InitTrHeight)
|
||||
},
|
||||
|
||||
destroyed()
|
||||
{
|
||||
window.removeEventListener('resize', this.InitTrHeight)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#emails-repeater-form {
|
||||
transition: 0.35s height;
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<b-modal id="modal-show-subusers"
|
||||
v-bind:centered="true"
|
||||
size="lg"
|
||||
v-on:shown="OnShown()"
|
||||
v-on:hide="OnHide()">
|
||||
<!-- Header -->
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<h2 class="text-primary m-0">{{ data.OwnerName }}'s Sub-users</h2>
|
||||
<img style="cursor: pointer;" src="@/assets/svg/cross.svg" alt="close-icon" v-on:click="close()">
|
||||
</template>
|
||||
|
||||
<!-- Body -->
|
||||
<template v-slot:default>
|
||||
<div v-if="Loading_D === 'pending'" class="text-center my-4">
|
||||
<b-spinner variant="secondary" label="Spinning"></b-spinner>
|
||||
</div>
|
||||
<div v-else-if="Loading_D === 'success' && Data_D.length > 0" style="overflow-x: auto;">
|
||||
<b-table striped hover v-bind:items="FilteredData()" class="mt-1"></b-table>
|
||||
</div>
|
||||
<b-alert v-else-if="Loading_D === 'success' && Data_D.length === 0" show variant="secondary" class="mt-1 py-1 px-2">
|
||||
This account currently has no subusers.
|
||||
</b-alert>
|
||||
<b-alert v-else-if="Loading_D === 'failure'" show variant="danger" class="mt-1 py-1 px-2">
|
||||
There was an error with the internet connection. Please try again.
|
||||
</b-alert>
|
||||
</template>
|
||||
|
||||
<!-- Footer -->
|
||||
<template v-slot:modal-footer="{ hide }">
|
||||
<a class="text-primary" v-on:click="hide()">Close</a>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BForm, BButton, BRow, BCol, BSpinner, BTable, BAlert } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import axios from '@/axios'
|
||||
|
||||
export default {
|
||||
name: 'ShowSubusersModalComponent',
|
||||
|
||||
props: {
|
||||
data: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
Loading_D: 'pending',
|
||||
Data_D: [],
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
BForm,
|
||||
BButton,
|
||||
BRow,
|
||||
BCol,
|
||||
BSpinner,
|
||||
BTable,
|
||||
BAlert,
|
||||
},
|
||||
|
||||
directives: {
|
||||
Ripple,
|
||||
},
|
||||
|
||||
methods: {
|
||||
async OnShown()
|
||||
{
|
||||
try
|
||||
{
|
||||
const { data } = await axios.get(`admin/api/v1/subuser/${this.data.OwnerId}`)
|
||||
this.Data_D = data.users
|
||||
this.Loading_D = 'success'
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
this.Loading_D = 'failure'
|
||||
}
|
||||
},
|
||||
|
||||
OnHide()
|
||||
{
|
||||
this.Data_D = []
|
||||
this.Loading_D = 'pending'
|
||||
},
|
||||
|
||||
FilteredData()
|
||||
{
|
||||
var index = 0
|
||||
return this.Data_D.map(item => {
|
||||
index = index + 1
|
||||
return {
|
||||
id: index,
|
||||
name: `${item.first_name} ${item.last_name}`,
|
||||
email_address: item.email_address,
|
||||
phone_number: item.phone_number,
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
|
@ -1,21 +1,23 @@
|
|||
<template>
|
||||
<!-- <div v-bind:class="{ 'page--main': Users_D.length === 0 }"> -->
|
||||
<div class="page--main">
|
||||
<FullPageLoadingComponent v-bind:show="$store.state.app.FullPageOverlayVisible" />
|
||||
|
||||
<!----------------------------- Title ----------------------------->
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3>Users List</h3>
|
||||
<div v-if="Users_D.length > 0" class="sx2">
|
||||
<!-- <div v-if="Users_D.length > 0" class="sx2">
|
||||
<b-button variant="danger" v-ripple.400="'rgba(255, 255, 255, 0.15)'">Remove</b-button>
|
||||
<b-button variant="primary" v-ripple.400="'rgba(255, 255, 255, 0.15)'" v-b-modal:modal-add-user>
|
||||
Add
|
||||
</b-button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!------------------------------ Body ----------------------------->
|
||||
<b-card class="position-relative mt-1" style="min-height: 95%;">
|
||||
<!-- No Users Exist -->
|
||||
<div id="no-users--container" v-if="Users_D.length === 0" class="d-flex flex-column justify-content-center align-items-center">
|
||||
<!-- <div id="no-users--container" v-if="Users_D.length === 0" class="d-flex flex-column justify-content-center align-items-center">
|
||||
<img src="@/assets/svg/hero.svg" alt="no-users-found">
|
||||
<h4 class="mt-2" style="text-align: center;">You haven't created any users.</h4>
|
||||
<p style="text-align: center;">Click the following button to create a new user.</p>
|
||||
|
|
@ -26,92 +28,132 @@
|
|||
>
|
||||
Add User
|
||||
</b-button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div v-else>
|
||||
<!-- Filters -->
|
||||
<b-button
|
||||
class="mb-1"
|
||||
variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
v-b-toggle.user-filters--sidebar
|
||||
>
|
||||
Filters
|
||||
</b-button>
|
||||
<div>
|
||||
<!-- LIST OF USERS -->
|
||||
<XTable ref="users"
|
||||
v-bind:model="Model_D"
|
||||
v-on:selectedRows="SelectedRows_D"
|
||||
v-bind:options="TableOptions_D"
|
||||
v-bind:ascending="false">
|
||||
<span slot="avatar" slot-scope="text, record">
|
||||
<img v-if="text.record.owner.firm_avatar_url"
|
||||
v-bind:src="text.record.owner.firm_avatar_url"
|
||||
alt="avatar"
|
||||
class="rounded-circle"
|
||||
style="object-fit: contain; width: 3rem; height: 3rem;">
|
||||
|
||||
<img v-else
|
||||
src="@/assets/images/default-profile.jpg"
|
||||
alt="avatar"
|
||||
class="rounded-circle"
|
||||
style="object-fit: contain; width: 3rem; height: 3rem;">
|
||||
</span>
|
||||
|
||||
<!-- List of Users -->
|
||||
<XTable
|
||||
ref="users"
|
||||
v-bind:model="Model_D"
|
||||
v-on:selectedRows="SelectedRows_D"
|
||||
v-bind:options="TableOptions_D"
|
||||
>
|
||||
<span slot="firm" slot-scope="text, record">
|
||||
<svg v-if="text.record.is_main" style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
<svg v-else style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||
</svg>
|
||||
<span slot="name" slot-scope="text, record" class="d-flex justify-content-between">
|
||||
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 12rem;">
|
||||
{{ `${text.record.owner.first_name} ${text.record.owner.last_name}` }}
|
||||
</span>
|
||||
|
||||
<a class="text-primary"
|
||||
style="margin-inline-start: 1rem;"
|
||||
v-on:click="OpenModal('user', text.record.owner)">
|
||||
More...
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<span slot="charge" slot-scope="text, record">
|
||||
<span v-if="text.record.subscription">${{ SeparateNumberByThousands(text.record.subscription.charge) }}</span>
|
||||
<!-- <span v-if="text.record.subscription">$ {{ text.record.subscription.charge }}</span> -->
|
||||
<span v-else>$0</span>
|
||||
<span slot="status" slot-scope="text, record">
|
||||
<b-alert v-if="text.record.status === 1" show variant="secondary" style="text-align: center; padding: 0.2rem 0.5rem;">Created</b-alert>
|
||||
<b-alert v-else-if="text.record.status === 2" show variant="success" style="text-align: center; padding: 0.2rem 0.5rem;">Active</b-alert>
|
||||
<b-alert v-else-if="text.record.status === 3" show variant="warning" style="text-align: center; padding: 0.2rem 0.5rem;">Inactive</b-alert>
|
||||
<b-alert v-else-if="text.record.status === 4" show variant="danger" style="text-align: center; padding: 0.2rem 0.5rem;">Expired</b-alert>
|
||||
</span>
|
||||
|
||||
<span slot="total_uploaded_size" slot-scope="text, record">
|
||||
<span>{{ FormatBytes(text.record.total_uploaded_size) }}</span>
|
||||
<span slot="plan" slot-scope="text, record">
|
||||
<a v-if="text.record.plan"
|
||||
class="text-primary"
|
||||
v-on:click="OpenModal('plan', text.record.plan)">
|
||||
{{ text.record.plan.title }}
|
||||
</a>
|
||||
<span v-else>-</span>
|
||||
</span>
|
||||
|
||||
<span slot="total_firm_uploaded_size" slot-scope="text, record">
|
||||
<span>{{ FormatBytes(text.record.total_firm_uploaded_size) }}</span>
|
||||
<span slot="storage_usage" slot-scope="text, record">
|
||||
<span style="white-space: nowrap;">{{ FormatBytes(text.record.storage_usage) }}</span>
|
||||
</span>
|
||||
|
||||
<span slot="action" slot-scope="text, record">
|
||||
<b-button
|
||||
v-if="text.record.is_main"
|
||||
variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
v-b-modal:modal-charge-user
|
||||
v-on:click="IdOfUserToCharge_D = text.record.id"
|
||||
>
|
||||
Charge
|
||||
<span slot="storage_limit" slot-scope="text, record">
|
||||
<span style="white-space: nowrap;">{{ FormatBytes(text.record.storage_limit) }}</span>
|
||||
</span>
|
||||
|
||||
<span slot="from_date" slot-scope="text, record">
|
||||
<span style="white-space: nowrap;">{{ GetDate(text.record.from_date) }}</span>
|
||||
</span>
|
||||
|
||||
<span slot="to_date" slot-scope="text, record">
|
||||
<span style="white-space: nowrap;">{{ GetDate(text.record.to_date) }}</span>
|
||||
</span>
|
||||
|
||||
<span slot="action" slot-scope="text, record" class="d-flex" style="column-gap: 1rem;">
|
||||
<!-- NOTE: Manager currently decided to disable this temporarily, but will want
|
||||
to enable it later on. -->
|
||||
<b-button v-if="false" variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
style="white-space: nowrap;"
|
||||
v-on:click="OpenModal('invoice', text.record.id)">
|
||||
Send Invoice
|
||||
</b-button>
|
||||
|
||||
<b-button variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
style="white-space: nowrap;"
|
||||
v-on:click="OpenModal('edit', text.record)">
|
||||
Edit
|
||||
</b-button>
|
||||
|
||||
<b-button variant="primary"
|
||||
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
|
||||
style="white-space: nowrap;"
|
||||
v-on:click="ShowSubusers(text.record)">
|
||||
Show Sub Users
|
||||
</b-button>
|
||||
</span>
|
||||
</XTable>
|
||||
</XTable> <!-- END LIST OF USERS -->
|
||||
</div>
|
||||
</b-card>
|
||||
|
||||
<UserFiltersRepeatingForm
|
||||
v-on:submit="ApplyFilters($event)"
|
||||
/>
|
||||
<!-- class="mt-1" -->
|
||||
<!-- v-if="Users_D.length > 0" -->
|
||||
<AddUserModal type="user" v-on:submit="HandleNewUserAdded()" />
|
||||
<ChargeUserModal v-bind:user-id="IdOfUserToCharge_D" v-bind:table-ref="$refs.users" v-on:submit="HandleUserCharged()" />
|
||||
<!-- <AddUserModal type="user" v-on:submit="HandleNewUserAdded()" /> -->
|
||||
<!-- <ChargeUserModal v-bind:user-id="IdOfUserToCharge_D" v-bind:table-ref="$refs.users" v-on:submit="HandleUserCharged()" /> -->
|
||||
<UserInfoModal v-bind:data="SelectedRecord_D" />
|
||||
<PlanInfoModal v-bind:data="SelectedRecord_D" />
|
||||
<SendInvoiceModal v-bind:user_id="UserId_D" />
|
||||
<EditUserModal v-bind:data="SelectedRecord_D" v-on:updated="$refs.users.fetch()" />
|
||||
<ShowSubusersModal v-bind:data="SelectedRecord_D" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BRow, BCol, BCard, BButton, VBModal, VBToggle } from 'bootstrap-vue'
|
||||
import { BRow, BCol, BCard, BButton, BBadge, BAlert, VBModal, VBToggle } from 'bootstrap-vue'
|
||||
import Ripple from 'vue-ripple-directive'
|
||||
import TableComponent from '@/components/ui/TableComponent'
|
||||
import AddUserModal from '@/components/ui/AddUserModal'
|
||||
import ChargeUserModal from '@/components/ui/ChargeUserModal'
|
||||
import * as TableCol from "./userTbl"
|
||||
import XTable from "@/components/x-table/XTable"
|
||||
import axios from '@/axios'
|
||||
import { SeparateNumberByThousands, FormatBytes } from '@/modules/core'
|
||||
import UserFiltersRepeatingForm from '@/components/ui/UserFiltersRepeatingForm.vue'
|
||||
import FullPageLoadingComponent from '@/components/ui/FullPageLoadingComponent.vue'
|
||||
// import CoreMixin from '@/mixins/index'
|
||||
|
||||
const InnerColumns = [
|
||||
{ title: 'Emails', key: 'Emails', scopedSlots: { customRender: 'emails' } },
|
||||
{ title: 'Websites', key: 'Websites', scopedSlots: { customRender: 'websites' } },
|
||||
{ title: 'Phones', key: 'Phones', scopedSlots: { customRender: 'phones' } },
|
||||
]
|
||||
// Modals
|
||||
import AddUserModal from '@/components/ui/AddUserModal'
|
||||
import ChargeUserModal from '@/components/ui/ChargeUserModal'
|
||||
import UserInfoModal from '@/components/ui/UserInfoModal'
|
||||
import PlanInfoModal from '@/components/ui/PlanInfoModal'
|
||||
import SendInvoiceModal from './Components/SendInvoiceModal'
|
||||
import EditUserModal from './Components/EditUserComponent'
|
||||
import ShowSubusersModal from './Components/ShowSubusersModal'
|
||||
|
||||
export default {
|
||||
name: 'UsersListView',
|
||||
|
|
@ -121,11 +163,18 @@ export default {
|
|||
BCol,
|
||||
BCard,
|
||||
BButton,
|
||||
BBadge,
|
||||
BAlert,
|
||||
TableComponent,
|
||||
AddUserModal,
|
||||
ChargeUserModal,
|
||||
UserInfoModal,
|
||||
PlanInfoModal,
|
||||
SendInvoiceModal,
|
||||
EditUserModal,
|
||||
ShowSubusersModal,
|
||||
XTable,
|
||||
UserFiltersRepeatingForm,
|
||||
FullPageLoadingComponent,
|
||||
},
|
||||
|
||||
directives: {
|
||||
|
|
@ -140,8 +189,8 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
Users_D: [ 1 ],
|
||||
IdOfUserToCharge_D: -1, // This is the id of the user that we want to charge his/her account
|
||||
Users_D: [ ],
|
||||
UserId_D: -1,
|
||||
|
||||
Query_D: {
|
||||
IsMain: false,
|
||||
|
|
@ -159,6 +208,8 @@ export default {
|
|||
is_row_selection: true,
|
||||
is_load_req: true,
|
||||
},
|
||||
|
||||
SelectedRecord_D: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -172,6 +223,11 @@ export default {
|
|||
{
|
||||
return FormatBytes(Bytes)
|
||||
},
|
||||
|
||||
GetDate(DateTimeString)
|
||||
{
|
||||
return new Date(DateTimeString).toDateString()
|
||||
},
|
||||
|
||||
HandleNewUserAdded()
|
||||
{
|
||||
|
|
@ -188,10 +244,38 @@ export default {
|
|||
// ]
|
||||
},
|
||||
|
||||
ApplyFilters(Filters)
|
||||
OpenModal(ModalType, Data)
|
||||
{
|
||||
this.$refs.users.query.query = Filters
|
||||
this.$refs.users.fetch()
|
||||
if (ModalType === 'user')
|
||||
{
|
||||
this.SelectedRecord_D = Data
|
||||
this.$bvModal.show('modal-show-user')
|
||||
}
|
||||
else if (ModalType === 'plan')
|
||||
{
|
||||
this.SelectedRecord_D = Data
|
||||
this.$bvModal.show('modal-show-plan')
|
||||
}
|
||||
else if (ModalType === 'invoice')
|
||||
{
|
||||
this.UserId_D = Data
|
||||
this.$bvModal.show('modal-send-invoice')
|
||||
}
|
||||
else if (ModalType === 'edit')
|
||||
{
|
||||
this.SelectedRecord_D = Data
|
||||
this.$bvModal.show('modal-edit-user')
|
||||
}
|
||||
else throw new Error('Incorrect Parameter: ModalType can be either \'user\', \'plan\' or \'invoice\'.')
|
||||
},
|
||||
|
||||
async ShowSubusers(Data)
|
||||
{
|
||||
this.SelectedRecord_D = {
|
||||
OwnerId: Data.owner_id,
|
||||
OwnerName: `${Data.owner.first_name} ${Data.owner.last_name}`
|
||||
}
|
||||
this.$bvModal.show('modal-show-subusers')
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
import XTbl, { Xtc } from '@/components/x-table/index'
|
||||
|
||||
const tbl = new XTbl('administrator/user/query/', 'Users')
|
||||
tbl.add(new Xtc('first_name', 'First Name'))
|
||||
tbl.add(new Xtc('last_name', 'Last Name'))
|
||||
tbl.add(new Xtc('email_address', 'Email'))
|
||||
tbl.add(new Xtc('is_main', 'Firm').renderSlot('firm'))
|
||||
tbl.add(new Xtc('subscription', 'Charge').renderSlot('charge')) // subscription.charge
|
||||
tbl.add(new Xtc('total_uploaded_size', 'Uploaded Size').renderSlot('total_uploaded_size'))
|
||||
tbl.add(new Xtc('total_firm_uploaded_size', 'Firm Uploaded Size').renderSlot('total_firm_uploaded_size'))
|
||||
const tbl = new XTbl('admin/api/v1/contract/', 'Users')
|
||||
tbl.add(new Xtc('avatar', 'Avatar').noSort().renderSlot('avatar'))
|
||||
tbl.add(new Xtc('name', 'Name').noSort().renderSlot('name'))
|
||||
tbl.add(new Xtc('status', 'Status').noSort().renderSlot('status'))
|
||||
tbl.add(new Xtc('user_usage', 'User Usage').noSort())
|
||||
tbl.add(new Xtc('user_limit', 'User Limit').noSort())
|
||||
tbl.add(new Xtc('payed_amount', 'Payed Amount').noSort())
|
||||
tbl.add(new Xtc('payable_amount', 'Payable Amount').noSort())
|
||||
tbl.add(new Xtc('total_price', 'Total Price').noSort())
|
||||
tbl.add(new Xtc('installments_count', 'Installments Count').noSort())
|
||||
tbl.add(new Xtc('storage_usage', 'Storage Usage').noSort().renderSlot('storage_usage'))
|
||||
tbl.add(new Xtc('storage_limit', 'Storage Limit').noSort().renderSlot('storage_limit'))
|
||||
tbl.add(new Xtc('from_date', 'Start Date').noSort().renderSlot('from_date'))
|
||||
tbl.add(new Xtc('to_date', 'End Date').noSort().renderSlot('to_date'))
|
||||
tbl.add(new Xtc('plan', 'Plan Info').noSort().renderSlot('plan'))
|
||||
tbl.add(new Xtc('action', 'Action').noSort().renderSlot('action'))
|
||||
|
||||
|
||||
export default tbl
|
||||
|
|
@ -230,8 +230,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
Payload_D: {
|
||||
email: 'admin@irelex.com',
|
||||
password: 'Iralex2021',
|
||||
email: '',
|
||||
password: '',
|
||||
},
|
||||
Loading_D: false,
|
||||
ModalMessage_D : undefined,
|
||||
|
|
|
|||
Loading…
Reference in New Issue