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>
<small>
<span
class="badge"
:class="{
'badge-success': quality >= 80,
'badge-warning': quality < 80
}">
{{ Math.round(quality * 100) / 100 }}
</span>
</small>
<span
class="badge"
:class="{
'bg-success': quality >= 80,
'bg-warning': quality < 80
}">
{{ Math.round(quality * 100) / 100 }}
</span>
</template>
<script>
......
<template>
<small>
<span class="badge" :class="this.displayStatus(membre).class">
{{ this.displayStatus(membre).str }}
</span>
</small>
<span class="badge" :class="displayStatus(membre).class">
{{ this.displayStatus(membre).str }}
</span>
</template>
<script>
......@@ -18,22 +16,20 @@ export default {
displayStatus: function (member) {
switch (member.status) {
case "NEWCOMER":
return { str: this.$i18n.t("statut.newcomer"), class: "badge-info" }
return { str: this.$i18n.t("statut.newcomer"), class: "bg-info" }
case "MISSING":
return { str: this.$i18n.t("statut.missing"), class: "badge-danger" }
return { str: this.$i18n.t("statut.missing"), class: "bg-danger" }
case "MEMBER":
if (this.$options.filters.dateStatus(member.limitDate) == "warning") {
return { str: this.$i18n.t("statut.renew"), class: "badge-warning" }
} else {
return {
str: this.$i18n.t("statut.member"),
class: "badge-success"
}
return {
str: this.$i18n.t("statut.member"),
class: "bg-success"
}
case "RENEW":
return { str: this.$i18n.t("statut.renew"), class: "bg-warning" }
case "REVOKED":
return {
str: this.$i18n.t("statut.revoked"),
class: "badge-secondary"
class: "bg-secondary"
}
default:
return "N/A"
......
<template>
<div class="clipboard input-group input-group-sm mb-3 mx-auto">
<div class="input-group-prepend">
<button
id="btncopy"
class="btn btn-outline-secondary px-4 py-1"
type="button"
@click="copyText"></button>
</div>
<div class="clipboard input-group mb-3 mx-auto">
<button
id="btncopy"
class="btn btn-secondary px-4 py-1"
type="button"
v-tooltip-click="$t('copie') + ' !'"
@click="copyText">
<solid-share-icon class="icon" aria-hidden="true" />
<span class="visually-hidden">{{ $t("aria.clipboard") }}</span>
</button>
<input
type="text"
class="form-control text-truncate"
......@@ -26,14 +28,6 @@ export default {
methods: {
copyText() {
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 {
.clipboard {
max-width: 500px;
button {
background: url("~assets/img/clipboard.svg") no-repeat 50% 50% #fff;
background-size: 40%;
input {
user-select: none;
}
}
</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>
<div class="d-inline-block position-absolute ml-2">
<div
class="up"
:class="{
sorted: currentSortDir == 'desc' && currentSort == fieldName,
invisible: currentSortDir == 'asc' && currentSort == fieldName
}">
</div>
<div
class="down"
:class="{
sorted: currentSortDir == 'asc' && currentSort == fieldName,
invisible: currentSortDir == 'desc' && currentSort == fieldName
}">
</div>
<div
class="btn-sort pointer px-2"
tabindex="0"
:title="$t('tri.action')"
@click="sort(fieldName)"
@keyup.enter="sort(fieldName)">
<span class="text-truncate">{{ title }}</span>
<solid-sort-ascending-icon
aria-hidden="true"
class="ms-2 icon flex-shrink-0"
v-if="currentSortDir == 'desc' && currentSort == fieldName" />
<solid-sort-descending-icon
aria-hidden="true"
class="ms-2 icon flex-shrink-0"
v-if="currentSortDir == 'asc' && currentSort == fieldName" />
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
},
fieldName: {
type: String,
required: true
},
tableName: {
type: String,
required: true
},
currentSort: {
type: String,
required: true
......@@ -37,8 +44,78 @@ export default {
}
}
},
created() {
console.log("created")
methods: {
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>
<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 @@
class="switch-checkbox" />
<label
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">
<span class="visually-hidden">
{{
this.userTheme === "light-theme"
? $t("aria.themedark")
: $t("aria.themelight")
}}
</span>
<span>🌙</span>
<span>☀️</span>
<div
......@@ -62,9 +69,9 @@ export default {
}
.switch-label {
background: var(--text-primary-color);
--element-size: 4rem;
background: var(--txt-primary-color);
border-radius: var(--element-size);
cursor: pointer;
font-size: calc(var(--element-size) * 0.3);
height: calc(var(--element-size) * 0.35);
padding: calc(var(--element-size) * 0.1);
......@@ -74,7 +81,7 @@ export default {
}
.switch-toggle {
background-color: var(--background-color-primary);
background-color: var(--bg-primary-color);
top: calc(var(--element-size) * 0.07);
left: calc(var(--element-size) * 0.07);
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>
<div class="table-responsive">
<table
class="table table-striped table-hover"
v-if="certifsPending.length > 0">
<tbody>
<tr
v-for="certif in certifsPending"
:key="certif.uid + certif.expires_on"
@click="
$router.push(
localePath({
name: 'membres-hash',
params: { hash: certif.hash }
})
)
">
<th scope="row" class="py-1">
<div>
{{ certif.uid }}
<BadgeDanger
:limitDate="certif.received_certifications.limit"
:memberStatus="certif.status" />
<BadgeQuality
:quality="certif.quality.ratio"
v-if="certif.status != 'REVOKED'" />
</div>
<div>
<div class="certifList" v-if="certifs.length > 0">
<button
:title="
isOpen ? $t('certification.masquer') : $t('certification.afficher')
"
@click="isOpen = !isOpen"
class="btn w-100 m-auto d-block rounded-0"
:class="btnClass"
type="button"
data-bs-toggle="collapse"
:data-bs-target="'#' + collapseId"
aria-expanded="false"
:aria-controls="collapseId">
<span v-if="!isOpen">
<solid-eye-icon class="icon" aria-hidden="true" />
</span>
<span v-else>
<solid-eye-off-icon class="icon" aria-hidden="true" />
</span>
<h4 class="d-inline align-middle">
{{ title }}&nbsp;&nbsp;
<span class="badge rounded-pill bg-white opacity-75 text-dark">{{
certifs.length
}}</span>
</h4>
</button>
<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" />
<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
:quality="certif.quality.ratio"
v-if="certif.status != 'REVOKED'" />
</div>
<div>
<BadgeStatus :membre="certif" />
v-if="!['REVOKED', 'NEWCOMER'].includes(certif.status)" />
<BadgeDispo
:isDispo="certif.minDatePassed"
:dateDispo="certif.minDate"
:certifs="certif.sent_certifications"
v-if="certif.status == 'MEMBER'" />
</div>
</th>
<td class="text-right py-1">
<BadgeDate :date="certif.expires_on" styleDate="long" />
</td>
</tr>
</tbody>
</table>
</td>
<td class="p-0 text-center col-4">
<div class="d-inline-flex flex-column gap-1">
<BadgeDate :date="certif.expires_on" />
<span
class="badge bg-secondary text-truncate d-block"
v-if="certif.pending"
>{{ $t("traitement") }}</span
>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
......@@ -106,71 +121,110 @@
export default {
data() {
return {
search: "",
currentSort: "expires_on",
currentSortDir: "asc"
currentSortDir: "asc",
isOpen: this.openDefault
}
},
props: {
certifs: Array,
type: {
type: String,
required: true,
validator: function (value) {
const types = ["received", "sent"]
return types.indexOf(value) !== -1
}
}
},
methods: {
sort(s) {
if (s === this.currentSort) {
this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc"
}
this.currentSort = s
certifStatus: String,
collapseId: String,
title: String,
openDefault: {
type: Boolean,
default: false
}
},
computed: {
certifsFiltrees() {
return this.certifs.filter((row, index) => {
return (
this.search == "" ||
row.uid.toLowerCase().includes(this.search.toLowerCase())
)
})
},
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) => {
let modifier = this.currentSortDir === "desc" ? -1 : 1
let sens = this.type == "received" ? "from" : "to"
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
} else {
if (a[sens]["uid"].toLowerCase() < b[sens]["uid"].toLowerCase())
if (a["uid"].toLowerCase() < b["uid"].toLowerCase())
return -1 * modifier
if (a[sens]["uid"].toLowerCase() > b[sens]["uid"].toLowerCase())
if (a["uid"].toLowerCase() > b["uid"].toLowerCase())
return 1 * modifier
}
return 0
})
.filter((el) => el.pending == false)
.map((certif) => ({
...certif,
...(this.type === "received" ? certif.from : certif.to)
}))
},
certifsPending() {
return this.certifs
.slice()
.sort((a, b) => a.expires_on - b.expires_on)
.filter((el) => el.pending == true)
.map((certif) => ({
...certif,
...(this.type === "received" ? certif.from : certif.to)
}))
collapseClass() {
if (this.collapseId.includes("entraitement")) return "bg-secondary"
if (this.collapseId.includes("perimees")) return "bg-warning"
if (this.collapseId == "sent-encours") return "bg-info"
return {
"bg-success": this.certifStatus == "success",
"bg-warning": this.certifStatus == "warning",
"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>
<style lang="scss" scoped>
thead th:last-child {
padding-right: 1.5rem;
text-align: right;
<style lang="scss">
.certifList {
.table-responsive tbody {
max-height: 456px;
}
tbody tr {
height: 80px;
}
@media (min-width: 576px) {
button {
font-size: 1.3rem;
}
tbody tr {
height: initial;
}
}
}
</style>
<template>
<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">
<h2 class="card-title text-center mb-4">
{{ hash.uid }}
<BadgeStatus :membre="hash" />
</h2>
<BtnClipboard :textContent="this.hash.pubkey" />
<div class="d-flex align-items-center justify-content-between mb-4">
<h2
class="h1 card-title text-center d-flex align-items-center flex-column m-0">
<span class="text-truncate d-inline-block mw-100">
{{ 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">
<table
class="table table-sm table-borderless"
v-if="hash.status != 'REVOKED'">
<tbody>
<tr v-if="hash.status == 'MEMBER'">
<th scope="row">{{ $t("membre.referent.title") }}&nbsp;:</th>
<td
:class="{
'list-group-item-success': hash.sentry,
'list-group-item-warning': !hash.sentry
}">
{{ hash.sentry ? $t("oui") : $t("non") }}
</td>
</tr>
<tr v-if="hash.status != 'NEWCOMER'">
<th scope="row">{{ $t("membre.qualite.title") }}&nbsp;:</th>
<td
:class="{
'list-group-item-success': hash.quality.ratio >= 80,
'list-group-item-warning': hash.quality.ratio < 80
}">
{{ Math.round(hash.quality.ratio * 100) / 100 }}
</td>
</tr>
<tr>
<th scope="row">{{ $t("membre.distance.title") }}&nbsp;:</th>
<td
:class="{
'list-group-item-success':
hash.status != 'NEWCOMER'
? hash.distance.dist_ok
: hash.distanceE.dist_ok,
'list-group-item-danger':
hash.status != 'NEWCOMER'
? !hash.distance.dist_ok
: !hash.distanceE.dist_ok
}">
{{
hash.status != "NEWCOMER"
? Math.round(hash.distance.value.ratio * 100) / 100
: Math.round(hash.distanceE.value.ratio * 100) / 100
}}
</td>
</tr>
<tr>
<th scope="row">
{{
hash.status != "MISSING"
? $t("membre.datelimadhesion")
: $t("membre.datelimrevoc")
}}&nbsp;:
</th>
<td
:class="
hash.status != 'MISSING'
? 'list-group-item-' +
$options.filters.dateStatus(hash.limitDate)
: 'list-group-item-danger'
">
{{ $d(new Date(hash.limitDate * 1000), "long") }}
</td>
</tr>
<tr v-if="hash.status == 'MEMBER'">
<th scope="row">{{ $t("membre.datemanquecertifs") }}&nbsp;:</th>
<td
:class="
'list-group-item-' +
$options.filters.dateStatus(
hash.received_certifications.limit
)
">
{{
$d(
new Date(hash.received_certifications.limit * 1000),
"long"
)
}}
</td>
</tr>
<tr v-if="hash.status == 'MEMBER'">
<th scope="row">{{ $t("membre.dispocertif") }}&nbsp;:</th>
<td
:class="{
'list-group-item-success': hash.minDatePassed,
'list-group-item-danger': !hash.minDatePassed
}">
{{ hash.minDatePassed ? $t("oui") : $t("non") }}
<small v-if="!hash.minDatePassed"
>( > {{ $d(new Date(hash.minDate * 1000), "long") }} )</small
>
</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>
<MemberProp
v-if="hash.status == 'MEMBER'"
:title="$t('membre.referent.title')"
:tooltip="$t('membre.referent.desc')"
:classClor="{
'table-success': hash.sentry,
'table-warning': !hash.sentry
}">
{{ hash.sentry ? $t("oui") : $t("non") }}
</MemberProp>
<MemberProp
v-if="hash.status != 'NEWCOMER'"
:title="$t('membre.qualite.title')"
:tooltip="$t('membre.qualite.desc')"
:classClor="{
'table-success': hash.quality.ratio >= 80,
'table-warning': hash.quality.ratio < 80
}">
{{ Math.round(hash.quality.ratio * 100) / 100 }}</MemberProp
>
<MemberProp
:title="$t('membre.distance.title')"
:tooltip="$t('membre.distance.desc')"
:classClor="{
'table-success':
hash.status != 'NEWCOMER'
? hash.distanceE.dist_ok
: hash.distanceE.dist_ok,
'table-danger':
hash.status != 'NEWCOMER'
? !hash.distanceE.dist_ok
: !hash.distanceE.dist_ok
}">
{{ Math.round(hash.distanceE.value.ratio * 100) / 100 }}
</MemberProp>
<MemberProp
:title="
hash.status != 'MISSING'
? $t('membre.datelimadhesion.title')
: $t('membre.datelimrevoc.title')
"
:tooltip="
hash.status != 'MISSING'
? $t('membre.datelimadhesion.desc')
: $t('membre.datelimrevoc.desc')
"
:classClor="
hash.status != 'MISSING'
? 'table-' + $options.filters.dateStatus(hash.limitDate)
: 'table-danger'
">
{{ $d(new Date(hash.limitDate * 1000)).toLocaleString() }}
</MemberProp>
<MemberProp
v-if="hash.status == 'MEMBER'"
:title="$t('membre.datemanquecertifs.title')"
:tooltip="$t('membre.datemanquecertifs.desc')"
:classClor="
'table-' + $options.filters.dateStatus(hash.certsLimit)
">
{{ $d(new Date(hash.certsLimit * 1000)).toLocaleString() }}
</MemberProp>
<MemberProp
v-if="hash.status == 'MEMBER'"
:title="$t('membre.dispocertif.title')"
:tooltip="$t('membre.dispocertif.desc')"
:classClor="{
'table-success': hash.minDatePassed,
'table-danger': !hash.minDatePassed
}">
{{ hash.minDatePassed ? $t("oui") : $t("non") }}
<small v-if="!hash.minDatePassed"
>( > {{ $d(new Date(hash.minDate * 1000)).toLocaleString() }} )</small
>
</MemberProp>
<MemberProp
v-if="hash.status == 'MEMBER'"
:title="$t('membre.nb_certifs.title')"
:tooltip="$t('membre.nb_certifs.desc')"
:classClor="{
'table-success': sentCertNotExpired.length <= 80,
'table-warning': sentCertNotExpired.length > 80,
'table-danger': sentCertNotExpired.length > 90
}">
{{ 100 - sentCertNotExpired.length }}
</MemberProp>
</tbody>
</table>
</div>
......@@ -129,99 +133,63 @@
<script>
export default {
data() {
return {
favourites: []
}
},
props: {
hash: Object
},
computed: {
sentCertNotExpired() {
return this.hash.sent_certifications.filter((el) => !el.expired)
}
},
methods: {
toggleFavourite() {
let $this = this
$("#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)
updateCurrentHash(e) {
if (this.$parent.registeredAccount.uid == this.hash.uid) {
this.$parent.registeredAccount = { hash: "", uid: "" }
} 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>
<style lang="scss">
#favori {
top: 1.25rem;
left: 1.25rem;
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");
}
.btn.iam:not(.btn-warning) {
z-index: 10;
animation: zoom-in-zoom-out 1s ease infinite;
}
&.remove {
background-image: url("~/assets/img/favori_remove.png");
@keyframes zoom-in-zoom-out {
50% {
opacity: 0.3;
}
}
.member {
h2 {
min-width: 0;
}
.table {
text-align: center;
width: auto;
margin: auto;
tr {
display: flex;
flex-direction: column;
}
}
}
@media (min-width: 576px) {
.member {
.table {
tr {
display: table-row;
}
th {
text-align: right;
}
td {
text-align: left;
}
.member .table {
th {
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>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="thead-light sortable" v-if="displayHead">
<div class="table-responsive pb-3">
<table
class="table table-striped table-hover table-fixed sortable border text-center">
<thead class="thead-light">
<tr>
<th class="position-relative" scope="col" @click="sort('uid')">
UID
<th class="p-0" scope="col">
<BtnSort
fieldName="uid"
:tableName="id"
title="UID"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th
scope="col"
class="d-none d-md-table-cell position-relative"
@click="sort('pubkey')"
v-if="displayPubkey">
{{ $t("cle.publique.title") }}
class="d-none d-lg-table-cell p-0"
v-if="id != 'default'">
<BtnSort
fieldName="pubkey"
fieldName="statut"
:tableName="id"
:title="$t('statut.title')"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</th>
<th
scope="col"
class="d-none d-sm-table-cell position-relative"
@click="sort('date_sortie')"
v-if="displayDate">
{{ $t("membre.datelimpertestatut") }}
class="td-quality d-none d-lg-table-cell p-0"
v-if="
['favoris', 'search', 'certificateurs', 'certifies'].includes(id)
">
<BtnSort
fieldName="date_sortie"
fieldName="quality"
:tableName="id"
:title="$t('membre.qualite.title')"
:currentSort="currentSort"
:currentSortDir="currentSortDir" />
</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>
</thead>
<tbody>
<tr
v-for="member in membersSorted"
:key="member.uid"
@click="redirect(member.hash)">
<th scope="row">
<span v-if="$isFavourite(member.uid)">&nbsp;</span>{{ member.uid }}
<BadgeDanger
:limitDate="
Math.min(member.received_certifications.limit, member.limitDate)
"
:memberStatus="member.status" />
tabindex="0"
:title="$t('membre.voirinfos')"
@click="redirect(member.hash)"
@keyup.enter="redirect(member.hash)">
<td>
<div class="d-flex">
<div
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" />
</th>
<td class="d-none d-md-table-cell" v-if="displayPubkey">
{{ member.pubkey.substring(0, 10) }}
</td>
<td class="d-none d-sm-table-cell" v-if="displayDate">
<BadgeDate
:date="
Math.min(member.limitDate, member.received_certifications.limit)
"
styleDate="long"
class="d-block text-center" />
<td
class="d-none d-lg-table-cell"
v-if="
['favoris', 'search', 'certificateurs', 'certifies'].includes(id)
">
<BadgeQuality :quality="member.quality.ratio" />
</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>
</tr>
</tbody>
......@@ -69,8 +220,8 @@
export default {
data() {
return {
currentSort: "uid",
currentSortDir: "asc"
currentSort: this.defaultSort,
currentSortDir: this.defaultSortDir
}
},
props: {
......@@ -78,64 +229,66 @@ export default {
type: Array,
required: true
},
displayHead: {
type: Boolean,
default: true
id: {
type: String,
required: true
},
displayPubkey: {
type: Boolean,
default: true
defaultSortDir: {
type: String,
default: "asc"
},
displayDate: {
type: Boolean,
default: true
defaultSort: {
type: String,
default: "uid"
}
},
methods: {
redirect(hash) {
this.$router.push(
this.localePath({ name: "membres-hash", params: { hash } })
this.localePath({ name: "membres-profil", query: { hash } })
)
},
sort(s) {
if (s === this.currentSort) {
this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc"
}
this.currentSort = s
getDate(member) {
if (this.id == "adhesion") return member.limitDate
if (this.id == "certif") return member.certsLimit
return Math.min(member.limitDate, member.certsLimit)
},
getOrder(a, b, order) {
if (a < b) return -1 * order
if (a > b) return 1 * order
return 0
}
},
computed: {
membersSorted() {
return this.members.slice().sort((a, b) => {
return this.members.sort((a, b) => {
let modifier = this.currentSortDir === "desc" ? -1 : 1
if (this.currentSort == "uid") {
if (a["uid"].toLowerCase() < b["uid"].toLowerCase())
return -1 * modifier
if (a["uid"].toLowerCase() > b["uid"].toLowerCase())
return 1 * 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 this.getOrder(
a["uid"].toLowerCase(),
b["uid"].toLowerCase(),
modifier
)
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
})
}
},
mounted() {
this.$favourites.fixColumns()
}
}
</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 @@
<header class="header position-fixed">
<div class="position-relative">
<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">
<span></span>
</button>
......@@ -47,7 +48,7 @@ $btn-width: 50px;
--menu-width: 0px;
width: 100%;
z-index: 100;
background: var(--background-color-primary);
background: var(--bg-primary-color);
transition: width 0.5s ease-in-out;
.open & {
......@@ -66,10 +67,10 @@ nav.breadcrumb-wrapper {
display: flex;
flex-direction: column;
gap: 1rem;
background: var(--background-color-secondary);
background: var(--bg-menu-color);
a {
color: var(--text-primary-color);
color: var(--txt-primary-color);
}
.breadcrumb-item.active {
......@@ -87,7 +88,7 @@ nav.breadcrumb-wrapper {
display: block;
height: 4px;
width: 0.8 * $btn-width;
background: var(--text-primary-color);
background: var(--txt-primary-color);
content: "";
position: absolute;
transition-property: transform;
......@@ -138,59 +139,30 @@ nav.breadcrumb-wrapper {
}
.menu {
background: var(--background-color-primary);
width: var(--menu-size);
top: 0;
z-index: 1200;
height: 100vh;
padding: 1.1rem 0.5rem;
overflow-y: scroll;
scrollbar-color: #6969dd #e0e0e0;
scrollbar-width: thin;
height: 100%;
transition: left 0.5s ease-in-out;
left: -400px;
h1 {
color: var(--text-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;
}
}
color: var(--txt-primary-color);
}
.open & {
left: 0;
}
.close {
--size: 50px;
.btn-close {
--size: 30px;
width: var(--size);
height: var(--size);
top: 0.8rem;
right: 0;
font-size: 2rem;
right: 0.8rem;
.dark-theme & {
filter: invert(1) grayscale(100%) brightness(200%);
}
}
h2 {
......@@ -216,7 +188,13 @@ nav.breadcrumb-wrapper {
}
.logo {
max-width: 75px;
&:hover {
text-decoration: none;
}
img {
max-width: 75px;
}
}
@media (min-width: 1200px) {
......
<template>
<nav aria-label="Fil d'Ariane" class="breadcrumb-wrapper rounded">
<ol class="breadcrumb m-0 p-0">
<nav
:aria-label="$t('aria.ariane')"
class="breadcrumb-wrapper rounded border">
<ol class="breadcrumb m-0 p-0 d-none d-sm-flex">
<li
class="breadcrumb-item"
:class="{ active: item.active }"
......@@ -14,7 +16,7 @@
</li>
</ol>
<div class="d-flex justify-content-between align-items-center">
<NavigationLanguage class="mr-3" />
<NavigationLanguage class="me-3" />
<BtnTheme />
</div>
</nav>
......
<template>
<div>
<select
class="form-control"
class="form-select"
:aria-label="$t('lang')"
@change="saveLocale($event)"
v-model="$i18n.locale">
<option v-for="lang in $i18n.locales" :key="lang.code" :value="lang.code">
v-model="activeLang">
<option
v-for="lang in $i18n.locales"
:key="lang.code"
:value="lang.code"
:selected="lang.code === activeLang">
{{ lang.name }}
</option>
</select>
......@@ -13,12 +18,22 @@
<script>
export default {
data() {
return {
activeLang: "en"
}
},
methods: {
saveLocale(e) {
this.$i18n.locale = e.target.value
this.$i18n.setLocaleCookie(e.target.value)
this.$router.push(this.switchLocalePath(e.target.value))
// this.$i18n.locale = e.target.value
this.$i18n.setLocale(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>
......@@ -23,6 +23,6 @@ export default {
left: 50%;
transform: translateX(-50%);
--color: #391855;
color: var(--text-primary-color);
color: var(--txt-primary-color);
}
</style>
<template>
<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) }}
</h2>
<div class="nav navbar-nav list-group list-group-flush">
<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)"
v-for="item in menu.items"
:key="item.path"
@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>
</div>
</div>
......@@ -30,3 +35,9 @@ export default {
}
}
</script>
<style lang="scss" scoped>
h2 {
letter-spacing: 0.02rem;
}
</style>