Backup before installing packages

This commit is contained in:
Your Name 2022-01-27 13:09:27 +03:30
parent 278ced151f
commit f34605b9a2
20 changed files with 1104 additions and 178 deletions

2
.gitignore vendored
View File

@ -27,3 +27,5 @@ yarn-error.log*
# Docs
/docs/.temp
[Table Engine Example] Matters.vue

View File

@ -0,0 +1,388 @@
<template>
<div>
<a-card class="py-5 mt-5" v-if="rows">
<div class="container mx-auto text-center">
<img src="../../../public/task.svg"/>
<h2>You haven't created any matter.</h2>
<p class="text-base font-medium">
Click <a href="#">here</a> or use the button in the bottom to create a new matter.
</p>
<vs-button color="primary" type="filled" class="font-bold" @click="addMatter">New Matter</vs-button>
</div>
</a-card>
<div v-else>
<a-card>
<div class="flex justify-between items-center">
<h3 class="mb-0">Matter</h3>
<vs-button color="primary" type="filled" class="matter-add" @click="addMatter">New Matter</vs-button>
</div>
</a-card>
<a-card class="mt-5 overflow-auto">
<a-card title="Filters">
<div class="flex flex-col md:flex-row justify-between space-y-4 md:space-y-0">
<a-radio-group v-model="type_matter" button-style="solid" class="self-center" style="white-space: nowrap">
<a-radio-button value="All" @click="filterStatus('all')">
All
</a-radio-button>
<a-radio-button value="Open" @click="filterStatus(1)">
Open
</a-radio-button>
<a-radio-button value="Closed" @click="filterStatus(2)">
Closed
</a-radio-button>
<a-radio-button value="Pending" @click="filterStatus(3)">
Pending
</a-radio-button>
</a-radio-group>
<vs-button color="primary"
type="border"
icon="filter_list"
@click="showDrawer"
class=""
style="letter-spacing: 0.6px"
>
Filters
</vs-button>
</div>
</a-card>
<XTable :model="model" ref="matters" :options="options" @refresh="refresh" @clickRow="(record)=>clickRow(`/matters/edit/${record.id}`)" class="mt-4 overflow-auto">
<span slot="title_matter" slot-scope="text,record">
<a-button type="link" class="p-0" @click="matterDashboard(text.record)">{{ text.record.title }}</a-button>
</span>
<span slot="client" slot-scope="text,record">
<template v-if="text.record.client.person">
{{ text.record.client.person.first_name }} {{ text.record.client.person.last_name }}
</template>
<template v-else>
{{ text.record.client.company.name }}
</template>
</span>
<span slot="action" slot-scope="text,record" class="flex space-x-3">
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="2" @click="toEditItem(text.record.id)"><feather-icon icon="EditIcon" svg-classes="text-success w-4 h-4" class="pr-2"/>Edit</a-menu-item>
<a-menu-item key="1" @click="promptDelete(text.record.id)"> <feather-icon icon="TrashIcon" svg-classes="text-danger w-4 h-4" class="pr-2"/>Delete</a-menu-item>
<!--<a-menu-item key="3"> <a-icon type="clock-circle" />Clock</a-menu-item>-->
</a-menu>
<a-button style="margin-left: 8px" @click="is_click_add_time = false"> Actions <a-icon type="down" /></a-button>
</a-dropdown>
</span>
<span slot="status" slot-scope="text,record">
<template v-if="text.record.status == 1">
Open
</template>
<template v-if="text.record.status == 2">
Closed
</template>
<template v-if="text.record.status == 3">
Pending
</template>
</span>
<span slot="responsible" slot-scope="text,record" v-if="text.record.responsible">
{{ text.record.responsible.first_name }} {{ text.record.responsible.last_name }}
</span>
<span slot="origination" slot-scope="text,record" v-if="text.record.origination">
{{ text.record.origination.first_name }} {{ text.record.origination.last_name }}
</span>
</XTable>
</a-card>
</div>
<Drawer
ref="drawer"
:form="form"
:ele="$refs.matters"
:query="[
{ field : 'title', value: form.getFieldValue('title') , condition:'like'},
{ field : 'client', value: form.getFieldValue('client') , condition: '=' },
{ field : 'practice_area', value: form.getFieldValue('practice_area') , condition: '=' },
{ field : 'responsible', value: form.getFieldValue('responsible') , condition: '=' },
{ field : 'origination', value: form.getFieldValue('origination') , condition: '=' },
{ field : 'group', value: form.getFieldValue('group') , condition: '=' },
{ field : 'location', value: form.getFieldValue('location') , condition: 'like' },
{ field : 'reference_code', value: form.getFieldValue('reference_code') , condition: 'like' },
]"
>
<a-form-item label="title" has-feedback>
<input
v-decorator="['title',{ rules: [{ required: false }] }]"
placeholder="title"
autocomplete="none"
class="ant-input"
/>
</a-form-item>
<selectSearch
:options="{placeholder: 'Search contacts',label: 'Client',field: 'client',width: '100%',allow_clear: true}"
/>
<SelectOption url="/dictionary/practice-area/" label="Practice area" placeholder="Practice area" field="practice_area" method="get"/>
<SelectOption url="/account/list/" label="Responsible attorney" placeholder="Responsible" field="responsible" method="get"/>
<SelectOption url="/account/list/" label="Originating attorney" placeholder="Originating" field="origination" method="get"/>
<SelectOption url="/account/group/" label="Permissions" placeholder="groups" field="group" method="get" />
<a-form-item label="Location" has-feedback>
<input
v-decorator="['location',{ rules: [{ required: false }] }]"
placeholder="Location"
class="ant-input"
autocomplete="none"
/>
</a-form-item>
<a-form-item label="Client reference number" has-feedback>
<input
v-decorator="['reference_code',{rules: [{ required: false }] }]"
placeholder="reference"
class="ant-input"
autocomplete="none"
/>
</a-form-item>
<div
:style="{
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'left',
direction:'rtl',
zIndex: 1,
}"
>
<a-button :style="{ marginLeft: '8px' }" @click="$refs.drawer.visible_drawer = false" style="border-radius: 3px !important;">
Cancel
</a-button>
<a-button type="primary" html-type="submit" style="border-radius: 3px !important;">
Save filter
</a-button>
</div>
</Drawer>
<vs-prompt
color="danger"
title="Delete Matter"
@cancel="active_prompt = false"
@close="active_prompt = false"
:active.sync="active_prompt"
buttons-hidden
>
<div class="text-left">
<div class="font-medium">Are you sure to delete this matter ?</div>
</div>
<div class="flex justify-center space-x-3 mt-5">
<vs-button color="danger" @click="deleteItem">delete</vs-button>
<vs-button color="danger" type="flat" @click="active_prompt = false">Cancel</vs-button>
</div>
</vs-prompt>
</div>
</template>
<script>
import XTable from '../../components/x-table/XTable'
import * as TableCol from './mattersTbl'
import axios from "@/axios"
import SelectSearch from '../forms/selectSearch'
import SelectOption from '../forms/SelectOptionSingle'
import Mixin from '../mixin/mixin'
import Drawer from '../drawer/Drawer'
export default {
name: "Matters",
mixins:[Mixin],
components:{
XTable,
SelectSearch,
SelectOption,
Drawer
},
data(){
return{
model:null,
active_prompt: false,
current_id: null,
area: [],
type_matter: 'All',
options:{
placeholder:'Search matters',
noContent: 'No matters',
is_row_selection: false,
is_load_req: true
}
}
},
methods:{
matterDashboard(record){
this.is_click_add_time = true
this.$router.push(`/matters/dashboard/${record.id}`)
this.$store.commit('DASHBOARD_ID',record.id)
},
refresh(){
this.type_matter = 'All'
},
showDrawer(){
this.$refs.drawer.visible_drawer = true
},
async filterStatus(sts){
this.checkPagenation(this.$refs.matters)
sts === 'all' ? this.$refs.matters.query.query = [] : this.$refs.matters.query.query = [{ field: 'status',condition: '=',value: sts }]
await this.$refs.matters.fetch()
},
addMatter(){
this.$router.push('/matters/add')
},
toEditItem(id){
this.$router.push(`/matters/edit/${id}`)
},
promptDelete(id){
this.current_id = id
this.active_prompt = true
},
async deleteItem(){
try{
const id = this.current_id
this.active_prompt = false
this.$refs.matters.loading = true
await axios.delete(`/matter/${id}/`)
await this.$refs.matters.fetch()
this.$notification['success']({
message: 'Success',
description: `Matter deleted.`,
});
}catch (e) {
this.$notification['error']({
message: 'Error',
description: `Please try again.`,
});
}finally {
this.$refs.matters.loading = false
}
},
async getPracticeArea(){
const {data} = await axios.get('/dictionary/practice-area/')
this.area = data
},
// handleFilter(){
// this.form.validateFields(async (err, values) => {
// try{
// this.visible_drawer = false
// this.$refs.matters.loading = true
// const modelFilter = [
// {
// value: 'title'
// },
// {
// value: 'practice_area'
// },
// {
// value: 'location'
// },
// {
// value: 'reference_code'
// },
// {
// value: 'client'
// },
// {
// value: 'group'
// },
// {
// value: 'responsible'
// },
// {
// value: 'origination'
// }
// ]
// const filter = modelFilter.filter(item=>this.form.getFieldValue(item.value)).map(item=>({operation: item.value !== 'title' ? 'and' : undefined ,condition: '=', field: item.value, value : this.form.getFieldValue(item.value) }))
// this.$refs.matters.query.query = filter
// this.checkPagenation(this.$refs.matters)
// await this.$refs.matters.fetch()
// this.form.resetFields()
// }catch (e){
// this.$message.error('Error receiving information from server.');
// }finally {
// this.$refs.matters.loading = false
// }
//
// })
// },
},
computed:{
rows(){
if(this.$refs.matters){
return this.$refs.matters.query
}
}
},
watch:{
"this.$refs.matters.query":{
handler(newVal,oldVal) {
console.log(this.rows,'rows rows')
},
deep: true
}
},
mounted() {
this.is_click_add_time = true
},
created() {
this.getPracticeArea()
this.model = TableCol.default
}
}
</script>
<style>
@import "../style/style.css";
[dir=ltr] .ant-modal-footer{
display: none;
}
.matter-add{
text-align: right;
display: block;
margin-left: auto;
}
[dir=ltr] .ant-radio-button-wrapper:first-child{
border-radius: 3px 0 0 3px;
}
[dir=ltr] .ant-radio-button-wrapper:last-child{
border-radius: 0 3px 3px 0;
}
[dir] .ant-card-bordered{
border-radius: 6px;
}
[dir] .ant-input, [dir] .ant-calendar-picker-input, [dir] .ant-select-selection {
border-radius: 3px !important;
}
.ant-calendar-picker-input,.ant-select-selection {
border-radius: 3px !important;
}
.vs-button--icon{
z-index: 10 !important;
}
.ant-drawer-content-wrapper {
z-index: 10000 !important;
}
.ant-select-dropdown-menu.ant-select-dropdown-menu-vertical.ant-select-dropdown-menu-root{
z-index: 200000000 !important;
position: relative;
}
[dir=ltr] .ant-drawer-mask{
z-index: 1000 !important;
}
</style>

View File

@ -1,15 +1,11 @@
<template>
<div
id="app"
class="h-100"
:class="[skinClasses]"
>
<component :is="layout">
<router-view />
</component>
<div id="app" class="h-100" v-bind:class="[skinClasses]">
<component v-bind:is="layout">
<router-view />
</component>
<scroll-to-top v-if="enableScrollToTop" />
</div>
<scroll-to-top v-if="enableScrollToTop" />
</div>
</template>
<script>
@ -30,77 +26,84 @@ const LayoutHorizontal = () => import('@/layouts/horizontal/LayoutHorizontal.vue
const LayoutFull = () => import('@/layouts/full/LayoutFull.vue')
export default {
components: {
components: {
// Layouts
LayoutHorizontal,
LayoutVertical,
LayoutFull,
// Layouts
LayoutHorizontal,
LayoutVertical,
LayoutFull,
ScrollToTop,
},
// ! We can move this computed: layout & contentLayoutType once we get to use Vue 3
// Currently, router.currentRoute is not reactive and doesn't trigger any change
computed: {
layout() {
if (this.$route.meta.layout === 'full') return 'layout-full'
return `layout-${this.contentLayoutType}`
},
contentLayoutType() {
return this.$store.state.appConfig.layout.type
},
},
beforeCreate() {
// Set colors in theme
const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
ScrollToTop,
},
// eslint-disable-next-line no-plusplus
for (let i = 0, len = colors.length; i < len; i++) {
$themeColors[colors[i]] = useCssVar(`--${colors[i]}`, document.documentElement).value.trim()
}
// ! We can move this computed: layout & contentLayoutType once we get to use Vue 3
// Currently, router.currentRoute is not reactive and doesn't trigger any change
computed: {
layout()
{
if (this.$route.meta.layout === 'full') return 'layout-full'
return `layout-${this.contentLayoutType}`
},
// Set Theme Breakpoints
const breakpoints = ['xs', 'sm', 'md', 'lg', 'xl']
contentLayoutType()
{
return this.$store.state.appConfig.layout.type
},
},
// eslint-disable-next-line no-plusplus
for (let i = 0, len = breakpoints.length; i < len; i++) {
$themeBreakpoints[breakpoints[i]] = Number(useCssVar(`--breakpoint-${breakpoints[i]}`, document.documentElement).value.slice(0, -2))
}
beforeCreate() {
// Set colors in theme
const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
// Set RTL
const { isRTL } = $themeConfig.layout
document.documentElement.setAttribute('dir', isRTL ? 'rtl' : 'ltr')
},
setup() {
const { skin, skinClasses } = useAppConfig()
const { enableScrollToTop } = $themeConfig.layout
// eslint-disable-next-line no-plusplus
for (let i = 0, len = colors.length; i < len; i++) {
$themeColors[colors[i]] = useCssVar(`--${colors[i]}`, document.documentElement).value.trim()
}
// If skin is dark when initialized => Add class to body
if (skin.value === 'dark') document.body.classList.add('dark-layout')
// Set Theme Breakpoints
const breakpoints = ['xs', 'sm', 'md', 'lg', 'xl']
// Provide toast for Composition API usage
// This for those apps/components which uses composition API
// Demos will still use Options API for ease
provideToast({
hideProgressBar: true,
closeOnClick: false,
closeButton: false,
icon: false,
timeout: 3000,
transition: 'Vue-Toastification__fade',
})
// eslint-disable-next-line no-plusplus
for (let i = 0, len = breakpoints.length; i < len; i++) {
$themeBreakpoints[breakpoints[i]] = Number(useCssVar(`--breakpoint-${breakpoints[i]}`, document.documentElement).value.slice(0, -2))
}
// Set Window Width in store
store.commit('app/UPDATE_WINDOW_WIDTH', window.innerWidth)
const { width: windowWidth } = useWindowSize()
watch(windowWidth, val => {
store.commit('app/UPDATE_WINDOW_WIDTH', val)
})
// Set RTL
const { isRTL } = $themeConfig.layout
document.documentElement.setAttribute('dir', isRTL ? 'rtl' : 'ltr')
},
return {
skinClasses,
enableScrollToTop,
}
},
setup()
{
const { skin, skinClasses } = useAppConfig()
const { enableScrollToTop } = $themeConfig.layout
// If skin is dark when initialized => Add class to body
if (skin.value === 'dark') document.body.classList.add('dark-layout')
// Provide toast for Composition API usage
// This for those apps/components which uses composition API
// Demos will still use Options API for ease
provideToast({
hideProgressBar: true,
closeOnClick: false,
closeButton: false,
icon: false,
timeout: 3000,
transition: 'Vue-Toastification__fade',
})
// Set Window Width in store
store.commit('app/UPDATE_WINDOW_WIDTH', window.innerWidth)
const { width: windowWidth } = useWindowSize()
watch(windowWidth, val => {
store.commit('app/UPDATE_WINDOW_WIDTH', val)
})
return {
skinClasses,
enableScrollToTop,
}
},
}
</script>

View File

@ -108,6 +108,7 @@ body {
right: 0;
background: #eceff8;
padding-top: 4.5rem;
overflow-y: auto;
}
@media (min-width: 375px) {
.page--main {

40
src/assets/svg/error.svg Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<circle style="fill:#D75A4A;" cx="25" cy="25" r="25"/>
<polyline style="fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" points="16,34 25,25 34,16
"/>
<polyline style="fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" points="16,16 25,25 34,34
"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<circle style="fill:#25AE88;" cx="25" cy="25" r="25"/>
<polyline style="fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;" points="
38,15 22,33 12,25 "/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 701 B

View File

@ -11,7 +11,8 @@ const ConfiguredAxios = axios.create({
'Access-Control-Allow-Header': 'Content-Type',
'Accept': "application/json",
'Content-Type' : 'application/json',
'Auth': localStorage.getItem("accessToken") || localStorage.accessToken,
// 'Auth': localStorage.getItem("accessToken") || localStorage.accessToken,
'Auth': 'lua vpXyFsb8AbgMJ4iZ2N0knRB2MNEC9pMU6a4Mbp3eIZxE4K6XrfX58TGOeENnSV5p',
'Cache-Control': 'no-cache',
'Pragma':'Pragma',
'Expires': '0',
@ -20,7 +21,8 @@ const ConfiguredAxios = axios.create({
ConfiguredAxios.interceptors.request.use(
config => {
config.headers["Auth"] = localStorage.getItem("accessToken")
// config.headers["Auth"] = localStorage.getItem("accessToken")
config.headers["Auth"] = 'lua vpXyFsb8AbgMJ4iZ2N0knRB2MNEC9pMU6a4Mbp3eIZxE4K6XrfX58TGOeENnSV5p'
config.headers["Access-Control-Allow-Origin"] = '*'
return config
},

View File

@ -5,8 +5,9 @@ Description:
A modal that prompts for a user/lawyer/law firm's information.
How to use:
First, import the component, then place it in a template, and then use VBModal
bootstrap-vue component and pass it the modal-add-use argument to open the modale.
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>
@ -225,4 +226,3 @@ export default {
}
}
</style>
0

View File

@ -0,0 +1,100 @@
<!--
Documentation:
Description:
A modal that prompts for an integer and charges the user's account that much.
How to use:
First, import the component, put it in the template section, and then use
VBModal bootstrap-vue component and pass it modal-charge-use argument to open
the modal.
Example:
<b-button v-b-modal:modal-charge-user>Charge User</b-button>
Props:
Name Type Description
------- ------- -------------------------------------------------------------
user-id Number the id of the user you want to charge his/her account
-->
<template>
<div>
<b-modal
id="modal-charge-user"
v-bind:centered="true"
title="How much to charge"
size="xs"
ok-only
ok-title="Charge"
v-on:ok="HandleChargeButtonClicked()"
>
<div class="d-flex align-items-center" style="column-gap: 0.5rem;">
<span>$</span>
<b-form-input v-bind:value="Payload_D.amount" />
</div>
</b-modal>
<success-modal message="Account charged successfully" />
<error-modal message="Failed to charge account" />
<full-page-loading-component v-bind:show="$store.state.app.FullPageOverlayVisible" />
</div>
</template>
<script>
import { BFormInput } from 'bootstrap-vue'
import SuccessModal from '@/components/ui/SuccessModal.vue'
import ErrorModal from '@/components/ui/ErrorModal.vue'
import FullPageLoadingComponent from '@/components/ui/FullPageLoadingComponent.vue'
import axios from '@/axios'
import { Log } from '@/modules/log'
export default {
name: 'AddUserModalComponent',
props: {
userId: {
type: Number,
required: true,
},
},
components: {
BFormInput,
SuccessModal,
ErrorModal,
FullPageLoadingComponent,
},
data() {
return {
Payload_D: {
amount: 5,
},
}
},
methods: {
async HandleChargeButtonClicked()
{
this.$store.commit('app/toggle_full_page_overlay')
try
{
let { data } = await axios.post(`https://api.ira-lex.com/administrator/user/${this.userId}/charge/`, this.Payload_D)
this.$store.commit('app/toggle_full_page_overlay')
this.$bvModal.show('modal-success')
}
catch (e)
{
this.$store.commit('app/toggle_full_page_overlay')
this.$bvModal.show('modal-error')
Log('ERROR::AXIOS::COMPONENTS/UI/CHARGE_USER_MODAL.VUE>CHARGE_USER_MODAL', e)
}
},
},
}
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,56 @@
<!--
Documentation:
Description:
A modal that indicates some operation has been failed
How to use:
First, import the component, put it in the template section, and then use
VBModal bootstrap-vue component and pass it modal-error argument to open the
modal.
Example:
<b-button v-b-modal:modal-error>Add New User</b-button>
Props:
Name Type Description
------- ------- -------------------------------------------------------------
message String A message to be displayed to the user
-->
<template>
<b-modal
id="modal-error"
v-bind:centered="true"
size="xs"
ok-only
title="Error"
>
<div class="d-flex flex-column justify-content-between align-items-center" style="row-gap: 1rem;">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50; width: 60px; height: 60px;" xml:space="preserve">
<circle style="fill:#d75a4a;" cx="25" cy="25" r="25"/>
<polyline style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" points="16,34 25,25 34,16"/>
<polyline style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" points="16,16 25,25 34,34"/><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g>
</svg>
<h5 v-if="message !== ''">{{ message }}</h5>
</div>
</b-modal>
</template>
<script>
export default {
name: 'AddUserModalComponent',
props: {
message: {
type: String,
required: false,
default: '',
},
},
}
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,89 @@
<!--
You can visit the following websites for more spinners:
https://loading.io/css/
https://projects.lukehaas.me/css-loaders/
-->
<template>
<div
v-if="show"
class="loading-component-container"
v-bind:style="{ 'background-color': backcolor }"
>
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>
</div>
</template>
<script>
export default {
name: 'FullPageLoadingComponent',
props: {
show: {
type: Boolean,
required: true,
},
backcolor: {
type: String,
required: false,
default: '#22292f80'
}
},
data() {
return {
BackgroundColor_D: this.backcolor,
}
},
}
</script>
<style scoped lang="scss">
.loading-component-container {
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
z-index: 1000000000000;
display: flex;
justify-content: center;
align-items: center;
}
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border: 8px solid #fff;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #fff transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@ -0,0 +1,55 @@
<!--
Documentation:
Description:
A modal that indicates some operation has been successful
How to use:
First, import the component, put it in the template section, and then use
VBModal bootstrap-vue component and pass it modal-success argument to open the
modal.
Example:
<b-button v-b-modal:modal-success>Add New User</b-button>
Props:
Name Type Description
------- ------- -------------------------------------------------------------
message String A message to be displayed to the user
-->
<template>
<b-modal
id="modal-success"
v-bind:centered="true"
size="xs"
ok-only
title="Success"
>
<div class="d-flex flex-column justify-content-between align-items-center" style="row-gap: 1rem;">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50; width: 60px; height: 60px;" xml:space="preserve">
<circle style="fill:#25ae88;" cx="25" cy="25" r="25"/>
<polyline style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;" points="38,15 22,33 12,25 "/><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g>
</svg>
<h5 v-if="message !== ''">{{ message }}</h5>
</div>
</b-modal>
</template>
<script>
export default {
name: 'AddUserModalComponent',
props: {
message: {
type: String,
required: false,
default: '',
},
},
}
</script>
<style scoped lang="scss">
</style>

View File

@ -1,4 +1,9 @@
export default [
{
title : 'Dashboard',
icon : 'HomeIcon' ,
route : 'dashboard',
},
{
title: 'Users',
icon: 'UsersIcon',

View File

@ -97,6 +97,11 @@ export default [
// -----------------------------------------------------------------------
// Misc Routes
// -----------------------------------------------------------------------
{
path: '/dashboard',
name: 'dashboard',
component: () => import('@/views/Misc/Dashboard.vue'),
},
{
path: '/permissions',
name: 'permissions',

View File

@ -1,60 +1,79 @@
import { $themeConfig } from '@themeConfig'
export default {
namespaced: true,
state: {
layout: {
isRTL: $themeConfig.layout.isRTL,
skin: localStorage.getItem('vuexy-skin') || $themeConfig.layout.skin,
routerTransition: $themeConfig.layout.routerTransition,
type: $themeConfig.layout.type,
contentWidth: $themeConfig.layout.contentWidth,
menu: {
hidden: $themeConfig.layout.menu.hidden,
},
navbar: {
type: $themeConfig.layout.navbar.type,
backgroundColor: $themeConfig.layout.navbar.backgroundColor,
},
footer: {
type: $themeConfig.layout.footer.type,
},
},
},
getters: {},
mutations: {
TOGGLE_RTL(state) {
state.layout.isRTL = !state.layout.isRTL
document.documentElement.setAttribute('dir', state.layout.isRTL ? 'rtl' : 'ltr')
},
UPDATE_SKIN(state, skin) {
state.layout.skin = skin
namespaced: true,
// Update value in localStorage
localStorage.setItem('vuexy-skin', skin)
state: {
layout: {
isRTL: $themeConfig.layout.isRTL,
skin: localStorage.getItem('vuexy-skin') || $themeConfig.layout.skin,
routerTransition: $themeConfig.layout.routerTransition,
type: $themeConfig.layout.type,
contentWidth: $themeConfig.layout.contentWidth,
menu: {
hidden: $themeConfig.layout.menu.hidden,
},
navbar: {
type: $themeConfig.layout.navbar.type,
backgroundColor: $themeConfig.layout.navbar.backgroundColor,
},
footer: {
type: $themeConfig.layout.footer.type,
},
},
},
// Update DOM for dark-layout
if (skin === 'dark') document.body.classList.add('dark-layout')
else if (document.body.className.match('dark-layout')) document.body.classList.remove('dark-layout')
},
UPDATE_ROUTER_TRANSITION(state, val) {
state.layout.routerTransition = val
},
UPDATE_LAYOUT_TYPE(state, val) {
state.layout.type = val
},
UPDATE_CONTENT_WIDTH(state, val) {
state.layout.contentWidth = val
},
UPDATE_NAV_MENU_HIDDEN(state, val) {
state.layout.menu.hidden = val
},
UPDATE_NAVBAR_CONFIG(state, obj) {
Object.assign(state.layout.navbar, obj)
},
UPDATE_FOOTER_CONFIG(state, obj) {
Object.assign(state.layout.footer, obj)
},
},
actions: {},
getters: {},
mutations: {
TOGGLE_RTL(state)
{
state.layout.isRTL = !state.layout.isRTL
document.documentElement.setAttribute('dir', state.layout.isRTL ? 'rtl' : 'ltr')
},
UPDATE_SKIN(state, skin)
{
state.layout.skin = skin
// Update value in localStorage
localStorage.setItem('vuexy-skin', skin)
// Update DOM for dark-layout
if (skin === 'dark') document.body.classList.add('dark-layout')
else if (document.body.className.match('dark-layout')) document.body.classList.remove('dark-layout')
},
UPDATE_ROUTER_TRANSITION(state, val)
{
state.layout.routerTransition = val
},
UPDATE_LAYOUT_TYPE(state, val)
{
state.layout.type = val
},
UPDATE_CONTENT_WIDTH(state, val)
{
state.layout.contentWidth = val
},
UPDATE_NAV_MENU_HIDDEN(state, val)
{
state.layout.menu.hidden = val
},
UPDATE_NAVBAR_CONFIG(state, obj)
{
Object.assign(state.layout.navbar, obj)
},
UPDATE_FOOTER_CONFIG(state, obj)
{
Object.assign(state.layout.footer, obj)
},
},
actions: {},
}

View File

@ -1,28 +1,54 @@
import { $themeBreakpoints } from '@themeConfig'
export default {
namespaced: true,
state: {
windowWidth: 0,
shallShowOverlay: false,
},
getters: {
currentBreakPoint: state => {
const { windowWidth } = state
if (windowWidth >= $themeBreakpoints.xl) return 'xl'
if (windowWidth >= $themeBreakpoints.lg) return 'lg'
if (windowWidth >= $themeBreakpoints.md) return 'md'
if (windowWidth >= $themeBreakpoints.sm) return 'sm'
return 'xs'
},
},
mutations: {
UPDATE_WINDOW_WIDTH(state, val) {
state.windowWidth = val
},
TOGGLE_OVERLAY(state, val) {
state.shallShowOverlay = val !== undefined ? val : !state.shallShowOverlay
},
},
actions: {},
namespaced: true,
state: {
windowWidth: 0,
shallShowOverlay: false, // whether to display a full page overlay or not (excluding the main menu)
FullPageOverlayVisible: false,
},
getters: {
currentBreakPoint: state => {
const { windowWidth } = state
if (windowWidth >= $themeBreakpoints.xl) return 'xl'
if (windowWidth >= $themeBreakpoints.lg) return 'lg'
if (windowWidth >= $themeBreakpoints.md) return 'md'
if (windowWidth >= $themeBreakpoints.sm) return 'sm'
return 'xs'
},
},
mutations: {
UPDATE_WINDOW_WIDTH(state, val)
{
state.windowWidth = val
},
TOGGLE_OVERLAY(state, val)
{
state.shallShowOverlay = val !== undefined ? val : !state.shallShowOverlay
},
toggle_full_page_overlay(state, val)
{
state.FullPageOverlayVisible = (val !== undefined) ? val : !state.FullPageOverlayVisible
// Why it is implemented like this and not like this:
// state.FullPageOverlayVisible = !state.FullPageOverlayVisible
//
// This is because we can use this function in two different
// ways:
// 1)
// this way we toggle the boolean value
// this.$store.commit('app/toggle_full_page_overlay')
//
// 2)
// this way we can assign to the boolean value
// this.$store.commit('app/toggle_full_page_overlay', true)
},
},
actions: {},
}

View File

@ -3,18 +3,19 @@ import Vuex from 'vuex'
// Modules
import ecommerceStoreModule from '@/views/apps/e-commerce/eCommerceStoreModule'
import app from './app'
import appConfig from './app-config'
import app from './app' // Contains the application-agnostic data
import appConfig from './app-config' // Contains application-agnostic static configurations
import verticalMenu from './vertical-menu'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
app,
appConfig,
verticalMenu,
'app-ecommerce': ecommerceStoreModule,
},
strict: process.env.DEV,
modules: {
app,
appConfig,
verticalMenu,
'app-ecommerce': ecommerceStoreModule,
},
strict: process.env.DEV,
})

View File

@ -0,0 +1,23 @@
<template>
<div>
<h3>Dashboard</h3>
<b-card>
</b-card>
</div>
</template>
<script>
import { BCard } from 'bootstrap-vue'
export default {
name: 'DashboardView',
components: {
BCard,
},
}
</script>
<style scoped lang="scss">
</style>

View File

@ -1,9 +1,10 @@
<template>
<div v-bind:class="{ 'page--main': users.length === 0 }">
<!-- <div v-bind:class="{ 'page--main': Users_D.length === 0 }"> -->
<div class="page--main">
<!----------------------------- Title ----------------------------->
<div class="d-flex justify-content-between">
<h3>Users List</h3>
<div v-if="users.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
@ -12,10 +13,27 @@
</div>
<!------------------------------ Body ----------------------------->
<b-card class="mt-1 position-relative" style="min-height: 95%;">
<!-- Table Engine Filters -->
<b-card v-if="Users_D.length > 0" class="mt-1">
<h4>Filters</h4>
<b-row>
<b-col>
<b-form-checkbox v-model="Query_D.IsMain">Firms Only</b-form-checkbox>
</b-col>
</b-row>
<div class="mt-1 d-flex justify-content-end">
<b-button variant="primary" v-ripple.400="'rgba(255, 255, 255, 0.15)'" v-on:click="ApplyFilters()">
Apply Filters
</b-button>
</div>
</b-card>
<!-- Table Engine Itself -->
<b-card class="position-relative" style="min-height: 95%;">
<!-- No Users Exist -->
<div id="no-users--container" v-if="users.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>
@ -36,22 +54,41 @@
v-on:selectedRows="SelectedRows_D"
v-bind:options="TableOptions_D"
>
<!-- <span v-slot:action="{ text, record }"> -->
<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
</b-button>
</span>
<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>
</XTable>
</div>
</b-card>
<AddUserModal type="user" v-on:submit="HandleNewUserAdded()" />
<ChargeUserModal v-bind:user-id="IdOfUserToCharge_D" v-on:submit="HandleUserCharged()" />
</div>
</template>
<script>
import { BCard, BButton, VBModal } from 'bootstrap-vue'
import { BRow, BCol, BCard, BButton, VBModal, BFormCheckbox } 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'
@ -67,10 +104,14 @@ export default {
name: 'UsersListView',
components: {
BRow,
BCol,
BCard,
BButton,
BFormCheckbox,
TableComponent,
AddUserModal,
ChargeUserModal,
XTable,
},
@ -85,7 +126,12 @@ export default {
data() {
return {
users: [],
Users_D: [ 1 ],
IdOfUserToCharge_D: -1, // This is the id of the user that we want to charge his/her account
Query_D: {
IsMain: false,
},
Model_D: null,
CurrentRow_D: null,
@ -105,7 +151,32 @@ export default {
methods: {
HandleNewUserAdded()
{
this.users.push(2)
this.Users_D.push(2)
// try
// {
// let { data } = await axios.get()
// }
},
HandleUserCharged()
{
// this.$refs.users.query.query = [
// ]
},
ApplyFilters()
{
console.log('FETCH')
this.$refs.users.query.query = [
]
if( this.Query_D.IsMain )
this.$refs.users.query.query.push({
field: 'is_main',
condition: '=',
value: this.Query_D.IsMain,
})
console.log(this.$refs.users.query.query)
this.$refs.users.fetch()
},
},

View File

@ -1,11 +1,13 @@
import XTbl, { Xtc } from '@/components/x-table/index'
const tbl = new XTbl('', 'Users')
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('user_type', 'User Type'))
// tbl.add(new Xtc('birthday', 'Birthday'))
// tbl.add(new Xtc('status', 'Status'))
tbl.add(new Xtc('action', 'Action').noSort().renderSlot('action'))
tbl.add(new Xtc('name', 'Name'))
tbl.add(new Xtc('email', 'Email'))
tbl.add(new Xtc('user_type', 'User Type'))
tbl.add(new Xtc('birthday', 'Birthday'))
tbl.add(new Xtc('status', 'status'))
export default tbl