This commit is contained in:
Jonathan Chevalier
2023-01-17 18:26:31 +01:00
commit 937ee196a0
22 changed files with 3661 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

8
jsconfig.json Normal file
View File

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

1805
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "vue-admin",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.2.1",
"@popperjs/core": "^2.11.6",
"@vuepic/vue-datepicker": "^3.6.4",
"bootstrap": "^5.2.3",
"bootstrap-icons": "^1.10.3",
"cookie-universal": "^2.2.2",
"date-fns": "^2.29.3",
"pinia": "^2.0.28",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"sass": "^1.57.1",
"vite": "^4.0.0"
}
}

36
src/App.vue Normal file
View File

@@ -0,0 +1,36 @@
<script setup>
import Header from "./components/TemplateParts/Header.vue";
</script>
<template>
<Header></Header>
<main id="main" class="main">
<!--
<div class="pagetitle">
<h1>Dashboard</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="index.html">Home</a></li>
<li class="breadcrumb-item active">Dashboard</li>
</ol>
</nav>
</div>
-->
<!-- End Page Title -->
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'" mode="out-in">
<div :key="route.path">
<component :is="Component"/>
</div>
</transition>
</router-view>
</main>
</template>
<style scoped>
</style>

42
src/components/Search.vue Normal file
View File

@@ -0,0 +1,42 @@
<template>
<div class="search-form d-flex align-items-center">
<input type="text" name="query" placeholder="Scanner un bon de livraison" v-model="query" @input="searchDeliveries"
autofocus>
<button type="submit" title="Valider"><i class="fa-solid fa-barcode fa-fw"></i></button>
</div>
</template>
<script>
import {ref} from 'vue';
import {useShipmentStore} from '@/stores/shipment.js'
import ShipmentApi from '@/services/ShipmentApi.js'
export default {
name: "Search",
setup() {
const query = ref();
const shipmentStore = useShipmentStore()
return {
shipmentStore,
query
}
},
methods: {
searchDeliveries: async function () {
if (this.query.length !== 10) {
return
}
const apiResult = await ShipmentApi.getDeliveriesByOrder(this.query)
this.shipmentStore.setOrderNumber(this.query)
this.shipmentStore.setDeliveryLines(apiResult.data)
},
}
}
</script>
<style scoped>
</style>

13
src/components/Table.vue Normal file
View File

@@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "Table"
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,66 @@
<template>
<header id="header" class="header fixed-top d-flex align-items-center">
<div class="d-flex align-items-center justify-content-between">
<a href="index.html" class="logo d-flex align-items-center">
<span class="d-none d-lg-block">GulliverShip</span>
</a>
</div><!-- End Logo -->
<div class="search-bar">
<Search></Search>
</div><!-- End Search Bar -->
<nav class="header-nav ms-auto">
<ul class="d-flex align-items-center">
<li class="nav-item dropdown pe-3">
<a class="nav-link nav-profile d-flex align-items-center pe-0" href="#" data-bs-toggle="dropdown">
<i class="fa-solid fa-user-lock me-1"></i>
<span class="d-none d-md-block dropdown-toggle ps-2">Jonathan</span>
</a><!-- End Profile Iamge Icon -->
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-arrow profile">
<li class="dropdown-header">
<h6>Jonathan</h6>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item d-flex align-items-center">
<i class="bi bi-person"></i>
<span>My Profile</span>
</a>
</li>
<li>
<a class="dropdown-item d-flex align-items-center" href="#">
<i class="bi bi-box-arrow-right"></i>
<span>Sign Out</span>
</a>
</li>
</ul><!-- End Profile Dropdown Items -->
</li><!-- End Profile Nav -->
</ul>
</nav><!-- End Icons Navigation -->
</header>
</template>
<script>
import Search from "../Search.vue";
export default {
name: "Header",
components: {Search}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,15 @@
<template>
<aside id="sidebar" class="sidebar">
</aside>
</template>
<script>
export default {
name: "SideBar"
}
</script>
<style scoped>
</style>

37
src/main.js Normal file
View File

@@ -0,0 +1,37 @@
import { createApp } from 'vue'
import "bootstrap/scss/bootstrap.scss";
import 'bootstrap-icons/font/bootstrap-icons.css'
import * as bootstrap from 'bootstrap'
import './style.css'
import '@fortawesome/fontawesome-free/css/fontawesome.css'
import '@fortawesome/fontawesome-free/css/solid.css'
import App from './App.vue'
import router from "./router/index"
import { createPinia } from 'pinia'
import Datepicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css'
const pinia = createPinia()
const app = createApp(App)
app.use(router);
app.use(pinia)
app.component('Datepicker', Datepicker);
app.mount("#app");

View File

@@ -0,0 +1,45 @@
import Cookie from 'cookie-universal'
import {useGlobalStore} from '@/stores/global.js'
export default async function (routeTo) {
const store = useGlobalStore()
const cookies = Cookie()
const query = routeTo.query
let sessionToCheck = false;
if (query.NUMC) {
sessionToCheck = query.NUMC;
} else if (cookies.get('session')) {
sessionToCheck = cookies.get('session');
} else if (store.idSession !== false) {
sessionToCheck = store.idSession
} else {
return false
}
if (sessionToCheck) {
return await fetch('/php/api/v3/?NUMC=' + sessionToCheck + '&module=getClient').then(function (response) {
return response.json();
}).then(function (jsonObj) {
if (jsonObj.hasOwnProperty('identity')) {
cookies.set('session', sessionToCheck)
store.setIdSession(sessionToCheck)
return true
} else {
return false
}
}).catch(error => {
return false
});
}
}

33
src/router/index.js Normal file
View File

@@ -0,0 +1,33 @@
import {createRouter, createWebHistory} from 'vue-router'
import sessionCheck from "./guards/sessionCheck.js";
const routes = [
{
path: '/',
name: 'home',
component: () => import('/src/views/index.vue'),
meta: { transition: 'slide-right' },
}
]
const router = createRouter({
history: createWebHistory('/vue-admin'),
routes,
})
router.beforeEach(async (to, from) => {
if (to.path === '/login') {
return true
}
const canAccess = await sessionCheck(to)
if (!canAccess) return '/login'
})
export default router

View File

@@ -0,0 +1,26 @@
import {useGlobalStore} from '@/stores/global.js'
export default {
getApiUrl(exec, param = null) {
const store = useGlobalStore()
return '/php/api/v3/?controller=ShipmentController&NUMC=' + store.idSession + '&EXEC=' + exec + ((param !== null) ? param : null)
},
async getDeliveriesByOrder(idOrder) {
const apiUrl = this.getApiUrl('getDeliveriesByOrder', '&query=' + idOrder)
if (import.meta.env.DEV) {
console.log(apiUrl)
}
return await fetch(apiUrl)
.then(response => {
if (response.ok) {
return response.json()
} else {
return false
}
})
}
}

15
src/stores/global.js Normal file
View File

@@ -0,0 +1,15 @@
import {defineStore} from 'pinia'
export const useGlobalStore = defineStore('global', {
state: () => ({
idSession: '',
}),
getters: {
},
actions: {
setIdSession(idSession) {
this.idSession = idSession
},
},
})

54
src/stores/shipment.js Normal file
View File

@@ -0,0 +1,54 @@
import {defineStore} from 'pinia'
const shipmentStatus = [
{
code: "30",
label: 'Colisage',
allowStatus: [3, 80, 39, 37]
},
{
code: "3",
label: 'A livrer',
allowStatus: []
},
{
code: "31",
label: 'A livrer Dépôt',
allowStatus: [3, 80, 39, 37]
},
{
code: "80",
label: 'Manquant à rembourser',
allowStatus: []
},
{
code: "39",
label: 'Produit en commande',
allowStatus: [30, 3, 80, 39, 37]
},
]
export const useShipmentStore = defineStore('shipment', {
state: () => ({
orderNumber: '',
deliveryLines: []
}),
getters: {
deliveriesCounter: (state) => state.deliveryLines.length,
},
actions: {
setDeliveryLines(lines) {
// for (const line of lines) {
// let labelFinder = shipmentStatus.filter(status => status.code === line.status)
// line.labelStatus = (labelFinder.length > 0) ? labelFinder.code : 'Unknown'
// }
this.deliveryLines = lines
},
setOrderNumber(orderNumber) {
this.orderNumber = orderNumber
},
},
})

1266
src/style.css Normal file

File diff suppressed because it is too large Load Diff

91
src/views/index.vue Normal file
View File

@@ -0,0 +1,91 @@
<template>
<div>
<div class="table-responsive mb-4" v-if="store.deliveriesCounter >0">
<table class="table table-striped table-hover align-middle">
<thead style="font-size:.8em">
<tr>
<th></th>
<th>Désignation</th>
<th>EAN</th>
<th>EAN Colisé <i class="fa-solid fa-circle-question" data-bs-toggle="tooltip"
data-bs-title="Article qui sera envoyé à Winpharma. Pourra être différent du EAN lorsque la boîte mise dans le colis ne correspond pas."></i>
</th>
<!--<th>Réf.</th>
<th>Localisation</th>
<th>Type</th>-->
<th>Statut</th>
<!--<th title="N° expédition transporteur"><small>N° exp. transporteur</small></th>-->
<th><span class="text-nowrap">N° tracking</span></th>
<th>Informations</th>
<th>Historique</th>
</tr>
</thead>
<tbody v-for="location in locationGroup">
<tr class="table-group-divider table-secondary">
<th></th>
<th colspan="7"
class="text-uppercase"><i class="fa-solid fa-location-dot"></i> {{ location }}
</th>
</tr>
<tr v-for="line in store.deliveryLines.filter(obj => obj.physical_location === location)">
<td class="text-center"><input class="form-check-input" type="checkbox" role="switch"
name="id_product_to_deliver[]"></td>
<td>{{ line.product_name }}</td>
<td>{{ line.product_ref }}</td>
<td class="text-nowrap">
<div class="input-group input-group-sm flex-nowrap">
<button type="button" name="ean_winpharma_btn" class="btn btn-primary to_edit" data-bs-toggle="tooltip"
data-bs-title="Scanner le code barre à coliser" disabled="" aria-label="Editer"
data-bs-original-title="Editer"><i class="fa-solid fa-barcode fa-sm"></i></button>
<input class="form-control to_edit" type="text" name="line_1_ean_winpharma" value="" disabled=""
required="required"></div>
</td>
<td><span class="text-nowrap badge bg-info label_status_initial">{{ line.labelStatus }}</span><select
class="form-select form-select-sm to_edit d-none" name="line_1_new_status" required="required" disabled=""
data-type="new_status_select">
<option value=""></option>
<option value="3">A livrer</option>
<option value="31">A livrer Dépôt</option>
<option value="80">Manquant à rembourser</option>
<option value="39">Produit en commande</option>
<option value="37">Produit manquant</option>
<option value="38">Reliquat</option>
</select></td>
<td></td>
<td class="lh-sm"><small>CIP : 2106369<br>G5E / R23A<br>PARAPHARMACIE</small></td>
<td class="text-center">
<button type="button" class="btn btn-light btn-sm" title="Historique des statuts"><i
class="fa-solid fa-list-check"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import {useShipmentStore} from '@/stores/shipment.js'
import {reactive, ref} from "vue";
export default {
name: "index",
setup() {
const store = useShipmentStore()
const locationGroup = ['pharmacy', 'warehouse']
return {
store,
locationGroup,
}
},
}
</script>
<style scoped>
</style>

13
src/views/login.vue Normal file
View File

@@ -0,0 +1,13 @@
<template>
No
</template>
<script>
export default {
name: "login"
}
</script>
<style scoped>
</style>

21
vite.config.js Normal file
View File

@@ -0,0 +1,21 @@
import { fileURLToPath, URL } from "url";
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
proxy: {
'/php': {
target: 'https://para-php7-dev.parapharmacie-et-medicament.com/',
changeOrigin: true,
}
}
},
})