upgrade project sass/ts/stylelint/eslint/vue

This commit is contained in:
Alexis Gousseau 2023-06-12 16:45:30 +02:00
parent 0aab2213ae
commit ad5becf181
42 changed files with 8256 additions and 4539 deletions

View file

@ -13,3 +13,9 @@ trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[*.vue]
indent_size = 2
[docker-compose.yml]
indent_size = 4

41
.eslintrc.json Normal file
View file

@ -0,0 +1,41 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-strongly-recommended",
"@vue/eslint-config-typescript"
],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
"no-undef": "off"
}
},
{
"files": ["*.vue"],
"rules": {
"indent": "off"
}
}
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
"vue/one-component-per-file": "off"
}
}

5
.gitignore vendored
View file

@ -1,6 +1,11 @@
/node_modules
/public/hot
/public/storage
/public/build
/public/medias
/public/uploads
/public/cache
/public/sitemap.xml
/storage/*.key
/vendor
.vscode/

43
.stylelintrc.json Normal file
View file

@ -0,0 +1,43 @@
{
"extends": [
"stylelint-config-prettier",
"stylelint-config-standard-scss",
"stylelint-config-sass-guidelines",
"stylelint-config-idiomatic-order"
],
"plugins": ["stylelint-scss"],
"files": ["**/*.css", "**/*.scss"],
"ignoreFiles": [
"**/vendor/*.css",
"**/vendor/**/*.css",
"**/vendor/*.scss",
"**/vendor/**/*.scss",
"**/vendor/*.sass",
"**/vendor/**/*.sass",
"**/vendor/*.less",
"**/vendor/**/*.less"
],
"customSyntax": "postcss-scss",
"rules": {
"indentation": 4,
"string-quotes": "double",
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null,
"max-nesting-depth": 5,
"selector-max-compound-selectors": 5,
"declaration-colon-space-after": "always-single-line",
"function-parentheses-space-inside": "never-single-line",
"declaration-colon-newline-after": null,
"scss/double-slash-comment-empty-line-before": [
"always",
{
"ignore": [
"between-comments",
"stylelint-commands",
"inside-block"
]
}
]
}
}

11849
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,28 +7,53 @@
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
"production": "mix --production",
"eslint": "node ./node_modules/.bin/eslint -c .eslintrc.json ./resources",
"eslint-ci": "node ./node_modules/.bin/eslint -c .eslintrc.json ./resources -f junit",
"stylelint": "npx stylelint '**/*.scss'",
"stylelint-ci": "npx stylelint '**/*.scss' -f json"
},
"devDependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@rushstack/eslint-patch": "^1.3.1",
"@types/bootstrap": "^5.2.6",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"@vue/eslint-config-typescript": "^11.0.3",
"axios": "^0.21",
"bootstrap": "^5.3.0",
"clean-webpack-plugin": "^4.0.0",
"eslint": "^8.42.0",
"eslint-plugin-vue": "^9.14.1",
"eslint-webpack-plugin": "^4.0.1",
"laravel-mix": "^6.0.6",
"laravel-mix-clean": "^0.1.0",
"laravel-mix-eslint": "^0.2.0",
"laravel-mix-polyfill": "^3.0.0",
"laravel-mix-stylelint": "^1.0.0",
"moment": "^2.29.1",
"postcss": "^8.1.14",
"postcss-discard-comments": "^4.0.2",
"prettier": "^2.8.8",
"resolve-url-loader": "^3.1.3",
"sass": "^1.32.11",
"sass-loader": "^11.0.1",
"stylelint": "^14.11.0",
"stylelint-config-idiomatic-order": "^8.1.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-config-standard-scss": "^6.1.0",
"stylelint-webpack-plugin": "^4.1.1",
"sweetalert2": "^11.7.12",
"swiper": "^6.6.1",
"vue": "^2.6.12",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.12"
"ts-loader": "^9.4.3",
"typescript": "^4.8.2",
"typescript-eslint": "^0.0.1-alpha.0",
"vue": "^3.2.38",
"vue-eslint-parser": "^9.1.0",
"vue-loader": "^16.8.3"
}
}

View file

@ -1,4 +1,5 @@
{
"/js/preload-back.js": "/js/preload-back.js",
"/css/preload-back.css": "/css/preload-back.css"
"/css/preload-back.css": "/css/preload-back.css",
"/assets/images/logo_ldc.png": "/assets/images/logo_ldc.png"
}

View file

@ -1,7 +0,0 @@
require('./utilities/bootstrap');
require('./utilities/fontawesome');
require('./pages/customer.delete')
require('./pages/service.index')
require('./pages/single_optin.index')
require('./pages/statsExport')

View file

@ -1,34 +0,0 @@
let customerDelete = document.getElementById('customerDelete');
if (customerDelete) {
customerDelete.addEventListener('submit', (e) => {
let self = e.target
e.preventDefault()
Swal.fire({
title: 'Supprimer ce client pour cette marque ?',
text: 'Attention cette action est irréversible, si le client n\'appartient plus à aucune marque il sera complètement retiré des bases',
icon: 'warning',
confirmButtonText: 'Je comprends le danger et suis conscient de ce que je fait',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Attention au conséquences',
html: 'La supression de ce compte dans la crm n\'entrainera pas ça supression dans MESBR ni dans le front cache DB (pour les sites WPCRM). <br><h3><b>Une fois le compte supprimé je ne pourrai plus récupérer le hashcode nécessaire pour effectuer ces opérations.</b></h3>',
icon: 'info',
confirmButtonText: 'Je comprends le danger et suis conscient que celà aura des conséquences',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
self.submit()
}
})
}
})
})
}

View file

@ -1,36 +0,0 @@
let genForms = document.getElementsByClassName('genToken');
if (genForms.length) {
for (let form of genForms) {
form.addEventListener('submit', (e) => {
let self = e.target
e.preventDefault()
Swal.fire({
title: 'Regénérer un token ?',
text: 'Attention la regeneration empêchera le service de fonctionner tant que son token n\'aura pas été mis à jour',
icon: 'warning',
confirmButtonText: 'Je comprends le danger et suis conscient de ce que je fait',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Vous allez régénérer un token de service en production',
html: '<h3><b>Je suis conscient que celà va avoir des implications et qu\'il me faudra mettre à jour le bearer token du service concerné dans son envirronement actuel</b></h3>',
icon: 'info',
confirmButtonText: 'Oui, feu patate !!',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
self.submit()
}
})
}
})
})
}
}

View file

@ -1,11 +0,0 @@
let brandSelect = document.getElementById('brandSelect');
if (brandSelect) {
brandSelect.addEventListener('change', function (e) {
if (brandSelect.value !== '0') {
const url = new URL(location.href);
url.searchParams.set('brand', brandSelect.value);
url.searchParams.delete('rst');
location.assign(url.search);
}
});
}

View file

@ -1,16 +0,0 @@
const { default: axios } = require("axios")
let statsCSV = document.getElementById('StatsCSV')
let statsPDF = document.getElementById('StatsPDF')
if (statsCSV) {
statsCSV.addEventListener('click', () => {
window.location = statsCSV.dataset['request']
})
}
if (statsPDF) {
statsPDF.addEventListener('click', () => {
printJS({ printable: document.getElementById('chart').toDataURL(), type: 'image', imageStyle: 'width:100%' });
})
}

View file

@ -1,31 +0,0 @@
window._ = require('lodash');
import axios from "axios";
import * as bootstrap from "bootstrap";
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = axios;
window.bootstrap = bootstrap;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// window.Pusher = require('pusher-js');
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// forceTLS: true
// });

View file

@ -1,17 +0,0 @@
.bodyFull {
margin: 0 !important;
padding: 0 !important;
min-height: calc(100vh - #{$navBarHeight} - #{$footerHeight});
}
#app {
& > .navbar {
min-height: $navBarHeight;
}
#footer {
height: $footerHeight;
padding: 0 !important;
color: $body-bg;
background-color: $footerBgColor;
}
}

View file

@ -1,28 +0,0 @@
// Body
$body-bg: #f8fafc;
// Typography
$font-family-sans-serif: 'Nunito', sans-serif;
$font-size-base: 0.9rem;
$line-height-base: 1.6;
// Colors
$blue: #3490dc;
$indigo: #6574cd;
$purple: #9561e2;
$pink: #f66d9b;
$red: #e3342f;
$orange: #f6993f;
$yellow: #ffed4a;
$green: #38c172;
$teal: #4dc0b5;
$cyan: #6cb2eb;
// Theme
$theme-colors: (
"primary": #3176C4,
"secondary": #E7374B
);
$navBarHeight: 4rem;
$footerHeight: 3rem;
$footerBgColor: #161616;

11
resources/sass/app.scss vendored Normal file
View file

@ -0,0 +1,11 @@
// * Utilities
@import "./utilities/variables";
// * VENDOR
@import "./vendor/bootstrap";
// * PARTIALS
@import "./partials/layout";
// * PAGES
@import "./pages/login";

View file

@ -1,5 +0,0 @@
// Variables
@import 'variables';
@import 'layout';
@import "pages/login";

View file

@ -1,9 +1,7 @@
/* stylelint-disable selector-no-qualifying-type */
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@ -12,15 +10,15 @@
font-size: 3.5rem;
}
}
div.login {
height: 100%;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
height: 100%;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
-ms-flex-align: center;
}
.form-signin {
@ -29,24 +27,29 @@ div.login {
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;

18
resources/sass/partials/_layout.scss vendored Normal file
View file

@ -0,0 +1,18 @@
.body-full {
min-height: calc(100vh - #{$nav-bar-height} - #{$footer-height});
padding: 0 !important;
margin: 0 !important;
}
#app {
.navbar {
min-height: $nav-bar-height;
}
#footer {
height: $footer-height;
padding: 0 !important;
background-color: $footer-bg-color;
color: $body-bg;
}
}

View file

@ -0,0 +1,6 @@
@import "./mixin";
// DEFINE OTHERS VARIABLES
.w-fit {
width: fit-content;
}

View file

@ -0,0 +1,19 @@
// Override bootstrap colors
$primary: $color-primary;
$secondary: $color-secondary;
$success: $color-success;
$info: $color-info;
$warning: $color-warning;
$danger: $color-danger;
$light: $color-light;
$dark: $color-dark;
$theme-colors: (
primary: $primary,
secondary: $secondary,
success: $success,
info: $info,
warning: $warning,
danger: $danger,
light: $light,
dark: $dark,
);

1
resources/sass/utilities/_mixin.scss vendored Normal file
View file

@ -0,0 +1 @@
// empty

View file

@ -0,0 +1,28 @@
// Body
$body-bg: #f8fafc;
// Typography
$font-family-sans-serif: "Nunito", sans-serif;
$font-size-base: 0.9rem;
$line-height-base: 1.6;
// * COLORS
$color-light: #f8f9fa;
$color-dark: #212529;
$color-success: #198754;
$color-danger: #dc3545;
$color-info: #0dcaf0;
$color-warning: #ffc107;
$color-primary: #0d6efd;
$color-secondary: #6c757d;
// Theme
$theme-colors: (
"primary": #3176c4,
"secondary": #e7374b,
);
// Others
$nav-bar-height: 4rem;
$footer-height: 3rem;
$footer-bg-color: #161616;

47
resources/sass/vendor/_bootstrap.scss vendored Normal file
View file

@ -0,0 +1,47 @@
// 1. Include new custom variables
@import "./../utilities/bootstrap-variables";
// 2. Include functions first (so you can manipulate colors, SVGs, calc, etc)
@import "~bootstrap/scss/functions";
// 3. Include remainder of required Bootstrap stylesheets
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/variables-dark";
// 4. Include remainder of required parts
@import "~bootstrap/scss/maps";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/root";
// 5. Optionally include any other parts as needed
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/containers";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/helpers";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/spinners";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/close";
@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/card";
// 6. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss`
@import "~bootstrap/scss/utilities/api";
// 7 Overload bootstrap classes
@import "./../utilities/bootstrap-custom";

9
resources/ts/app.ts Normal file
View file

@ -0,0 +1,9 @@
// UTILITIES
import "./utilities/bootstrap";
import "./utilities/fontawesome";
// CUSTOM
import "./pages/customer.delete";
import "./pages/service.index";
import "./pages/single_optin.index";
import "./pages/statsExport";

86
resources/ts/index.d.ts vendored Normal file
View file

@ -0,0 +1,86 @@
export {};
import { AxiosStatic } from "axios";
// * Enum
interface EnumInterface {
label: string;
value: number | string;
}
// * Laravel Paginator
interface PaginatorLink {
url: null | string;
label: string;
active: boolean;
}
interface Paginator {
current_page: number;
first_page_url: string;
from: number;
last_page: number;
last_page_url: string;
links: Array<PaginatorLink>;
next_page_url: string | null;
path: string;
per_page: number;
prev_page_url: string | null;
to: number;
total: number;
}
// * Recursive List<string>
type StringList = Record<PropertyKey, string>;
interface NestedStringListInterface
extends Record<PropertyKey, NestedStringListInterface | StringList> {}
// * Recursive List<number>
type NumberList = Record<PropertyKey, number>;
interface NestedNumberListInterface
extends Record<PropertyKey, NestedNumberListInterface | NumberList> {}
// * Recursive List<Model>
type Model = Record<
string,
| string
| boolean
| number
| object
| NestedStringList
| NestedNumberList
| NestedModelInterface
| ModelListInterface
>;
interface NestedModelInterface extends Model {}
type NestedStringList = NestedStringListInterface | StringList | string;
type NestedNumberList = NestedNumberListInterface | NumberList | number;
type ModelList = Array<Model>;
interface ModelListInterface extends ModelList {}
declare global {
type CustomTag = LaravelModel;
type LaravelModel = Model;
type LaravelModelList = ModelList;
type LaravelPaginator = Paginator;
type LaravelPaginatorLink = PaginatorLink;
type Enum = EnumInterface;
interface Window {
axios: AxiosStatic;
Swal: Function;
vueDebug: boolean | undefined;
__SYSTEM: {
_asset: string;
_locale: string;
_routes: Record<string, string>;
_translations: {
json: NestedStringList;
php: NestedStringList;
};
};
}
}

View file

@ -0,0 +1,36 @@
import type * as bootstrap from "bootstrap";
/** ! Ignore here because there is no good TS vue mixin implementation at this time */
export default {
data(): {
_tooltips: Array<bootstrap.Tooltip>;
} {
return {
_tooltips: [],
};
},
methods: {
initTooltips() {
// @ts-ignore
if (this._tooltips) {
// @ts-ignore
this._tooltips.forEach((tooltip) => {
tooltip.disable();
tooltip.hide();
});
}
// @ts-ignore
this._tooltips = [].slice
.call(document.querySelectorAll("[data-bs-toggle=\"tooltip\"]"))
.map(function (tooltipTriggerEl) {
return new window.bootstrap.Tooltip(tooltipTriggerEl, {
customClass: "large-tooltip",
delay: {
show: 1250,
hide: 0,
},
});
});
},
},
};

View file

@ -0,0 +1,36 @@
import Swal from "sweetalert2";
const customerDelete = document.getElementById("customerDelete");
if (customerDelete) {
customerDelete.addEventListener("submit", (e) => {
const self = e.target as HTMLFormElement;
e.preventDefault();
Swal.fire({
title: "Supprimer ce client pour cette marque ?",
text: "Attention cette action est irréversible, si le client n'appartient plus à aucune marque il sera complètement retiré des bases",
icon: "warning",
confirmButtonText:
"Je comprends le danger et suis conscient de ce que je fait",
showCancelButton: true,
cancelButtonText: "Annuler",
confirmButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: "Attention au conséquences",
html: "La supression de ce compte dans la crm n'entrainera pas ça supression dans MESBR ni dans le front cache DB (pour les sites WPCRM). <br><h3><b>Une fois le compte supprimé je ne pourrai plus récupérer le hashcode nécessaire pour effectuer ces opérations.</b></h3>",
icon: "info",
confirmButtonText:
"Je comprends le danger et suis conscient que celà aura des conséquences",
showCancelButton: true,
cancelButtonText: "Annuler",
confirmButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
self?.submit();
}
});
}
});
});
}

View file

@ -0,0 +1,37 @@
import Swal from "sweetalert2";
const genForms = document.getElementsByClassName("genToken");
if (genForms.length) {
for (const form of genForms) {
form.addEventListener("submit", (e) => {
const self = e.target as HTMLFormElement;
e.preventDefault();
Swal.fire({
title: "Regénérer un token ?",
text: "Attention la regeneration empêchera le service de fonctionner tant que son token n'aura pas été mis à jour",
icon: "warning",
confirmButtonText:
"Je comprends le danger et suis conscient de ce que je fait",
showCancelButton: true,
cancelButtonText: "Annuler",
confirmButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: "Vous allez régénérer un token de service en production",
html: "<h3><b>Je suis conscient que celà va avoir des implications et qu'il me faudra mettre à jour le bearer token du service concerné dans son envirronement actuel</b></h3>",
icon: "info",
confirmButtonText: "Oui, feu patate !!",
showCancelButton: true,
cancelButtonText: "Annuler",
confirmButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
self.submit();
}
});
}
});
});
}
}

View file

@ -0,0 +1,11 @@
const brandSelect = document.getElementById("brandSelect") as HTMLSelectElement;
if (brandSelect) {
brandSelect.addEventListener("change", function () {
if (brandSelect.value !== "0") {
const url = new URL(location.href);
url.searchParams.set("brand", brandSelect.value);
url.searchParams.delete("rst");
location.assign(url.search);
}
});
}

View file

@ -0,0 +1,22 @@
const statsCSV = document.getElementById("StatsCSV");
const statsPDF = document.getElementById("StatsPDF");
if (statsCSV) {
statsCSV.addEventListener("click", () => {
window.location = statsCSV.dataset["request"] as
| Location
| (string & Location);
});
}
if (statsPDF) {
statsPDF.addEventListener("click", () => {
const canvas = document.getElementById("chart") as HTMLCanvasElement;
//@ts-ignore
printJS({
printable: canvas.toDataURL(),
type: "image",
imageStyle: "width:100%",
});
});
}

View file

@ -0,0 +1,21 @@
import axios from "axios";
import * as bootstrap from "bootstrap";
import bsTooltips from "./../modules/bsTooltips";
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
axios.defaults.headers.common = {
Accept: "application/json, text/plain, */*",
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document
.querySelector("meta[name=\"csrf-token\"]")!
.getAttribute("content"),
};
window.axios = axios;
window.bootstrap = bootstrap;
bsTooltips.methods.initTooltips();

View file

@ -3,7 +3,6 @@
<head>
@include('layouts.backend.head')
@stack('styles')
@crudPoliciesCSS()
</head>
<body>
<div id="app">
@ -11,14 +10,9 @@
<div class="container-fluid">
<div class="row">
@if(auth()->check())
@if(!isset($modelsClasses))
{{-- Set CrudModels for non crud controller in order to display sidemenu --}}
@php $modelsClasses = (new BrandController())->getCrudModels(); @endphp
@endif
@include('layouts.backend.sidenav')
<main role="main" class="col-md-9 mt-5 mb-5 ml-sm-auto col-lg-10 px-md-4 bodyFull">
<main role="main" class="col-md-9 mt-5 mb-5 ml-sm-auto col-lg-10 px-md-4 bg-white body-full">
@else
<main role="main" class="mt-5 mb-5 bodyFull">
<main role="main" class="mt-5 mb-5 bg-white body-full">
@endif
@yield('content')
</main>
@ -30,10 +24,7 @@
</div>
@stack('scriptsConstants')
@include('crud-policies::crud.modules.scriptConstants')
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha256-edRDsV9ULIqKzKjpN/KjyQ7Lp4vUn9usbJuHjH8Sk+k=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.16/dist/sweetalert2.all.min.js" integrity="sha256-551HBsteMvKOSqjUXSmRy/EOF0bBNbWB96b5L3Deh8A=" crossorigin="anonymous"></script>
<script src="{{ asset('js/preload-back.js') }}"></script>
@crudPoliciesJS()
<script src="{{ mix('js/preload-back.js', 'build') }}"></script>
@stack('scripts')
</body>
</html>

View file

@ -2,8 +2,12 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{ config('app.name') }}@hasSection('title') - @yield('title')@endif</title>
@preload('build/mix-manifest.json')
<meta name="description" content="@yield('description')">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
@hasSection('canonical')
<link rel="canonical" href="@yield('canonical')" />
@ -24,9 +28,9 @@
<link rel="icon" sizes="32x32" href="{{ asset('assets/images/LG-favicons_32x32.png') }}" type="image/png">
<link rel="icon" sizes="48x48" href="{{ asset('assets/images/LG-favicons_48x48.png') }}" type="image/png"> --}}
@preload('mix-manifest.json')
<!-- STYLES -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" integrity="sha256-hHKA3d/HttC8OW3Sl093W8DoZudhHJDj++kZYo6MLzA=" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.16/dist/sweetalert2.min.css" integrity="sha256-qpXwccFYncH5gTqXFe9EXZ8QeDw1Re68bbNVfFZzIzg=" crossorigin="anonymous">
<link rel="stylesheet" href="{{ asset('css/preload-back.css') }}">
<link rel="stylesheet" href="{{ mix('css/preload-back.css', 'build') }}">
@if(config('app.debug'))
<script nonce="{{ $nonce }}">window.vueDebug = true;</script>
@endif

View file

@ -16,8 +16,8 @@
data-bs-toggle="dropdown"
aria-expanded="false">{{ __('Statistiques') }}</a>
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="navbarDarkDropdownStats">
<li><a class="nav-link" href="{{ route('stats.viewByBrand') }}">{{ __('Statistiques par marques') }}</a></li>
<li><a class="nav-link" href="{{ route('stats.viewByQuestion') }}">{{ __('Statistiques par questions') }}</a></li>
<li><a class="dropdown-item" href="{{ route('stats.viewByBrand') }}">{{ __('Statistiques par marques') }}</a></li>
<li><a class="dropdown-item" href="{{ route('stats.viewByQuestion') }}">{{ __('Statistiques par questions') }}</a></li>
</ul>
</li>
<li class="nav-item dropdown">
@ -25,8 +25,8 @@
data-bs-toggle="dropdown"
aria-expanded="false">{{ __('Exports') }}</a>
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="navbarDarkDropdownExport">
<li><a class="nav-link" href="{{ route('customers.export') }}">{{ __('Exporter des Clients') }}</a></li>
<li><a class="nav-link" href="{{ route('questions.export') }}">{{ __('Exporter des Questions/réponses') }}</a></li>
<li><a class="dropdown-item" href="{{ route('customers.export') }}">{{ __('Exporter des Clients') }}</a></li>
<li><a class="dropdown-item" href="{{ route('questions.export') }}">{{ __('Exporter des Questions/réponses') }}</a></li>
</ul>
</li>
@endauth

View file

@ -10,10 +10,10 @@
@endphp
<ul class="nav flex-column mb-2">
@can('viewAny', $crudModel)
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable())) }}">{{ __('Liste') }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable())) }}" title="{{ __('Afficher la liste') }}">{{ __('Liste') }}</a></li>
@endcan
@can('create', $crudModel)
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.create', $prefix ?? '', $crudModel->getTable())) }}">{{ __('Ajouter') }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.create', $prefix ?? '', $crudModel->getTable())) }}" title="{{ __('Ajouter une nouvelle ligne') }}">{{ __('Ajouter') }}</a></li>
@endcan
</ul>
<hr>
@ -32,10 +32,10 @@
@endphp
<ul class="nav flex-column mb-2">
@can('viewAny', $crudModel)
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable())) }}">{{ __('Liste') }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable())) }}" title="{{ __('Afficher la liste') }}">{{ __('Liste') }}</a></li>
@endcan
@can('create', $crudModel)
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.create', $prefix ?? '', $crudModel->getTable())) }}">{{ __('Ajouter') }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ route(sprintf('%s%s.create', $prefix ?? '', $crudModel->getTable())) }}" title="{{ __('Ajouter une nouvelle ligne') }}">{{ __('Ajouter') }}</a></li>
@endcan
</ul>
@if(!$loop->last)

View file

@ -8,8 +8,8 @@
$iArgs['search'] = request()->sort_way;
}
@endphp
<div class="row mb-3">
<div class="col-12">
<div class="row w-100 mx-auto mb-3">
<div class="col-12 p-0">
<form id="searchForm" action="/{{ request()->path() }}">
<div class="input-group">
<label class="input-group-text" for="searchField">{{ $searchFields }}</label>

View file

@ -2,9 +2,10 @@
$route = request()->route();
$routeName = $route->getName();
$rst = !is_null(request()->rst) || !Session::has("$routeName.sorted");
$noOrder = !empty($noOrder);
$noOrder = $noOrder || (Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way")));
$noOrder = $noOrder || !empty(request()->search);
$noOrder = Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"));
$noOrder = Session::get("$routeName.sort_col") !== '' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"));
View::share('noOrder', $noOrder);
$ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
@endphp
<tr>
@ -17,30 +18,10 @@ $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
@endphp
{{-- NO Sort for order column --}}
@if($col === 'order' or in_array($col, $ignore))
@if(!$noOrder or $rst)
@if ($col === 'multi_pdf')
@if($noOrder and !$rst and !in_array($col, $ignore)) @continue @endif
<th scope="col" class="text-center @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
<span>{{ $colname }}</span>
<a class="btn btn-sm btn-outline-dark"
href="{{ route('products.multipdf.show') }}"
target="_blank"
data-bs="tooltip"
title="{{ __('Générer le .pdf de la sélection') }}">
<i class="fa fa-print"></i>
</a>
<a class="btn btn-sm btn-outline-dark"
href="{{ route('products.multipdf.destroy') }}"
data-bs="tooltip"
title="{{ __('Supprimer la sélection') }}">
<i class="fa fa-delete-left"></i>
</a>
<span class="text-nowrap">{{ $colname }}</span>
</th>
@else
<th scope="col" class="text-center @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
<span>{{ $colname }}</span>
</th>
@endif
@endif
@continue
@endif
<th scope="col" class="text-center @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
@ -52,10 +33,10 @@ $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'asc', 'rst' => null])) }}">
<button
class="btn btn-sm btn-outline-dark"
title="{{ __('Trier :name dans l\'ordre ascendant', ['name' => Str::lower($colname)]) }}"
data-bs="tooltip">
title="{{ __('Trier :name en ordre ascendant', ['name' => Str::lower($colname)]) }}"
data-bs-toggle="tooltip">
<i class="fas fa-arrow-up 2xs"></i>
</button>
</button>
</a>
@endif
@if(!session("$routeName.sort_way") or session("$routeName.sort_col") !== $col or (session("$routeName.sort_way") === 'asc' and session("$routeName.sort_col") === $col))
@ -63,8 +44,8 @@ $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'desc', 'rst' => null])) }}">
<button
class="btn btn-sm btn-outline-dark"
title="{{ __('Trier :name dans l\'ordre descendant', ['name' => Str::lower($colname)]) }}"
data-bs="tooltip">
title="{{ __('Trier :name en ordre descendant', ['name' => Str::lower($colname)]) }}"
data-bs-toggle="tooltip">
<i class="fas fa-arrow-down 2xs"></i>
</button>
</a>
@ -76,12 +57,12 @@ $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
@if($noOrder and !$rst)
<a class="btn btn-sm btn-danger"
href="{{ request()->fullUrlWithQuery(['sort_col' => null, 'sort_way' => null, 'rst' => true, 'search' => null]) }}"
data-bs="tooltip"
data-bs-toggle="tooltip"
title="{{ __('Effacer le tri') }}">
<i class="fa fa-eraser"></i>
</a>
@else
<span data-bs="tooltip" data-bs-placement="top" title="{{ __('Ici, les flèches sont utilisées pour trier et non pour filtrer') }}">
<span data-bs-toggle="tooltip" data-bs-placement="top" title="{{ __('Ici, les flèches servent à trier et non pas à filtrer') }}">
<i class="fas fa-question-circle"></i>
</span>
@endif

1
routes/breadcrumbs.php Normal file
View file

@ -0,0 +1 @@
<?php

16
tsconfig.json Normal file
View file

@ -0,0 +1,16 @@
{
"sourceMap": true,
"compilerOptions": {
"target": "esnext",
"lib": ["es5", "es6", "es2021", "dom", "dom.iterable"],
"module": "esnext",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"typeRoots": ["resources/ts/@types", "node_modules/@types"]
},
"include": ["resources/**/*.js", "resources/**/*.ts", "resources/**/*.vue"],
"exclude": ["node_modules"],
"compileOnSave": false,
"experimentalDecorators": true
}

110
webpack.mix.js vendored
View file

@ -1,45 +1,95 @@
const path = require('path');
const mix = require('laravel-mix');
require('laravel-mix-clean');
require('laravel-mix-polyfill');
require("laravel-mix-polyfill");
require("laravel-mix-clean");
require("laravel-mix-eslint");
require("laravel-mix-stylelint");
const assetDir = "assets";
const polyfill = {
enabled: true,
bugfixes: true,
useBuiltIns: "usage",
targets: "last 3 version, not dead, >0.3%",
};
mix.setPublicPath('public/build');
mix.setResourceRoot('/build');
mix.webpackConfig({
resolve: {
extensions: ["*", ".js", ".vue", ".ts"],
alias: {
'@': path.resolve('resources/assets')
}
}
"@": path.resolve("resources/assets"),
vue$: "vue/dist/vue.runtime.esm-bundler.js",
},
},
module: {
rules: [
{
test: /\.ts$/,
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
},
},
{
test: /\.vue$/,
loader: "vue-loader",
options: {
esModule: true,
},
},
],
},
// Pour debug compilation errors
// stats: {
// children: true,
// warningsFilter: [/\-\-underline\-color/],
// },
});
let assetDir = 'assets';
mix.options({
fileLoaderDirs: {
images: `${assetDir}/images`,
fonts: `${assetDir}/fonts`
fonts: `${assetDir}/fonts`,
images: `${assetDir}/images`
},
postCss: [
require('postcss-discard-comments')({
removeAll: true
})
]
require("postcss-discard-comments")({
removeAll: true,
}),
],
});
// mix Backend
mix.js("resources/js/back.js", "public/js/preload-back.js")
.sass("resources/sass/back.scss", "public/css/preload-back.css")
.clean({
cleanOnceBeforeBuildPatterns: [
'css/*',
'js/*',
'images/*',
'assets/*'
]
})
.polyfill({
enabled: true,
useBuiltIns: "usage",
targets: { "firefox": "50", "ie": 11 }
});
mix.clean({
cleanOnceBeforeBuildPatterns: [
"vendor/*",
"build/*"
],
});
// Copy Blade Images Assets
mix.copyDirectory('resources/assets', 'public/assets');
// * COMMON
mix.vue({
runtimeOnly: true,
extractStyles: "css/v-extract.css",
globalStyles: false,
})
.eslint({
fix: true,
extensions: ["js", "ts"],
})
.stylelint({
configFile: ".stylelintrc.json",
files: ["**/*.scss"],
})
.polyfill(polyfill)
.version()
.sourceMaps(false, "inline-source-map")
// * BACK
.ts("resources/ts/app.ts", "js/preload-back.js", { transpileOnly: true })
.sass("resources/sass/app.scss", "css/preload-back.css");
// * Copy Blade Images Assets
mix.copyDirectory("resources/assets", "public/build/assets");