Interface SAV

This commit is contained in:
Jonathan Chevalier
2023-07-04 18:22:06 +02:00
parent 7fbe4e48a6
commit 1fc0418ef4
9 changed files with 196 additions and 121 deletions

22
package-lock.json generated
View File

@@ -18,6 +18,7 @@
"vue": "^3.2.47"
},
"devDependencies": {
"@iconify/vue": "^4.1.1",
"@vitejs/plugin-vue": "^4.1.0",
"sass": "^1.62.1",
"vite": "^4.3.2"
@@ -406,6 +407,27 @@
"node": ">=6"
}
},
"node_modules/@iconify/types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
"dev": true
},
"node_modules/@iconify/vue": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-4.1.1.tgz",
"integrity": "sha512-RL85Bm/DAe8y6rT6pux7D2FJSiUEM/TPfyK7GrbAOfTSwrhvwJW+S5yijdGcmtXouA8MtuH9C7l4hiSE4mLMjg==",
"dev": true,
"dependencies": {
"@iconify/types": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/cyberalien"
},
"peerDependencies": {
"vue": ">=3"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",

View File

@@ -19,6 +19,7 @@
"vue": "^3.2.47"
},
"devDependencies": {
"@iconify/vue": "^4.1.1",
"@vitejs/plugin-vue": "^4.1.0",
"sass": "^1.62.1",
"vite": "^4.3.2"

View File

@@ -1,26 +1,26 @@
<script setup>
import TicketList from '@/components/TicketList.vue'
import Modal from "@/components/base/Modal.vue";
import {computed, nextTick, onMounted, ref, watch} from 'vue';
import {Tab} from 'bootstrap'
import TicketApi from "@/services/TicketApi.js";
import {useGlobalStore} from "@/stores/global";
import {onMounted, ref} from 'vue';
import {storeToRefs} from "pinia";
import {useGlobalStore} from "@/stores/global";
import {useTicketStore} from "@/stores/ticket";
import {showModal, hideModal} from "@/setup/global.js";
import {Tab} from 'bootstrap'
import Messenger from "@/components/Messenger.vue";
import {showModal, hideModal, idTicket, codeTicket} from "@/setup/global.js";
import TicketList from '@/components/TicketList.vue'
import Modal from '@/components/base/Modal.vue'
const store = useGlobalStore()
const storeTicket = useTicketStore();
const {
newTickets,
pendingTickets,
myTickets,
originFilter,
idTicket,
currentTicket,
} = storeToRefs(storeTicket)
const filter = ref('ALL');
const tickets = ref({});
const apiCounter = ref(0)
const waitingPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([]);
}, 100000);
});
}
const openExternalPage = async (script, params = null, features = null) => {
if (store.gulliver === null) {
@@ -29,46 +29,30 @@ const openExternalPage = async (script, params = null, features = null) => {
window.open(store.url + script + '?NUMC=' + store.numc + (params !== null ? '&' + params : ''), '_blank', features);
}
const newTickets = computed(() => {
return tickets.value.hasOwnProperty('new_tickets') ? mapListOfTickets(tickets.value.new_tickets.list, filter.value) : waitingPromise
})
const pendingTickets = computed(() => {
return tickets.value.hasOwnProperty('pending_tickets') ? mapListOfTickets(tickets.value.pending_tickets.list, filter.value) : []
})
const myTickets = computed(() => {
return tickets.value.hasOwnProperty('my_tickets') ? mapListOfTickets(tickets.value.my_tickets.list, filter.value) : []
})
const mapListOfTickets = (tickets, originFilter) => {
return tickets.filter(ticket => ((originFilter !== 'ALL') ? ticket.origin === originFilter : true)).map(ticket =>
[ticket.code, ticket.datetime, ticket.filter1, ticket.filter2, ticket, ticket.advisor, ticket.datetime, ticket.state2, ticket.first_name + ' ' + ticket.last_name, ticket.is_customer]);
const waitingPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([]);
}, 100000);
});
}
const currentTicket = computed(()=>{
const vracTickets = [
...tickets.value.my_tickets.list,
...tickets.value.pending_tickets.list,
...tickets.value.new_tickets.list
]
const currentTickets = vracTickets.filter(t=>t.id === idTicket.value)
return (currentTickets.length === 1) ? currentTickets[0] : []
})
const mapListOfTickets = (tickets) => {
return tickets.map(ticket =>
[ticket.code, ticket.datetime, ticket.filter1, ticket.filter2, ticket, ticket.advisor, ticket.datetime, ticket.state2, ticket.first_name + ' ' + ticket.last_name, ticket.is_customer]);
}
onMounted(async () => {
tickets.value = await TicketApi.getTickets()
await store.registerUserSession();
await storeTicket.registerTicketList()
apiCounter.value++
const myInterval = setInterval(async function () {
tickets.value = await TicketApi.getTickets()
await storeTicket.registerTicketList()
apiCounter.value++
}, 180000);
})
</script>
@@ -78,10 +62,11 @@ onMounted(async () => {
<Modal :show="showModal" :hide="hideModal" size="modal-fullscreen">
<template v-slot:modal-title>
<h2 v-if="idTicket !== 0">Fiche n° {{idTicket}} - {{currentTicket.first_name}} {{currentTicket.last_name}}</h2>
<h2 v-if="idTicket !== 0">Fiche n° {{ idTicket }} - {{ currentTicket.first_name }}
{{ currentTicket.last_name }}</h2>
</template>
<template v-slot:modal-body>
<Messenger :key="showModal" v-if="showModal" :id-ticket="idTicket" :code-ticket="codeTicket"></Messenger>
<Messenger :key="showModal" v-if="showModal"></Messenger>
</template>
</Modal>
@@ -92,7 +77,6 @@ onMounted(async () => {
</div>
<div class="card">
<div class="card-body">
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#"
@@ -153,7 +137,7 @@ onMounted(async () => {
<label for="origineFilter" class="col-auto col-form-label fw-bold"><i class="fa-solid fa-filter"></i>
Filtrer par origine</label>
<div class="col-auto">
<select class="form-select" id="origineFilter" v-model="filter">
<select class="form-select" id="origineFilter" v-model="originFilter">
<option value="ALL">Toute</option>
<option value="ORDER">Commande</option>
<option value="CONTACT">Fiche contact</option>
@@ -164,13 +148,15 @@ onMounted(async () => {
</div>
</div>
<div class="tab-pane show active" id="new" role="tabpanel" tabindex="0">
<ticket-list :rows="newTickets" :key="'new_'+filter+'_'+apiCounter"></ticket-list>
<ticket-list :rows="mapListOfTickets(newTickets)"
:key="'new_'+originFilter+'_'+apiCounter"></ticket-list>
</div>
<div class="tab-pane" id="pending" role="tabpanel" tabindex="0">
<ticket-list :rows="pendingTickets" :key="'new_'+filter+'_'+apiCounter"></ticket-list>
<ticket-list :rows="mapListOfTickets(pendingTickets)"
:key="'new_'+originFilter+'_'+apiCounter"></ticket-list>
</div>
<div class="tab-pane" id="my" role="tabpanel" tabindex="0">
<ticket-list :rows="myTickets" :key="'new_'+filter+'_'+apiCounter"></ticket-list>
<ticket-list :rows="mapListOfTickets(myTickets)" :key="'new_'+originFilter+'_'+apiCounter"></ticket-list>
</div>
</div>
</div>

View File

@@ -1,17 +1,17 @@
<script setup>
import TicketApi from "@/services/TicketApi.js";
import {onMounted, ref} from 'vue';
import {codeTicket} from "@/setup/global";
import {format, formatDistance} from "date-fns";
import {fr} from 'date-fns/locale';
const props = defineProps({
idTicket: [Number,String],
codeTicket : [Number,String],
})
import {useTicketStore} from "@/stores/ticket";
const storeTicket = useTicketStore();
const messages = ref([])
onMounted(async () => {
messages.value = await TicketApi.getMessages(props.codeTicket, props.idTicket)
messages.value = await TicketApi.getMessages(storeTicket.currentTicket.codeTicket, storeTicket.idTicket)
})
@@ -39,34 +39,56 @@ onMounted(async () => {
<small>Last seen: 2 hours ago</small>
</div>
</div>
<div class="col-lg-6 hidden-sm text-end">
<a href="javascript:void(0);" class="btn btn-outline-secondary me-2"><i class="fa-solid fa-camera"></i></a>
<a href="javascript:void(0);" class="btn btn-outline-primary me-2"><i class="fa-solid fa-image"></i></a>
<a href="javascript:void(0);" class="btn btn-outline-info me-2"><i class="fa-solid fa-cogs"></i></a>
<a href="javascript:void(0);" class="btn btn-outline-warning"><i class="fa-solid fa-question"></i></a>
</div>
</div>
</div>
<div class="chat-history">
<ul class="m-b-0">
<li class="clearfix" v-for="(message, i) in messages" :key="i">
<div class="message-data" :class="message.sender === 'pharma' ? 'text-end' : ''">
<span class="message-data-time">{{message.date}}</span>
<i class="fa-solid fa-circle-user ms-2 fa-2x text-primary" v-if="message.sender === 'client'"></i>
<div class="message-data" :class="message.sender === 'pharmacy' ? 'text-end' : ''">
<Icon icon="ph:user-light" width="40" class="text-primary" v-if="message.sender === 'client'"/>
<span class="message-data-time">{{
format(new Date(message.date), 'dd MMMM yyyy à HH:mm', {
addSuffix: true,
locale: fr
})
}}</span>
<Icon icon="mdi:support" width="40" class="text-success ms-2" v-if="message.sender === 'pharmacy'"/>
</div>
<div class="message" :class="message.sender === 'pharma' ? 'other-message float-end' : 'my-message'">
<div class="message" :class="message.sender === 'pharmacy' ? 'other-message float-end' : 'my-message'">
<div v-if="message.htmlBody !== null">
<strong v-if="message.object !== null ">{{ message.object }}<br/></strong>
<div v-html="message.htmlBody"></div>
</div>
<div v-else>
<strong v-if="message.object !== null && message.body === null">{{ message.object }}<br/></strong>
{{ message.body }}
</div>
<div v-if="message.htmlBody !== null" v-html="message.htmlBody"></div>
<div v-else>{{message.body}}</div>
<div v-if="message.document.length >0">
<a class="btn me-2 mt-2" :class="document.color" v-for="(document, i) in message.document"
:key="'document-'+i"
:href="'https://gta.parapharmacie-et-medicament.com/interface/ase_get_doc.php?prj=pharmamp&id='+document.id">
<Icon width="40" :icon="document.icon"></Icon>
</a>
</div>
</div>
</li>
</ul>
</div>
<div class="col-lg-12 hidden-sm text-end" style="padding: 20px">
<a href="javascript:void(0);" class="btn btn-outline-secondary me-2"><i class="fa-solid fa-camera"></i></a>
<a href="javascript:void(0);" class="btn btn-outline-primary me-2"><i class="fa-solid fa-image"></i></a>
<a href="javascript:void(0);" class="btn btn-outline-info me-2"><i class="fa-solid fa-cogs"></i></a>
<a href="javascript:void(0);" class="btn btn-outline-warning"><i class="fa-solid fa-question"></i></a>
</div>
<div class="chat-message clearfix">
<div class="input-group mb-3">
<span class="input-group-text"><i class="fa-solid fa-paper-plane"></i></span>
@@ -78,7 +100,6 @@ onMounted(async () => {
</div>
</div>
<div v-else class="text-center mt-5">
<i class="fa-solid fa-circle-notch fa-spin fa-fw text-primary fa-4x"></i>

View File

@@ -1,12 +1,16 @@
<script setup>
import {computed, nextTick, onMounted, ref, watch} from 'vue';
import {showModal, hideModal, idTicket, codeTicket} from "@/setup/global.js";
import {onMounted, ref, watch} from 'vue';
import {showModal, hideModal} from "@/setup/global.js";
import {useTicketStore} from "@/stores/ticket";
import {format, formatDistance} from "date-fns";
import {fr} from 'date-fns/locale';
import {Grid, html, h} from "gridjs";
import {Tooltip} from 'bootstrap'
const storeTicket = useTicketStore();
const props = defineProps({
rows: {type: [Array, Function]},
})
@@ -36,7 +40,7 @@ const grid = new Grid({
formatter: (cell, row) => {
return h('i', {
className: 'fas fa-eye text-primary cursor-pointer',
onClick: () => {showModal.value++; idTicket.value = row.cells[4].data.id; codeTicket.value = row.cells[4].data.code} // id_last_event
onClick: () => {showModal.value++; storeTicket.setIdTicket(row.cells[4].data.id)} // id_last_event
}, '');
},

View File

@@ -12,12 +12,14 @@ import '@fortawesome/fontawesome-free/css/solid.css'
import '@fortawesome/fontawesome-free/css/regular.css'
import "gridjs/dist/theme/mermaid.css";
import { Icon } from '@iconify/vue';
const pinia = createPinia()
const app = createApp(App)
//app.use(router);
app.use(pinia)
//app.component('Datepicker', Datepicker);
app.component('Icon', Icon);
app.mount("#app");

View File

@@ -2,5 +2,3 @@ import {ref} from "vue";
export const showModal = ref(0)
export const hideModal = ref(0)
export const idTicket = ref(0)
export const codeTicket = ref(0)

View File

@@ -3,15 +3,21 @@ import Api from "@/services/Api.js";
export const useGlobalStore = defineStore('global', {
state: () => ({
gulliver: null
gulliver: null,
gta: {idProject: null, projectName: null},
}),
getters: {
numc: (state) => state.gulliver.numc,
url: (state) => state.gulliver.url,
idProject: (state) => state.gta.idProject,
projectName: (state) => state.gta.projectName,
},
actions: {
async registerSession() {
this.gulliver = await Api.call('AdminController', 'getSession')
},
async registerUserSession() {
this.gta = {...await Api.call('AdminController', 'getUserSession')}
}
},
})

View File

@@ -0,0 +1,35 @@
import {defineStore} from 'pinia'
import TicketApi from "@/services/TicketApi";
const splitTicketList = (ticketList, pileName, filter) => {
return (ticketList.hasOwnProperty(pileName)) ? ticketList[pileName].list.filter(ticket => ((filter !== 'ALL') ? ticket.origin === filter : true)) : []
}
export const useTicketStore = defineStore('ticket', {
state: () => ({
ticketList: [],
originFilter: 'ALL',
idTicket: 0,
codeTicket: 0
}),
getters: {
newTickets: (state) => splitTicketList(state.ticketList, 'new_tickets', state.originFilter),
pendingTickets: (state) => splitTicketList(state.ticketList, 'pending_tickets', state.originFilter),
myTickets: (state) => splitTicketList(state.ticketList, 'my_tickets', state.originFilter),
currentTicket: (state) => {
return [
...splitTicketList(state.ticketList, 'new_tickets', state.originFilter),
...splitTicketList(state.ticketList, 'pending_tickets', state.originFilter),
...splitTicketList(state.ticketList, 'my_tickets', state.originFilter)
].filter(t => t.id === state.idTicket)[0]
},
},
actions: {
async registerTicketList() {
this.ticketList = await TicketApi.getTickets()
},
setIdTicket(id) {
this.idTicket = id
}
},
})