Pass everything to Typescript + fix some js issues
This commit is contained in:
parent
45bc580c1e
commit
0a7f27d221
48 changed files with 4013 additions and 3574 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -16,7 +16,8 @@
|
|||
"titleBar.activeBackground": "#275782",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#27578299",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
"titleBar.inactiveForeground": "#e7e7e799",
|
||||
"commandCenter.border": "#e7e7e799"
|
||||
},
|
||||
"peacock.remoteColor": "#275782",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,9 +70,11 @@ class StoreScheduleRequest extends FormRequest
|
|||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'activity' => trans('catégorie'),
|
||||
'model_lie' => trans('type d\'élement lié'),
|
||||
'id_lie' => trans('élement lié')
|
||||
'user_id' => \trans('utilisateur'),
|
||||
'activity' => \trans('type d\'activitée'),
|
||||
'model_lie' => \trans('type d\'élément lié'),
|
||||
'id_lie' => \trans('élément lié'),
|
||||
'slot' => \trans('ligne de la journée'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
{
|
||||
"include": [
|
||||
"./resources/js/**/*"
|
||||
]
|
||||
}
|
||||
{
|
||||
"include": ["./resources/ts/**/*"]
|
||||
}
|
||||
|
|
|
|||
2250
package-lock.json
generated
2250
package-lock.json
generated
File diff suppressed because it is too large
Load diff
31
package.json
31
package.json
|
|
@ -11,12 +11,15 @@
|
|||
"prod": "npm run production"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-async-to-generator": "^7.16.8",
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.1.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"@vue/compiler-sfc": "^3.2.37",
|
||||
"autoprefixer": "10.4.5",
|
||||
"@types/bootstrap": "^5.1.12",
|
||||
"@types/toastify-js": "^1.11.1",
|
||||
"@types/vue-select": "^3.16.0",
|
||||
"@types/webpack-env": "^1.17.0",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^5.1.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
|
|
@ -24,20 +27,20 @@
|
|||
"css-loader": "^6.7.1",
|
||||
"del-cli": "^4.0.1",
|
||||
"glightbox": "^3.2.0",
|
||||
"laravel-mix": "^6.0.16",
|
||||
"laravel-mix": "^6.0.6",
|
||||
"laravel-mix-clean": "^0.1.0",
|
||||
"laravel-mix-polyfill": "^3.0.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.29.3",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.2",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sass": "^1.52.1",
|
||||
"sass-loader": "^12.6.0",
|
||||
"sweetalert2": "^11.4.16",
|
||||
"toastify-js": "^1.11.2",
|
||||
"vue": "^2.6.14",
|
||||
"vue-loader": "^15.9.7",
|
||||
"vue-select": "^3.13.0",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.7.3",
|
||||
"vue": "^3.2.36",
|
||||
"vue-loader": "^16.8.3",
|
||||
"vue-select": "^4.0.0-beta.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
resources/js/app.js
vendored
6
resources/js/app.js
vendored
|
|
@ -1,6 +0,0 @@
|
|||
require('./bootstrap')
|
||||
require('./session_watch')
|
||||
require('./vue')
|
||||
require('./files_add')
|
||||
require('./label_colorize')
|
||||
require('./confirm')
|
||||
33
resources/js/bootstrap.js
vendored
33
resources/js/bootstrap.js
vendored
|
|
@ -1,33 +0,0 @@
|
|||
// SweetAlert
|
||||
import Swal from 'sweetalert2/dist/sweetalert2.js'
|
||||
import GLightbox from 'glightbox'
|
||||
|
||||
// bootstrap styling
|
||||
window.Swal = Swal.mixin({
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-lg btn-success ms-3 me-3',
|
||||
cancelButton: 'btn btn-lg btn-danger ms-3 me-3'
|
||||
},
|
||||
buttonsStyling: false
|
||||
})
|
||||
|
||||
window._ = require('lodash')
|
||||
|
||||
/**
|
||||
* 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 = require('axios')
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
||||
let csrfToken = document.querySelector('meta[name="csrf-token"]')
|
||||
if (csrfToken.getAttribute('content')) {
|
||||
window.axios.defaults.headers.common['Authorization'] = `Bearer ${csrfToken.getAttribute('content')}`
|
||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken.getAttribute('content')
|
||||
}
|
||||
|
||||
const glightbox = new GLightbox({
|
||||
'selector': '.glightbox'
|
||||
})
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-select multiple v-model="selectedLabels" :options="labels" @search="addOption">
|
||||
<template v-slot:option="option">
|
||||
<span class="py-1 px-2" :style="`background-color: ${option.color};color: ${invertColor(option.color, true)};`">{{ option.label }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
<input v-for="label of selectedLabels" type="hidden" :key="label.code" :value="label.code" :name="name + '[]'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.name = data.name
|
||||
for(let label of (data.value ?? [])) {
|
||||
this.selectedLabels.push({label: label.name, code: label.id, color: label.color})
|
||||
}
|
||||
for(let label of data.labels) {
|
||||
this.labels.push({label: label.name, code: label.id, color: label.color})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
selectedLabels: [],
|
||||
inputValue: '',
|
||||
labels: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,737 +0,0 @@
|
|||
<template>
|
||||
<!-- TODO: order by name, status -->
|
||||
<div ref="macroschedule" class="macroschedule">
|
||||
<div class="row mb-3">
|
||||
<div class="col-1">
|
||||
<button class="btn btn-primary" @click.prevent="goPreviousWeek()">←</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h1 class="text-secondary">Plan de charge</h1>
|
||||
<div class="input-group">
|
||||
<label for="scheduleDateSelect" class="input-group-text">Voir date : </label>
|
||||
<input id="scheduleDateSelect" class="form-control" type="date" :value="startMoment.format('YYYY-MM-DD')" @input="showMacroScheduleDate($event.target.value)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<fieldset>
|
||||
<legend>Légende</legend>
|
||||
<template v-if="isAdmin">
|
||||
<span v-for="activityEnum of activitiesEnum" :key="activityEnum.value" :class="'p-1 m-2 border badge badge-activity-' + activityEnum.value">{{ activityEnum.label }}</span>
|
||||
<span class="p-1 m-2 border badge badge-marker">Marqueur</span>
|
||||
<span class="p-1 m-2 border badge badge-today">Aujourd'hui</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="p-1 m-2 border badge badge-activity-light">Libre</span>
|
||||
<span class="p-1 m-2 border badge badge-activity-dark">Occupé</span>
|
||||
</template>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button class="btn btn-primary" @click.prevent="goNextWeek()">→</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" rowspan="3" class="align-middle">{{ isAdmin ? 'Projets' : '' }}</th>
|
||||
<th scope="col" rowspan="3" class="align-middle">{{ isAdmin ? 'Statut' : '' }}</th>
|
||||
<th v-if="daysThisMonth.length" :colspan="daysThisMonth.length" class="text-center">
|
||||
{{ daysThisMonth[0].format('MMMM') | ucfirst }}
|
||||
</th>
|
||||
<th v-if="daysNextMonth.length" :colspan="daysNextMonth.length" class="text-center">
|
||||
{{ daysNextMonth[0].format('MMMM') | ucfirst }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th v-for="weekNumber in macroscheduleConfig.numberOfWeeksToShow" :key="weekNumber" :colspan="7 - 2" class="text-center">
|
||||
SEMAINE {{ currentWeek + weekNumber - 1}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<template v-for="(currentDay, index) of daysToShow">
|
||||
<th scope="col" v-if="dayShouldbeenSeen(currentDay)" :key="index" :class="'dayCell p-0 text-center ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')">
|
||||
{{ currentDay.format('dd') }} {{ String(currentDay.date()).padStart(2, '0') }}
|
||||
</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-if="isAdmin">
|
||||
<tr v-for="project of projects" :key="project.id" class="projectRow">
|
||||
<th scope="col">
|
||||
<a :href="route('projects.show', {PROJECT: project.id })" :class="'rounded-1 p-1 project text-nowrap text-truncate overflow-hidden bg-enum-'+project.status">{{ project.name | str_limit(45) }}</a>
|
||||
</th>
|
||||
<td>
|
||||
<span class="fw-bold text-nowrap text-truncate overflow-hidden">{{ projectStatusEnum[project.status].label }}</span>
|
||||
</td>
|
||||
<template v-for="(currentDay, index) of daysToShow">
|
||||
<td :class="'p-0 border border-dark activities ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')" v-if="dayShouldbeenSeen(currentDay)" :key="index">
|
||||
<div
|
||||
v-for="(activity, index) in [getActivity(project, getMorningMoment(currentDay)), getActivity(project, getAfterNoonMoment(currentDay))]"
|
||||
:key="index"
|
||||
:class="getActivityClass(activity)+ ' activity ' + (hoverActivityId === activity.id ? 'hover' : '')"
|
||||
@click="showModale(project, activity, currentDay, index === 0)"
|
||||
@mouseover="setActivityHover(activity)"
|
||||
@mouseleave="setActivityHover(activity, false)">
|
||||
<div :title="getActivityTooltip(activity)" data-bs-toggle="tooltip">
|
||||
|
||||
</div>
|
||||
<template v-if="projectHasMarkersForTheDay(currentDay, project)">
|
||||
<div
|
||||
class="marker"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-html="true"
|
||||
:title="getMarkersTooltip(currentDay, project)"></div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
<tr class="projectRow">
|
||||
<th scope="col" class="border-end-0 d-flex justify-content-between"><span class="fw-bold h5">Disponibilité</span></th>
|
||||
<td class="border-start-0"></td>
|
||||
<template v-for="(currentDay, index) of daysToShow">
|
||||
<td :class="'p-0 border border-dark activities ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')" v-if="dayShouldbeenSeen(currentDay)" :key="index">
|
||||
<template v-if="isAdmin">
|
||||
<div class="activity text-center" :style="{ 'background-color' : 'rgba(0,0,0,'+ getLoadPercent(getMorningMoment(currentDay)) }">
|
||||
{{ getLoad(getMorningMoment(currentDay)) > 1 ? '🔥' : ' ' }}
|
||||
</div>
|
||||
<div class="activity text-center" :style="{ 'background-color' : 'rgba(0,0,0,'+ getLoadPercent(getAfterNoonMoment(currentDay)) }">
|
||||
{{ getLoad(getAfterNoonMoment(currentDay)) > 1 ? '🔥' : ' ' }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :class="'activity text-center ' +(getLoad(getMorningMoment(currentDay)) > macroscheduleConfig.loadDevThreshold ? 'bg-dark' : 'bg-light')"> </div>
|
||||
<div :class="'activity text-center ' +(getLoad(getAfterNoonMoment(currentDay)) > macroscheduleConfig.loadDevThreshold ? 'bg-dark' : 'bg-light')"> </div>
|
||||
</template>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
- une Date début, et un choix matin/après-midi
|
||||
- une Date Fin, et un choix matin/après-midi
|
||||
- une activité (cf. liste annexe ci-dessous)
|
||||
- une charge (en nombre d’hommes occupés)
|
||||
- un commentaire
|
||||
-->
|
||||
<div ref="editModal" class="modal fade" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" >
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalLabel" ref="modalLabel">Activité pour {{ modaleProps.name }}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div v-if="error" class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="form-group mb-3">
|
||||
<label for="categorie" class="col-form-label text-md-right ms-5">Activité</label>
|
||||
<select id="categorie" ref="type" class="form-select" aria-label="Sélectionnez une catégorie" :disabled="modalLoading" v-model="modaleProps.activity" required>
|
||||
<option :value="undefined" disabled>Sélectionnez une activité</option>
|
||||
<option v-for="activityEnum in activitiesEnum" :key="activityEnum.value" :value="activityEnum.value" :class="activityEnum.value">{{ activityEnum.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="start" class="col-form-label text-md-right ms-5">Date début</label>
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" id="start" type="date" name="start" :value="moment(modaleProps.start).format('YYYY-MM-DD')" @input="updateDateField($event, 'start')" :disabled="modalLoading">
|
||||
<select class="form-select" :disabled="modalLoading" v-model="modaleProps.startMorning" @input="updateHourField($event, 'start')">
|
||||
<option value="1" :selected="moment(modaleProps.start).format('HH') === '00'">Matin</option>
|
||||
<option value="0" :selected="moment(modaleProps.start).format('HH') === '12'">Midi</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="end" class="col-form-label text-md-right ms-5">Date fin</label>
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" id="end" type="date" name="end" :value="moment(modaleProps.end).format('YYYY-MM-DD')" @input="updateDateField($event, 'end')" :disabled="modalLoading">
|
||||
<select class="form-select" :disabled="modalLoading" v-model="modaleProps.endMorning" @input="updateHourField($event, 'end')">
|
||||
<option value="1" :selected="moment(modaleProps.end).format('HH') === '11'">Midi</option>
|
||||
<option value="0" :selected="moment(modaleProps.end).format('HH') === '23'">Soir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="comment" class="col-form-label text-md-right ms-5">Commentaire</label>
|
||||
<textarea ref="comment" class="form-control" name="comment" id="comment" rows="2" placeholder="" @keydown="limitLines($event)" v-model="modaleProps.comment" :disabled="modalLoading"></textarea>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="load" class="col-form-label text-md-right ms-5">Charge <i>(nombre de personnes consommées sur la période)</i></label>
|
||||
<input type="number" class="form-control" name="load" id="load" v-model="modaleProps.load" :disabled="modalLoading" min="1"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-8 text-center">
|
||||
<button class="btn btn-primary me-3" type="button" :disabled="modalLoading" @click="saveActivity()">
|
||||
<span v-if="modalLoading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
{{ modalLoading ? 'Chargement...' : 'Valider' }}
|
||||
</button>
|
||||
<button class="btn btn-danger" @click="modale.hide();$nextTick(refreshTooltips)" :disabled="modalLoading">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Toastify from 'toastify-js'
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
mounted() {
|
||||
let self = this
|
||||
window.Toastify = Toastify
|
||||
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.projects = data.projects
|
||||
this.loadCollection = data.loadCollection
|
||||
this.macroscheduleConfig = data.macroscheduleConfig
|
||||
this.activitiesEnum = data.activitiesEnum
|
||||
this.projectStatusEnum = data.projectStatusEnum
|
||||
this.today = data.today
|
||||
this.startDay = data.startDay
|
||||
this.isAdmin = data.isAdmin
|
||||
this.$nextTick(this.refreshTooltips)
|
||||
this.modale = new this.bootstrap.Modal(this.$refs.editModal)
|
||||
this.$refs.editModal.addEventListener('hide.bs.modal', function (e) {
|
||||
if(self.modalLoading) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isAdmin: false,
|
||||
today: '',
|
||||
startDay: '',
|
||||
textLimit: 254,
|
||||
hoverActivityId: null,
|
||||
error: '',
|
||||
modaleProps: {
|
||||
activity: null,
|
||||
start: '',
|
||||
startMorning: 1,
|
||||
end: '',
|
||||
endMorning: 1,
|
||||
load: 0,
|
||||
comment: '',
|
||||
name: '',
|
||||
project: null
|
||||
},
|
||||
currentActivity: null,
|
||||
projects: [],
|
||||
activitiesEnum: [],
|
||||
projectStatusEnum: [],
|
||||
macroscheduleConfig: [],
|
||||
loadCollection: {},
|
||||
modale: null,
|
||||
modalLoading: false,
|
||||
tooltipTriggerList: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
todayMoment() {
|
||||
return this.moment(this.today)
|
||||
},
|
||||
startMoment() {
|
||||
return this.moment(this.startDay)
|
||||
},
|
||||
selectedMoment() {
|
||||
return this.moment(this.startMoment.week(), 'w').hours(0).minutes(0).seconds(0).milliseconds(0).add(1, 'day').startOf('week')
|
||||
},
|
||||
currentMonthName() {
|
||||
return this.selectedMoment.format('MMMM')
|
||||
},
|
||||
currentWeek() {
|
||||
return this.selectedMoment.week()
|
||||
},
|
||||
daysToShow() {
|
||||
let days = [], startMoment = this.selectedMoment.clone()
|
||||
for (let i = this.macroscheduleConfig.numberOfWeeksToShow; i > 0; i--) {
|
||||
for(let dayNumber = 0; dayNumber < 7; dayNumber++) {
|
||||
days.push(startMoment)
|
||||
startMoment = startMoment.clone().add(1, 'day')
|
||||
}
|
||||
}
|
||||
return days
|
||||
},
|
||||
daysThisMonth() {
|
||||
let days = this.daysToShow, currentMonth = days[0]?.month(),
|
||||
out = []
|
||||
for (let day of days) {
|
||||
if(currentMonth === day.month() && this.dayShouldbeenSeen(day)) {
|
||||
out.push(day)
|
||||
}
|
||||
}
|
||||
return out
|
||||
},
|
||||
daysNextMonth() {
|
||||
let days = this.daysToShow, currentMonth = days[0]?.month(),
|
||||
out = []
|
||||
for (let day of days) {
|
||||
if(currentMonth !== day.month() && this.dayShouldbeenSeen(day)) {
|
||||
out.push(day)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modaleProps: {
|
||||
handler(object) {
|
||||
// * Validate Date fields
|
||||
this.error = ''
|
||||
this.validateDateField(this.moment(this.modaleProps.start), 'start')
|
||||
this.validateDateField(this.moment(this.modaleProps.end), 'end')
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
currentActivity: {
|
||||
handler(activityObject) {
|
||||
if (activityObject.project_id) {
|
||||
let project = this.getProject(activityObject.project_id)
|
||||
// * Insert (or update) activity
|
||||
this.$set(project.activities, activityObject.id, Object.assign({}, activityObject))
|
||||
this.$nextTick(this.refreshTooltips)
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateDateField(moment, fieldName) {
|
||||
if(!this.dayShouldbeenSeen(moment)) {
|
||||
this.error = 'La date de ' + (fieldName === 'start' ? 'début' : 'fin') + ' ne peut être un jour de weekend.'
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
updateDateField(e, fieldName) {
|
||||
let date = this.moment(this.modaleProps[fieldName]),
|
||||
time = Number.parseInt(this.modaleProps[fieldName + 'Morning']) === 1 ?
|
||||
this.getMorningMoment(date, fieldName === 'start') :
|
||||
this.getAfterNoonMoment(date, fieldName === 'start'),
|
||||
input = this.moment(e.target.value)
|
||||
if(this.validateDateField(input, fieldName)) {
|
||||
this.error = ''
|
||||
this.$set(this.modaleProps, fieldName, input.format('YYYY-MM-DD') + 'T' + time.format('HH:mm:ss'))
|
||||
}
|
||||
},
|
||||
updateHourField(e, fieldName) {
|
||||
let date = this.moment(this.modaleProps[fieldName])
|
||||
if(Number.parseInt(e.target.value) === 1) {
|
||||
this.$set(this.modaleProps, fieldName, this.getMorningMoment(date, fieldName === 'start').format('YYYY-MM-DD\THH:mm:ss'))
|
||||
} else {
|
||||
this.$set(this.modaleProps, fieldName, this.getAfterNoonMoment(date, fieldName === 'start').format('YYYY-MM-DD\THH:mm:ss'))
|
||||
}
|
||||
},
|
||||
saveActivity() {
|
||||
let self = this,
|
||||
activity = Object.assign({}, this.currentActivity)
|
||||
|
||||
if(this.error.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.modalLoading = true
|
||||
|
||||
activity.activity = self.modaleProps.activity
|
||||
activity.start = self.modaleProps.start
|
||||
activity.end = self.modaleProps.end
|
||||
activity.load = self.modaleProps.load
|
||||
activity.comment = self.modaleProps.comment
|
||||
activity.project_id = self.modaleProps.project.id
|
||||
|
||||
axios.post(self.route('macroplanning.activity'), activity)
|
||||
.then(response => {
|
||||
self.error = ''
|
||||
self.sendToast('L\'activité à été mis à jour')
|
||||
self.$nextTick(() => {
|
||||
self.modalLoading = false
|
||||
self.modale.hide()
|
||||
activity.id = response.data.id
|
||||
self.$set(self, 'currentActivity', activity)
|
||||
})
|
||||
if(response.data.hasOverlap) {
|
||||
window.location.reload()
|
||||
}
|
||||
}).catch(error => {
|
||||
if(error.response.status === 429) {
|
||||
location.reload()
|
||||
}
|
||||
if(error.response.status === 422) {
|
||||
if(error.response.data.errors) {
|
||||
self.error = ''
|
||||
for(let id in error.response.data.errors) {
|
||||
let field = error.response.data.errors[id]
|
||||
self.error += field.join('\n') + '\n'
|
||||
}
|
||||
return
|
||||
} else if(error.response.data.message) {
|
||||
self.error = error.response.data.message
|
||||
return
|
||||
}
|
||||
}
|
||||
self.error = error
|
||||
}).then(() => {
|
||||
self.modalLoading = false
|
||||
self.$nextTick(self.refreshTooltips)
|
||||
})
|
||||
},
|
||||
showModale(project, activity, selectedMoment, isMorning = true) {
|
||||
|
||||
selectedMoment = selectedMoment.clone()
|
||||
|
||||
let self = this,
|
||||
startMoment = (activity.start ? this.moment(activity.start) :
|
||||
(isMorning ? this.getMorningMoment(selectedMoment, true) : this.getAfterNoonMoment(selectedMoment, true))),
|
||||
endMoment = (activity.end ? this.moment(activity.end) :
|
||||
(isMorning ? this.getMorningMoment(selectedMoment, false) : this.getAfterNoonMoment(selectedMoment, false)))
|
||||
|
||||
this.modaleProps = {
|
||||
activity: activity.activity,
|
||||
start: activity.start ?? startMoment.format('YYYY-MM-DD\THH:mm:ss'),
|
||||
startMorning: startMoment.format('HH') === '00' ? 1 : 0,
|
||||
end: activity.end ?? endMoment.format('YYYY-MM-DD\THH:mm:ss'),
|
||||
endMorning: endMoment.format('HH') === '11' ? 1 : 0,
|
||||
load: activity.load,
|
||||
comment: activity.comment,
|
||||
name: project.name,
|
||||
project: project
|
||||
}
|
||||
this.currentActivity = activity
|
||||
this.modale.show()
|
||||
|
||||
setTimeout(() => {
|
||||
self.$nextTick(self.refreshTooltips)
|
||||
}, 2000)
|
||||
},
|
||||
dayShouldbeenSeen(moment) {
|
||||
return moment.day() !== 6 && moment.day() !== 0
|
||||
},
|
||||
projectHasMarkersForTheDay(moment, project) {
|
||||
for(let marker of project.markers) {
|
||||
if (moment.isSame(marker.marker, 'day')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
setActivityHover(activity, activate = true) {
|
||||
if(!activate) {
|
||||
this.hoverActivityId = null
|
||||
} else {
|
||||
this.hoverActivityId = activity.activity !== undefined ? activity.id : ''
|
||||
}
|
||||
},
|
||||
|
||||
// * GETTERS
|
||||
getLoadPercent(moment) {
|
||||
let load = this.getLoad(moment)
|
||||
return load > 1 ? 1 : load
|
||||
},
|
||||
getLoad(moment) {
|
||||
return this.loadCollection[moment.format('YYYY-MM-DD 00:00:00')] ?? null
|
||||
},
|
||||
getActivityTooltip(activity) {
|
||||
let activityEnumLabel = activity.activity !== undefined ? (this.activitiesEnum[activity.activity] ? this.activitiesEnum[activity.activity].label : '') : ''
|
||||
return activity.activity !== undefined ? `${activityEnumLabel}, Charge ${activity.load} personnes, ${activity.comment}` : ''
|
||||
},
|
||||
getActivityClass(activity) {
|
||||
return activity ? `bg-enum-${activity.activity}` : 'bg-white'
|
||||
},
|
||||
getProject(project_id) {
|
||||
return this.projects[project_id]
|
||||
},
|
||||
getActivity(project, moment) {
|
||||
for(let activity of Object.values(project.activities)) {
|
||||
if(moment.isBetween(this.moment(activity.start, this.moment.DATETIME_LOCAL_SECONDS), this.moment(activity.end, this.moment.DATETIME_LOCAL_SECONDS), 'hour', '[]')) {
|
||||
return activity
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
getMorningMoment(moment, start = true) {
|
||||
moment = moment.clone()
|
||||
return start ? moment.hours(0).minutes(0).seconds(0) : moment.hours(11).minutes(59).seconds(0)
|
||||
},
|
||||
getAfterNoonMoment(moment, start = true) {
|
||||
moment = moment.clone()
|
||||
return start ? moment.hours(12).minutes(0).seconds(0) : moment.hours(23).minutes(59).seconds(0)
|
||||
},
|
||||
getMarkersTooltip(moment, project) {
|
||||
let tooltip = ''
|
||||
for(let marker of project.markers) {
|
||||
if (moment.isSame(marker.marker, 'day')) {
|
||||
tooltip += `${this.moment(marker.marker).format('DD/MM/YYYY')} ${marker.label}<br>`
|
||||
}
|
||||
}
|
||||
return tooltip
|
||||
},
|
||||
|
||||
// * NAVIGATION
|
||||
goPreviousWeek() {
|
||||
let moment = this.startMoment ?? this.todayMoment
|
||||
moment = this.moment(moment.week(), 'w')
|
||||
this.showMacroScheduleDate('', moment.subtract(1, 'week'))
|
||||
},
|
||||
goNextWeek() {
|
||||
let moment = this.startMoment ?? this.todayMoment
|
||||
moment = this.moment(moment.week(), 'w')
|
||||
this.showMacroScheduleDate('', moment.add(1, 'week'))
|
||||
},
|
||||
showMacroScheduleDate(dateString, moment = null) {
|
||||
let url = new URL(window.location)
|
||||
moment = moment ?? this.moment(dateString)
|
||||
location = url.origin+ url.pathname + '?date='+moment.format('DD/MM/YYYY')
|
||||
},
|
||||
|
||||
sendToast(message) {
|
||||
Toastify({
|
||||
text: message,
|
||||
duration: 3000,
|
||||
gravity: "top",
|
||||
position: "right",
|
||||
offset: {
|
||||
x: '2rem',
|
||||
y: '3rem'
|
||||
},
|
||||
style: {
|
||||
background: "linear-gradient(to right, #00317d, #0058ca)",
|
||||
}
|
||||
}).showToast();
|
||||
},
|
||||
refreshTooltips() {
|
||||
let exisitingToolips = document.querySelectorAll('.tooltip[role="tooltip"]')
|
||||
for(let tooltip of exisitingToolips) {
|
||||
tooltip.remove()
|
||||
}
|
||||
this.tooltipTriggerList = [].slice.call(this.$refs.macroschedule.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
this.tooltipTriggerList.map((tooltipTriggerEl) => {
|
||||
return new this.bootstrap.Tooltip(tooltipTriggerEl, { delay: 500 })
|
||||
})
|
||||
},
|
||||
limitLines(e) {
|
||||
let keynum, lines = this.$refs.comment.value.split('\n').length;
|
||||
// IE
|
||||
if(window.event) {
|
||||
keynum = e.keyCode;
|
||||
// Netscape/Firefox/Opera
|
||||
} else if(e.which) {
|
||||
keynum = e.which;
|
||||
}
|
||||
if(keynum != 8 && this.$refs.comment.value.length >= this.textLimit) {
|
||||
e.preventDefault()
|
||||
return false;
|
||||
}
|
||||
if(keynum == 13 && lines == this.$refs.comment.rows) {
|
||||
e.preventDefault()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './../../sass/variables';
|
||||
@import './../../sass/complement';
|
||||
@import "toastify-js/src/toastify.css";
|
||||
|
||||
.macroschedule {
|
||||
@each $index, $color in $enum-activities {
|
||||
.badge-activity-#{$index} {
|
||||
background-color: $color;
|
||||
color: a11y-color(#353535 ,$color);
|
||||
}
|
||||
}
|
||||
.badge-marker {
|
||||
background-color: rgba(255, 2, 204, 0.6);
|
||||
color: a11y-color(#353535 ,rgba(255, 2, 204, 0.6));
|
||||
}
|
||||
.badge-today {
|
||||
background-color: rgba(0, 255, 0, 0.6);
|
||||
color: a11y-color(#353535 ,rgba(0, 255, 0, 0.6));
|
||||
}
|
||||
.badge-activity-light {
|
||||
background-color: $light;
|
||||
color: a11y-color(#353535 ,$light);
|
||||
}
|
||||
.badge-activity-dark {
|
||||
background-color: $dark;
|
||||
color: a11y-color(#FFF ,$dark);
|
||||
}
|
||||
|
||||
.project {
|
||||
@each $index, $color in $enum-project-status {
|
||||
&.bg-enum-#{$index} {
|
||||
background-color: $color;
|
||||
color: a11y-color(#f6f6f6 ,$color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th.today,td.today > div {
|
||||
position: relative;
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0.2rem;
|
||||
transform: translateY(-50%);
|
||||
height: 110%;
|
||||
width: 0.3rem;
|
||||
background-color: rgba(0, 255, 0, 0.6);
|
||||
|
||||
}
|
||||
}
|
||||
tr:last-child > td.today {
|
||||
position: relative;
|
||||
&:after {
|
||||
content: '↑ Aujourd\'hui';
|
||||
position: absolute;
|
||||
bottom: -2.5rem;
|
||||
left: 0;
|
||||
background-color: rgb(0, 255, 0);
|
||||
padding: 0.2rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
border-radius: 0.2rem;
|
||||
width: 7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dayCell {
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.projectRow {
|
||||
&:not(:first-child) {
|
||||
border-top: 2px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
.activities .activity {
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
$buttonBgColor: #4d4d4d;
|
||||
|
||||
.activity {
|
||||
position: relative;
|
||||
|
||||
.bg-white {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@each $index, $color in $enum-activities {
|
||||
&.bg-enum-#{$index} {
|
||||
background-color: $color;
|
||||
color: a11y-color(#353535 ,$color);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset button Style to nice one
|
||||
button {
|
||||
display: inline;
|
||||
border: none;
|
||||
padding: 0.6rem 3rem;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
background: $buttonBgColor;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
button[disabled] {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: darken($buttonBgColor, 10%);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0.1rem;
|
||||
left: 0.1rem;
|
||||
padding: 0;
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
margin: 0;
|
||||
background-color: $buttonBgColor;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken($buttonBgColor, 10%);
|
||||
}
|
||||
&.btn-clear {
|
||||
color: darken($color: #fff, $amount: 5);
|
||||
}
|
||||
}
|
||||
|
||||
.infos {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.hover > div {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
& > div.marker {
|
||||
z-index: 200;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.2rem;
|
||||
transform: translateY(-50%);
|
||||
height: 110%;
|
||||
width: 0.7rem;
|
||||
background-color: rgba(255, 2, 204, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-select v-model="value" :options="labels" @search="addOption"/>
|
||||
<input type="hidden" :value="inputValue" :name="name" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.name = data.name
|
||||
this.labels = data.labels
|
||||
this.inputValue = data.value
|
||||
this.value = {
|
||||
code: this.labels.length,
|
||||
label: this.inputValue
|
||||
}
|
||||
for (let label of this.labels) {
|
||||
label.default = true
|
||||
if (label.label == this.dbValue) {
|
||||
this.value = label
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
value: {},
|
||||
inputValue: '',
|
||||
labels: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.inputValue = this.value.label
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addOption(newOption) {
|
||||
if(newOption.length) {
|
||||
for (let key in this.labels) {
|
||||
if(!this.labels[key].default) {
|
||||
this.labels.splice(key, 1)
|
||||
}
|
||||
}
|
||||
this.labels.push({label: newOption, code: this.labels.length})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,785 +0,0 @@
|
|||
<template>
|
||||
<div class="plannings">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-1 text-center actions_col">
|
||||
<a class="sticky-top btn btn-primary mt-5" @click="goToPlanning(false)">←</a>
|
||||
</div>
|
||||
|
||||
<div class="col-10">
|
||||
<table class="table table-bordered align-middle text-center schedules_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2" >
|
||||
|
||||
</th>
|
||||
<th :colspan="showWeekends ? 7:5" v-for="item in weeksCells" class="Mon Fri" :key="item.num">
|
||||
S {{item.num}} - {{item.month}} {{item.year}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th v-for="day in days" :class="'day '+day.eday" :key="day.num">
|
||||
{{day.day}} {{day.num}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(user,index) in schedules">
|
||||
<tr v-for="(slot,key) in user" :key="index + '' + key">
|
||||
<td :rowspan="daySlots" class="resource_ident" v-if="key == 1">
|
||||
{{users[index].name}}
|
||||
</td>
|
||||
<td v-for="day in days" :class="'dynCell day '+user[key][day.key].type.value+ ' ' +day.eday" :key="day.key">
|
||||
<div :class="'cellsizer '+ (user[key][day.key].type.value ? user[key][day.key].type.value : 'empty')">
|
||||
<b v-if="user[key][day.key]['model_lie']['value']">
|
||||
<a target="_blank" class="text-light bg-dark"
|
||||
:href="getCellObjectInfo(user[key][day.key]).url">
|
||||
{{user[key][day.key]['id_lie']['label']}}
|
||||
</a>
|
||||
</b><br/>
|
||||
{{user[key][day.key]['text']}}
|
||||
|
||||
<button class="btn btn-edit" type="button" data-bs-toggle="modal"
|
||||
data-bs-target="#editModal" @click="setModalData(index,key,day.key,user[key][day.key]['id'])">✎</button>
|
||||
<button :class="'btn btn-paste '+(!clipBoard.set ? 'hide' : '') " type="button"
|
||||
@click="pasteCell(index,key,day.key,user[key][day.key]['id'])">📋</button>
|
||||
<button class="btn btn-copy" type="button"
|
||||
@click="copyCell(index,key,day.key,user[key][day.key]['id'])">
|
||||
<span style="font-size: .875em; margin-right: .125em; position: relative; top: -.25em; left: -.125em">
|
||||
📄<span style="position: absolute; top: .25em; left: .25em">📄</span>
|
||||
</span></button>
|
||||
<button :class="'btn btn-clear '+(!user[key][day.key]['id'] ? 'hide' : '')" @click="eraseCell(index,key,day.key,user[key][day.key]['id'])">⌫</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-1 text-center actions_col">
|
||||
<a class="sticky-top btn btn-primary mt-5" @click="goToPlanning(true)">→</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" >
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalLabel" ref="modalLabel">{{modale.title}}</h5>
|
||||
<button ref="close" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" ></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div v-if="error" class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="form-group mb-3">
|
||||
<label for="categorie" class="col-form-label text-md-right ms-5">Categorie</label>
|
||||
<select id="categorie" ref="type" class="form-select" aria-label="Sélectionnez une catégorie">
|
||||
<option value="" :selected="!modale.activity.value" disabled>Sélectionnez une catégorie</option>
|
||||
<option v-for="activity in activities" :key="activity.value" :value="activity.value"
|
||||
:selected="modale.activity.value == activity.value"
|
||||
:class="activity.value">{{ activity.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="morph" class="col-form-label text-md-right ms-5">Type d'élément lié</label>
|
||||
<select id="morph" ref="morph" class="form-select" @change="selectChange" aria-label="Sélectionnez un élément à lier">
|
||||
<option value="">Sélectionnez un type d'objet à lier</option>
|
||||
<option v-for="model in models" :key="model.value" :value="model.value"
|
||||
:selected="modale.model_lie.value == model.value"
|
||||
>{{ model.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mb-3" v-if="modale.dDisplay">
|
||||
<label for="select_id_lie" class="col-form-label text-md-right ms-5">Élément lié</label>
|
||||
<v-select
|
||||
id="select_id_lie"
|
||||
ref="select"
|
||||
:loading="loading"
|
||||
v-model="value"
|
||||
:clearable="false"
|
||||
label="label"
|
||||
:options='data'
|
||||
@search="search"
|
||||
:disabled="dReadonly||dDisabled"
|
||||
:filterable="true"
|
||||
:searchable="true"
|
||||
@input="setSelected">
|
||||
<li slot="list-footer" class="pagination">
|
||||
<button
|
||||
@click="$event.preventDefault(); page -= 1"
|
||||
:disabled="!hasPrevPage"
|
||||
class="btn btn-sm btn-primary mt-2 mb-2 ms-2 me-2">Précédent</button>
|
||||
<button
|
||||
@click="$event.preventDefault(); page += 1"
|
||||
:disabled="!hasNextPage"
|
||||
class="btn btn-sm btn-primary mt-2 mb-2">Suivant</button>
|
||||
</li>
|
||||
</v-select>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="text" class="col-form-label text-md-right ms-5">Description courte</label>
|
||||
<textarea ref="text" class="form-control" name="text"
|
||||
id="text" rows="2" placeholder="" @keydown="limitLines($event)" v-model="modale.text"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-8 text-center">
|
||||
<button class="btn btn-primary me-3" @click="saveCell()">Valider</button>
|
||||
<button class="btn btn-danger" @click="$refs.close.click()">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Toastify from 'toastify-js'
|
||||
export default {
|
||||
mounted() {
|
||||
window.Toastify = Toastify
|
||||
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.daySlots = Number.parseInt(data.daySlots) !== NaN ? Number.parseInt(data.daySlots) : 4
|
||||
this.showWeekends = data.daySlots == 'true' ? true : false
|
||||
this.currentDate = data.browseDate ?? new Date()
|
||||
this.weeksCells = data.weeks
|
||||
this.numweeks = this.weeksCells.length
|
||||
this.days = this.prepareDays();
|
||||
this.users = data.users
|
||||
this.schedules = this.prepareSched(data.schedules)
|
||||
this.activities = data.activities
|
||||
this.models = data.models
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showWeekends: false,
|
||||
daySlots: 4,
|
||||
currentDate: null,
|
||||
weeksCells: [],
|
||||
days: [],
|
||||
users:[],
|
||||
schedules: [],
|
||||
activities: [],
|
||||
models: [],
|
||||
error: '',
|
||||
modale:{
|
||||
id: null,
|
||||
userid: 0,
|
||||
slot: 0,
|
||||
day: '01/01/1900',
|
||||
title: 0,
|
||||
dDisplay: false,
|
||||
text: '',
|
||||
activity: {
|
||||
label: '',
|
||||
value: '',
|
||||
},
|
||||
model_lie: {
|
||||
label: '',
|
||||
value: '',
|
||||
},
|
||||
id_lie: {
|
||||
label: '',
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
//v-select de ses morts
|
||||
dDisabled : false,
|
||||
dReadonly : false,
|
||||
loading: false,
|
||||
value: {
|
||||
label: null,
|
||||
value: null
|
||||
},
|
||||
searchText: '',
|
||||
data: [],
|
||||
page: 1,
|
||||
perPage: 15,
|
||||
hasNextPage: true,
|
||||
hasPrevPage: false,
|
||||
emptyCell: {
|
||||
"type": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"text":"",
|
||||
"id_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"model_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"id":null
|
||||
},
|
||||
clipBoard: {
|
||||
"set": false,
|
||||
"type": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"text":"",
|
||||
"id_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"model_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"id":null
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
page() {
|
||||
if(this.page < 1) {
|
||||
this.page = 1
|
||||
}
|
||||
if(this.page > 1) {
|
||||
this.hasPrevPage = true
|
||||
} else {
|
||||
this.hasPrevPage = false
|
||||
}
|
||||
this.search()
|
||||
},
|
||||
data() {
|
||||
if(this.data.length == this.perPage) {
|
||||
this.hasNextPage = true
|
||||
} else {
|
||||
this.hasNextPage = false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sendToast(message) {
|
||||
Toastify({
|
||||
text: message,
|
||||
duration: 3000,
|
||||
gravity: "top",
|
||||
position: "right",
|
||||
offset: {
|
||||
x: '2rem',
|
||||
y: '3rem'
|
||||
},
|
||||
style: {
|
||||
background: "linear-gradient(to right, #00317d, #0058ca)",
|
||||
}
|
||||
}).showToast();
|
||||
},
|
||||
goToPlanning(forward) {
|
||||
// console.log(forward);
|
||||
let date = this.moment(this.currentDate).startOf('week').subtract(7, 'day');
|
||||
if(forward) {
|
||||
date = this.moment(this.currentDate).add(7, 'day')
|
||||
}
|
||||
let url = new URL(this.route('schedule.index'))
|
||||
location = url.origin+ url.pathname + '?p='+date.format('DD/MM/YYYY')
|
||||
},
|
||||
prepareDays() {
|
||||
let mydays = [];
|
||||
for(let i=0; i < this.numweeks * 7; i++) {
|
||||
let date = this.moment(this.currentDate).add(i, 'day');
|
||||
let obj = {
|
||||
day: date.format('ddd'),
|
||||
num: date.format('DD'),
|
||||
eday: date.locale("en").format('ddd'),
|
||||
key: date.format('DD/MM/YYYY'),
|
||||
}
|
||||
mydays.push(obj);
|
||||
if(!this.showWeekends) {
|
||||
if(date.day() == 5) {
|
||||
i+=2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mydays;
|
||||
},
|
||||
prepareSched(dbscheds) {
|
||||
var templateObj = {
|
||||
"type": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"text":"",
|
||||
"id_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"model_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"id":null}
|
||||
//we need to make missing users
|
||||
Object.keys(this.users).forEach(id => {
|
||||
if(dbscheds[id] === undefined) {
|
||||
dbscheds[id] = {};
|
||||
}
|
||||
});
|
||||
Object.keys(dbscheds).forEach(userid => {
|
||||
//what if we missing a slot for the whole period ?
|
||||
//we need to make missing slots too
|
||||
for(let i=1; i < this.daySlots+1; i++) {
|
||||
if(dbscheds[userid][i] === undefined) {
|
||||
dbscheds[userid][i] = {};
|
||||
}
|
||||
}
|
||||
Object.keys(dbscheds[userid]).forEach(slotkey => {
|
||||
this.days.forEach(day => {
|
||||
if(dbscheds[userid][slotkey][day.key] === undefined) {
|
||||
//console.log('need ' + day.key + ' for slot ' + slotkey + ' for user ' + userid);
|
||||
dbscheds[userid][slotkey][day.key] = templateObj;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return dbscheds;
|
||||
},
|
||||
getCellObjectInfo(cellData) {
|
||||
for(let model of this.models) {
|
||||
if(cellData['model_lie'].value === model.value && model.value == 'tickets') {
|
||||
return {
|
||||
'url': this.route('ticket.show', { TICKET: cellData['id_lie'].value }),
|
||||
'text': cellData['model_lie'].label
|
||||
}
|
||||
}
|
||||
if(cellData['model_lie'].value === model.value && model.value == 'sites') {
|
||||
return {
|
||||
'url': this.route('sites.show', { SITE: cellData['id_lie'].value}),
|
||||
'text': cellData['model_lie'].label
|
||||
}
|
||||
}
|
||||
if(cellData['model_lie'].value === model.value && model.value == 'projects') {
|
||||
return {
|
||||
'url': this.route('projects.show', { PROJECT: cellData['id_lie'].value}),
|
||||
'text': cellData['model_lie'].label
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
resetModalFields() {
|
||||
this.$set(this, 'currentType' , null)
|
||||
this.$set(this, 'value' , {
|
||||
label: null,
|
||||
value: null
|
||||
})
|
||||
this.$refs.type.value = ''
|
||||
this.$refs.morph.value = ''
|
||||
},
|
||||
setModalData(userid,slotkey,day,id) {
|
||||
let text = this.schedules[userid][slotkey][day]['text'];
|
||||
let activity = this.schedules[userid][slotkey][day]['type'];
|
||||
let model = this.schedules[userid][slotkey][day]['model_lie'];
|
||||
let enumobject = {
|
||||
label: '',
|
||||
value: '',
|
||||
};
|
||||
let currentModale = {
|
||||
id: this.schedules[userid][slotkey][day]['id'],
|
||||
userid: userid,
|
||||
slot: slotkey,
|
||||
day: day,
|
||||
title: id,
|
||||
dDisplay: false,
|
||||
text: text,
|
||||
activity: activity,
|
||||
model_lie: model ?? enumobject,
|
||||
id_lie: this.schedules[userid][slotkey][day]['id_lie'],
|
||||
}
|
||||
this.modale = currentModale;
|
||||
this.$nextTick(() => {
|
||||
let route = null
|
||||
switch(this.modale.model_lie.value) {
|
||||
case 'tickets':
|
||||
this.dDisplay = true
|
||||
route = this.route('ticket.show', {
|
||||
TICKET: this.modale.id_lie
|
||||
})
|
||||
break
|
||||
case 'projects':
|
||||
this.dDisplay = true
|
||||
route = this.route('projects.show', {
|
||||
PROJECT: this.modale.id_lie
|
||||
})
|
||||
break
|
||||
case 'sites':
|
||||
this.dDisplay = true
|
||||
route = this.route('sites.show', {
|
||||
SITE: this.modale.id_lie
|
||||
})
|
||||
break
|
||||
default: this.dDisplay = false
|
||||
}
|
||||
this.selectChange();
|
||||
this.value = this.schedules[userid][slotkey][day]['id_lie'];
|
||||
})
|
||||
},
|
||||
pasteCell(userid,slotkey,day,id) {
|
||||
//this.eraseCell(userid,slotkey,day,id)
|
||||
const data = {
|
||||
'id': id,
|
||||
'user_id': userid,
|
||||
'date': day,
|
||||
'activity': this.clipBoard.activity.value,
|
||||
'slot': slotkey,
|
||||
'model_lie': this.clipBoard.model_lie.value,
|
||||
'id_lie': this.clipBoard.id_lie.value,
|
||||
'description': this.clipBoard.description,
|
||||
}
|
||||
this.saveCellOnServer(data)
|
||||
//do not clear clipboard
|
||||
//this.clearClipBoard()
|
||||
this.sendToast('Cellule collée')
|
||||
},
|
||||
clearClipBoard() {
|
||||
this.clipBoard = {
|
||||
"set": false,
|
||||
"type": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"text":"",
|
||||
"id_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"model_lie": {
|
||||
"label":"",
|
||||
"value":""
|
||||
},
|
||||
"id":null
|
||||
}
|
||||
},
|
||||
copyCell(userid,slotkey,day,id) {
|
||||
this.clipBoard = {
|
||||
'set' : true,
|
||||
'id': id,
|
||||
'user_id': userid,
|
||||
'date': day,
|
||||
'activity': this.schedules[userid][slotkey][day].type,
|
||||
'slot': slotkey,
|
||||
'model_lie': this.schedules[userid][slotkey][day].model_lie,
|
||||
'id_lie': this.schedules[userid][slotkey][day].id_lie,
|
||||
'description': this.schedules[userid][slotkey][day].text,
|
||||
}
|
||||
this.sendToast('Cellule copiée')
|
||||
},
|
||||
setSelected(value) {
|
||||
this.modale.id_lie = value;
|
||||
},
|
||||
search (search = null) {
|
||||
this.$nextTick(() => {
|
||||
this.error = null
|
||||
let route;
|
||||
switch(this.$refs.morph.value) {
|
||||
case 'tickets':
|
||||
route = this.route('ticket.index')
|
||||
break
|
||||
case 'projects':
|
||||
route = this.route('projects.index')
|
||||
break
|
||||
case 'sites':
|
||||
route = this.route('sites.index')
|
||||
break
|
||||
default: return
|
||||
}
|
||||
if(search !== false && search !== null) {
|
||||
this.searchText = search
|
||||
}
|
||||
let init = search === false ? true : false
|
||||
route += '?page='+this.page
|
||||
route += '&s='+this.searchText
|
||||
this.loading = true
|
||||
axios.get(route)
|
||||
.then(response => {
|
||||
this.data = response.data
|
||||
})
|
||||
.catch(error => {
|
||||
if(error.response.status === 429) {
|
||||
location.reload()
|
||||
}
|
||||
this.error = error
|
||||
}).then(() => {
|
||||
this.loading = false
|
||||
})
|
||||
});
|
||||
},
|
||||
saveCell() {
|
||||
this.error = false
|
||||
const data = {
|
||||
'id': this.modale.id,
|
||||
'user_id': this.modale.userid,
|
||||
'date': this.modale.day,
|
||||
'activity': this.$refs.type.value,
|
||||
'slot': this.modale.slot,
|
||||
'model_lie': this.$refs.morph.value,
|
||||
'id_lie': this.value.value,
|
||||
'description': this.$refs.text.value,
|
||||
}
|
||||
this.saveCellOnServer(data)
|
||||
},
|
||||
saveCellOnServer(data) {
|
||||
this.error = false
|
||||
axios.post(this.route('schedule.setCellContent'), data)
|
||||
.then(response => {
|
||||
if(response.data.status) {
|
||||
let sched = response.data.sched;
|
||||
// sched.date = Date.parse(sched.date)
|
||||
// sched.date = this.moment(sched.date).format('DD/MM/YYYY');
|
||||
let cellData = {
|
||||
"type": sched.activity,
|
||||
"text": sched.description,
|
||||
"id_lie": sched.id_lie,
|
||||
"model_lie": sched.model_lie,
|
||||
"id": sched.id
|
||||
};
|
||||
this.$set(this.schedules[sched.user_id][sched.slot], sched.date, cellData);
|
||||
this.$refs.close.click()
|
||||
} else {
|
||||
this.error = 'Réponse inconnue';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
if(error.response.status === 429) {
|
||||
location.reload()
|
||||
}
|
||||
if(error.response.status === 422) {
|
||||
if(error.response.data.errors) {
|
||||
this.error = ''
|
||||
for(let id in error.response.data.errors) {
|
||||
let field = error.response.data.errors[id]
|
||||
this.error += field.join('\n') + '\n'
|
||||
}
|
||||
return
|
||||
} else if(error.response.data.message) {
|
||||
this.error = error.response.data.message
|
||||
return
|
||||
}
|
||||
}
|
||||
this.error = error
|
||||
})
|
||||
},
|
||||
eraseCell(userid,slotkey,day,id) {
|
||||
const data = {
|
||||
'id': id,
|
||||
}
|
||||
axios.post(this.route('schedule.eraseCellContent'), data)
|
||||
.then(response => {
|
||||
if(response.data.status) {
|
||||
this.$set(this.schedules[userid][slotkey], day, this.emptyCell);
|
||||
} else {
|
||||
console.error('Unknown response')
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if(error.response.status === 429) {
|
||||
location.reload()
|
||||
}
|
||||
console.error(error)
|
||||
})
|
||||
},
|
||||
selectChange() {
|
||||
this.value = {
|
||||
label: null,
|
||||
value: null
|
||||
}
|
||||
switch(this.$refs.morph.value) {
|
||||
case 'tickets':
|
||||
case 'projects':
|
||||
case 'sites':
|
||||
this.modale.dDisplay = true
|
||||
this.search(false)
|
||||
break
|
||||
default: this.modale.dDisplay = false
|
||||
}
|
||||
},
|
||||
limitLines(e) {
|
||||
let keynum, lines = this.$refs.text.value.split('\n').length;
|
||||
// IE
|
||||
if(window.event) {
|
||||
keynum = e.keyCode;
|
||||
// Netscape/Firefox/Opera
|
||||
} else if(e.which) {
|
||||
keynum = e.which;
|
||||
}
|
||||
if(keynum != 8 && this.$refs.text.value.length >= this.textLimit) {
|
||||
e.preventDefault()
|
||||
return false;
|
||||
}
|
||||
if(keynum == 13 && lines == this.$refs.text.rows) {
|
||||
e.preventDefault()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "vue-select/src/scss/vue-select.scss";
|
||||
|
||||
.actions_col {
|
||||
width: 45px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.plannings {
|
||||
.sticky-top {
|
||||
top: 20rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
.Mon {
|
||||
border-left: 4px solid;
|
||||
}
|
||||
// .Fri {
|
||||
// border-right: 4px solid;
|
||||
// }
|
||||
}
|
||||
|
||||
table.schedules_table {
|
||||
table-layout: fixed;
|
||||
|
||||
.resource_ident {
|
||||
font-weight: bold;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.cellsizer {
|
||||
height: 50px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
b {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dynCell{
|
||||
position: relative;
|
||||
&.day {
|
||||
//position: relative;
|
||||
&:hover .btn {
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover .cellsizer{
|
||||
height: 140px;
|
||||
padding-top: 2rem;
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
left: -50%;
|
||||
top: -55%;
|
||||
z-index: 20;
|
||||
border: 1px solid;
|
||||
}
|
||||
$buttonBgColor: #4d4d4d;
|
||||
|
||||
// Reset button Style to nice one
|
||||
button {
|
||||
display: inline;
|
||||
border: none;
|
||||
padding: 0.6rem 3rem;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
background: $buttonBgColor;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
button[disabled] {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: darken($buttonBgColor, 10%);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.btn {
|
||||
position: absolute;
|
||||
top: 0.1rem;
|
||||
left: 0.1rem;
|
||||
padding: 0;
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
margin: 0;
|
||||
background-color: $buttonBgColor;
|
||||
opacity: 0;
|
||||
&.hide {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken($buttonBgColor, 10%);
|
||||
}
|
||||
&.btn-clear {
|
||||
color: darken($color: #fff, $amount: 5);
|
||||
}
|
||||
&:nth-last-child(2) {
|
||||
top: 0.1rem;
|
||||
right: inherit;
|
||||
left: 2.1rem;
|
||||
}
|
||||
&:nth-last-child(3) {
|
||||
right: inherit;
|
||||
top: 0.1rem;
|
||||
left: 4.1rem;
|
||||
}
|
||||
&:nth-last-child(1) {
|
||||
top: 0.1rem;
|
||||
left: calc(100% - 1.9rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@import "toastify-js/src/toastify.css";
|
||||
@import "vue-select/src/scss/vue-select.scss";
|
||||
.planning {
|
||||
overflow: auto;
|
||||
|
||||
td {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#text {
|
||||
height: 5.5rem;
|
||||
padding: 1rem 2rem;
|
||||
resize: none;
|
||||
width: 37rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
61
resources/js/confirm.js
vendored
61
resources/js/confirm.js
vendored
|
|
@ -1,61 +0,0 @@
|
|||
|
||||
const confirmJS = require('./modules/confirm.js').default.methods.confirm
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
let elements = document.getElementsByClassName('confirmDeleteJS')
|
||||
for (let element of elements) {
|
||||
element.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
confirmJS(
|
||||
'Confirmez',
|
||||
'Voulez-vous vraiment supprimer cet élément ?',
|
||||
this,
|
||||
function (response) { this.submit() },
|
||||
{ 'icon': 'error' }
|
||||
)
|
||||
return false
|
||||
})
|
||||
}
|
||||
elements = document.getElementsByClassName('confirmDoneJS')
|
||||
for (let element of elements) {
|
||||
element.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
confirmJS(
|
||||
'Confirmez',
|
||||
'Voulez-vous vraiment marquer cet élément comme traité ?',
|
||||
this,
|
||||
function (response) { this.submit() },
|
||||
{ 'icon': 'info' }
|
||||
)
|
||||
return false
|
||||
})
|
||||
}
|
||||
elements = document.getElementsByClassName('confirmSubmitJS')
|
||||
for (let element of elements) {
|
||||
element.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
confirmJS(
|
||||
'Confirmez',
|
||||
'Voulez-vous enregistrer cet élément tel-quel ?',
|
||||
this,
|
||||
function (response) { this.submit() },
|
||||
{ 'icon': 'warning' }
|
||||
)
|
||||
return false
|
||||
})
|
||||
}
|
||||
elements = document.getElementsByClassName('confirmJS')
|
||||
for (let element of elements) {
|
||||
element.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
confirmJS(
|
||||
'Confirmez',
|
||||
'Êtes vous sure ?',
|
||||
this,
|
||||
function (response) { this.submit() },
|
||||
{ 'icon': 'warning' }
|
||||
)
|
||||
return false
|
||||
})
|
||||
}
|
||||
})
|
||||
31
resources/js/files_add.js
vendored
31
resources/js/files_add.js
vendored
|
|
@ -1,31 +0,0 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Page Project File Add
|
||||
if (document.querySelector('.content.pageFile')) {
|
||||
let selectFileType = document.getElementById('type'),
|
||||
inputUri = document.getElementById('uri'),
|
||||
inputFile = document.getElementById('file')
|
||||
|
||||
|
||||
let changeFields = () => {
|
||||
if (selectFileType.value == 0) {
|
||||
inputUri.attributes['required'] = true
|
||||
inputUri.attributes['disabled'] = false
|
||||
inputUri.parentElement.parentElement.classList.remove('d-none')
|
||||
inputFile.attributes['required'] = false
|
||||
inputFile.attributes['disabled'] = true
|
||||
inputFile.parentElement.parentElement.classList.add('d-none')
|
||||
} else {
|
||||
inputUri.attributes['required'] = false
|
||||
inputUri.attributes['disabled'] = true
|
||||
inputUri.parentElement.parentElement.classList.add('d-none')
|
||||
inputFile.attributes['required'] = true
|
||||
inputFile.attributes['disabled'] = false
|
||||
inputFile.parentElement.parentElement.classList.remove('d-none')
|
||||
}
|
||||
}
|
||||
|
||||
changeFields()
|
||||
selectFileType.addEventListener('change', changeFields)
|
||||
}
|
||||
})
|
||||
19
resources/js/label_colorize.js
vendored
19
resources/js/label_colorize.js
vendored
|
|
@ -1,19 +0,0 @@
|
|||
import colors from './modules/colors'
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Ticket index colorize labels
|
||||
if (document.querySelector('.content.ticketsIndex') || document.querySelector('.content.ticketsShow')) {
|
||||
let labels = document.querySelectorAll('.label.colorize')
|
||||
|
||||
labels.forEach(label => {
|
||||
if (label.dataset.color) {
|
||||
if (colors.methods.isrgb(label.dataset.color)) {
|
||||
label.dataset.color = colors.methods.rgb2hex(label.dataset.color)
|
||||
}
|
||||
label.style.color = colors.methods.invertColor(label.dataset.color, true)
|
||||
label.style.backgroundColor = label.dataset.color
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
40
resources/js/modules/colors.js
vendored
40
resources/js/modules/colors.js
vendored
|
|
@ -1,40 +0,0 @@
|
|||
export default {
|
||||
methods: {
|
||||
isrgb(rgb) {
|
||||
return (String(rgb).match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) ?? []).length !== 0
|
||||
},
|
||||
rgb2hex(rgb) {
|
||||
return `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(
|
||||
n => parseInt(n, 10).toString(16).padStart(2, '0')
|
||||
).join('')}`
|
||||
},
|
||||
invertColor(hex, bw) {
|
||||
if (hex.indexOf('#') === 0) {
|
||||
hex = hex.slice(1);
|
||||
}
|
||||
// convert 3-digit hex to 6-digits.
|
||||
if (hex.length === 3) {
|
||||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||
}
|
||||
if (hex.length !== 6) {
|
||||
throw new Error('Invalid HEX color.');
|
||||
}
|
||||
var r = parseInt(hex.slice(0, 2), 16),
|
||||
g = parseInt(hex.slice(2, 4), 16),
|
||||
b = parseInt(hex.slice(4, 6), 16);
|
||||
if (bw) {
|
||||
// https://stackoverflow.com/a/3943023/112731
|
||||
return (r * 0.299 + g * 0.587 + b * 0.114) > 186
|
||||
? '#000000'
|
||||
: '#FFFFFF';
|
||||
}
|
||||
// invert color components
|
||||
r = (255 - r).toString(16);
|
||||
g = (255 - g).toString(16);
|
||||
b = (255 - b).toString(16);
|
||||
// pad each with zeros and return
|
||||
return "#" + r.padStart(1, '0') + g.padStart(1, '0') + b.padStart(1, '0')
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
37
resources/js/modules/confirm.js
vendored
37
resources/js/modules/confirm.js
vendored
|
|
@ -1,37 +0,0 @@
|
|||
export default {
|
||||
methods: {
|
||||
confirm(title, text = '', self, after, options = {}) {
|
||||
if (!self) {
|
||||
throw Error('self is needed to set "this" on callback');
|
||||
}
|
||||
if (!after) {
|
||||
throw Error('callback is needed when using confirm');
|
||||
}
|
||||
let icon = options.icon ?? 'warning'
|
||||
let confirm = options.confirm ?? 'Oui'
|
||||
let cancel = options.confirm ?? 'Annuler'
|
||||
Swal.fire({
|
||||
title: title,
|
||||
text: text,
|
||||
icon: icon,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: confirm,
|
||||
cancelButtonText: cancel,
|
||||
reverseButtons: true
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
if (after) {
|
||||
after.apply(self, [result])
|
||||
}
|
||||
} else if (
|
||||
/* Read more about handling dismissals below */
|
||||
result.dismiss === Swal.DismissReason.cancel
|
||||
) {
|
||||
Swal.fire('L\'opération n\'as pas été effectuée')
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
45
resources/js/modules/route.js
vendored
45
resources/js/modules/route.js
vendored
|
|
@ -1,45 +0,0 @@
|
|||
export default {
|
||||
methods: {
|
||||
route(routeName, parameters = []) {
|
||||
let route = Object.keys(window._routes).indexOf(routeName) !== -1 ?
|
||||
window._routes[routeName] : null;
|
||||
if (!route) {
|
||||
return route;
|
||||
}
|
||||
// copy parameters
|
||||
let iParameters = Object.assign({}, parameters);
|
||||
let origin = window.location.origin;
|
||||
let pathname = route.replace(origin, '');
|
||||
let hash = window.location.hash;
|
||||
|
||||
// replace in path name
|
||||
for (let paramName in iParameters) {
|
||||
let paramValue = parameters[paramName];
|
||||
if (pathname.indexOf(paramName) !== -1) {
|
||||
pathname = pathname.replace(paramName, paramValue);
|
||||
}
|
||||
delete parameters[paramName];
|
||||
}
|
||||
|
||||
// search, dont copy if target url is different than actual
|
||||
let search = pathname == window.location.pathname ? window.location.search : ''
|
||||
|
||||
for (let paramName in parameters) {
|
||||
let paramValue = parameters[paramName];
|
||||
if (search.indexOf(paramName + "=") >= 0) {
|
||||
prefix = search.substring(0, search.indexOf(paramName + "="));
|
||||
suffix = search.substring(search.indexOf(paramName + "="));
|
||||
suffix = suffix.substring(suffix.indexOf("=") + 1);
|
||||
suffix = (suffix.indexOf("&") >= 0) ? suffix.substring(suffix.indexOf("&")) : '';
|
||||
search = prefix + paramName + "=" + paramValue + suffix;
|
||||
} else if (search.indexOf("?") < 0) {
|
||||
search += "?" + paramName + "=" + paramValue
|
||||
} else {
|
||||
search += "&" + paramName + "=" + paramValue
|
||||
}
|
||||
}
|
||||
return origin + pathname + search + hash;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
16
resources/js/modules/session_pinger.js
vendored
16
resources/js/modules/session_pinger.js
vendored
|
|
@ -1,16 +0,0 @@
|
|||
import axios from "axios"
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async validateSession(callback) {
|
||||
axios.get('/ping').then(response => {
|
||||
if (response.status !== 200) {
|
||||
callback()
|
||||
}
|
||||
}).catch(() => {
|
||||
callback()
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
22
resources/js/session_watch.js
vendored
22
resources/js/session_watch.js
vendored
|
|
@ -1,22 +0,0 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
let validateSession = require('./modules/session_pinger').default.methods.validateSession,
|
||||
sessionInvalidAction = () => {
|
||||
Swal.fire({
|
||||
title: 'Session expirée',
|
||||
text: 'Votre session à expiré vous devrez recharger la page pour soumettre des informations.',
|
||||
icon: 'error'
|
||||
})
|
||||
clearTimeout(timeOut)
|
||||
},
|
||||
timer = 10000,
|
||||
timeOut
|
||||
|
||||
let timerAction = () => {
|
||||
validateSession(sessionInvalidAction)
|
||||
timeOut = setTimeout(timerAction, timer)
|
||||
}
|
||||
|
||||
if (window.__SESSION_WATCH) {
|
||||
timerAction()
|
||||
}
|
||||
})
|
||||
85
resources/js/vue.js
vendored
85
resources/js/vue.js
vendored
|
|
@ -1,85 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import vSelect from 'vue-select'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/fr'
|
||||
|
||||
let bootstrap = require('bootstrap')
|
||||
|
||||
// enable bs tooltips
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
})
|
||||
|
||||
moment.locale('fr')
|
||||
|
||||
window.Vue = Vue
|
||||
|
||||
if (window.vueDebug) {
|
||||
Vue.config.devtools = true;
|
||||
Vue.config.debug = true;
|
||||
}
|
||||
|
||||
Vue.prototype.moment = moment
|
||||
Vue.prototype.bootstrap = bootstrap
|
||||
|
||||
// v-select
|
||||
Vue.component('v-select', vSelect)
|
||||
|
||||
// Register modules
|
||||
const modules = require.context("./modules", true, /\.js$/i);
|
||||
modules.keys().map(key => {
|
||||
Vue.mixin(modules(key).default)
|
||||
})
|
||||
|
||||
window.confirmJS = modules('./confirm.js').default.methods.confirm
|
||||
|
||||
// Register vue function
|
||||
Vue.filter('str_limit', function (value, size) {
|
||||
if (!value) return ''
|
||||
value = value.toString()
|
||||
if (value.length <= size) {
|
||||
return value;
|
||||
}
|
||||
return value.substr(0, size) + '...';
|
||||
})
|
||||
Vue.filter('ucfirst', function (value) {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1)
|
||||
})
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Register components
|
||||
const vues = require.context("./components", true, /\.vue$/i)
|
||||
vues.keys().map(key => {
|
||||
let htmlKey = key.split('/')
|
||||
.pop()
|
||||
.split('.')[0]
|
||||
.replace(/\.?([A-Z])/g, function (x, y) {
|
||||
return '-' + y.toLowerCase();
|
||||
})
|
||||
.replace(/^-/, '')
|
||||
.replace('-component', '')
|
||||
|
||||
let el = document.getElementById(htmlKey),
|
||||
elGroup = document.getElementsByClassName(htmlKey)
|
||||
if (el) {
|
||||
new Vue({
|
||||
el: el,
|
||||
json: el.dataset.json ?? {},
|
||||
// * For runtime vuejs
|
||||
render: createElement => createElement(vues(key).default)
|
||||
})
|
||||
}
|
||||
for (let grpEl of elGroup) {
|
||||
// * Hack to prevent loop weirdness
|
||||
setTimeout(() => {
|
||||
new Vue({
|
||||
el: grpEl,
|
||||
json: grpEl.dataset.json,
|
||||
// * For runtime vuejs
|
||||
render: createElement => createElement(vues(key).default)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
})
|
||||
16
resources/sass/app.scss
vendored
16
resources/sass/app.scss
vendored
|
|
@ -1,10 +1,18 @@
|
|||
// LIBS
|
||||
@import "~@fortawesome/fontawesome-svg-core/styles.css";
|
||||
|
||||
// Variables
|
||||
@import 'variables';
|
||||
@import 'complement';
|
||||
@import 'ckEditorContent';
|
||||
@import "variables";
|
||||
@import "complement";
|
||||
@import "ckEditorContent";
|
||||
|
||||
@import "bootstrap";
|
||||
|
||||
a.col_sort {
|
||||
&:link,:visited,:hover,:active {
|
||||
&:link,
|
||||
:visited,
|
||||
:hover,
|
||||
:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
8
resources/ts/app.ts
Normal file
8
resources/ts/app.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// LIBS
|
||||
import "./common";
|
||||
import "./vue";
|
||||
import "./fontawesome";
|
||||
import "./confirm";
|
||||
import "./pages/files_add";
|
||||
import "./pages/label_colorize";
|
||||
import "./session_watch";
|
||||
33
resources/ts/common.ts
Normal file
33
resources/ts/common.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import axios from "axios";
|
||||
// @ts-ignore Pas de déclaration ts disponible
|
||||
import GLightbox from "glightbox";
|
||||
|
||||
const bootstrap = require("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.
|
||||
*/
|
||||
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;
|
||||
|
||||
// enable bs tooltips
|
||||
var tooltipTriggerList = [].slice.call(
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
);
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
|
||||
new GLightbox({
|
||||
selector: ".glightbox",
|
||||
});
|
||||
72
resources/ts/components/LabelSelectComponent.vue
Normal file
72
resources/ts/components/LabelSelectComponent.vue
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<div>
|
||||
<VueSelect
|
||||
v-model="selectedLabels"
|
||||
:multiple="true"
|
||||
:clearable="true"
|
||||
label="label"
|
||||
:options="labels"
|
||||
>
|
||||
<template v-slot:option="{ option }: { option: SelectModel }">
|
||||
<span
|
||||
class="py-1 px-2"
|
||||
:style="`background-color: ${(option as SelectModel).color};color: ${invertColor((option as SelectModel).color, true)};`"
|
||||
>
|
||||
{{ String(option.label) }}
|
||||
</span>
|
||||
</template>
|
||||
<template
|
||||
v-slot:no-options="{ search, searching }: { search: string, searching: boolean }"
|
||||
>
|
||||
{{ __("Pus de label disponibles") }}
|
||||
</template>
|
||||
</VueSelect>
|
||||
<input
|
||||
v-for="label of selectedLabels"
|
||||
type="hidden"
|
||||
:key="label.value"
|
||||
:value="label.value"
|
||||
:name="`${name}[]`"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import VueSelect from "vue-select";
|
||||
import trans from "../modules/trans";
|
||||
import colors from "../modules/colors";
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [trans, colors],
|
||||
components: {
|
||||
VueSelect,
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.name = data.name;
|
||||
for (let label of data.value ?? []) {
|
||||
this.selectedLabels.push({
|
||||
label: label.name,
|
||||
value: label.id,
|
||||
color: label.color,
|
||||
});
|
||||
}
|
||||
for (let label of data.labels) {
|
||||
this.labels.push({ label: label.name, value: label.id, color: label.color });
|
||||
}
|
||||
},
|
||||
data(): {
|
||||
name: string;
|
||||
selectedLabels: SelectModel[];
|
||||
labels: SelectModel[];
|
||||
} {
|
||||
return {
|
||||
name: "",
|
||||
selectedLabels: [],
|
||||
labels: [],
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
1151
resources/ts/components/MacroPlanningComponent.vue
Normal file
1151
resources/ts/components/MacroPlanningComponent.vue
Normal file
File diff suppressed because it is too large
Load diff
74
resources/ts/components/MarkerLabelSelectComponent.vue
Normal file
74
resources/ts/components/MarkerLabelSelectComponent.vue
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div>
|
||||
<VueSelect v-model="value" :options="labels" @search="addOption" />
|
||||
<input type="hidden" :value="inputValue" :name="name" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import VueSelect from "vue-select";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
VueSelect,
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
|
||||
this.name = data.name;
|
||||
this.labels = data.labels;
|
||||
this.inputValue = data.value;
|
||||
this.value = {
|
||||
code: this.labels.length,
|
||||
label: this.inputValue,
|
||||
};
|
||||
for (let label of this.labels) {
|
||||
label.default = true;
|
||||
if (label.label == data.value) {
|
||||
this.value = label as {
|
||||
code: number;
|
||||
label: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
data(): {
|
||||
name: string;
|
||||
value: {
|
||||
code: number;
|
||||
label: string;
|
||||
};
|
||||
inputValue: string;
|
||||
labels: LaravelModelList;
|
||||
} {
|
||||
return {
|
||||
name: "",
|
||||
value: {
|
||||
code: 0,
|
||||
label: "",
|
||||
},
|
||||
inputValue: "",
|
||||
labels: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.inputValue = this.value.label;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addOption(newOption: string) {
|
||||
if (newOption.length) {
|
||||
for (let key in this.labels) {
|
||||
if (!this.labels[key].default) {
|
||||
this.labels.splice(Number.parseInt(key), 1);
|
||||
}
|
||||
}
|
||||
this.labels.push({ label: newOption, code: this.labels.length });
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
1115
resources/ts/components/ScheduleComponent.vue
Normal file
1115
resources/ts/components/ScheduleComponent.vue
Normal file
File diff suppressed because it is too large
Load diff
5
resources/ts/components/shims-vue.d.ts
vendored
Normal file
5
resources/ts/components/shims-vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
118
resources/ts/confirm.ts
Normal file
118
resources/ts/confirm.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import { SweetAlertResult } from "sweetalert2";
|
||||
import confirm from "./modules/confirm";
|
||||
import trans from "./modules/trans";
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
let elements = document.getElementsByClassName("confirmDeleteJS");
|
||||
for (const element of elements) {
|
||||
element.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const el = e.target;
|
||||
if (!el || !(el instanceof HTMLFormElement)) {
|
||||
throw new Error(
|
||||
"confirmDoneJS can only be executed on an exising form"
|
||||
);
|
||||
}
|
||||
confirm.methods.confirm(
|
||||
trans.methods.__("Confirmez"),
|
||||
trans.methods.__(
|
||||
"Voulez-vous vraiment supprimer cet élément ?"
|
||||
),
|
||||
el as HTMLFormElement,
|
||||
function (response: SweetAlertResult<any>) {
|
||||
(el as HTMLFormElement).submit();
|
||||
},
|
||||
{ icon: "error" }
|
||||
);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
elements = document.getElementsByClassName("confirmDoneJS");
|
||||
for (let element of elements) {
|
||||
element.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const el = e.target;
|
||||
if (!el || !(el instanceof HTMLFormElement)) {
|
||||
throw new Error(
|
||||
"confirmDoneJS can only be executed on an exising form"
|
||||
);
|
||||
}
|
||||
confirm.methods.confirm(
|
||||
trans.methods.__("Confirmez"),
|
||||
trans.methods.__(
|
||||
"Voulez-vous vraiment marquer cet élément comme traité ?"
|
||||
),
|
||||
el as HTMLFormElement,
|
||||
function (response: SweetAlertResult<any>) {
|
||||
(el as HTMLFormElement).submit();
|
||||
},
|
||||
{ icon: "info" }
|
||||
);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
elements = document.getElementsByClassName("confirmSubmitJS");
|
||||
for (let element of elements) {
|
||||
element.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const el = e.target;
|
||||
if (!el || !(el instanceof HTMLFormElement)) {
|
||||
throw new Error(
|
||||
"confirmSubmitJS can only be executed on an exising form"
|
||||
);
|
||||
}
|
||||
confirm.methods.confirm(
|
||||
trans.methods.__("Confirmez"),
|
||||
trans.methods.__(
|
||||
"Voulez-vous enregistrer cet élément tel-quel ?"
|
||||
),
|
||||
el as HTMLFormElement,
|
||||
function (response: SweetAlertResult<any>) {
|
||||
(el as HTMLFormElement).submit();
|
||||
},
|
||||
{ icon: "warning" }
|
||||
);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
elements = document.getElementsByClassName("confirmJS");
|
||||
for (let element of elements) {
|
||||
element.addEventListener(
|
||||
"click",
|
||||
function (e) {
|
||||
if (!element.getAttribute("confirmJS")) {
|
||||
e.preventDefault();
|
||||
const el = e.target;
|
||||
if (!el || !(el instanceof HTMLElement)) {
|
||||
throw new Error(
|
||||
"confirmJS can only be executed on a html element"
|
||||
);
|
||||
}
|
||||
(async () => {
|
||||
const promise = new Promise(
|
||||
(resolve: (value: boolean) => void, reject) => {
|
||||
console.log(el);
|
||||
confirm.methods.confirm(
|
||||
trans.methods.__("Confirmez"),
|
||||
trans.methods.__("Êtes vous sure ?"),
|
||||
el,
|
||||
function (response: SweetAlertResult<any>) {
|
||||
resolve(response.isConfirmed);
|
||||
},
|
||||
{ icon: "warning" }
|
||||
);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
if (await promise) {
|
||||
element.setAttribute("confirmJS", "true");
|
||||
el.click();
|
||||
element.removeAttribute("confirmJS");
|
||||
}
|
||||
})();
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
});
|
||||
42
resources/ts/fontawesome.ts
Normal file
42
resources/ts/fontawesome.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { dom, library, config, icon } from "@fortawesome/fontawesome-svg-core";
|
||||
import {
|
||||
faTags,
|
||||
faSquarePlus,
|
||||
faBoxArchive,
|
||||
faEraser,
|
||||
faSignHanging,
|
||||
faDiagramProject,
|
||||
faHatWizard,
|
||||
faDeleteLeft,
|
||||
faMagnifyingGlass,
|
||||
faEye,
|
||||
faComment,
|
||||
faPen,
|
||||
faArchive,
|
||||
faLock,
|
||||
faFileImage,
|
||||
faInbox,
|
||||
faArrowLeftLong,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faAccessibleIcon } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faCalendarAlt } from "@fortawesome/free-regular-svg-icons";
|
||||
|
||||
config.autoReplaceSvg = false;
|
||||
library.add(faTags);
|
||||
library.add(faSquarePlus);
|
||||
library.add(faBoxArchive);
|
||||
library.add(faEraser);
|
||||
library.add(faSignHanging);
|
||||
library.add(faDiagramProject);
|
||||
library.add(faHatWizard);
|
||||
library.add(faDeleteLeft);
|
||||
library.add(faMagnifyingGlass);
|
||||
library.add(faEye);
|
||||
library.add(faComment);
|
||||
library.add(faPen);
|
||||
library.add(faArchive);
|
||||
library.add(faLock);
|
||||
library.add(faFileImage);
|
||||
library.add(faInbox);
|
||||
library.add(faArrowLeftLong);
|
||||
dom.watch();
|
||||
64
resources/ts/index.d.ts
vendored
Normal file
64
resources/ts/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
export {};
|
||||
|
||||
import { AxiosStatic } from "axios";
|
||||
import type { VueSelectProps } from "vue-select";
|
||||
|
||||
// * CustomLabel
|
||||
type SelectModelType = {
|
||||
color: string;
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
// * 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
|
||||
>;
|
||||
interface NestedModelInterface extends Model {}
|
||||
|
||||
type NestedStringList = NestedStringListInterface | StringList | string;
|
||||
type NestedNumberList = NestedNumberListInterface | NumberList | number;
|
||||
type ModelList = Array<Model>;
|
||||
|
||||
declare global {
|
||||
type CustomTag = LaravelModel;
|
||||
type LaravelModel = Model;
|
||||
type LaravelModelList = ModelList;
|
||||
|
||||
type VSelectProps = VueSelectProps;
|
||||
type SelectModel = SelectModelType;
|
||||
|
||||
interface Window {
|
||||
$displayMenu: (show: boolean) => void;
|
||||
$toggleBasketMenu: () => void;
|
||||
axios: AxiosStatic;
|
||||
Swal: Function;
|
||||
__SESSION_WATCH: any;
|
||||
__SYSTEM: {
|
||||
_asset: string;
|
||||
_locale: string;
|
||||
_routes: Record<string, string>;
|
||||
_translations: {
|
||||
json: NestedStringList;
|
||||
php: NestedStringList;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
7
resources/ts/modules/asset.ts
Normal file
7
resources/ts/modules/asset.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
methods: {
|
||||
asset(path: string) {
|
||||
return `${window.__SYSTEM._asset}${path}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
59
resources/ts/modules/colors.ts
Normal file
59
resources/ts/modules/colors.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
export default {
|
||||
methods: {
|
||||
isrgb(rgb: string) {
|
||||
return (
|
||||
(rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) ?? []).length !==
|
||||
0
|
||||
);
|
||||
},
|
||||
rgb2hex(rgb: string) {
|
||||
const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
||||
if (!match) {
|
||||
throw new Error(`${rgb} cannot be converted to hex`);
|
||||
}
|
||||
return `#${match
|
||||
.slice(1)
|
||||
.map((n) => parseInt(n, 10).toString(16).padStart(2, "0"))
|
||||
.join("")}`;
|
||||
},
|
||||
/**
|
||||
* Invert color
|
||||
*
|
||||
* @param hex Hex color string
|
||||
* @param bw Use black or white bg
|
||||
* @returns
|
||||
*/
|
||||
invertColor(hex: string, bw: boolean) {
|
||||
if (hex.indexOf("#") === 0) {
|
||||
hex = hex.slice(1);
|
||||
}
|
||||
// convert 3-digit hex to 6-digits.
|
||||
if (hex.length === 3) {
|
||||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||
}
|
||||
if (hex.length !== 6) {
|
||||
throw new Error("Invalid HEX color.");
|
||||
}
|
||||
let r = parseInt(hex.slice(0, 2), 16),
|
||||
g = parseInt(hex.slice(2, 4), 16),
|
||||
b = parseInt(hex.slice(4, 6), 16);
|
||||
if (bw) {
|
||||
// https://stackoverflow.com/a/3943023/112731
|
||||
return r * 0.299 + g * 0.587 + b * 0.114 > 186
|
||||
? "#000000"
|
||||
: "#FFFFFF";
|
||||
}
|
||||
// invert color components
|
||||
g = parseInt((255 - g).toString(16));
|
||||
r = parseInt((255 - r).toString(16));
|
||||
b = parseInt((255 - b).toString(16));
|
||||
// pad each with zeros and return
|
||||
return (
|
||||
"#" +
|
||||
String(r).padStart(1, "0") +
|
||||
String(g).padStart(1, "0") +
|
||||
String(b).padStart(1, "0")
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
62
resources/ts/modules/confirm.ts
Normal file
62
resources/ts/modules/confirm.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// SweetAlert
|
||||
import Swal, { SweetAlertResult } from "sweetalert2";
|
||||
import { SweetAlertOptions } from "sweetalert2";
|
||||
|
||||
import * as trans from "./trans";
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
confirm(
|
||||
title: string,
|
||||
text: string = "",
|
||||
self: Object,
|
||||
after: (response: SweetAlertResult<any>) => void = (
|
||||
response: SweetAlertResult<any>
|
||||
) => {},
|
||||
options: SweetAlertOptions = {}
|
||||
) {
|
||||
if (!self) {
|
||||
throw Error('self is needed to set "this" on callback');
|
||||
}
|
||||
if (!after) {
|
||||
throw Error("callback is needed when using confirm");
|
||||
}
|
||||
let icon = options.icon ?? "warning";
|
||||
Swal.fire({
|
||||
title: title,
|
||||
text: text,
|
||||
icon: icon,
|
||||
showCancelButton: true,
|
||||
confirmButtonText:
|
||||
options.confirmButtonText ??
|
||||
trans.default.methods.__("Oui"),
|
||||
cancelButtonText:
|
||||
options.cancelButtonText ??
|
||||
trans.default.methods.__("Annuler"),
|
||||
reverseButtons: true,
|
||||
// * Bootstrap Styling
|
||||
customClass: {
|
||||
confirmButton: "btn btn-lg btn-success ms-3 me-3",
|
||||
cancelButton: "btn btn-lg btn-danger ms-3 me-3",
|
||||
},
|
||||
buttonsStyling: false,
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
if (after) {
|
||||
after.apply(self, [result]);
|
||||
}
|
||||
} else if (
|
||||
/* Read more about handling dismissals below */
|
||||
result.dismiss === Swal.DismissReason.cancel
|
||||
) {
|
||||
Swal.fire(
|
||||
trans.default.methods.__(
|
||||
"L'opération n'as pas été effectuée"
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
76
resources/ts/modules/route.ts
Normal file
76
resources/ts/modules/route.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
const route = {
|
||||
methods: {
|
||||
route(
|
||||
routeName: string,
|
||||
parameters: Record<string, number | string> = {}
|
||||
): string | null {
|
||||
const routes = route.flattenProperties(window.__SYSTEM._routes);
|
||||
const routeUrl =
|
||||
Object.keys(routes).indexOf(routeName) !== -1
|
||||
? routes[routeName]
|
||||
: null;
|
||||
if (!routeUrl) {
|
||||
return routeUrl;
|
||||
}
|
||||
// copy parameters
|
||||
const iParameters = Object.assign({}, parameters),
|
||||
origin = window.location.origin,
|
||||
hash = window.location.hash;
|
||||
let pathname = routeUrl.replace(origin, ""),
|
||||
search = window.location.search;
|
||||
|
||||
// Replace in path name.
|
||||
for (const paramName in iParameters) {
|
||||
const paramValue = parameters[paramName];
|
||||
if (pathname.indexOf(paramName) !== -1) {
|
||||
pathname = pathname.replace(
|
||||
paramName,
|
||||
paramValue as string
|
||||
);
|
||||
}
|
||||
delete parameters[paramName];
|
||||
}
|
||||
|
||||
for (let paramName in parameters) {
|
||||
let paramValue = parameters[paramName];
|
||||
if (search.indexOf(paramName + "=") >= 0) {
|
||||
const prefix = search.substring(
|
||||
0,
|
||||
search.indexOf(paramName + "=")
|
||||
);
|
||||
let suffix = search.substring(
|
||||
search.indexOf(paramName + "=")
|
||||
);
|
||||
suffix = suffix.substring(suffix.indexOf("=") + 1);
|
||||
suffix =
|
||||
suffix.indexOf("&") >= 0
|
||||
? suffix.substring(suffix.indexOf("&"))
|
||||
: "";
|
||||
search = `${prefix}${paramName}=${paramValue}${suffix}`;
|
||||
} else if (search.indexOf("?") < 0) {
|
||||
search += `?${paramName}=${paramValue}`;
|
||||
} else {
|
||||
search += `&${paramName}=${paramValue}`;
|
||||
}
|
||||
}
|
||||
return `${origin}${pathname}${search}${hash}`;
|
||||
},
|
||||
},
|
||||
flattenProperties(
|
||||
obj: Record<PropertyKey, any>,
|
||||
parent: string | null = null,
|
||||
res: Record<PropertyKey, any> = {}
|
||||
): Record<PropertyKey, any> {
|
||||
for (const key of Object.keys(obj)) {
|
||||
const propName = parent ? parent + "." + key : key;
|
||||
if (typeof obj[key] === "object") {
|
||||
route.flattenProperties(obj[key], propName, res);
|
||||
} else {
|
||||
res[propName] = obj[key];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
export default route;
|
||||
16
resources/ts/modules/session_pinger.ts
Normal file
16
resources/ts/modules/session_pinger.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export default {
|
||||
methods: {
|
||||
async validateSession(callback: CallableFunction) {
|
||||
window.axios
|
||||
.get("/ping")
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
callback();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
34
resources/ts/modules/toast.ts
Normal file
34
resources/ts/modules/toast.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import * as Toastify from "toastify-js";
|
||||
import { Options } from "toastify-js";
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
toast(
|
||||
message: string,
|
||||
duration = 4000,
|
||||
className = "notifications"
|
||||
): void {
|
||||
const options: Options = {
|
||||
duration: duration,
|
||||
newWindow: false,
|
||||
close: true,
|
||||
className: className,
|
||||
gravity: "top",
|
||||
offset: {
|
||||
x: "2em",
|
||||
y: "3em",
|
||||
},
|
||||
// * Prevents dismissing of toast on hover
|
||||
stopOnFocus: true,
|
||||
};
|
||||
options.text = message;
|
||||
Toastify(options).showToast();
|
||||
},
|
||||
notify(message: string, duration = 4000): void {
|
||||
this.toast(message, duration, "notifications");
|
||||
},
|
||||
errorNotify(message: string, duration = 4000): void {
|
||||
this.toast(message, duration, "notifications error");
|
||||
},
|
||||
},
|
||||
};
|
||||
49
resources/ts/modules/trans.ts
Normal file
49
resources/ts/modules/trans.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
locale: window.__SYSTEM._locale,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Translate the given key.
|
||||
*/
|
||||
__(key: string, replace: Record<PropertyKey, string> = {}): string {
|
||||
let translation: string,
|
||||
translationNotFound = true;
|
||||
|
||||
try {
|
||||
translation = key
|
||||
.split(".")
|
||||
.reduce(
|
||||
(t, i) => t[i as any] ?? null,
|
||||
window.__SYSTEM._translations.php
|
||||
) as string;
|
||||
if (translation) {
|
||||
translationNotFound = false;
|
||||
}
|
||||
} catch (e) {
|
||||
translation = key;
|
||||
}
|
||||
|
||||
if (translationNotFound) {
|
||||
translation = (window.__SYSTEM._translations.json[key as any] ??
|
||||
key) as string;
|
||||
}
|
||||
const vars = translation.match(/:[0-9A-Za-z_]+/g);
|
||||
if (vars) {
|
||||
for (const match of vars) {
|
||||
try {
|
||||
translation = translation.replace(
|
||||
new RegExp(match, "g"),
|
||||
replace[match.substring(1)]
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("String replace failed ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return translation;
|
||||
},
|
||||
},
|
||||
};
|
||||
47
resources/ts/pages/files_add.ts
Normal file
47
resources/ts/pages/files_add.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Page Project File Add
|
||||
if (document.querySelector(".content.pageFile")) {
|
||||
let selectFileType = document.getElementById(
|
||||
"type"
|
||||
) as HTMLSelectElement,
|
||||
inputUri = document.getElementById("uri") as HTMLInputElement,
|
||||
inputFile = document.getElementById("file") as HTMLInputElement;
|
||||
|
||||
let changeFields = () => {
|
||||
if (
|
||||
!selectFileType ||
|
||||
!inputUri ||
|
||||
!inputUri.parentElement ||
|
||||
!inputUri.parentElement.parentElement ||
|
||||
!inputFile ||
|
||||
!inputFile.parentElement ||
|
||||
!inputFile.parentElement.parentElement
|
||||
) {
|
||||
throw new Error(
|
||||
"Missing selectFileType or inputUri or inputFile on file page"
|
||||
);
|
||||
}
|
||||
if (selectFileType.value === "0") {
|
||||
inputUri.setAttribute("required", "");
|
||||
inputUri.removeAttribute("disabled");
|
||||
inputUri.parentElement.parentElement.classList.remove("d-none");
|
||||
inputFile.removeAttribute("required");
|
||||
inputFile.setAttribute("disabled", "");
|
||||
inputFile.parentElement.parentElement.classList.add("d-none");
|
||||
} else {
|
||||
inputUri.removeAttribute("required");
|
||||
inputUri.removeAttribute("required");
|
||||
inputUri.setAttribute("disabled", "");
|
||||
inputUri.parentElement.parentElement.classList.add("d-none");
|
||||
inputFile.setAttribute("required", "");
|
||||
inputFile.removeAttribute("disabled");
|
||||
inputFile.parentElement.parentElement.classList.remove(
|
||||
"d-none"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
changeFields();
|
||||
selectFileType.addEventListener("change", changeFields);
|
||||
}
|
||||
});
|
||||
29
resources/ts/pages/label_colorize.ts
Normal file
29
resources/ts/pages/label_colorize.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import colors from "../modules/colors";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Ticket index colorize labels
|
||||
if (
|
||||
document.querySelector(".content.ticketsIndex") ||
|
||||
document.querySelector(".content.ticketsShow")
|
||||
) {
|
||||
let labels = document.querySelectorAll(".label.colorize");
|
||||
|
||||
labels.forEach((label, key) => {
|
||||
if (!(label instanceof HTMLElement)) {
|
||||
throw new Error("label has to be a HTMLElement");
|
||||
}
|
||||
if (label.dataset.color) {
|
||||
if (colors.methods.isrgb(label.dataset.color)) {
|
||||
label.dataset.color = colors.methods.rgb2hex(
|
||||
label.dataset.color
|
||||
);
|
||||
}
|
||||
label.style.color = colors.methods.invertColor(
|
||||
label.dataset.color,
|
||||
true
|
||||
);
|
||||
label.style.backgroundColor = label.dataset.color;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
32
resources/ts/session_watch.ts
Normal file
32
resources/ts/session_watch.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import Swal from "sweetalert2";
|
||||
import trans from "./modules/trans";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let validateSession = require("./modules/session_pinger").default.methods
|
||||
.validateSession,
|
||||
sessionInvalidAction = () => {
|
||||
Swal.fire({
|
||||
title: trans.methods.__("Session expirée"),
|
||||
text: trans.methods.__(
|
||||
"Votre session à expiré vous devrez recharger la page pour soumettre des informations."
|
||||
),
|
||||
icon: "error",
|
||||
// * Bootstrap Styling
|
||||
customClass: {
|
||||
confirmButton: "btn btn-lg btn-success ms-3 me-3",
|
||||
cancelButton: "btn btn-lg btn-danger ms-3 me-3",
|
||||
},
|
||||
});
|
||||
clearTimeout(timeOut);
|
||||
},
|
||||
timer = 10000,
|
||||
timeOut: NodeJS.Timeout | undefined,
|
||||
timerAction = () => {
|
||||
validateSession(sessionInvalidAction);
|
||||
timeOut = setTimeout(timerAction, timer);
|
||||
};
|
||||
|
||||
if (window.__SESSION_WATCH) {
|
||||
timerAction();
|
||||
}
|
||||
});
|
||||
46
resources/ts/vue.ts
Normal file
46
resources/ts/vue.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { App, createApp } from "vue";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const setupApp = (app: App<Element>) => {
|
||||
app.config.globalProperties.window = window;
|
||||
app.config.globalProperties.document = document;
|
||||
};
|
||||
|
||||
// Register components
|
||||
const vues = require.context("./components", true, /\.vue$/i);
|
||||
vues.keys().map((key) => {
|
||||
let htmlKey = key.split("/").pop();
|
||||
if (!htmlKey) {
|
||||
throw new Error(`Cannot load ${key}`);
|
||||
}
|
||||
htmlKey = htmlKey
|
||||
.split(".")[0]
|
||||
.replace(/\.?([A-Z])/g, function (x, y) {
|
||||
return "-" + y.toLowerCase();
|
||||
})
|
||||
.replace(/^-/, "")
|
||||
.replace("-component", "");
|
||||
|
||||
let el = document.getElementById(htmlKey),
|
||||
elGroup = document.getElementsByClassName(htmlKey);
|
||||
// * Init using ID
|
||||
if (el) {
|
||||
const app = createApp(vues(key).default, {
|
||||
json: el.dataset.json ?? {},
|
||||
});
|
||||
setupApp(app);
|
||||
app.mount(el);
|
||||
}
|
||||
// * Init Using class
|
||||
for (const grpEl of elGroup) {
|
||||
// * Hack to prevent loop weirdness
|
||||
setTimeout(() => {
|
||||
const app = createApp(vues(key).default, {
|
||||
json: (grpEl as HTMLElement).dataset.json ?? {},
|
||||
});
|
||||
setupApp(app);
|
||||
app.mount(grpEl);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -18,9 +18,7 @@
|
|||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="{{ mix('css/bootstrap.css') }}" rel="stylesheet">
|
||||
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
|
||||
<link href="{{ mix('css/fontawesome.min.css') }}" rel="stylesheet">
|
||||
@crudPoliciesCSS
|
||||
@stack('styles')
|
||||
</head>
|
||||
|
|
@ -41,8 +39,8 @@
|
|||
</div>
|
||||
@include('layout.footer')
|
||||
@stack('scriptsConstants')
|
||||
@include('modules.scriptConstants')
|
||||
<script src="{{ mix('js/app.js') }}"></script>
|
||||
<script src="{{ mix('js/fontawesome.min.js') }}" data-auto-add-css="false"></script>
|
||||
@stack('scripts')
|
||||
@crudPoliciesJS()
|
||||
@auth
|
||||
|
|
|
|||
|
|
@ -21,12 +21,3 @@
|
|||
data-json='@json($dataVue)'></div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scriptsConstants')
|
||||
<script nonce="{{ $nonce }}">
|
||||
window._routes = {
|
||||
"macroplanning.activity": "{{ route('macroplanning.activity') }}",
|
||||
"projects.show": "{{ route('projects.show', ['project' => 'PROJECT']) }}",
|
||||
};
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
34
resources/views/modules/scriptConstants.blade.php
Normal file
34
resources/views/modules/scriptConstants.blade.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script @if(isset($nonce)) nonce="{{ $nonce }}" @endif>
|
||||
if (!window.__SYSTEM) {
|
||||
window.__SYSTEM = {};
|
||||
}
|
||||
window.__SYSTEM._locale = '{{ app()->getLocale() }}';
|
||||
window.__SYSTEM._translations = @json(cache(sprintf('translations.%s', app()->getLocale())) ?? []);
|
||||
window.__SYSTEM._asset = '{{ asset('') }}';
|
||||
if(!window.__SYSTEM._routes) {
|
||||
window.__SYSTEM._routes = {}
|
||||
}
|
||||
window.__SYSTEM._routes['crud-policies'] = {
|
||||
upload: '{{ route('crud-policies.upload') }}'
|
||||
};
|
||||
window.__SYSTEM._routes.macroplanning = {
|
||||
activity: "{{ route('macroplanning.activity') }}"
|
||||
};
|
||||
window.__SYSTEM._routes.ticket = {
|
||||
index: "{{ route('ticket.index') }}",
|
||||
show: "{{ route('ticket.show', ['ticket' => 'TICKET']) }}"
|
||||
};
|
||||
window.__SYSTEM._routes.sites = {
|
||||
index: "{{ route('sites.index') }}",
|
||||
show: "{{ route('sites.show', ['site' => 'SITE']) }}"
|
||||
};
|
||||
window.__SYSTEM._routes.projects = {
|
||||
index: "{{ route('projects.index') }}",
|
||||
show: "{{ route('projects.show', ['project' => 'PROJECT']) }}"
|
||||
};
|
||||
window.__SYSTEM._routes.schedule = {
|
||||
index: "{{ route('schedule.index') }}",
|
||||
setCellContent: "{{ route('schedule.setCellContent') }}",
|
||||
eraseCellContent: "{{ route('schedule.eraseCellContent') }}"
|
||||
};
|
||||
</script>
|
||||
|
|
@ -35,22 +35,6 @@
|
|||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scriptsConstants')
|
||||
<script nonce="{{ $nonce }}">
|
||||
window._routes = {
|
||||
"schedule.index": "{{ route('schedule.index') }}",
|
||||
"ticket.show": "{{ route('ticket.show', 'TICKET') }}",
|
||||
"projects.show": "{{ route('projects.show', 'PROJECT') }}",
|
||||
"sites.show": "{{ route('sites.show', 'SITE') }}",
|
||||
"ticket.index": "{{ route('ticket.index') }}",
|
||||
"projects.index": "{{ route('projects.index') }}",
|
||||
"sites.index": "{{ route('sites.index') }}",
|
||||
"schedule.setCellContent": "{{ route('schedule.setCellContent') }}",
|
||||
"schedule.eraseCellContent": "{{ route('schedule.eraseCellContent') }}"
|
||||
};
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@push('styles')
|
||||
<style nonce="{{ $nonce }}">
|
||||
.support {
|
||||
|
|
|
|||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"es5", "es6", "dom", "dom.iterable"
|
||||
],
|
||||
"module": "esnext",
|
||||
// "isolatedModules": false,
|
||||
// "experimentalDecorators": true,
|
||||
// "strictNullChecks": true,
|
||||
// "removeComments": true,
|
||||
// "allowSyntheticDefaultImports": true,
|
||||
|
||||
// this enables stricter inference for data properties on `this`
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"moduleResolution": "node",
|
||||
"typeRoots": ["resources/ts/@types", "node_modules/@types"],
|
||||
},
|
||||
"include": [
|
||||
"resources/**/*.js",
|
||||
"resources/**/*.ts",
|
||||
"resources/**/*.vue"
|
||||
],
|
||||
"compileOnSave": false
|
||||
}
|
||||
41
webpack.mix.js
vendored
41
webpack.mix.js
vendored
|
|
@ -1,24 +1,43 @@
|
|||
const path = require('path')
|
||||
const mix = require('laravel-mix')
|
||||
|
||||
require('laravel-mix-polyfill')
|
||||
require('laravel-mix-clean')
|
||||
|
||||
let assetDir = 'assets'
|
||||
let polyfill = {
|
||||
const assetDir = 'assets'
|
||||
const polyfill = {
|
||||
enabled: true,
|
||||
bugfixes: true,
|
||||
useBuiltIns: "usage",
|
||||
// legacy targets from gulp file
|
||||
targets: ['last 3 version', 'safari 5', 'ie 8', 'opera 12.1', 'ios 6', 'android 4']
|
||||
targets: 'last 3 version, not dead, >0.3%'
|
||||
}
|
||||
|
||||
mix.webpackConfig({
|
||||
mode: process.env.NODE_ENV,
|
||||
resolve: {
|
||||
extensions: ["*", ".js", ".vue", ".ts"],
|
||||
alias: {
|
||||
'@': path.resolve('resources/assets'),
|
||||
'vue$': 'vue/dist/vue.runtime.esm.js',
|
||||
'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,
|
||||
|
|
@ -49,20 +68,16 @@ mix.clean({
|
|||
})
|
||||
|
||||
// mix Backend
|
||||
mix.js('resources/js/app.js', 'public/js')
|
||||
mix.ts('resources/ts/app.ts', 'public/js/app.js', { transpileOnly: true })
|
||||
.vue({
|
||||
runtimeOnly: true,
|
||||
extractStyles: true,
|
||||
globalStyles: false
|
||||
})
|
||||
.sass('resources/sass/app.scss', 'public/css')
|
||||
.sass('resources/sass/bootstrap.scss', 'public/css')
|
||||
.polyfill(polyfill)
|
||||
.sourceMaps(false)
|
||||
.version()
|
||||
.sourceMaps(true, 'inline-source-map')
|
||||
|
||||
// Copy Blade Images Assets
|
||||
mix.copyDirectory('resources/assets', 'public/assets')
|
||||
|
||||
// fontawesome compile
|
||||
mix.copy('node_modules/@fortawesome/fontawesome-free/js/all.min.js', 'public/js/fontawesome.min.js')
|
||||
.copy('node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css', 'public/css/fontawesome.min.css');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue