Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • clients/wotwizard-ui
  • manutopik/wotwizard-ui
  • wellno1/wotwizard-ui
3 results
Show changes
Showing
with 1175 additions and 508 deletions
<template> <template>
<small> <span
<span class="badge"
class="badge" :class="{
:class="{ 'bg-success': quality >= 80,
'badge-success': quality >= 80, 'bg-warning': quality < 80
'badge-warning': quality < 80 }">
}"> {{ Math.round(quality * 100) / 100 }}
{{ Math.round(quality * 100) / 100 }} </span>
</span>
</small>
</template> </template>
<script> <script>
......
<template> <template>
<small> <span class="badge" :class="displayStatus(membre).class">
<span class="badge" :class="this.displayStatus(membre).class"> {{ this.displayStatus(membre).str }}
{{ this.displayStatus(membre).str }} </span>
</span>
</small>
</template> </template>
<script> <script>
...@@ -18,22 +16,20 @@ export default { ...@@ -18,22 +16,20 @@ export default {
displayStatus: function (member) { displayStatus: function (member) {
switch (member.status) { switch (member.status) {
case "NEWCOMER": case "NEWCOMER":
return { str: this.$i18n.t("statut.newcomer"), class: "badge-info" } return { str: this.$i18n.t("statut.newcomer"), class: "bg-info" }
case "MISSING": case "MISSING":
return { str: this.$i18n.t("statut.missing"), class: "badge-danger" } return { str: this.$i18n.t("statut.missing"), class: "bg-danger" }
case "MEMBER": case "MEMBER":
if (this.$options.filters.dateStatus(member.limitDate) == "warning") { return {
return { str: this.$i18n.t("statut.renew"), class: "badge-warning" } str: this.$i18n.t("statut.member"),
} else { class: "bg-success"
return {
str: this.$i18n.t("statut.member"),
class: "badge-success"
}
} }
case "RENEW":
return { str: this.$i18n.t("statut.renew"), class: "bg-warning" }
case "REVOKED": case "REVOKED":
return { return {
str: this.$i18n.t("statut.revoked"), str: this.$i18n.t("statut.revoked"),
class: "badge-secondary" class: "bg-secondary"
} }
default: default:
return "N/A" return "N/A"
......
<template> <template>
<div class="clipboard input-group input-group-sm mb-3 mx-auto"> <div class="clipboard input-group mb-3 mx-auto">
<div class="input-group-prepend"> <button
<button id="btncopy"
id="btncopy" class="btn btn-secondary px-4 py-1"
class="btn btn-outline-secondary px-4 py-1" type="button"
type="button" v-tooltip-click="$t('copie') + ' !'"
@click="copyText"></button> @click="copyText">
</div> <solid-share-icon class="icon" aria-hidden="true" />
<span class="visually-hidden">{{ $t("aria.clipboard") }}</span>
</button>
<input <input
type="text" type="text"
class="form-control text-truncate" class="form-control text-truncate"
...@@ -26,14 +28,6 @@ export default { ...@@ -26,14 +28,6 @@ export default {
methods: { methods: {
copyText() { copyText() {
navigator.clipboard.writeText(this.textContent) navigator.clipboard.writeText(this.textContent)
$("#btncopy").tooltip({
title: this.$t("copie") + " !",
trigger: "manual"
})
$("#btncopy").tooltip("show")
setTimeout(() => {
$("#btncopy").tooltip("hide")
}, 1000)
} }
} }
} }
...@@ -43,9 +37,8 @@ export default { ...@@ -43,9 +37,8 @@ export default {
.clipboard { .clipboard {
max-width: 500px; max-width: 500px;
button { input {
background: url("~assets/img/clipboard.svg") no-repeat 50% 50% #fff; user-select: none;
background-size: 40%;
} }
} }
</style> </style>
<template>
<button
class="btn btn-secondary"
v-tooltip-click="
$favourites.list.includes(uid)
? $t('suivis.enregistre')
: $t('suivis.supprime')
"
@click="$favourites.toggleFavourite(uid, $event)">
<span class="visually-hidden">{{
$favourites.list.includes(uid)
? $t("suivis.supprimer")
: $t("suivis.ajouter")
}}</span>
<solid-user-add-icon
aria-hidden="true"
style="width: 2rem"
v-if="!$favourites.list.includes(uid)" />
<solid-user-remove-icon
aria-hidden="true"
style="width: 2rem"
v-if="$favourites.list.includes(uid)" />
</button>
</template>
<script>
export default {
props: {
uid: {
type: String,
required: true
}
}
}
</script>
<template>
<button
v-if="membersToAdd.length != 0"
class="btn btn-secondary position-relative"
v-tooltip="{ title: $t('suivis.ajouter'), placement: 'left' }"
@click="$favourites.addFavorisArray(membersToAdd, $event)">
<solid-user-group-icon aria-hidden="true" class="icon" />
<span
aria-hidden="true"
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
+ {{ membersToAdd.length }}
</span>
</button>
</template>
<script>
export default {
props: {
listUID: {
type: Array,
required: true
}
},
computed: {
membersToAdd() {
return this.listUID.filter(
(el) => !this.$favourites.list.includes(el.uid)
)
}
}
}
</script>
<template>
<div>
<div class="input-group mb-3">
<span class="input-group-text"
><solid-search-icon class="icon" aria-hidden="true"
/></span>
<input
type="text"
class="form-control"
:value="value"
autocomplete="off"
@input="$emit('input', $event.target.value)"
@keyup="$emit('keyup', $event.keyCode)"
:placeholder="$t('recherche.title')"
:aria-label="$t('recherche.title')"
:aria-describedby="help ? 'rechHelp' : null" />
<button
v-if="value != ''"
:title="$t('recherche.effacer')"
class="btn"
type="button"
@click="$emit('erase')">
<solid-x-icon class="icon" aria-hidden="true" />
</button>
</div>
<div
v-if="help"
id="rechHelp"
class="small form-text text-muted text-center">
{{ help }}
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: String
},
help: {
type: String
}
}
}
</script>
<style lang="scss" scoped>
.input-group > * {
border-color: var(--border-color);
}
.btn {
background: var(--bg-menu-color);
color: var(--txt-secondary-color);
&:hover {
background: var(--bg-secondary-color);
}
}
</style>
<template> <template>
<div class="d-inline-block position-absolute ml-2"> <div
<div class="btn-sort pointer px-2"
class="up" tabindex="0"
:class="{ :title="$t('tri.action')"
sorted: currentSortDir == 'desc' && currentSort == fieldName, @click="sort(fieldName)"
invisible: currentSortDir == 'asc' && currentSort == fieldName @keyup.enter="sort(fieldName)">
}"> <span class="text-truncate">{{ title }}</span>
<solid-sort-ascending-icon
</div> aria-hidden="true"
<div class="ms-2 icon flex-shrink-0"
class="down" v-if="currentSortDir == 'desc' && currentSort == fieldName" />
:class="{ <solid-sort-descending-icon
sorted: currentSortDir == 'asc' && currentSort == fieldName, aria-hidden="true"
invisible: currentSortDir == 'desc' && currentSort == fieldName class="ms-2 icon flex-shrink-0"
}"> v-if="currentSortDir == 'asc' && currentSort == fieldName" />
</div>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: { props: {
title: {
type: String,
required: true
},
fieldName: { fieldName: {
type: String, type: String,
required: true required: true
}, },
tableName: {
type: String,
required: true
},
currentSort: { currentSort: {
type: String, type: String,
required: true required: true
...@@ -37,8 +44,78 @@ export default { ...@@ -37,8 +44,78 @@ export default {
} }
} }
}, },
created() { methods: {
console.log("created") sort(s) {
if (s === this.currentSort) {
this.$parent.currentSortDir =
this.currentSortDir === "asc" ? "desc" : "asc"
} else {
this.$parent.currentSortDir = "asc"
}
this.$parent.currentSort = s
let query = this.$route.query
let newQuery = {}
let same_array = false
if (Object.keys(query).length !== 0) {
for (const param in query) {
if (param.includes(this.tableName + "_")) {
same_array = true
newQuery[this.tableName + "_" + this.fieldName] =
this.$parent.currentSortDir
} else {
newQuery[param] = query[param]
}
}
}
if (!same_array) {
newQuery[this.tableName + "_" + this.fieldName] =
this.$parent.currentSortDir
}
this.$router.push({
hash: this.$route.hash,
query: newQuery
})
},
retrieveQuery() {
let query = this.$route.query
if (Object.keys(query).length !== 0) {
for (const param in query) {
if (param == this.tableName + "_" + this.fieldName) {
this.$parent.currentSort = this.fieldName
this.$parent.currentSortDir = query[param]
}
}
}
}
},
mounted() {
this.retrieveQuery()
},
watch: {
$route(n, o) {
this.retrieveQuery()
}
} }
} }
</script> </script>
<style lang="scss">
.btn-sort {
display: flex;
justify-content: center;
align-items: center;
min-height: 50px;
background: var(--bg-secondary-color);
color: var(--txt-secondary-color);
&:focus,
&:hover {
filter: brightness(90%);
}
}
</style>
...@@ -7,8 +7,15 @@ ...@@ -7,8 +7,15 @@
class="switch-checkbox" /> class="switch-checkbox" />
<label <label
for="checkbox" for="checkbox"
class="switch-label d-flex align-items-center justify-content-between position-relative mb-0 form-control" class="switch-label pointer d-flex align-items-center justify-content-between position-relative mb-0 form-control"
tabindex="0"> tabindex="0">
<span class="visually-hidden">
{{
this.userTheme === "light-theme"
? $t("aria.themedark")
: $t("aria.themelight")
}}
</span>
<span>🌙</span> <span>🌙</span>
<span>☀️</span> <span>☀️</span>
<div <div
...@@ -62,9 +69,9 @@ export default { ...@@ -62,9 +69,9 @@ export default {
} }
.switch-label { .switch-label {
background: var(--text-primary-color); --element-size: 4rem;
background: var(--txt-primary-color);
border-radius: var(--element-size); border-radius: var(--element-size);
cursor: pointer;
font-size: calc(var(--element-size) * 0.3); font-size: calc(var(--element-size) * 0.3);
height: calc(var(--element-size) * 0.35); height: calc(var(--element-size) * 0.35);
padding: calc(var(--element-size) * 0.1); padding: calc(var(--element-size) * 0.1);
...@@ -74,7 +81,7 @@ export default { ...@@ -74,7 +81,7 @@ export default {
} }
.switch-toggle { .switch-toggle {
background-color: var(--background-color-primary); background-color: var(--bg-primary-color);
top: calc(var(--element-size) * 0.07); top: calc(var(--element-size) * 0.07);
left: calc(var(--element-size) * 0.07); left: calc(var(--element-size) * 0.07);
height: calc(var(--element-size) * 0.4); height: calc(var(--element-size) * 0.4);
......
<template>
<div class="container-lg" v-if="idFromHash.status != 'REVOKED'">
<div class="row mt-3">
<div class="col-sm-10 col-md-8 col-lg-5 mx-auto">
<CertifGroup
:limitDate="idFromHash.certsLimit"
:memberStatus="idFromHash.status"
:certifs="idFromHash.received_certifications"
type="received"
:certifStatus="$options.filters.dateStatus(idFromHash.certsLimit)" />
</div>
<hr class="d-lg-none mt-4" style="height: 10px" />
<div
class="col-1 d-none d-lg-flex"
v-if="['MISSING', 'MEMBER'].includes(idFromHash.status)">
<div class="vr mx-auto"></div>
</div>
<div
class="col-sm-10 col-md-8 col-lg-5 mx-auto"
v-if="['MISSING', 'MEMBER'].includes(idFromHash.status)">
<CertifGroup :certifs="idFromHash.sent_certifications" type="sent" />
</div>
</div>
</div>
</template>
<script>
export default {
props: {
idFromHash: {
type: Object,
required: true
}
}
}
</script>
<template>
<div>
<div
class="d-flex align-items-center justify-content-between flex-column flex-sm-row mb-4">
<h3
class="h4 text-center d-flex"
style="min-width: 0"
:class="{
'text-success': certifStatus == 'success',
'text-warning': certifStatus == 'warning',
'text-danger': certifStatus == 'danger',
'text-info': type == 'sent'
}">
<span class="text-truncate d-block">
{{
type == "sent"
? $t("certification.envoyees")
: $t("certification.recues")
}}
</span>
&nbsp;<BadgeDanger
v-if="type == 'received'"
class="flex-shrink-0"
style="width: 2rem"
:limitDate="limitDate"
:memberStatus="memberStatus" />
</h3>
<BtnFavoriArray
:listUID="certifsNotPending"
v-if="$parent.$parent.registeredAccount.hash != $route.query.hash" />
</div>
<CertifList
:title="$t('certification.enattente')"
:certifs="certifsPending"
:collapseId="type + '-entraitement'" />
<hr v-if="certifsPending.length > 0 && certifsNotPending.length > 0" />
<CertifList
:certifStatus="certifStatus"
:openDefault="true"
:title="$t('certification.encours')"
:certifs="certifsNotPending"
:collapseId="type + '-encours'" />
<hr v-if="certifsExpired.length > 0" />
<CertifList
:title="$t('certification.perimees')"
:certifs="certifsExpired"
:collapseId="type + '-perimees'" />
</div>
</template>
<script>
export default {
props: {
limitDate: Number,
memberStatus: String,
certifs: Array,
certifStatus: {
type: String,
default: ""
},
type: {
type: String,
required: true,
validator: function (value) {
const types = ["received", "sent"]
return types.indexOf(value) !== -1
}
}
},
computed: {
certifsNotPending() {
return this.certifs.filter((el) => el.pending == false)
},
certifsPending() {
return this.certifs.filter((el) => el.pending == true)
},
certifsExpired() {
return this.certifs.filter((el) => el.expired == true)
}
}
}
</script>
<template> <template>
<div class="table-responsive"> <div class="certifList" v-if="certifs.length > 0">
<table <button
class="table table-striped table-hover" :title="
v-if="certifsPending.length > 0"> isOpen ? $t('certification.masquer') : $t('certification.afficher')
<tbody> "
<tr @click="isOpen = !isOpen"
v-for="certif in certifsPending" class="btn w-100 m-auto d-block rounded-0"
:key="certif.uid + certif.expires_on" :class="btnClass"
@click=" type="button"
$router.push( data-bs-toggle="collapse"
localePath({ :data-bs-target="'#' + collapseId"
name: 'membres-hash', aria-expanded="false"
params: { hash: certif.hash } :aria-controls="collapseId">
}) <span v-if="!isOpen">
) <solid-eye-icon class="icon" aria-hidden="true" />
"> </span>
<th scope="row" class="py-1"> <span v-else>
<div> <solid-eye-off-icon class="icon" aria-hidden="true" />
{{ certif.uid }} </span>
<BadgeDanger <h4 class="d-inline align-middle">
:limitDate="certif.received_certifications.limit" {{ title }}&nbsp;&nbsp;
:memberStatus="certif.status" /> <span class="badge rounded-pill bg-white opacity-75 text-dark">{{
<BadgeQuality certifs.length
:quality="certif.quality.ratio" }}</span>
v-if="certif.status != 'REVOKED'" /> </h4>
</div> </button>
<div> <div
:id="collapseId"
class="table-responsive collapse p-3"
:class="collapseClass">
<BtnSearch
@erase="search = ''"
v-model="search"
class="px-2"
v-if="certifs.length > 5" />
<table
class="table table-striped table-hover table-fixed sortable border m-0">
<thead class="thead-light">
<tr>
<th class="p-0">
<BtnSort
:title="$t('membre.title')"
fieldName="uid"
:tableName="collapseId"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th class="p-0 col-4">
<BtnSort
:title="$t('expire')"
fieldName="expires_on"
:tableName="collapseId"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
</tr>
</thead>
<tbody>
<tr
v-for="certif in certifsTriees"
:key="certif.uid"
tabindex="0"
:title="$t('membre.voirinfos')"
@click="
$router.push(
localePath({
name: 'membres-profil',
query: { hash: certif.hash }
})
)
"
@keyup.enter="
$router.push(
localePath({
name: 'membres-profil',
query: { hash: certif.hash }
})
)
">
<td class="py-1">
<div class="d-flex">
<span v-if="$favourites.list.includes(certif.uid)"
>&nbsp;</span
>
<div class="text-truncate">{{ certif.uid }}</div>
&nbsp;
<BadgeDanger
style="width: 1.2rem"
:limitDate="certif.certsLimit"
:memberStatus="certif.status" />
</div>
<BadgeStatus :membre="certif" /> <BadgeStatus :membre="certif" />
<BadgeDispo
:isDispo="certif.minDatePassed"
:dateDispo="certif.minDate"
v-if="certif.status == 'MEMBER'" />
</div>
</th>
<td class="text-right py-1">
<BadgeDate :date="certif.expires_on" styleDate="short" />
<small class="d-block">
<span class="badge badge-secondary">{{ $t("traitement") }}</span>
</small>
</td>
</tr>
</tbody>
</table>
<hr v-if="certifsPending.length > 0 && certifsTriees.length > 0" />
<table
class="table table-striped table-hover"
v-if="certifsTriees.length > 0">
<thead class="thead-light sortable">
<th class="position-relative" @click="sort('uid')">
{{ $t("membres") }}
<BtnSort
fieldName="uid"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th class="position-relative" @click="sort('expires_on')">
{{ $t("expire") }}
<BtnSort
fieldName="expires_on"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
</thead>
<tbody>
<tr
v-for="certif in certifsTriees"
:key="certif.uid + certif.expires_on"
@click="
$router.push(
localePath({
name: 'membres-hash',
params: { hash: certif.hash }
})
)
">
<th scope="row" class="py-1">
<div>
<span v-if="$isFavourite(certif.uid)">&nbsp;</span>
{{ certif.uid }}
<BadgeDanger
:limitDate="certif.received_certifications.limit"
:memberStatus="certif.status" />
<BadgeQuality <BadgeQuality
:quality="certif.quality.ratio" :quality="certif.quality.ratio"
v-if="certif.status != 'REVOKED'" /> v-if="!['REVOKED', 'NEWCOMER'].includes(certif.status)" />
</div>
<div>
<BadgeStatus :membre="certif" />
<BadgeDispo <BadgeDispo
:isDispo="certif.minDatePassed" :isDispo="certif.minDatePassed"
:dateDispo="certif.minDate" :dateDispo="certif.minDate"
:certifs="certif.sent_certifications"
v-if="certif.status == 'MEMBER'" /> v-if="certif.status == 'MEMBER'" />
</div> </td>
</th> <td class="p-0 text-center col-4">
<td class="text-right py-1"> <div class="d-inline-flex flex-column gap-1">
<BadgeDate :date="certif.expires_on" styleDate="long" /> <BadgeDate :date="certif.expires_on" />
</td> <span
</tr> class="badge bg-secondary text-truncate d-block"
</tbody> v-if="certif.pending"
</table> >{{ $t("traitement") }}</span
>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
</template> </template>
...@@ -106,71 +121,110 @@ ...@@ -106,71 +121,110 @@
export default { export default {
data() { data() {
return { return {
search: "",
currentSort: "expires_on", currentSort: "expires_on",
currentSortDir: "asc" currentSortDir: "asc",
isOpen: this.openDefault
} }
}, },
props: { props: {
certifs: Array, certifs: Array,
type: { certifStatus: String,
type: String, collapseId: String,
required: true, title: String,
validator: function (value) { openDefault: {
const types = ["received", "sent"] type: Boolean,
return types.indexOf(value) !== -1 default: false
}
}
},
methods: {
sort(s) {
if (s === this.currentSort) {
this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc"
}
this.currentSort = s
} }
}, },
computed: { computed: {
certifsFiltrees() {
return this.certifs.filter((row, index) => {
return (
this.search == "" ||
row.uid.toLowerCase().includes(this.search.toLowerCase())
)
})
},
certifsTriees() { certifsTriees() {
return this.certifs return this.certifsFiltrees
.map((el) => {
el.status =
this.$options.filters.dateStatus(el.limitDate) == "warning"
? "RENEW"
: el.status
return el
})
.sort((a, b) => { .sort((a, b) => {
let modifier = this.currentSortDir === "desc" ? -1 : 1 let modifier = this.currentSortDir === "desc" ? -1 : 1
let sens = this.type == "received" ? "from" : "to"
if (this.currentSort == "expires_on") { if (this.currentSort == "expires_on") {
if (a["expires_on"] < b["expires_on"]) return -1 * modifier if (a["expires_on"] < b["expires_on"]) return -1 * modifier
if (a["expires_on"] > b["expires_on"]) return 1 * modifier if (a["expires_on"] > b["expires_on"]) return 1 * modifier
} else { } else {
if (a[sens]["uid"].toLowerCase() < b[sens]["uid"].toLowerCase()) if (a["uid"].toLowerCase() < b["uid"].toLowerCase())
return -1 * modifier return -1 * modifier
if (a[sens]["uid"].toLowerCase() > b[sens]["uid"].toLowerCase()) if (a["uid"].toLowerCase() > b["uid"].toLowerCase())
return 1 * modifier return 1 * modifier
} }
return 0 return 0
}) })
.filter((el) => el.pending == false)
.map((certif) => ({
...certif,
...(this.type === "received" ? certif.from : certif.to)
}))
}, },
certifsPending() { collapseClass() {
return this.certifs if (this.collapseId.includes("entraitement")) return "bg-secondary"
.slice() if (this.collapseId.includes("perimees")) return "bg-warning"
.sort((a, b) => a.expires_on - b.expires_on) if (this.collapseId == "sent-encours") return "bg-info"
.filter((el) => el.pending == true) return {
.map((certif) => ({ "bg-success": this.certifStatus == "success",
...certif, "bg-warning": this.certifStatus == "warning",
...(this.type === "received" ? certif.from : certif.to) "bg-danger": this.certifStatus == "danger"
})) }
},
btnClass() {
if (this.collapseId.includes("entraitement")) return "btn-secondary"
if (this.collapseId.includes("perimees")) return "btn-warning"
if (this.collapseId == "sent-encours") return "btn-info"
return {
"btn-success": this.certifStatus == "success",
"btn-warning": this.certifStatus == "warning",
"btn-danger": this.certifStatus == "danger"
}
}
},
mounted() {
if (this.openDefault && this.certifs.length > 0) {
document.querySelector("#" + this.collapseId).classList.add("show")
}
},
watch: {
certifs: {
handler(n, o) {
this.search = ""
}
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss">
thead th:last-child { .certifList {
padding-right: 1.5rem; .table-responsive tbody {
text-align: right; max-height: 456px;
}
tbody tr {
height: 80px;
}
@media (min-width: 576px) {
button {
font-size: 1.3rem;
}
tbody tr {
height: initial;
}
}
} }
</style> </style>
<template> <template>
<div class="card member"> <div class="card member">
<button
id="favori"
class="btn btn-light position-absolute"
:class="{
add: !isFavorite,
remove: isFavorite
}"
@click="toggleFavourite"></button>
<div class="card-body"> <div class="card-body">
<h2 class="card-title text-center mb-4"> <div class="d-flex align-items-center justify-content-between mb-4">
{{ hash.uid }} <h2
<BadgeStatus :membre="hash" /> class="h1 card-title text-center d-flex align-items-center flex-column m-0">
</h2> <span class="text-truncate d-inline-block mw-100">
<BtnClipboard :textContent="this.hash.pubkey" /> {{ hash.uid }}
</span>
<small><BadgeStatus class="ms-2" :membre="hash" /></small>
</h2>
<div class="btn-group" role="group">
<button
v-if="
$parent.registeredAccount.uid == '' ||
$parent.registeredAccount.uid == hash.uid
"
class="btn iam"
:class="{
'btn-success': $parent.registeredAccount.uid != hash.uid,
'btn-warning': $parent.registeredAccount.uid == hash.uid
}"
@click="updateCurrentHash($event)">
<span v-if="$parent.registeredAccount.uid != hash.uid">{{
$t("suivis.iam")
}}</span>
<span v-else>{{ $t("suivis.iamnot") }}</span>
</button>
<BtnFavori
:uid="hash.uid"
v-if="$parent.registeredAccount.uid != hash.uid" />
</div>
</div>
<BtnClipboard :textContent="hash.pubkey" />
<AlertMember :hash="hash" />
<div class="table-responsive"> <div class="table-responsive">
<table <table
class="table table-sm table-borderless" class="table table-sm table-borderless"
v-if="hash.status != 'REVOKED'"> v-if="hash.status != 'REVOKED'">
<tbody> <tbody>
<tr v-if="hash.status == 'MEMBER'"> <MemberProp
<th scope="row">{{ $t("membre.referent.title") }}&nbsp;:</th> v-if="hash.status == 'MEMBER'"
<td :title="$t('membre.referent.title')"
:class="{ :tooltip="$t('membre.referent.desc')"
'list-group-item-success': hash.sentry, :classClor="{
'list-group-item-warning': !hash.sentry 'table-success': hash.sentry,
}"> 'table-warning': !hash.sentry
{{ hash.sentry ? $t("oui") : $t("non") }} }">
</td> {{ hash.sentry ? $t("oui") : $t("non") }}
</tr> </MemberProp>
<tr v-if="hash.status != 'NEWCOMER'"> <MemberProp
<th scope="row">{{ $t("membre.qualite.title") }}&nbsp;:</th> v-if="hash.status != 'NEWCOMER'"
<td :title="$t('membre.qualite.title')"
:class="{ :tooltip="$t('membre.qualite.desc')"
'list-group-item-success': hash.quality.ratio >= 80, :classClor="{
'list-group-item-warning': hash.quality.ratio < 80 'table-success': hash.quality.ratio >= 80,
}"> 'table-warning': hash.quality.ratio < 80
{{ Math.round(hash.quality.ratio * 100) / 100 }} }">
</td> {{ Math.round(hash.quality.ratio * 100) / 100 }}</MemberProp
</tr> >
<tr> <MemberProp
<th scope="row">{{ $t("membre.distance.title") }}&nbsp;:</th> :title="$t('membre.distance.title')"
<td :tooltip="$t('membre.distance.desc')"
:class="{ :classClor="{
'list-group-item-success': 'table-success':
hash.status != 'NEWCOMER' hash.status != 'NEWCOMER'
? hash.distance.dist_ok ? hash.distanceE.dist_ok
: hash.distanceE.dist_ok, : hash.distanceE.dist_ok,
'list-group-item-danger': 'table-danger':
hash.status != 'NEWCOMER' hash.status != 'NEWCOMER'
? !hash.distance.dist_ok ? !hash.distanceE.dist_ok
: !hash.distanceE.dist_ok : !hash.distanceE.dist_ok
}"> }">
{{ {{ Math.round(hash.distanceE.value.ratio * 100) / 100 }}
hash.status != "NEWCOMER" </MemberProp>
? Math.round(hash.distance.value.ratio * 100) / 100 <MemberProp
: Math.round(hash.distanceE.value.ratio * 100) / 100 :title="
}} hash.status != 'MISSING'
</td> ? $t('membre.datelimadhesion.title')
</tr> : $t('membre.datelimrevoc.title')
<tr> "
<th scope="row"> :tooltip="
{{ hash.status != 'MISSING'
hash.status != "MISSING" ? $t('membre.datelimadhesion.desc')
? $t("membre.datelimadhesion") : $t('membre.datelimrevoc.desc')
: $t("membre.datelimrevoc") "
}}&nbsp;: :classClor="
</th> hash.status != 'MISSING'
<td ? 'table-' + $options.filters.dateStatus(hash.limitDate)
:class=" : 'table-danger'
hash.status != 'MISSING' ">
? 'list-group-item-' + {{ $d(new Date(hash.limitDate * 1000)).toLocaleString() }}
$options.filters.dateStatus(hash.limitDate) </MemberProp>
: 'list-group-item-danger' <MemberProp
"> v-if="hash.status == 'MEMBER'"
{{ $d(new Date(hash.limitDate * 1000), "long") }} :title="$t('membre.datemanquecertifs.title')"
</td> :tooltip="$t('membre.datemanquecertifs.desc')"
</tr> :classClor="
<tr v-if="hash.status == 'MEMBER'"> 'table-' + $options.filters.dateStatus(hash.certsLimit)
<th scope="row">{{ $t("membre.datemanquecertifs") }}&nbsp;:</th> ">
<td {{ $d(new Date(hash.certsLimit * 1000)).toLocaleString() }}
:class=" </MemberProp>
'list-group-item-' + <MemberProp
$options.filters.dateStatus( v-if="hash.status == 'MEMBER'"
hash.received_certifications.limit :title="$t('membre.dispocertif.title')"
) :tooltip="$t('membre.dispocertif.desc')"
"> :classClor="{
{{ 'table-success': hash.minDatePassed,
$d( 'table-danger': !hash.minDatePassed
new Date(hash.received_certifications.limit * 1000), }">
"long" {{ hash.minDatePassed ? $t("oui") : $t("non") }}
) <small v-if="!hash.minDatePassed"
}} >( > {{ $d(new Date(hash.minDate * 1000)).toLocaleString() }} )</small
</td> >
</tr> </MemberProp>
<tr v-if="hash.status == 'MEMBER'"> <MemberProp
<th scope="row">{{ $t("membre.dispocertif") }}&nbsp;:</th> v-if="hash.status == 'MEMBER'"
<td :title="$t('membre.nb_certifs.title')"
:class="{ :tooltip="$t('membre.nb_certifs.desc')"
'list-group-item-success': hash.minDatePassed, :classClor="{
'list-group-item-danger': !hash.minDatePassed 'table-success': sentCertNotExpired.length <= 80,
}"> 'table-warning': sentCertNotExpired.length > 80,
{{ hash.minDatePassed ? $t("oui") : $t("non") }} 'table-danger': sentCertNotExpired.length > 90
<small v-if="!hash.minDatePassed" }">
>( > {{ $d(new Date(hash.minDate * 1000), "long") }} )</small {{ 100 - sentCertNotExpired.length }}
> </MemberProp>
</td>
</tr>
<tr v-if="hash.status == 'MEMBER'">
<th scope="row">{{ $t("membre.nb_certifs") }}&nbsp;:</th>
<td
:class="{
'list-group-item-success':
hash.sent_certifications.length <= 80,
'list-group-item-warning':
hash.sent_certifications.length > 80,
'list-group-item-danger': hash.sent_certifications.length > 90
}">
{{ 100 - hash.sent_certifications.length }}
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
...@@ -129,99 +133,63 @@ ...@@ -129,99 +133,63 @@
<script> <script>
export default { export default {
data() {
return {
favourites: []
}
},
props: { props: {
hash: Object hash: Object
}, },
computed: {
sentCertNotExpired() {
return this.hash.sent_certifications.filter((el) => !el.expired)
}
},
methods: { methods: {
toggleFavourite() { updateCurrentHash(e) {
let $this = this if (this.$parent.registeredAccount.uid == this.hash.uid) {
this.$parent.registeredAccount = { hash: "", uid: "" }
$("#favori").tooltip({
title: function () {
return $this.isFavorite
? $this.$t("favoris.supprime")
: $this.$t("favoris.enregistre")
},
html: true,
trigger: "manual"
})
$("#favori").tooltip("show")
setTimeout(() => {
$("#favori").tooltip("hide")
}, 1000)
if (!this.isFavorite) {
this.favourites.push(this.hash.uid)
} else { } else {
this.favourites.splice(this.favourites.indexOf(this.hash.uid), 1) this.$parent.registeredAccount = {
hash: this.hash.hash,
uid: this.hash.uid
}
if (this.$favourites.list.includes(this.hash.uid)) {
this.$favourites.toggleFavourite(this.hash.uid, e)
}
} }
localStorage.favourites = JSON.stringify(this.favourites)
}
},
computed: {
isFavorite() {
this.favourites = localStorage.favourites
? JSON.parse(localStorage.favourites)
: []
return this.favourites.includes(this.hash.uid)
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
#favori { .btn.iam:not(.btn-warning) {
top: 1.25rem; z-index: 10;
left: 1.25rem; animation: zoom-in-zoom-out 1s ease infinite;
background-color: var(--light); }
background-size: 75%;
background-repeat: no-repeat;
background-position: center;
width: 50px;
height: 50px;
&.add {
background-image: url("~/assets/img/favori_add.png");
}
&.remove { @keyframes zoom-in-zoom-out {
background-image: url("~/assets/img/favori_remove.png"); 50% {
opacity: 0.3;
} }
} }
.member { .member {
h2 {
min-width: 0;
}
.table { .table {
text-align: center; text-align: center;
width: auto; width: auto;
margin: auto; margin: auto;
tr {
display: flex;
flex-direction: column;
}
} }
} }
@media (min-width: 576px) { @media (min-width: 576px) {
.member { .member .table {
.table { th {
tr { text-align: right;
display: table-row; }
} td {
th { text-align: left;
text-align: right;
}
td {
text-align: left;
}
} }
} }
} }
......
<template>
<div class="text-muted">
<p id="filterStatutTitle">{{ $t("filter.statut") }}&nbsp;:</p>
<ul class="p-0 m-0" aria-labelledby="filterStatutTitle" role="group">
<li
class="form-check form-check-inline mb-3"
v-if="type != 'certificateurs'">
<input
class="btn-check"
v-model="checkedStatus"
type="checkbox"
:id="'check-newcomer-' + _uid"
value="NEWCOMER"
@change="fixColumns" />
<label class="btn btn-outline-info" :for="'check-newcomer-' + _uid">{{
$t("statut.newcomer")
}}</label>
</li>
<li class="form-check form-check-inline mb-3">
<input
class="btn-check"
v-model="checkedStatus"
type="checkbox"
:id="'check-member-' + _uid"
value="MEMBER"
@change="fixColumns" />
<label class="btn btn-outline-success" :for="'check-member-' + _uid">{{
$t("statut.member")
}}</label>
</li>
<li class="form-check form-check-inline mb-3">
<input
class="btn-check"
v-model="checkedStatus"
type="checkbox"
:id="'check-renew-' + _uid"
value="RENEW"
@change="fixColumns" />
<label class="btn btn-outline-warning" :for="'check-renew-' + _uid">{{
$t("statut.renew")
}}</label>
</li>
<li class="form-check form-check-inline mb-3">
<input
class="btn-check"
v-model="checkedStatus"
type="checkbox"
:id="'check-missing-' + _uid"
value="MISSING"
@change="fixColumns" />
<label class="btn btn-outline-danger" :for="'check-missing-' + _uid">{{
$t("statut.missing")
}}</label>
</li>
<li class="form-check form-check-inline mb-3" v-if="type == 'favoris'">
<input
class="btn-check"
v-model="checkedStatus"
type="checkbox"
:id="'check-revoked-' + _uid"
value="REVOKED"
@change="fixColumns" />
<label
class="btn btn-outline-secondary"
:for="'check-revoked-' + _uid"
>{{ $t("statut.revoked") }}</label
>
</li>
</ul>
<div v-if="type != 'favoris'">
<p>{{ $t("filter.certif") }}&nbsp;:</p>
<input
class="btn-check"
v-model="certifStatus"
type="radio"
:id="'radio-current-' + _uid"
value="current"
autocomplete="off" />
<label
class="btn btn-outline-success me-3 mb-3"
:for="'radio-current-' + _uid">
{{ $t("certification.title") + " " + $t("certification.encours") }}
</label>
<input
class="btn-check"
v-model="certifStatus"
type="radio"
:id="'radio-outdated-' + _uid"
value="outdated"
autocomplete="off" />
<label
class="btn btn-outline-danger mb-3"
:for="'radio-outdated-' + _uid">
{{ $t("certification.title") + " " + $t("certification.perimees") }}
</label>
</div>
</div>
</template>
<script>
export default {
data() {
return {
checkedStatus: ["NEWCOMER", "MEMBER", "RENEW", "MISSING", ""],
certifStatus: "current"
}
},
props: {
type: {
type: String,
default: ""
}
},
methods: {
fixColumns(e) {
setTimeout(() => {
this.$favourites.fixColumns()
}, 5)
}
},
mounted() {
this.$emit("update:selectedStatus", this.checkedStatus)
this.$emit("update:selectedCertifStatus", this.certifStatus)
},
watch: {
checkedStatus(n, o) {
this.$emit("update:selectedStatus", n)
},
certifStatus(n, o) {
this.$emit("update:selectedCertifStatus", n)
}
}
}
</script>
<template> <template>
<div class="table-responsive"> <div class="table-responsive pb-3">
<table class="table table-striped table-hover"> <table
<thead class="thead-light sortable" v-if="displayHead"> class="table table-striped table-hover table-fixed sortable border text-center">
<thead class="thead-light">
<tr> <tr>
<th class="position-relative" scope="col" @click="sort('uid')"> <th class="p-0" scope="col">
UID
<BtnSort <BtnSort
fieldName="uid" fieldName="uid"
:tableName="id"
title="UID"
:currentSort="currentSort" :currentSort="currentSort"
:currentSortDir="currentSortDir" /> :currentSortDir="currentSortDir" />
</th> </th>
<th <th
scope="col" scope="col"
class="d-none d-md-table-cell position-relative" class="d-none d-lg-table-cell p-0"
@click="sort('pubkey')" v-if="id != 'default'">
v-if="displayPubkey">
{{ $t("cle.publique.title") }}
<BtnSort <BtnSort
fieldName="pubkey" fieldName="statut"
:tableName="id"
:title="$t('statut.title')"
:currentSort="currentSort" :currentSort="currentSort"
:currentSortDir="currentSortDir" /> :currentSortDir="currentSortDir" />
</th> </th>
<th <th
scope="col" scope="col"
class="d-none d-sm-table-cell position-relative" class="td-quality d-none d-lg-table-cell p-0"
@click="sort('date_sortie')" v-if="
v-if="displayDate"> ['favoris', 'search', 'certificateurs', 'certifies'].includes(id)
{{ $t("membre.datelimpertestatut") }} ">
<BtnSort <BtnSort
fieldName="date_sortie" fieldName="quality"
:tableName="id"
:title="$t('membre.qualite.title')"
:currentSort="currentSort" :currentSort="currentSort"
:currentSortDir="currentSortDir" /> :currentSortDir="currentSortDir" />
</th> </th>
<th
scope="col"
class="d-none d-xl-table-cell p-0"
v-if="
['favoris', 'search', 'certificateurs', 'certifies'].includes(id)
">
<BtnSort
fieldName="dispo"
:tableName="id"
:title="$t('membre.disponibilite')"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th
scope="col"
class="td-date d-none d-sm-table-cell p-0"
v-if="
[
'adhesion',
'favoris',
'search',
'certificateurs',
'certifies'
].includes(id)
">
<BtnSort
fieldName="date_membership"
:tableName="id"
:title="
['certif', 'adhesion'].includes(id)
? $t('date')
: $t('membre.datelimpertestatut')
"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th
scope="col"
class="td-date d-none p-0"
:class="{
'd-sm-table-cell': id == 'certif',
'd-md-table-cell': id != 'certif'
}"
v-if="
[
'certif',
'favoris',
'search',
'certificateurs',
'certifies'
].includes(id)
">
<BtnSort
fieldName="date_certs"
:tableName="id"
:title="
['certif', 'adhesion'].includes(id)
? $t('date')
: $t('membre.datemanquecertifs.title')
"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th v-if="id == 'favoris'" style="width: 60px"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
v-for="member in membersSorted" v-for="member in membersSorted"
:key="member.uid" :key="member.uid"
@click="redirect(member.hash)"> tabindex="0"
<th scope="row"> :title="$t('membre.voirinfos')"
<span v-if="$isFavourite(member.uid)">&nbsp;</span>{{ member.uid }} @click="redirect(member.hash)"
<BadgeDanger @keyup.enter="redirect(member.hash)">
:limitDate=" <td>
Math.min(member.received_certifications.limit, member.limitDate) <div class="d-flex">
" <div
:memberStatus="member.status" /> class="d-flex flex-column align-items-center justify-content-evenly flex-grow-1 mw-100">
<div class="d-flex justify-content-center mw-100">
<span v-if="$favourites.list.includes(member.uid)"
>&nbsp;</span
>
<div class="text-truncate">{{ member.uid }}</div>
&nbsp;
<BadgeDanger
style="width: 1.2rem"
:limitDate="member.certsLimit"
:memberStatus="member.status" />
</div>
<div class="text-muted small">
{{ member.pubkey.substring(0, 10) }}
</div>
<div
v-if="['adhesion', 'certif'].includes(id)"
class="d-sm-none">
<BadgeDate :date="getDate(member)" />
</div>
</div>
<div
class="w-50 d-flex flex-column align-items-center justify-content-evenly gap-1 d-lg-none"
v-if="
['favoris', 'search', 'certificateurs', 'certifies'].includes(
id
)
">
<BadgeStatus :membre="member" class="mw-100 text-truncate" />
<BadgeQuality :quality="member.quality.ratio" />
<BadgeDispo
:isDispo="member.minDatePassed"
:dateDispo="member.minDate"
:certifs="member.sent_certifications"
class="mw-100 text-truncate" />
</div>
</div>
</td>
<td class="d-none d-lg-table-cell" v-if="id != 'default'">
<BadgeStatus :membre="member" /> <BadgeStatus :membre="member" />
</th>
<td class="d-none d-md-table-cell" v-if="displayPubkey">
{{ member.pubkey.substring(0, 10) }}
</td> </td>
<td class="d-none d-sm-table-cell" v-if="displayDate"> <td
<BadgeDate class="d-none d-lg-table-cell"
:date=" v-if="
Math.min(member.limitDate, member.received_certifications.limit) ['favoris', 'search', 'certificateurs', 'certifies'].includes(id)
" ">
styleDate="long" <BadgeQuality :quality="member.quality.ratio" />
class="d-block text-center" /> </td>
<td
class="d-none d-xl-table-cell"
v-if="
['favoris', 'search', 'certificateurs', 'certifies'].includes(id)
">
<BadgeDispo
:isDispo="member.minDatePassed"
:dateDispo="member.minDate"
:certifs="member.sent_certifications" />
</td>
<td
class="d-none d-sm-table-cell"
v-if="
[
'adhesion',
'favoris',
'search',
'certificateurs',
'certifies'
].includes(id)
">
<BadgeDate :date="member.limitDate" />
</td>
<td
class="d-none"
:class="{
'd-sm-table-cell': id == 'certif',
'd-md-table-cell': id != 'certif'
}"
v-if="
[
'certif',
'favoris',
'search',
'certificateurs',
'certifies'
].includes(id)
">
<BadgeDate :date="member.certsLimit" />
</td>
<td class="py-1" v-if="id == 'favoris'" style="width: 60px">
<button
class="btn btn-danger"
v-if="$favourites.list.includes(member.uid)"
@click="$favourites.toggleFavourite(member.uid, $event)"
:title="$t('suivis.supprimer')">
<solid-trash-icon class="icon" aria-hidden="true" />
</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
...@@ -69,8 +220,8 @@ ...@@ -69,8 +220,8 @@
export default { export default {
data() { data() {
return { return {
currentSort: "uid", currentSort: this.defaultSort,
currentSortDir: "asc" currentSortDir: this.defaultSortDir
} }
}, },
props: { props: {
...@@ -78,64 +229,66 @@ export default { ...@@ -78,64 +229,66 @@ export default {
type: Array, type: Array,
required: true required: true
}, },
id: {
displayHead: { type: String,
type: Boolean, required: true
default: true
}, },
displayPubkey: { defaultSortDir: {
type: Boolean, type: String,
default: true default: "asc"
}, },
displayDate: { defaultSort: {
type: Boolean, type: String,
default: true default: "uid"
} }
}, },
methods: { methods: {
redirect(hash) { redirect(hash) {
this.$router.push( this.$router.push(
this.localePath({ name: "membres-hash", params: { hash } }) this.localePath({ name: "membres-profil", query: { hash } })
) )
}, },
sort(s) { getDate(member) {
if (s === this.currentSort) { if (this.id == "adhesion") return member.limitDate
this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc" if (this.id == "certif") return member.certsLimit
} return Math.min(member.limitDate, member.certsLimit)
this.currentSort = s },
getOrder(a, b, order) {
if (a < b) return -1 * order
if (a > b) return 1 * order
return 0
} }
}, },
computed: { computed: {
membersSorted() { membersSorted() {
return this.members.slice().sort((a, b) => { return this.members.sort((a, b) => {
let modifier = this.currentSortDir === "desc" ? -1 : 1 let modifier = this.currentSortDir === "desc" ? -1 : 1
if (this.currentSort == "uid") { if (this.currentSort == "uid") {
if (a["uid"].toLowerCase() < b["uid"].toLowerCase()) return this.getOrder(
return -1 * modifier a["uid"].toLowerCase(),
if (a["uid"].toLowerCase() > b["uid"].toLowerCase()) b["uid"].toLowerCase(),
return 1 * modifier modifier
} else if (this.currentSort == "pubkey") {
if (a["pubkey"].toLowerCase() < b["pubkey"].toLowerCase())
return -1 * modifier
if (a["pubkey"].toLowerCase() > b["pubkey"].toLowerCase())
return 1 * modifier
} else if (this.currentSort == "date_sortie") {
if (
Math.min(a["limitDate"], a["received_certifications"]["limit"]) <
Math.min(b["limitDate"], b["received_certifications"]["limit"])
)
return -1 * modifier
if (
Math.min(a["limitDate"], a["received_certifications"]["limit"]) >
Math.min(b["limitDate"], b["received_certifications"]["limit"])
) )
return 1 * modifier } else if (this.currentSort == "dispo") {
if (a.minDate == null) return 1 * modifier
if (b.minDate == null) return -1 * modifier
return this.getOrder(a.minDate, b.minDate, modifier)
} else if (this.currentSort == "quality") {
return this.getOrder(a.quality.ratio, b.quality.ratio, modifier)
} else if (this.currentSort == "statut") {
return this.getOrder(a["status"], b["status"], modifier)
} else if (this.currentSort == "date_membership") {
return this.getOrder(a["limitDate"], b["limitDate"], modifier)
} else if (this.currentSort == "date_certs") {
return this.getOrder(a["certsLimit"], b["certsLimit"], modifier)
} }
return 0 return 0
}) })
} }
},
mounted() {
this.$favourites.fixColumns()
} }
} }
</script> </script>
<template>
<tr class="help" v-tooltip="{ title: tooltip, placement: 'right' }">
<th scope="row" class="fw-normal">{{ title }}&nbsp;:</th>
<td :class="classClor">
<slot></slot>
</td>
</tr>
</template>
<script>
export default {
props: {
title: String,
tooltip: {
type: String,
default: ""
},
classClor: [String, Object]
}
}
</script>
<style lang="scss" scoped>
tr {
display: flex;
flex-direction: column;
user-select: none;
}
@media (min-width: 576px) {
tr {
display: table-row;
}
}
</style>
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<header class="header position-fixed"> <header class="header position-fixed">
<div class="position-relative"> <div class="position-relative">
<button <button
class="toggle btn border-secondary position-absolute p-1 ml-4" :title="isOpen ? $t('aria.closemenu') : $t('aria.openmenu')"
class="toggle btn border-secondary position-absolute p-1 ms-4"
@click="toggleMenu"> @click="toggleMenu">
<span></span> <span></span>
</button> </button>
...@@ -47,7 +48,7 @@ $btn-width: 50px; ...@@ -47,7 +48,7 @@ $btn-width: 50px;
--menu-width: 0px; --menu-width: 0px;
width: 100%; width: 100%;
z-index: 100; z-index: 100;
background: var(--background-color-primary); background: var(--bg-primary-color);
transition: width 0.5s ease-in-out; transition: width 0.5s ease-in-out;
.open & { .open & {
...@@ -66,10 +67,10 @@ nav.breadcrumb-wrapper { ...@@ -66,10 +67,10 @@ nav.breadcrumb-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
background: var(--background-color-secondary); background: var(--bg-menu-color);
a { a {
color: var(--text-primary-color); color: var(--txt-primary-color);
} }
.breadcrumb-item.active { .breadcrumb-item.active {
...@@ -87,7 +88,7 @@ nav.breadcrumb-wrapper { ...@@ -87,7 +88,7 @@ nav.breadcrumb-wrapper {
display: block; display: block;
height: 4px; height: 4px;
width: 0.8 * $btn-width; width: 0.8 * $btn-width;
background: var(--text-primary-color); background: var(--txt-primary-color);
content: ""; content: "";
position: absolute; position: absolute;
transition-property: transform; transition-property: transform;
...@@ -138,59 +139,30 @@ nav.breadcrumb-wrapper { ...@@ -138,59 +139,30 @@ nav.breadcrumb-wrapper {
} }
.menu { .menu {
background: var(--background-color-primary);
width: var(--menu-size); width: var(--menu-size);
top: 0; top: 0;
z-index: 1200; z-index: 1200;
height: 100vh; height: 100%;
padding: 1.1rem 0.5rem;
overflow-y: scroll;
scrollbar-color: #6969dd #e0e0e0;
scrollbar-width: thin;
transition: left 0.5s ease-in-out; transition: left 0.5s ease-in-out;
left: -400px; left: -400px;
h1 { h1 {
color: var(--text-primary-color); color: var(--txt-primary-color);
}
.list-group-item {
&-action:not(.active) {
background: transparent;
&:hover {
background: rgba(0, 0, 255, 0.075);
color: var(--text-primary-color);
}
}
div {
transition: left 0.3s ease-in-out;
left: 0;
&::before {
content: "›";
position: relative;
left: -0.5em;
}
&:hover {
left: 0.5em;
}
}
} }
.open & { .open & {
left: 0; left: 0;
} }
.close { .btn-close {
--size: 50px; --size: 30px;
width: var(--size); width: var(--size);
height: var(--size); height: var(--size);
top: 0.8rem; top: 0.8rem;
right: 0; right: 0.8rem;
font-size: 2rem; .dark-theme & {
filter: invert(1) grayscale(100%) brightness(200%);
}
} }
h2 { h2 {
...@@ -216,7 +188,13 @@ nav.breadcrumb-wrapper { ...@@ -216,7 +188,13 @@ nav.breadcrumb-wrapper {
} }
.logo { .logo {
max-width: 75px; &:hover {
text-decoration: none;
}
img {
max-width: 75px;
}
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
......
<template> <template>
<nav aria-label="Fil d'Ariane" class="breadcrumb-wrapper rounded"> <nav
<ol class="breadcrumb m-0 p-0"> :aria-label="$t('aria.ariane')"
class="breadcrumb-wrapper rounded border">
<ol class="breadcrumb m-0 p-0 d-none d-sm-flex">
<li <li
class="breadcrumb-item" class="breadcrumb-item"
:class="{ active: item.active }" :class="{ active: item.active }"
...@@ -14,7 +16,7 @@ ...@@ -14,7 +16,7 @@
</li> </li>
</ol> </ol>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<NavigationLanguage class="mr-3" /> <NavigationLanguage class="me-3" />
<BtnTheme /> <BtnTheme />
</div> </div>
</nav> </nav>
......
<template> <template>
<div> <div>
<select <select
class="form-control" class="form-select"
:aria-label="$t('lang')"
@change="saveLocale($event)" @change="saveLocale($event)"
v-model="$i18n.locale"> v-model="activeLang">
<option v-for="lang in $i18n.locales" :key="lang.code" :value="lang.code"> <option
v-for="lang in $i18n.locales"
:key="lang.code"
:value="lang.code"
:selected="lang.code === activeLang">
{{ lang.name }} {{ lang.name }}
</option> </option>
</select> </select>
...@@ -13,12 +18,22 @@ ...@@ -13,12 +18,22 @@
<script> <script>
export default { export default {
data() {
return {
activeLang: "en"
}
},
methods: { methods: {
saveLocale(e) { saveLocale(e) {
this.$i18n.locale = e.target.value // this.$i18n.locale = e.target.value
this.$i18n.setLocaleCookie(e.target.value) this.$i18n.setLocale(e.target.value)
this.$router.push(this.switchLocalePath(e.target.value)) // this.$i18n.setLocaleCookie(e.target.value)
// this.$router.push(this.switchLocalePath(e.target.value))
// this.$router.replace(this.switchLocalePath(e))
} }
},
mounted() {
this.activeLang = this.$i18n.locale
} }
} }
</script> </script>
...@@ -23,6 +23,6 @@ export default { ...@@ -23,6 +23,6 @@ export default {
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
--color: #391855; --color: #391855;
color: var(--text-primary-color); color: var(--txt-primary-color);
} }
</style> </style>
<template> <template>
<div class="mb-4"> <div class="mb-4">
<h2 class="small text-muted text-uppercase ml-4 mb-2 pb-3 border-bottom"> <h2 class="small text-muted text-uppercase ms-5 mb-0 pb-2">
{{ $t(menu.title) }} {{ $t(menu.title) }}
</h2> </h2>
<div class="nav navbar-nav list-group list-group-flush"> <div class="nav navbar-nav list-group list-group-flush">
<NuxtLink <NuxtLink
class="list-group-item list-group-item-action p-0 pl-3" class="list-group-item list-group-item-action p-0 ps-3 border-0"
:to="localePath(item.path)" :to="localePath(item.path)"
v-for="item in menu.items" v-for="item in menu.items"
:key="item.path" :key="item.path"
@click.native="toggleMenu()"> @click.native="toggleMenu()">
<div class="position-relative py-3">{{ $t(item.title) }}</div> <div class="menu-item position-relative py-2">
<component
aria-hidden="true"
:is="'solid-' + item.icon + '-icon'"
class="icon" />&nbsp;{{ $t(item.title) }}
</div>
</NuxtLink> </NuxtLink>
</div> </div>
</div> </div>
...@@ -30,3 +35,9 @@ export default { ...@@ -30,3 +35,9 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
h2 {
letter-spacing: 0.02rem;
}
</style>