forked from Ivasoft/geovisio-website
Compare commits
19 Commits
develop
...
feat/new-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a7dc6d07d | ||
|
|
fd8d3168a8 | ||
|
|
db29dfdc4c | ||
|
|
e27c68c46c | ||
|
|
403188eea1 | ||
|
|
b48acd231d | ||
|
|
86d2ad76cb | ||
|
|
a68a08f909 | ||
|
|
cbe3f8e7be | ||
|
|
da67295896 | ||
|
|
62acc345c2 | ||
|
|
9162a3a600 | ||
|
|
dcb9b1eb8e | ||
|
|
3e2ea47f74 | ||
|
|
4bb802acbc | ||
|
|
12ef180e90 | ||
|
|
7ef22ea09d | ||
|
|
0e9ec753da | ||
|
|
ad60792020 |
@@ -1,48 +1,56 @@
|
||||
@mixin text($size) {
|
||||
@if $size == h1 {
|
||||
font-weight: normal;
|
||||
font-size: 4rem;
|
||||
font-size: toRem(3);
|
||||
|
||||
@media (max-width: 500px) {
|
||||
font-size: 2.6rem;
|
||||
@media (max-width: toRem(50)) {
|
||||
font-size: toRem(2.6);
|
||||
}
|
||||
}
|
||||
@if $size == h2 {
|
||||
font-weight: normal;
|
||||
font-size: 2rem;
|
||||
font-size: toRem(2);
|
||||
|
||||
@media (max-width: 500px) {
|
||||
font-size: 1.8rem;
|
||||
@media (max-width: toRem(50)) {
|
||||
font-size: toRem(1.8);
|
||||
}
|
||||
}
|
||||
@if $size == h3 {
|
||||
font-weight: normal;
|
||||
font-size: toRem(1.8);
|
||||
|
||||
@media (max-width: toRem(50)) {
|
||||
font-size: toRem(1.6);
|
||||
}
|
||||
}
|
||||
@if $size == h4 {
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
font-size: toRem(1.6);
|
||||
}
|
||||
@if $size == m-regular {
|
||||
font-size: 1.6rem;
|
||||
font-size: toRem(1.6);
|
||||
font-weight: normal;
|
||||
}
|
||||
@if $size == m-r-regular {
|
||||
font-size: 1.6rem;
|
||||
font-size: toRem(1.6);
|
||||
font-weight: normal;
|
||||
@media (max-width: 500px) {
|
||||
font-size: 1.2rem;
|
||||
@media (max-width: toRem(50)) {
|
||||
font-size: toRem(1.2);
|
||||
}
|
||||
}
|
||||
@if $size == s-regular {
|
||||
font-size: 1.4rem;
|
||||
font-size: toRem(1.4);
|
||||
font-weight: normal;
|
||||
}
|
||||
@if $size == xs-r-regular {
|
||||
font-size: 1.2rem;
|
||||
font-size: toRem(1.2);
|
||||
font-weight: normal;
|
||||
@media (max-width: 500px) {
|
||||
font-size: 1rem;
|
||||
@media (max-width: toRem(50)) {
|
||||
font-size: toRem(1);
|
||||
}
|
||||
}
|
||||
@if $size == xss-regular {
|
||||
font-size: 0.9rem;
|
||||
font-size: toRem(0.9);
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/assets/images/360.png
Normal file
BIN
src/assets/images/360.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/images/building.png
Normal file
BIN
src/assets/images/building.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/images/person-map.png
Normal file
BIN
src/assets/images/person-map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
BIN
src/assets/images/pointer-map.png
Normal file
BIN
src/assets/images/pointer-map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/images/pointer.png
Normal file
BIN
src/assets/images/pointer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/uploading.png
Normal file
BIN
src/assets/images/uploading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@@ -1,5 +1,4 @@
|
||||
html {
|
||||
font-size: 62.5%; /* 1rem = 10px */
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
body {
|
||||
@@ -25,33 +24,36 @@ h5 {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
--white: #ffffff;
|
||||
--black: #181818;
|
||||
--black-pale: #1b1a17;
|
||||
--red: #f70000;
|
||||
--red-pale: #ff726f;
|
||||
--grey: #e6e6e6;
|
||||
--grey-pale: #cfd2cf;
|
||||
--grey-semi-dark: #808080;
|
||||
--grey-dark: #54595e;
|
||||
--blue: #4945ff;
|
||||
--blue-dark: #051f61;
|
||||
--blue-geovisio: #34495e;
|
||||
--blue-semi: rgba(207, 226, 255, 0.5);
|
||||
--blue-pale: #f9fafd;
|
||||
--beige: #f5f3ec;
|
||||
--beige-pale: #ececec80;
|
||||
--yellow: #fec868;
|
||||
--orange: #ff6f00;
|
||||
--green: #59ce8f;
|
||||
--green-pale: #f0ffee;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
@media (min-width: toRem(102.4)) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
/* CSS specific to iOS devices */
|
||||
body {
|
||||
4
src/assets/rem-calc.scss
Normal file
4
src/assets/rem-calc.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
@function toRem($value) {
|
||||
$remValue: calc($value / 1.6) + rem;
|
||||
@return $remValue;
|
||||
}
|
||||
@@ -13,32 +13,32 @@ import title from '@/utils/index'
|
||||
.beta {
|
||||
@include text(xs-r-regular);
|
||||
color: var(--red);
|
||||
border: 1px solid var(--red);
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.2rem 0.3rem;
|
||||
margin-left: 0.5rem;
|
||||
border: toRem(0.1) solid var(--red);
|
||||
border-radius: toRem(0.5);
|
||||
padding: toRem(0.2) toRem(0.3);
|
||||
margin-left: toRem(0.5);
|
||||
position: absolute;
|
||||
top: -1rem;
|
||||
right: -9.5rem;
|
||||
width: 9rem;
|
||||
top: toRem(-1);
|
||||
right: toRem(-9.5);
|
||||
width: toRem(9);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.instance-beta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--grey);
|
||||
padding: 1rem;
|
||||
padding: toRem(1);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.beta {
|
||||
position: relative;
|
||||
top: initial;
|
||||
right: initial;
|
||||
margin-left: 1rem;
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<button
|
||||
:disabled="isLoading || disabled"
|
||||
type="button"
|
||||
:class="[look, 'default', { disabled }]"
|
||||
:type="type"
|
||||
:class="[look, 'default', { disabled: disabled || isLoading }]"
|
||||
@click="$emit('trigger')"
|
||||
>
|
||||
<i v-if="icon" :class="[icon, 'icon']"></i>
|
||||
@@ -14,109 +14,120 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
type TypeInterface = 'button' | 'submit' | 'reset'
|
||||
defineProps({
|
||||
icon: { type: String, default: null },
|
||||
disabled: { type: Boolean, default: false },
|
||||
isLoading: { type: Boolean, default: false },
|
||||
text: { type: String, default: '' },
|
||||
tooltip: { type: String, default: '' },
|
||||
look: { type: String, default: '' }
|
||||
look: { type: String, default: '' },
|
||||
type: { type: String as PropType<TypeInterface>, default: 'button' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
@media (min-width: toRem(76.8)) {
|
||||
.default:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.default {
|
||||
height: toRem(3.5);
|
||||
min-width: toRem(3.5);
|
||||
@include text(s-regular);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-radius: toRem(0.5);
|
||||
padding: toRem(1.3) toRem(2) toRem(1.3);
|
||||
.icon {
|
||||
font-size: 2.5rem;
|
||||
font-size: toRem(2);
|
||||
}
|
||||
&:hover .tooltip-button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
.button--black {
|
||||
height: 3.5rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.3rem 2rem 1.3rem;
|
||||
color: var(--white);
|
||||
background-color: var(--black);
|
||||
}
|
||||
.button--blue {
|
||||
color: var(--white);
|
||||
background-color: var(--blue);
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
color: var(--white);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.button--transparent {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 0.1rem solid var(--white);
|
||||
border: toRem(0.1) solid var(--white);
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
}
|
||||
.button--red {
|
||||
height: 3.5rem;
|
||||
min-width: 3.5rem;
|
||||
border-radius: 0.5rem;
|
||||
color: var(--red);
|
||||
background-color: var(--white);
|
||||
border: 0.1rem solid var(--red);
|
||||
border: toRem(0.1) solid var(--red);
|
||||
.icon {
|
||||
margin-right: 0;
|
||||
font-size: 1.4rem;
|
||||
font-size: toRem(1.4);
|
||||
color: var(--red);
|
||||
}
|
||||
.text {
|
||||
margin-left: 1rem;
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
}
|
||||
.button--white {
|
||||
height: 3.5rem;
|
||||
border-radius: 0.5rem;
|
||||
color: var(--black);
|
||||
background-color: var(--white);
|
||||
border: 0.1rem solid var(--black);
|
||||
border: toRem(0.1) solid var(--black);
|
||||
.icon {
|
||||
font-size: 1.4rem;
|
||||
font-size: toRem(1.4);
|
||||
color: var(--black);
|
||||
margin-right: 0;
|
||||
}
|
||||
.text {
|
||||
margin-left: 1rem;
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
}
|
||||
.no-text {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
height: toRem(3);
|
||||
width: toRem(3);
|
||||
padding: 0;
|
||||
.icon {
|
||||
color: var(---black);
|
||||
font-size: toRem(1.8);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.link--grey {
|
||||
color: var(--grey-semi-dark);
|
||||
.icon {
|
||||
font-size: 1.4rem;
|
||||
font-size: toRem(1.4);
|
||||
color: var(--grey-semi-dark);
|
||||
}
|
||||
}
|
||||
.link--red {
|
||||
height: 3rem;
|
||||
color: var(--red);
|
||||
background-color: var(--white);
|
||||
.icon {
|
||||
font-size: 1.4rem;
|
||||
font-size: toRem(1.4);
|
||||
color: var(--red);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 1rem;
|
||||
font-size: 2rem;
|
||||
margin-right: toRem(1);
|
||||
font-size: toRem(2);
|
||||
color: var(--white);
|
||||
}
|
||||
.button--rounded {
|
||||
@@ -125,32 +136,28 @@ defineProps({
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
height: 2.5rem;
|
||||
width: 2.5rem;
|
||||
height: toRem(2.5);
|
||||
width: toRem(2.5);
|
||||
.icon {
|
||||
color: var(---black);
|
||||
font-size: 1.8rem;
|
||||
font-size: toRem(1.8);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.default .tooltip-button {
|
||||
.tooltip-button {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
text-align: center;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: toRem(0.5);
|
||||
padding: toRem(0.5) toRem(1);
|
||||
position: absolute;
|
||||
bottom: -100%;
|
||||
visibility: hidden;
|
||||
width: 18rem;
|
||||
width: toRem(18);
|
||||
right: 0;
|
||||
@include text(xss-regular);
|
||||
}
|
||||
|
||||
.default:hover .tooltip-button {
|
||||
visibility: visible;
|
||||
}
|
||||
.disabled {
|
||||
color: var(--grey-pale);
|
||||
border-color: var(--grey-pale);
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
<nav class="nav">
|
||||
<div class="wrapper-logo desktop">
|
||||
<Link
|
||||
:image="{
|
||||
url: 'logo.jpeg',
|
||||
alt: $t('general.header.alt_logo')
|
||||
}"
|
||||
:text="title($t('general.header.title'))"
|
||||
path="/"
|
||||
:image="{ url: 'logo.jpeg', alt: $t('general.header.alt_logo') }"
|
||||
:route="{ name: 'home' }"
|
||||
/>
|
||||
<span class="title-text"
|
||||
>{{ $t('general.header.title') }}
|
||||
<span v-if="instanceName" class="instance-text">{{
|
||||
instanceName
|
||||
}}</span></span
|
||||
>
|
||||
</div>
|
||||
<div class="wrapper-logo responsive">
|
||||
<Link
|
||||
@@ -20,7 +22,7 @@
|
||||
url: 'logo.jpeg',
|
||||
alt: $t('general.header.alt_logo')
|
||||
}"
|
||||
path="/"
|
||||
:route="{ name: 'home' }"
|
||||
/>
|
||||
</div>
|
||||
<div ref="list" class="wrapper-entries">
|
||||
@@ -32,13 +34,13 @@
|
||||
<Link
|
||||
:text="$t('general.header.sequences_text')"
|
||||
icon="bi bi-images"
|
||||
path="/mes-sequences"
|
||||
:route="{ name: 'my-sequences' }"
|
||||
@click.native="closeModal"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="userProfileUrl.length" class="logged-link">
|
||||
<Link
|
||||
path="/mes-informations"
|
||||
:route="{ name: 'my-information' }"
|
||||
icon="bi bi-person"
|
||||
:text="$t('general.header.my_information_text')"
|
||||
@click.native="closeModal"
|
||||
@@ -46,7 +48,7 @@
|
||||
</li>
|
||||
<li class="logged-link">
|
||||
<Link
|
||||
path="/mes-parametres"
|
||||
:route="{ name: 'my-settings' }"
|
||||
icon="bi bi-gear"
|
||||
:text="$t('general.header.my_settings_text')"
|
||||
@click.native="closeModal"
|
||||
@@ -57,17 +59,24 @@
|
||||
type="external"
|
||||
icon="bi bi-power"
|
||||
:text="$t('general.header.logout_text')"
|
||||
:path="getAuthRoute('auth/logout', route.path)"
|
||||
:path-external="getAuthRoute('auth/logout', route.path)"
|
||||
@click.native="closeModal"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="wrapper-right-entries">
|
||||
<div v-if="isLogged && authEnabled">
|
||||
<Link
|
||||
:text="$t('general.header.upload_text')"
|
||||
look="button button--blue"
|
||||
:route="{ name: 'upload-pictures' }"
|
||||
@click.native="closeModal"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
:text="$t('general.header.contribute_text')"
|
||||
look="button white"
|
||||
path="/partager-des-photos"
|
||||
:route="{ name: 'share-pictures' }"
|
||||
@click.native="closeModal"
|
||||
/>
|
||||
</div>
|
||||
@@ -89,7 +98,7 @@
|
||||
<Link
|
||||
type="external"
|
||||
icon="bi bi-person-circle"
|
||||
:path="getAuthRoute('auth/login', route.path)"
|
||||
:path-external="getAuthRoute('auth/login', route.path)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,7 +114,6 @@ import { useCookies } from 'vue3-cookies'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { getAuthRoute } from '@/utils/auth'
|
||||
import title from '@/utils/index'
|
||||
import Link from '@/components/Link.vue'
|
||||
import BetaText from '@/components/BetaText.vue'
|
||||
|
||||
@@ -129,6 +137,7 @@ function toggleMenu(): void {
|
||||
menuIsClosed.value = !menuIsClosed.value
|
||||
}
|
||||
const isLogged = computed((): boolean => !!cookies.get('user_id'))
|
||||
const instanceName = computed((): string => import.meta.env.VITE_INSTANCE_NAME)
|
||||
const ariaLabel = computed((): string =>
|
||||
menuIsClosed.value
|
||||
? t('general.header.burger_menu_aria_label_open')
|
||||
@@ -147,19 +156,29 @@ const userName = computed((): string =>
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 8rem;
|
||||
border-bottom: 0.1rem solid var(--black);
|
||||
height: toRem(8);
|
||||
background-color: var(--blue-pale);
|
||||
}
|
||||
.nav {
|
||||
width: 100%;
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.wrapper-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--blue-dark);
|
||||
}
|
||||
.title-text {
|
||||
font-weight: bolder;
|
||||
}
|
||||
.instance-text {
|
||||
font-weight: normal;
|
||||
border-left: toRem(0.1) solid var(--blue-dark);
|
||||
padding-left: toRem(0.7);
|
||||
margin-left: toRem(0.5);
|
||||
}
|
||||
.wrapper-entries {
|
||||
display: flex;
|
||||
@@ -168,12 +187,12 @@ const userName = computed((): string =>
|
||||
display: none;
|
||||
}
|
||||
.desktop {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
.wrapper-logo p {
|
||||
@include text(m-r-regular);
|
||||
margin-bottom: 0;
|
||||
margin-left: 1rem;
|
||||
margin-left: toRem(1);
|
||||
position: relative;
|
||||
}
|
||||
.item-with-sub {
|
||||
@@ -186,45 +205,42 @@ const userName = computed((): string =>
|
||||
}
|
||||
.sub-nav-block {
|
||||
display: none;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--black);
|
||||
border-radius: toRem(0.5);
|
||||
border: toRem(0.1) solid var(--black);
|
||||
background-color: var(--white);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 3.5rem;
|
||||
top: toRem(3.5);
|
||||
z-index: 1;
|
||||
width: 15rem;
|
||||
width: toRem(15);
|
||||
}
|
||||
.logged-link {
|
||||
display: flex;
|
||||
padding: 0.5rem 2rem 0.7rem;
|
||||
padding: toRem(0.5) toRem(2) toRem(0.7);
|
||||
}
|
||||
.logged-link:hover {
|
||||
border-radius: 0.5rem;
|
||||
border-radius: toRem(0.5);
|
||||
background-color: var(--grey);
|
||||
}
|
||||
.nav-list-item {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
.menu-burger {
|
||||
display: none;
|
||||
margin-right: toRem(1.5);
|
||||
}
|
||||
.wrapper-right-entries {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
div:first-child {
|
||||
margin-right: 2rem;
|
||||
div {
|
||||
margin-right: toRem(2);
|
||||
}
|
||||
}
|
||||
.cross {
|
||||
font-size: 2rem;
|
||||
font-size: toRem(2);
|
||||
}
|
||||
.item-with-sub {
|
||||
margin-right: 1.5rem;
|
||||
margin-right: toRem(1.5);
|
||||
}
|
||||
.nav {
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
padding: toRem(1.5);
|
||||
}
|
||||
.nav-list {
|
||||
display: none;
|
||||
@@ -232,23 +248,23 @@ const userName = computed((): string =>
|
||||
justify-content: center;
|
||||
align-items: initial;
|
||||
position: absolute;
|
||||
width: 20rem;
|
||||
top: 8rem;
|
||||
width: toRem(20);
|
||||
top: toRem(8);
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
background-color: var(--white);
|
||||
box-shadow: 0 0.2rem 0.4rem rgb(0 0 0 / 10%);
|
||||
box-shadow: 0 toRem(0.2) toRem(0.4) rgb(0 0 0 / 10%);
|
||||
padding-left: 0;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-radius: 1rem;
|
||||
padding-top: toRem(1);
|
||||
padding-bottom: toRem(1);
|
||||
border-radius: toRem(1);
|
||||
}
|
||||
.menu-burger {
|
||||
display: block;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
width: 2.5rem;
|
||||
font-size: 2.5rem;
|
||||
width: toRem(2.5);
|
||||
font-size: toRem(2.5);
|
||||
padding: 0;
|
||||
.item-with-sub {
|
||||
@include text(s-regular);
|
||||
@@ -257,8 +273,8 @@ const userName = computed((): string =>
|
||||
align-items: center;
|
||||
background-color: var(--blue);
|
||||
color: var(--white);
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
height: toRem(3);
|
||||
width: toRem(3);
|
||||
border-radius: 50%;
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -266,19 +282,18 @@ const userName = computed((): string =>
|
||||
.menu-open {
|
||||
display: flex;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
height: 11rem;
|
||||
height: toRem(11);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 4;
|
||||
background: var(--white);
|
||||
}
|
||||
.nav-list {
|
||||
top: 11rem;
|
||||
top: toRem(11);
|
||||
width: 100%;
|
||||
}
|
||||
.desktop {
|
||||
|
||||
@@ -47,11 +47,11 @@
|
||||
>
|
||||
<div class="button-info">
|
||||
<Link
|
||||
look="button--blue no-text"
|
||||
look="button button--white-blue no-text"
|
||||
icon="bi bi-cloud-download-fill"
|
||||
type="external"
|
||||
target="_blank"
|
||||
:path="hrefHd"
|
||||
:path-external="hrefHd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,8 +87,8 @@ defineProps({
|
||||
position: relative;
|
||||
}
|
||||
.selected {
|
||||
border: 0.1rem solid var(--blue);
|
||||
border-radius: 0.5rem;
|
||||
border: toRem(0.1) solid var(--blue);
|
||||
border-radius: toRem(0.5);
|
||||
box-shadow: 0px 4px 4px 0px #00000040;
|
||||
}
|
||||
.wrapper-image {
|
||||
@@ -98,45 +98,45 @@ defineProps({
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-top-right-radius: 0.5rem;
|
||||
border-top-left-radius: 0.5rem;
|
||||
height: 16rem;
|
||||
border-top-right-radius: toRem(0.5);
|
||||
border-top-left-radius: toRem(0.5);
|
||||
height: toRem(16);
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.photo-img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 0.5rem;
|
||||
border-radius: toRem(0.5);
|
||||
}
|
||||
.icon-hidden {
|
||||
color: var(--grey-dark);
|
||||
position: absolute;
|
||||
font-size: 4rem;
|
||||
font-size: toRem(4);
|
||||
}
|
||||
.waiting-wrapper {
|
||||
height: 16rem;
|
||||
height: toRem(16);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--blue);
|
||||
}
|
||||
.icon-waiting {
|
||||
height: 4rem;
|
||||
font-size: 4rem;
|
||||
height: toRem(4);
|
||||
font-size: toRem(4);
|
||||
}
|
||||
.icon-img {
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
top: toRem(1);
|
||||
right: toRem(1);
|
||||
background-color: var(--white);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
height: toRem(2);
|
||||
width: toRem(2);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.3rem;
|
||||
font-size: toRem(1.3);
|
||||
}
|
||||
.pointer-map,
|
||||
.button-check-pointer {
|
||||
@@ -146,11 +146,11 @@ defineProps({
|
||||
opacity: 1;
|
||||
}
|
||||
.photo-info {
|
||||
height: 5rem;
|
||||
height: toRem(5);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
padding: toRem(1);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
@@ -163,9 +163,9 @@ defineProps({
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 0.5rem 0.8rem;
|
||||
padding: toRem(0.5) toRem(0.8);
|
||||
background-color: var(--white);
|
||||
border-radius: 0.5rem;
|
||||
border-radius: toRem(0.5);
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ function updateValue(value: boolean): void {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
height: toRem(2);
|
||||
width: toRem(2);
|
||||
}
|
||||
.input {
|
||||
-webkit-appearance: none;
|
||||
@@ -66,7 +66,7 @@ function updateValue(value: boolean): void {
|
||||
width: 100%;
|
||||
}
|
||||
.icon {
|
||||
font-size: 2rem;
|
||||
font-size: toRem(2);
|
||||
position: absolute;
|
||||
color: var(--grey-semi-dark);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ function updateValue(value: boolean): void {
|
||||
}
|
||||
.label {
|
||||
cursor: pointer;
|
||||
margin-left: 0.5rem;
|
||||
margin-left: toRem(0.5);
|
||||
@include text(s-regular);
|
||||
}
|
||||
</style>
|
||||
|
||||
120
src/components/InputUpload.vue
Normal file
120
src/components/InputUpload.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<label
|
||||
@dragover="dragover"
|
||||
@dragleave="dragleave"
|
||||
@drop="drop"
|
||||
:class="['file-upload', { dragging: isDragging }]"
|
||||
>
|
||||
<input
|
||||
ref="upload"
|
||||
type="file"
|
||||
multiple
|
||||
:accept="accept"
|
||||
capture="environment"
|
||||
class="input-file"
|
||||
@change="changeFile"
|
||||
/>
|
||||
<i class="bi bi-cloud-upload-fill"></i>
|
||||
<span v-if="text" class="input-text">
|
||||
{{ text }}
|
||||
<span v-if="textSecondPart" class="last-word">{{ textSecondPart }}</span>
|
||||
</span>
|
||||
<span v-if="textPictureType" class="input-text-type">{{
|
||||
textPictureType
|
||||
}}</span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const emit = defineEmits<{ (e: 'trigger', value: FileList): void }>()
|
||||
let isDragging = ref<boolean>(false)
|
||||
|
||||
defineProps({
|
||||
text: { type: String, default: null },
|
||||
textPictureType: { type: String, default: null },
|
||||
textSecondPart: { type: String, default: null },
|
||||
accept: { type: String, default: '' }
|
||||
})
|
||||
|
||||
interface HTMLInputChangeEvent extends Event {
|
||||
target: HTMLInputElement & EventTarget
|
||||
}
|
||||
function changeFile(event: Event): void {
|
||||
const { target } = event as HTMLInputChangeEvent
|
||||
if (target && target.files) {
|
||||
if (!checkPicturesType(target.files)) return
|
||||
emit('trigger', target.files)
|
||||
}
|
||||
}
|
||||
|
||||
function dragover(event: DragEvent): void {
|
||||
event.preventDefault()
|
||||
isDragging.value = true
|
||||
}
|
||||
|
||||
function dragleave(): void {
|
||||
isDragging.value = false
|
||||
}
|
||||
function drop(event: DragEvent): void | boolean {
|
||||
event.preventDefault()
|
||||
const { dataTransfer } = event
|
||||
if (dataTransfer && dataTransfer.files) {
|
||||
if (!checkPicturesType(dataTransfer.files))
|
||||
return (isDragging.value = false)
|
||||
emit('trigger', dataTransfer.files)
|
||||
isDragging.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function checkPicturesType(files: FileList): number {
|
||||
const picturesToUpload = [...files]
|
||||
.filter((p) => p.type == 'image/jpeg')
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
return picturesToUpload.length
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.file-upload {
|
||||
border: toRem(0.1) dashed var(--blue);
|
||||
background-color: var(--blue-semi);
|
||||
border-radius: toRem(0.5);
|
||||
padding: toRem(0.3) toRem(0.3) toRem(3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dragging {
|
||||
border-color: var(--green);
|
||||
background-color: var(--green-pale);
|
||||
.last-word,
|
||||
.bi-cloud-upload-fill {
|
||||
color: var(--green);
|
||||
}
|
||||
}
|
||||
.file-upload input {
|
||||
overflow: hidden;
|
||||
width: 0;
|
||||
}
|
||||
.bi-cloud-upload-fill {
|
||||
color: var(--blue);
|
||||
font-size: toRem(7);
|
||||
}
|
||||
.input-text {
|
||||
font-size: toRem(2);
|
||||
@include text(m-regular);
|
||||
font-weight: bold;
|
||||
width: toRem(21);
|
||||
text-align: center;
|
||||
margin-bottom: toRem(1);
|
||||
}
|
||||
.input-text-type {
|
||||
@include text(xs-r-regular);
|
||||
}
|
||||
.last-word {
|
||||
color: var(--blue);
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<a
|
||||
v-if="type === 'external'"
|
||||
:href="path"
|
||||
:href="pathExternal"
|
||||
:target="target"
|
||||
:class="['default', look, { disabled }]"
|
||||
:title="titleImg"
|
||||
@@ -14,7 +14,8 @@
|
||||
</a>
|
||||
<router-link
|
||||
v-else
|
||||
:to="path"
|
||||
:to="route"
|
||||
:target="target"
|
||||
:class="['default', look, { disabled }]"
|
||||
:title="text"
|
||||
>
|
||||
@@ -28,6 +29,7 @@
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { computed } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
import { img } from '../utils/image'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -38,7 +40,8 @@ interface ImageInterface {
|
||||
|
||||
const props = defineProps({
|
||||
text: { type: String, default: null },
|
||||
path: { type: String, default: '' },
|
||||
route: { type: Object as PropType<RouteLocationRaw>, default: {} },
|
||||
pathExternal: { type: String, default: '' },
|
||||
look: { type: String, default: '' },
|
||||
type: { type: String, default: null },
|
||||
alt: { type: String, default: '' },
|
||||
@@ -54,70 +57,84 @@ const titleImg = computed<string>(() =>
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
color: var(--black);
|
||||
font-size: 2.4rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.default {
|
||||
@include text(s-regular);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--black);
|
||||
color: var(--blue-dark);
|
||||
text-decoration: none;
|
||||
.icon {
|
||||
margin-right: 1rem;
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
&:hover {
|
||||
opacity: toRem(0.8);
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
color: var(--black);
|
||||
font-size: toRem(2.4);
|
||||
}
|
||||
.logo {
|
||||
height: toRem(4);
|
||||
border-radius: toRem(0.5);
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
.button {
|
||||
height: toRem(4);
|
||||
border-radius: toRem(0.5);
|
||||
padding: toRem(1.3) toRem(2) toRem(1.3);
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
}
|
||||
.text {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
.link:hover {
|
||||
background-color: transparent;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.button {
|
||||
height: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.3rem 2rem 1.3rem;
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
}
|
||||
.white {
|
||||
.button--white {
|
||||
background-color: var(--white);
|
||||
color: var(--black);
|
||||
border: 0.1rem solid var(--black);
|
||||
border: toRem(0.1) solid var(--black);
|
||||
.icon {
|
||||
font-size: toRem(1.6);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
.icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.white .icon {
|
||||
font-size: 1.6rem;
|
||||
.button--white-blue {
|
||||
background-color: var(--white);
|
||||
border: toRem(0.1) solid var(--blue);
|
||||
.icon {
|
||||
font-size: toRem(1.4);
|
||||
color: var(--blue);
|
||||
}
|
||||
}
|
||||
.button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.white:hover {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.white:hover > .icon {
|
||||
color: white;
|
||||
}
|
||||
.logo {
|
||||
height: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
.button--blue {
|
||||
background-color: var(--blue);
|
||||
border: toRem(0.1) solid var(--blue);
|
||||
.icon {
|
||||
font-size: toRem(1.4);
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
.disabled {
|
||||
color: grey;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.disabled .icon {
|
||||
color: grey;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.disabled:hover {
|
||||
text-decoration: none;
|
||||
.icon {
|
||||
color: grey;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.button--rounded {
|
||||
background-color: var(--black);
|
||||
@@ -126,50 +143,29 @@ const titleImg = computed<string>(() =>
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
height: toRem(4);
|
||||
width: toRem(4);
|
||||
.icon {
|
||||
color: var(--white);
|
||||
font-size: 2.8rem;
|
||||
font-size: toRem(2.8);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.button--white {
|
||||
height: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
color: var(--black);
|
||||
background-color: var(--white);
|
||||
border: 0.1rem solid var(--black);
|
||||
.icon {
|
||||
font-size: 1.4rem;
|
||||
color: var(--black);
|
||||
}
|
||||
}
|
||||
.button--blue {
|
||||
height: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--white);
|
||||
border: 0.1rem solid var(--blue);
|
||||
.icon {
|
||||
font-size: 1.4rem;
|
||||
color: var(--blue);
|
||||
}
|
||||
}
|
||||
.no-text {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
height: toRem(3);
|
||||
width: toRem(3);
|
||||
padding: 0;
|
||||
.icon {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.icon {
|
||||
margin-right: 0.5rem;
|
||||
margin-right: toRem(0.5);
|
||||
}
|
||||
.button {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: toRem(1);
|
||||
padding-left: toRem(1);
|
||||
}
|
||||
.disable-mobile {
|
||||
pointer-events: none;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="lds-ring">
|
||||
<div :class="['lds-ring', look, { loaded: isLoaded }]">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
@@ -7,27 +7,66 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
text: { type: String, default: null },
|
||||
look: {
|
||||
type: String,
|
||||
validator: (value: string): boolean => ['sm', 'md', 'lg'].includes(value),
|
||||
default: 'sm'
|
||||
},
|
||||
isLoaded: { type: Boolean, default: true }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped scss>
|
||||
<style scoped lang="scss">
|
||||
.lds-ring {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.lds-ring div {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 8px;
|
||||
border: 8px solid var(--blue);
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
margin: 10px;
|
||||
border: 10px solid var(--blue);
|
||||
border-radius: 50%;
|
||||
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
border-color: var(--blue) transparent transparent transparent;
|
||||
}
|
||||
.loaded {
|
||||
div {
|
||||
border-color: var(--blue);
|
||||
}
|
||||
}
|
||||
.sm {
|
||||
width: toRem(8);
|
||||
height: toRem(8);
|
||||
div {
|
||||
width: toRem(8.4);
|
||||
height: toRem(8.4);
|
||||
}
|
||||
}
|
||||
.md {
|
||||
width: toRem(16);
|
||||
height: toRem(16);
|
||||
div {
|
||||
width: toRem(16.8);
|
||||
height: toRem(16.8);
|
||||
}
|
||||
}
|
||||
.lg {
|
||||
width: toRem(32);
|
||||
height: toRem(32);
|
||||
div {
|
||||
width: toRem(25.6);
|
||||
height: toRem(25.6);
|
||||
}
|
||||
}
|
||||
.lds-ring div:nth-child(1) {
|
||||
animation-delay: -0.45s;
|
||||
}
|
||||
|
||||
@@ -40,34 +40,34 @@ function triggerPagination(): void {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrapper-pagination {
|
||||
border: 0.1rem solid var(--black);
|
||||
border: toRem(0.1) solid var(--black);
|
||||
border-radius: 50%;
|
||||
background-color: var(--blue-pale);
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
height: toRem(3);
|
||||
width: toRem(3);
|
||||
display: flex;
|
||||
&:first-child {
|
||||
margin-right: 2rem;
|
||||
margin-right: toRem(2);
|
||||
}
|
||||
&:last-child {
|
||||
margin-left: 2rem;
|
||||
margin-left: toRem(2);
|
||||
}
|
||||
&:nth-child(2) {
|
||||
margin-right: 1rem;
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
&:nth-child(3) {
|
||||
margin-left: 1rem;
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
}
|
||||
.pagination-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 1.7rem;
|
||||
font-size: toRem(1.7);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.chevron {
|
||||
width: 3.5rem;
|
||||
width: toRem(3.5);
|
||||
}
|
||||
}
|
||||
.no-border {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="entry-button-terminal">
|
||||
<Button
|
||||
look="button--transparent"
|
||||
:text="$t('pages.upload.button_copy')"
|
||||
:text="$t('pages.share_pictures.button_copy')"
|
||||
:icon="clipboardIcon"
|
||||
@trigger="copyText(textInstall)"
|
||||
class="entry-button"
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="entry-button-terminal">
|
||||
<Button
|
||||
look="button--transparent"
|
||||
:text="$t('pages.upload.button_copy')"
|
||||
:text="$t('pages.share_pictures.button_copy')"
|
||||
:icon="clipboardIcon"
|
||||
@trigger="copyText(textUpload)"
|
||||
class="entry-button"
|
||||
@@ -65,23 +65,23 @@ async function copyText(text: string): Promise<void> {
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top-right-radius: 1.5rem;
|
||||
border-top-left-radius: 1.5rem;
|
||||
border-top-right-radius: toRem(1.5);
|
||||
border-top-left-radius: toRem(1.5);
|
||||
background-color: var(--grey);
|
||||
padding-left: 0.5rem;
|
||||
height: 3rem;
|
||||
padding-left: toRem(0.5);
|
||||
height: toRem(3);
|
||||
width: 100%;
|
||||
}
|
||||
.editor {
|
||||
border-bottom-right-radius: 1.5rem;
|
||||
border-bottom-left-radius: 1.5rem;
|
||||
border-bottom-right-radius: toRem(1.5);
|
||||
border-bottom-left-radius: toRem(1.5);
|
||||
background-color: var(--black-pale);
|
||||
height: 32rem;
|
||||
height: toRem(32);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.screen {
|
||||
padding: 3rem 2rem 2rem;
|
||||
padding: toRem(3) toRem(2) toRem(2);
|
||||
height: 100%;
|
||||
}
|
||||
.screen:nth-child(2n) {
|
||||
@@ -90,10 +90,10 @@ async function copyText(text: string): Promise<void> {
|
||||
.upload-command {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 2rem;
|
||||
margin-top: toRem(2);
|
||||
}
|
||||
.entry-button-terminal {
|
||||
margin-top: 2rem;
|
||||
margin-top: toRem(2);
|
||||
margin-left: auto;
|
||||
}
|
||||
.code {
|
||||
@@ -105,13 +105,13 @@ async function copyText(text: string): Promise<void> {
|
||||
}
|
||||
.tilde {
|
||||
color: var(--green);
|
||||
margin-right: 1rem;
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
.round {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
height: toRem(1.5);
|
||||
width: toRem(1.5);
|
||||
border-radius: 50%;
|
||||
margin: 0.5rem;
|
||||
margin: toRem(0.5);
|
||||
}
|
||||
.red {
|
||||
background-color: var(--red);
|
||||
@@ -123,9 +123,9 @@ async function copyText(text: string): Promise<void> {
|
||||
background-color: var(--green);
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.editor {
|
||||
min-height: 27rem;
|
||||
min-height: toRem(27);
|
||||
}
|
||||
.upload-command {
|
||||
margin-top: 0;
|
||||
|
||||
@@ -27,26 +27,26 @@ defineProps({
|
||||
.toast-wrapper {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 2rem;
|
||||
bottom: toRem(2);
|
||||
transform: translateX(100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--white);
|
||||
@include text(s-regular);
|
||||
border-radius: 0.5rem;
|
||||
height: 4rem;
|
||||
min-width: 10rem;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
border-radius: toRem(0.5);
|
||||
height: toRem(4);
|
||||
min-width: toRem(10);
|
||||
padding-right: toRem(1);
|
||||
padding-left: toRem(1);
|
||||
}
|
||||
.button-close {
|
||||
position: absolute;
|
||||
top: -0.5rem;
|
||||
right: -0.5rem;
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
border: 0.1rem solid var(--black);
|
||||
top: toRem(-0.5);
|
||||
right: toRem(-0.5);
|
||||
height: toRem(1.8);
|
||||
width: toRem(1.8);
|
||||
border: toRem(0.1) solid var(--black);
|
||||
background-color: var(--white);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
@@ -55,10 +55,10 @@ defineProps({
|
||||
}
|
||||
.toast-text {
|
||||
margin-bottom: 0;
|
||||
margin-left: 1rem;
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
.display {
|
||||
transform: translateX(-3rem);
|
||||
transform: translateX(toRem(-3));
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.error {
|
||||
|
||||
38
src/components/share-pictures/ShareCard.vue
Normal file
38
src/components/share-pictures/ShareCard.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="share-card">
|
||||
<img v-if="image" :src="img(image.url)" :alt="image.alt" class="image" />
|
||||
<h2 class="title">{{ title }}</h2>
|
||||
<p class="text">{{ text }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { img } from '../../utils/image'
|
||||
import type { PropType } from 'vue'
|
||||
interface ImageInterface {
|
||||
url: string
|
||||
alt: string
|
||||
}
|
||||
defineProps({
|
||||
image: { type: Object as PropType<ImageInterface>, default: null },
|
||||
title: { type: String, default: '' },
|
||||
text: { type: String, default: '' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.share-card {
|
||||
color: var(--blue-dark);
|
||||
}
|
||||
.title {
|
||||
@include text(h2);
|
||||
}
|
||||
.text {
|
||||
@include text(s-regular);
|
||||
color: var(--grey-semi-dark);
|
||||
}
|
||||
.image {
|
||||
height: toRem(8);
|
||||
margin-bottom: toRem(1);
|
||||
}
|
||||
</style>
|
||||
133
src/components/upload/ImportedSection.vue
Normal file
133
src/components/upload/ImportedSection.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div :class="['imported', { 'first-sequence': index === 0 }]">
|
||||
<div class="uploaded-pictures">
|
||||
<p v-if="sequence.pictures" class="uploaded-title">
|
||||
<span
|
||||
>{{ $t('pages.upload.import') }} {{ sequence.title }} -
|
||||
{{ sequence.pictures.length }}/{{ sequence.pictureCount }}</span
|
||||
>
|
||||
</p>
|
||||
<ul class="uploaded-picture-list">
|
||||
<PictureItem
|
||||
v-for="picture in uploadPictures"
|
||||
:text="$t('pages.upload.uploaded_word')"
|
||||
:name="picture.name"
|
||||
look="success"
|
||||
>
|
||||
<i class="bi bi-check-circle"></i>
|
||||
</PictureItem>
|
||||
</ul>
|
||||
<div v-if="sequence.id" class="wrapper-button-sequence">
|
||||
<Link
|
||||
:text="$t('pages.upload.sequence_link')"
|
||||
look="button button--white"
|
||||
target="_blank"
|
||||
:route="{ name: 'sequence', params: { id: sequence.id } }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="errors-pictures">
|
||||
<p v-if="uploadErrors.length" class="uploaded-title">
|
||||
{{ uploadErrors.length }} {{ $t('pages.upload.error_word') }}
|
||||
</p>
|
||||
<ul class="uploaded-error-list">
|
||||
<PictureItem
|
||||
v-for="error in uploadErrors"
|
||||
:text="error.message"
|
||||
:name="error.name"
|
||||
look="error"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import Link from '@/components/Link.vue'
|
||||
import PictureItem from '@/components/upload/PictureItem.vue'
|
||||
import type { uploadErrorInterface } from '@/views/interfaces/UploadPicturesView'
|
||||
defineProps({
|
||||
index: { type: Number, default: 0 },
|
||||
sequence: { type: Object, default: {} },
|
||||
picturesCount: { type: Number, default: null },
|
||||
uploadErrors: {
|
||||
type: Array as PropType<uploadErrorInterface[]>,
|
||||
default: []
|
||||
},
|
||||
uploadPictures: {
|
||||
type: Array as PropType<uploadErrorInterface[]>,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.imported {
|
||||
display: flex;
|
||||
padding-bottom: toRem(2);
|
||||
}
|
||||
.first-sequence {
|
||||
border-radius: toRem(0.5);
|
||||
}
|
||||
.uploaded-pictures,
|
||||
.errors-pictures {
|
||||
width: 50%;
|
||||
}
|
||||
.title-current-upload {
|
||||
margin-bottom: toRem(2);
|
||||
@include text(m-regular);
|
||||
color: var(--blue-dark);
|
||||
margin-left: toRem(2);
|
||||
}
|
||||
.uploaded-title {
|
||||
margin-bottom: toRem(2);
|
||||
width: 100%;
|
||||
color: var(--grey-semi-dark);
|
||||
font-weight: bold;
|
||||
@include text(s-regular);
|
||||
}
|
||||
.uploaded-picture-list {
|
||||
padding-left: 0;
|
||||
}
|
||||
.uploaded-picture-list,
|
||||
.uploaded-error-list {
|
||||
padding-top: 0rem;
|
||||
padding-right: toRem(2);
|
||||
padding-bottom: toRem(2);
|
||||
overflow-y: auto;
|
||||
max-height: toRem(50);
|
||||
}
|
||||
.bi-check-circle {
|
||||
color: var(--green);
|
||||
font-size: toRem(2);
|
||||
}
|
||||
.wrapper-button-sequence {
|
||||
padding-top: toRem(2);
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
.errors-pictures {
|
||||
padding-left: toRem(2);
|
||||
padding-right: toRem(2);
|
||||
.uploaded-title {
|
||||
margin-top: toRem(4);
|
||||
}
|
||||
}
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.uploaded-pictures,
|
||||
.errors-pictures {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: toRem(50)) {
|
||||
.imported {
|
||||
flex-direction: column;
|
||||
}
|
||||
.uploaded-error-list {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
53
src/components/upload/PictureItem.vue
Normal file
53
src/components/upload/PictureItem.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<li :class="['uploaded-picture-item', look]">
|
||||
<div class="uploaded-information">
|
||||
<span v-if="itemUploadedText">{{ itemUploadedText }}</span>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
const props = defineProps({
|
||||
name: { type: String, default: '' },
|
||||
text: { type: String, default: '' },
|
||||
look: {
|
||||
type: String,
|
||||
validator: (value: string): boolean =>
|
||||
['error', 'success', 'default'].includes(value),
|
||||
default: 'default'
|
||||
}
|
||||
})
|
||||
|
||||
const itemUploadedText = computed<string | null>(
|
||||
() => `${props.name} - ${props.text}`
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.uploaded-picture-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: toRem(1) toRem(2);
|
||||
margin-top: toRem(1);
|
||||
border-radius: toRem(0.5);
|
||||
width: 100%;
|
||||
@include text(xs-r-regular);
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: var(--white);
|
||||
border: toRem(0.1) solid var(--grey);
|
||||
color: var(--black);
|
||||
}
|
||||
.error {
|
||||
background-color: var(--white);
|
||||
border: toRem(0.1) solid var(--red-pale);
|
||||
color: var(--red);
|
||||
}
|
||||
.uploaded-information {
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
</style>
|
||||
46
src/components/upload/TooltipBanner.vue
Normal file
46
src/components/upload/TooltipBanner.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div v-if="isDisplayed" class="tooltip-banner">
|
||||
<div class="top-banner">
|
||||
<span
|
||||
><i class="bi bi-info-circle"></i>
|
||||
{{ $t('pages.upload.tooltip_banner_title') }}</span
|
||||
>
|
||||
<Button look="no-text" icon="bi bi-x-lg" @trigger="isDisplayed = false" />
|
||||
</div>
|
||||
<p class="text-banner">{{ $t('pages.upload.tooltip_banner_text') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Button from '@/components/Button.vue'
|
||||
|
||||
let isDisplayed = ref<boolean>(true)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tooltip-banner {
|
||||
border-left: toRem(1) solid var(--blue);
|
||||
border-radius: toRem(1);
|
||||
padding: toRem(2);
|
||||
background-color: var(--white);
|
||||
}
|
||||
.top-banner {
|
||||
@include text(h2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: toRem(0.5);
|
||||
color: var(--blue-dark);
|
||||
}
|
||||
.text-banner {
|
||||
@include text(s-regular);
|
||||
margin-bottom: 0;
|
||||
margin-right: 3rem;
|
||||
color: var(--grey-semi-dark);
|
||||
}
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.text-banner {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
107
src/components/upload/UploadLoader.vue
Normal file
107
src/components/upload/UploadLoader.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="wrapper-loader">
|
||||
<div class="loader">
|
||||
<span class="loader-percentage">{{ loadPercentage }}</span>
|
||||
<Loader look="lg" :is-loaded="isLoaded" />
|
||||
</div>
|
||||
<div v-if="loadPercentage === '100%'" class="wrapper-button-new-upload">
|
||||
<Button
|
||||
:text="$t('pages.upload.button_new_upload')"
|
||||
look="button button--blue"
|
||||
@trigger="$emit('triggerNewUpload')"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="uploadedSequences[0]" class="loader-information">
|
||||
<span class="loader-title">{{ uploadPendingTitle }}</span>
|
||||
<span v-if="loadPercentage !== '100%'" class="loader-text">{{
|
||||
$t('pages.upload.upload_pending_pictures', { count: picturesCount })
|
||||
}}</span>
|
||||
<span class="loader-text-size"
|
||||
>{{ uploadedSequences[0].pictureSize }}/{{ loadTextSize }}
|
||||
</span>
|
||||
<span class="loader-text-warning">{{
|
||||
$t('pages.upload.leave_message')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Loader from '@/components/Loader.vue'
|
||||
import Button from '@/components/Button.vue'
|
||||
import { computed } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import type { sequenceInterface } from '@/views/interfaces/UploadPicturesView'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
loadPercentage: { type: String, default: '0%' },
|
||||
loadTextSize: { type: String, default: '0 Mo' },
|
||||
isLoaded: { type: Boolean, default: false },
|
||||
uploadedSequences: {
|
||||
type: Array as PropType<sequenceInterface[]>,
|
||||
default: []
|
||||
},
|
||||
picturesCount: { type: Number, default: null }
|
||||
})
|
||||
|
||||
const uploadPendingTitle = computed<string>(() => {
|
||||
if (props.loadPercentage !== '100%') return t('pages.upload.upload_pending')
|
||||
return t('pages.upload.upload_done')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wrapper-loader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.loader {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.loader-percentage {
|
||||
width: toRem(10);
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
@include text(h1);
|
||||
color: var(--blue);
|
||||
}
|
||||
.wrapper-button-new-upload {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.loader-information {
|
||||
margin-top: toRem(2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.loader-title,
|
||||
.loader-text,
|
||||
.loader-text-size {
|
||||
text-align: center;
|
||||
@include text(s-regular);
|
||||
}
|
||||
.loader-title {
|
||||
@include text(h2);
|
||||
margin-bottom: toRem(0.5);
|
||||
}
|
||||
.loader-text {
|
||||
color: var(--blue);
|
||||
border-bottom: toRem(0.1) solid var(--grey);
|
||||
}
|
||||
.loader-text-size {
|
||||
color: var(--grey-semi-dark);
|
||||
}
|
||||
.loader-text-warning {
|
||||
text-align: center;
|
||||
@include text(s-regular);
|
||||
color: var(--orange);
|
||||
margin-top: toRem(1);
|
||||
width: toRem(31);
|
||||
}
|
||||
</style>
|
||||
@@ -6,10 +6,11 @@
|
||||
"description": "Panoramax, l’alternative libre pour photo-cartographier les territoires"
|
||||
},
|
||||
"header": {
|
||||
"contribute_text": "Partager vos photos",
|
||||
"contribute_text": "Comment contribuer ?",
|
||||
"upload_text": "Partager des photos",
|
||||
"sequences_text": "Mes photos",
|
||||
"alt_logo": "Logo de l'instance",
|
||||
"title": "Instance Panoramax",
|
||||
"title": "Panoramax",
|
||||
"beta_text": "Version beta",
|
||||
"logout_text": "Déconnexion",
|
||||
"my_information_text": "Mes informations",
|
||||
@@ -73,14 +74,21 @@
|
||||
"button_upload": "Partager vos photos",
|
||||
"sequence_deleted": "La séquence a bien été supprimée"
|
||||
},
|
||||
"upload": {
|
||||
"title": "Partagez vos photos",
|
||||
"sub_title": "Un compte utilisateur est obligatoire pour partager des photos",
|
||||
"photo_type1": "Des lieux visibles depuis la voie publique",
|
||||
"photo_type2": "360° ou non",
|
||||
"photo_type3": "Vues du sol",
|
||||
"photo_type4": "Géolocalisées",
|
||||
"description": "Ici, vos photos sont accessibles à tous :\n\n{check} automatiquement floutées dans le respect de <a href='https://panoramax.fr/foire-aux-questions' target='_blank' style='color:black'>la législation</a>\n{check} libres de droit, <a href='https://www.etalab.gouv.fr/licence-ouverte-open-licence/' target='_blank' style='color:black'>sous licence ouverte</a>\n{check} sous forme «brute» pour des réutilisations variées (ex: préparation des chantiers)\n\n",
|
||||
"share_pictures": {
|
||||
"title": "Pourquoi partager ses photos sur Panoramax ?",
|
||||
"description": "Avec Panoramax, vos photos sont accessibles à tous. Elles seront automatiquement floutées grâce à notre algorithme de floutage dans le respect de <a href='https://panoramax.fr/foire-aux-questions' target='_blank' style='color:black'>la législation</a> et libres de droit.\nLa mise en ligne se fera <a href='https://www.etalab.gouv.fr/licence-ouverte-open-licence/' target='_blank' style='color:black'>sous licence ouverte</a> sous forme «brute» pour des réutilisations variées (ex: préparation des chantiers, gestion de la voirie etc).",
|
||||
"arg_title1": "Des lieux visibles depuis la voie publique",
|
||||
"arg_title2": "Une gestion des photos au format 360° ou non",
|
||||
"arg_title3": "Une fonctionnalité de visualisation de vue du sol",
|
||||
"arg_title4": "Des photos automatiquement géolocalisées",
|
||||
"arg_text1": "Le lorem ipsum est, en imprimerie, une suite de mots sans signification utilisée à titre provisoire pour calibrer une mise en page, le texte définitif venant remplacer le faux-texte dès qu'il est prêt ou que la",
|
||||
"arg_text2": "Le lorem ipsum est, en imprimerie, une suite de mots sans signification utilisée à titre provisoire pour calibrer une mise en page, le texte définitif venant remplacer le faux-texte dès qu'il est prêt ou que la",
|
||||
"arg_text3": "Le lorem ipsum est, en imprimerie, une suite de mots sans signification utilisée à titre provisoire pour calibrer une mise en page, le texte définitif venant remplacer le faux-texte dès qu'il est prêt ou que la",
|
||||
"arg_text4": "Le lorem ipsum est, en imprimerie, une suite de mots sans signification utilisée à titre provisoire pour calibrer une mise en page, le texte définitif venant remplacer le faux-texte dès qu'il est prêt ou que la",
|
||||
"arg_alt1": "Image d'un immeuble",
|
||||
"arg_alt2": "Image d'un signe 360",
|
||||
"arg_alt3": "Image d'un pointer de localisation sur une carte",
|
||||
"arg_alt4": "Image d'un pointer de localisation",
|
||||
"footer_block": "⚠️️️ Aujourd'hui, le versement de grands volumes d'images est possible via une ligne de commande. Bientôt, d'autres moyens de versement seront disponibles, notamment via une interface web.",
|
||||
"button": "Accéder à l'outil",
|
||||
"alt_img_upload": "Image qui représente plusieurs photos en cours de téléchargement",
|
||||
@@ -92,6 +100,31 @@
|
||||
"terminal_install": "pip install geovisio_cli",
|
||||
"terminal_text": "geovisio upload --api-url {url} <DOSSIER_PHOTOS>",
|
||||
"button_copy": "Copier"
|
||||
},
|
||||
"upload": {
|
||||
"title": "Déposez vos photos",
|
||||
"title_uploading": "Traitement des images",
|
||||
"text": "Déposez vos fichiers dans cet espace. Chaque photo ou groupe de photos envoyé constituera une 'séquence'. Vous pourrez retrouver ensuite toutes vos séquences dans la section 'Mes images'. Déposez vos fichiers dans cet espace. Chaque photo ou groupe de photos envoyé",
|
||||
"no_picture_uploading_text": "Aucune photo en cours de téléchargement actuellement",
|
||||
"tooltip_banner_title": "Contribuez à la base de photos de Panoramax",
|
||||
"tooltip_banner_text": "Déposez vos fichiers dans cet espace. Chaque photo ou groupe de photos envoyé constituera une 'séquence'. Vous pourrez retrouver ensuite toutes vos séquences dans la section 'Mes images'.",
|
||||
"input_label": "Glissez vos images ici ou cliquez sur ",
|
||||
"import_word": "importer",
|
||||
"import_type": "Format JPEG uniquement",
|
||||
"sequence_title": "Séquence du ",
|
||||
"button_text": "Télécharger",
|
||||
"uploaded_files": "{count} fichier| {count} fichiers",
|
||||
"no_uploaded_files": "Aucun fichier sélectionné",
|
||||
"uploaded_word": " Image téléchargée",
|
||||
"import": "Imports",
|
||||
"error_word": "Images en erreur",
|
||||
"sequence_uploading_title": "Dernier import",
|
||||
"upload_pending": "Transfert en cours...",
|
||||
"upload_done": "Transfert terminé !",
|
||||
"upload_pending_pictures": "Envoi de {count} photo en cours |Envoi de {count} photos en cours",
|
||||
"sequence_link": "Voir la sequence",
|
||||
"button_new_upload": "Nouveau téléchargement",
|
||||
"leave_message": "⚠️ Attention, le téléchargement sera interrompu si vous quittez la page avant la fin."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ import { globalCookiesConfig } from 'vue3-cookies'
|
||||
import { createMetaManager } from 'vue-meta'
|
||||
import { pinia } from './store'
|
||||
import fr from './locales/fr.json'
|
||||
import './assets/main.css'
|
||||
import './assets/main.scss'
|
||||
import 'bootstrap/dist/css/bootstrap.css'
|
||||
import 'bootstrap/dist/js/bootstrap.js'
|
||||
import 'bootstrap-icons/font/bootstrap-icons.css'
|
||||
import 'geovisio/build/index.css'
|
||||
|
||||
axios.defaults.baseURL = import.meta.env.VITE_API_URL
|
||||
axios.defaults.withCredentials = true
|
||||
|
||||
@@ -12,7 +12,8 @@ import MyInformationView from '../views/MyInformationView.vue'
|
||||
import MySettingsView from '../views/MySettingsView.vue'
|
||||
import MySequencesView from '../views/MySequencesView.vue'
|
||||
import MySequenceView from '../views/MySequenceView.vue'
|
||||
import UploadView from '../views/UploadView.vue'
|
||||
import SharePicturesView from '../views/SharePicturesView.vue'
|
||||
import UploadPicturesView from '../views/UploadPicturesView.vue'
|
||||
const { cookies } = useCookies()
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
@@ -38,15 +39,19 @@ const routes: Array<RouteRecordRaw> = [
|
||||
{ path: '/sequence/:id', name: 'sequence', component: MySequenceView },
|
||||
{
|
||||
path: '/partager-des-photos',
|
||||
name: 'upload',
|
||||
component: UploadView
|
||||
name: 'share-pictures',
|
||||
component: SharePicturesView
|
||||
},
|
||||
{
|
||||
path: '/telecharger',
|
||||
name: 'upload-pictures',
|
||||
component: UploadPicturesView
|
||||
}
|
||||
]
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
router.beforeResolve(
|
||||
async (
|
||||
to: RouteLocationNormalized,
|
||||
@@ -56,7 +61,8 @@ router.beforeResolve(
|
||||
const siteLoggedRoutes =
|
||||
to.name === 'my-settings' ||
|
||||
to.name === 'my-sequences' ||
|
||||
to.name === 'sequence'
|
||||
to.name === 'sequence' ||
|
||||
to.name === 'upload-pictures'
|
||||
|
||||
if (siteLoggedRoutes) {
|
||||
if (!isSiteLogged()) goToLoginPage(to.path)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
describe('In the home page', () => {
|
||||
it('click on the link in the header to go to the upload page', () => {
|
||||
it('click on the link in the header to go to the utils page', () => {
|
||||
cy.visit('/')
|
||||
cy.fixture('home').then((homeData) => {
|
||||
cy.contains(homeData.textLinkUpload).click()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('In the upload page', () => {
|
||||
describe('In the utils page', () => {
|
||||
it('go to the login page', () => {
|
||||
cy.visit('partager-des-photos')
|
||||
cy.fixture('upload').then((uploadData) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"addressToSearch": "97 boulevard Voltaire 75011 paris",
|
||||
"textAddressToSelect": "Boulevard Voltaire, Quartier de la Folie-Méricourt, Paris 11e Arrondissement, Paris, Île-de-France, France métropolitaine, 75011, France",
|
||||
"textLinkUpload": "Partager vos photos"
|
||||
"textLinkUpload": "Aide"
|
||||
}
|
||||
|
||||
127
src/tests/unit/components/Button.spec.js
Normal file
127
src/tests/unit/components/Button.spec.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { test, describe, expect } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import Button from '../../../components/Button.vue'
|
||||
import i18n from '../config'
|
||||
describe('Template', () => {
|
||||
describe('Props', () => {
|
||||
test('should have default props', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.icon).toBe(null)
|
||||
expect(wrapper.vm.disabled).toBe(false)
|
||||
expect(wrapper.vm.isLoading).toBe(false)
|
||||
expect(wrapper.vm.text).toBe('')
|
||||
expect(wrapper.vm.tooltip).toBe('')
|
||||
expect(wrapper.vm.look).toBe('')
|
||||
expect(wrapper.vm.type).toBe('button')
|
||||
})
|
||||
})
|
||||
describe('When the component is disabled', () => {
|
||||
test('should render the button with disabled class', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
disabled: true
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="default disabled"')
|
||||
})
|
||||
})
|
||||
describe('When the component is loading', () => {
|
||||
test('should render the button with disabled class', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
isLoading: true
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="default disabled"')
|
||||
})
|
||||
})
|
||||
describe('When the component have an icon', () => {
|
||||
test('should render the button the icon displayed', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
icon: 'my-icon'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('<i')
|
||||
expect(wrapper.html()).contains('class="my-icon icon"')
|
||||
})
|
||||
})
|
||||
describe('When the component have a type submit', () => {
|
||||
test('should render the type to submit', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
type: 'submit'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('type="submit"')
|
||||
})
|
||||
})
|
||||
describe('When the component have tooltip', () => {
|
||||
test('should render the button with a tooltip', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
tooltip: 'my tooltip'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains(
|
||||
'class="tooltip-button">my tooltip</span>'
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('When the component have a text', () => {
|
||||
test('should render the button with a text', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
text: 'My text'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="text">My text</span>')
|
||||
})
|
||||
})
|
||||
describe('When the component have specific look', () => {
|
||||
test('should render the button with a specific class', () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
look: 'my--look'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="my--look default"')
|
||||
})
|
||||
})
|
||||
describe('When the button is trigger', () => {
|
||||
test('should emit', async () => {
|
||||
const wrapper = shallowMount(Button, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
})
|
||||
await wrapper.trigger('click')
|
||||
expect(wrapper.emitted()).toHaveProperty('trigger')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -5,6 +5,9 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { useCookies } from 'vue3-cookies'
|
||||
import fr from '../../../locales/fr.json'
|
||||
import Header from '../../../components/Header.vue'
|
||||
import MyInformation from '../../../views/MyInformationView.vue'
|
||||
import MySettings from '../../../views/MySettingsView.vue'
|
||||
import SharePictures from '../../../views/SharePicturesView.vue'
|
||||
vi.mock('vue-router')
|
||||
vi.mock('vue3-cookies', () => {
|
||||
const mockCookies = {
|
||||
@@ -80,26 +83,6 @@ describe('Template', () => {
|
||||
expect(wrapper.html()).contains('general.header.sequences_text')
|
||||
expect(wrapper.html()).contains('general.header.my_settings_text')
|
||||
})
|
||||
it('should render the component with all links', async () => {
|
||||
vi.spyOn(useCookies().cookies, 'get').mockReturnValue('user_id=id')
|
||||
import.meta.env.VITE_API_URL = 'api-url/'
|
||||
const wrapper = shallowMount(Header, {
|
||||
props: {
|
||||
authEnabled: true,
|
||||
userProfileUrl: 'profil'
|
||||
},
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
$t: (msg) => msg
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('auth/logout')
|
||||
expect(wrapper.html()).contains('path="/mes-informations"')
|
||||
expect(wrapper.html()).contains('path="/mes-parametres"')
|
||||
expect(wrapper.html()).contains('path="/mes-sequences"')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ describe('Template', () => {
|
||||
expect(wrapper.html()).contains('<img')
|
||||
expect(wrapper.html()).contains('src="my-url"')
|
||||
expect(wrapper.html()).contains('<link-stub')
|
||||
expect(wrapper.html()).contains('path="my-url-hd"')
|
||||
expect(wrapper.html()).contains('10 mars 2023')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -34,7 +34,8 @@ describe('Template', () => {
|
||||
})
|
||||
|
||||
expect(wrapper.vm.text).toBe(null)
|
||||
expect(wrapper.vm.path).toBe('')
|
||||
expect(wrapper.vm.route).toStrictEqual({})
|
||||
expect(wrapper.vm.pathExternal).toBe('')
|
||||
expect(wrapper.vm.look).toBe('')
|
||||
expect(wrapper.vm.type).toBe(null)
|
||||
expect(wrapper.vm.alt).toBe('')
|
||||
|
||||
45
src/tests/unit/components/Loader.spec.js
Normal file
45
src/tests/unit/components/Loader.spec.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { it, describe, expect } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import Loader from '../../../components/Loader.vue'
|
||||
import i18n from '../config'
|
||||
|
||||
describe('Template', () => {
|
||||
describe('Props', () => {
|
||||
it('should have default props', () => {
|
||||
const wrapper = shallowMount(Loader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.text).toBe(null)
|
||||
expect(wrapper.vm.look).toBe('sm')
|
||||
expect(wrapper.vm.isLoaded).toBe(true)
|
||||
})
|
||||
})
|
||||
describe('When the component is loading', () => {
|
||||
it('should not have the loaded class', () => {
|
||||
const wrapper = shallowMount(Loader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
isLoaded: false
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="lds-ring sm"')
|
||||
})
|
||||
})
|
||||
describe('When the component have a look', () => {
|
||||
it('should have a look classe', () => {
|
||||
const wrapper = shallowMount(Loader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
look: 'md'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="lds-ring md loaded"')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -26,7 +26,7 @@ describe('Template', () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('pages.upload.button_copy')
|
||||
expect(wrapper.html()).contains('pages.share_pictures.button_copy')
|
||||
})
|
||||
it('should render the view with the button', () => {
|
||||
const wrapper = shallowMount(Terminal, {
|
||||
|
||||
111
src/tests/unit/components/upload/ImportedSection.spec.js
Normal file
111
src/tests/unit/components/upload/ImportedSection.spec.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { it, describe, expect } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import ImportedSection from '../../../../components/upload/ImportedSection.vue'
|
||||
import i18n from '../../config'
|
||||
|
||||
describe('Template', () => {
|
||||
describe('Props', () => {
|
||||
it('should have default props', () => {
|
||||
const wrapper = shallowMount(ImportedSection, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.index).toBe(0)
|
||||
expect(wrapper.vm.sequence).toStrictEqual({})
|
||||
expect(wrapper.vm.picturesCount).toBe(null)
|
||||
expect(wrapper.vm.uploadErrors).toStrictEqual([])
|
||||
expect(wrapper.vm.uploadPictures).toStrictEqual([])
|
||||
})
|
||||
})
|
||||
describe('When the index is 0', () => {
|
||||
it('should have a specific class and wordings', () => {
|
||||
const wrapper = shallowMount(ImportedSection, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
index: 0
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains(
|
||||
'class="information-section first-sequence"'
|
||||
)
|
||||
expect(wrapper.html()).contains('Dernier impor')
|
||||
})
|
||||
})
|
||||
describe('When the sequence have pictures', () => {
|
||||
it('should have a title', () => {
|
||||
const wrapper = shallowMount(ImportedSection, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
sequence: {
|
||||
id: 'id132435',
|
||||
title: 'my title',
|
||||
pictureCount: 3,
|
||||
pictures: [{ id: 'id' }]
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="uploaded-title"')
|
||||
expect(wrapper.html()).contains('Imports my title - 1/3')
|
||||
})
|
||||
it('should have a Link', () => {
|
||||
const wrapper = shallowMount(ImportedSection, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
sequence: {
|
||||
id: 'id132435',
|
||||
title: 'my title',
|
||||
pictureCount: 3,
|
||||
pictures: [{ id: 'id' }]
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('<link-stub')
|
||||
expect(wrapper.html()).contains('text="Voir la sequence"')
|
||||
})
|
||||
})
|
||||
describe('When there are uploaded pictures', () => {
|
||||
it('should have a list of pictures', () => {
|
||||
const wrapper = shallowMount(ImportedSection, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
uploadPictures: [{ name: 'my name1' }, { name: 'my name2' }]
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('name="my name1"')
|
||||
expect(wrapper.html()).contains('name="my name2"')
|
||||
expect(wrapper.html()).contains('text=" Image téléchargée"')
|
||||
expect(wrapper.html()).contains('look="success"')
|
||||
expect(wrapper.html()).contains('<picture-item-stub')
|
||||
})
|
||||
})
|
||||
describe('When there are uploaded errors', () => {
|
||||
it('should have a list of errors', () => {
|
||||
const wrapper = shallowMount(ImportedSection, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
uploadErrors: [
|
||||
{ name: 'my name1', message: 'my message1' },
|
||||
{ name: 'my name2', message: 'my message2' }
|
||||
]
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('name="my name1"')
|
||||
expect(wrapper.html()).contains('name="my name2"')
|
||||
expect(wrapper.html()).contains('text="my message1"')
|
||||
expect(wrapper.html()).contains('text="my message2"')
|
||||
expect(wrapper.html()).contains('look="error"')
|
||||
expect(wrapper.html()).contains('<picture-item-stub')
|
||||
})
|
||||
})
|
||||
})
|
||||
47
src/tests/unit/components/upload/PictureItem.spec.js
Normal file
47
src/tests/unit/components/upload/PictureItem.spec.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { it, describe, expect } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import PictureItem from '../../../../components/upload/PictureItem.vue'
|
||||
import i18n from '../../config'
|
||||
|
||||
describe('Template', () => {
|
||||
describe('Props', () => {
|
||||
it('should have default props', () => {
|
||||
const wrapper = shallowMount(PictureItem, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.name).toBe('')
|
||||
expect(wrapper.vm.text).toStrictEqual('')
|
||||
expect(wrapper.vm.look).toBe('default')
|
||||
})
|
||||
})
|
||||
describe('When the component have a look', () => {
|
||||
it('should have a specific class', () => {
|
||||
const wrapper = shallowMount(PictureItem, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
look: 'success'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="uploaded-picture-item success"')
|
||||
})
|
||||
})
|
||||
describe('When the component have a name and a text', () => {
|
||||
it('should have a specific class', () => {
|
||||
const wrapper = shallowMount(PictureItem, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
name: 'name',
|
||||
text: 'text'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('class="uploaded-information"')
|
||||
expect(wrapper.html()).contains('name - text')
|
||||
})
|
||||
})
|
||||
})
|
||||
68
src/tests/unit/components/upload/UploadLoader.spec.js
Normal file
68
src/tests/unit/components/upload/UploadLoader.spec.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { it, describe, expect } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import UploadLoader from '../../../../components/upload/UploadLoader.vue'
|
||||
import i18n from '../../config'
|
||||
|
||||
describe('Template', () => {
|
||||
describe('Props', () => {
|
||||
it('should have default props', () => {
|
||||
const wrapper = shallowMount(UploadLoader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.loadPercentage).toBe('0%')
|
||||
expect(wrapper.vm.loadTextSize).toBe('0 Mo')
|
||||
expect(wrapper.vm.isLoaded).toBe(false)
|
||||
expect(wrapper.vm.picturesCount).toBe(null)
|
||||
expect(wrapper.vm.uploadedSequences).toStrictEqual([])
|
||||
})
|
||||
})
|
||||
describe('When the component have a percentage equal to 100%', () => {
|
||||
it('should have a button to do a new upload', () => {
|
||||
const wrapper = shallowMount(UploadLoader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
loadPercentage: '100%'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('button-stub')
|
||||
expect(wrapper.html()).contains('text="Nouveau téléchargement"')
|
||||
})
|
||||
})
|
||||
describe('When the component have an uploaded sequence', () => {
|
||||
it('should render the uploaded sequences information', () => {
|
||||
const wrapper = shallowMount(UploadLoader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
uploadedSequences: [{ pictureSize: '2345 Mo' }],
|
||||
loadTextSize: '2345 Mo',
|
||||
loadPercentage: '97%'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('Transfert en cours...')
|
||||
expect(wrapper.html()).contains('2345 Mo/2345 Mo')
|
||||
})
|
||||
describe('When the loading is completed', () => {
|
||||
it('should render the loading ended information', () => {
|
||||
const wrapper = shallowMount(UploadLoader, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
},
|
||||
props: {
|
||||
uploadedSequences: [{ pictureSize: '2345 Mo' }],
|
||||
loadTextSize: '2345 Mo',
|
||||
loadPercentage: '100%'
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('text="Nouveau téléchargement"')
|
||||
expect(wrapper.html()).contains('Transfert terminé !')
|
||||
expect(wrapper.html()).contains('2345 Mo/2345 Mo')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
183
src/tests/unit/utils.spec.js
Normal file
183
src/tests/unit/utils.spec.js
Normal file
@@ -0,0 +1,183 @@
|
||||
import { it, describe, expect } from 'vitest'
|
||||
import {
|
||||
imageStatus,
|
||||
photoToDeleteOrPatchSelected,
|
||||
spliceIntoChunks,
|
||||
formatPaginationItems
|
||||
} from '../../views/utils/sequence/index'
|
||||
import {
|
||||
formatPictureSize,
|
||||
formatTextSize
|
||||
} from '../../views/utils/upload/index'
|
||||
import { getAuthRoute } from '../../utils/auth'
|
||||
import { img, getPicId } from '../../utils/image'
|
||||
import title from '../../utils/index'
|
||||
|
||||
describe('imageStatus', () => {
|
||||
it('should render the "status" value', () => {
|
||||
const sequenceStatus = 'hidden'
|
||||
const imgStatus = 'not hidden'
|
||||
expect(imageStatus(imgStatus, sequenceStatus)).toEqual('hidden')
|
||||
})
|
||||
it('should render the "sequenceStatus" value', () => {
|
||||
const sequenceStatus = 'not hidden'
|
||||
const status = 'hidden'
|
||||
expect(imageStatus(status, sequenceStatus)).toEqual('hidden')
|
||||
})
|
||||
})
|
||||
|
||||
describe('photoToDeleteOrPatchSelected', () => {
|
||||
it('should render true', () => {
|
||||
const imagesToDelete = ['1', '2']
|
||||
const item = {
|
||||
assets: { thumb: { href: '' }, hd: { href: '' } },
|
||||
properties: { created: new Date(), 'geovisio:status': '' },
|
||||
id: '1',
|
||||
bbox: [1, 3]
|
||||
}
|
||||
expect(photoToDeleteOrPatchSelected(item, imagesToDelete)).toEqual(true)
|
||||
})
|
||||
it('should render false', () => {
|
||||
const imagesToDelete = ['1', '2']
|
||||
const item = {
|
||||
assets: { thumb: { href: '' }, hd: { href: '' } },
|
||||
properties: { created: new Date(), 'geovisio:status': '' },
|
||||
id: '3',
|
||||
bbox: [1, 3]
|
||||
}
|
||||
expect(photoToDeleteOrPatchSelected(item, imagesToDelete)).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('spliceIntoChunks', () => {
|
||||
it('should render an chunked array of array with 4 elements max', () => {
|
||||
const array = ['123', '345', '6777', '0000', '66666', '222222', '9393888']
|
||||
const chunkSize = 4
|
||||
expect(spliceIntoChunks(array, chunkSize)).toEqual([
|
||||
['123', '345', '6777', '0000'],
|
||||
['66666', '222222', '9393888']
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatPaginationItems', () => {
|
||||
it('should render the "rel" links formated and without the "left" element', () => {
|
||||
const links = [
|
||||
{
|
||||
href: 'http://localhost:5000/api/',
|
||||
rel: 'root',
|
||||
title: 'Instance catalog',
|
||||
type: 'application/json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff5-4d70-88fa-6b2be3357709',
|
||||
rel: 'parent',
|
||||
type: 'application/json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff5-4d70-88fa-6b2be3357709/items?limit=100',
|
||||
rel: 'self',
|
||||
type: 'application/geo+json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff5-4d70-88fa-6b2be3357709/items?limit=100',
|
||||
rel: 'first',
|
||||
type: 'application/geo+json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff…a-6b2be3357709/items?limit=100&startAfterRank=100',
|
||||
rel: 'next',
|
||||
type: 'application/geo+json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff…-6b2be3357709/items?limit=100&startAfterRank=1023',
|
||||
rel: 'last',
|
||||
type: 'application/geo+json'
|
||||
}
|
||||
]
|
||||
expect(formatPaginationItems(links)).toEqual([
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff5-4d70-88fa-6b2be3357709/items?limit=100',
|
||||
rel: 'double-left',
|
||||
type: 'application/geo+json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff…a-6b2be3357709/items?limit=100&startAfterRank=100',
|
||||
rel: 'right',
|
||||
type: 'application/geo+json'
|
||||
},
|
||||
{
|
||||
href: 'http://localhost:5000/api/collections/076e04c2-5ff…-6b2be3357709/items?limit=100&startAfterRank=1023',
|
||||
rel: 'double-right',
|
||||
type: 'application/geo+json'
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatPictureSize', () => {
|
||||
it('should render the size number', () => {
|
||||
const size = 560673
|
||||
expect(formatPictureSize(size)).toEqual(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatTextSize', () => {
|
||||
const size = 2260121
|
||||
it('should render the size text formated in ko', () => {
|
||||
expect(formatTextSize(size, 1)).toEqual('2207.15 Ko')
|
||||
})
|
||||
it('should render the size text formated in mo', () => {
|
||||
expect(formatTextSize(size, 2)).toEqual('2.16 Mo')
|
||||
})
|
||||
it('should render the size text formated in go', () => {
|
||||
expect(formatTextSize(size, 3)).toEqual('0 Go')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAuthRoute', () => {
|
||||
it('should render auth route', () => {
|
||||
import.meta.env.VITE_API_URL = 'my-url/'
|
||||
const authRoute = 'auth'
|
||||
const nextRoute = 'mes-sequences'
|
||||
const returnedRoute = `${
|
||||
import.meta.env.VITE_API_URL
|
||||
}api/${authRoute}?next_url=${encodeURIComponent(
|
||||
`${location.protocol}//${location.host}${nextRoute}`
|
||||
)}`
|
||||
expect(getAuthRoute(authRoute, nextRoute)).toEqual(returnedRoute)
|
||||
})
|
||||
})
|
||||
|
||||
describe('img', () => {
|
||||
it('should render the formated img path', () => {
|
||||
const name = 'my-img'
|
||||
expect(img(name)).contains('src/assets/images')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPicId', () => {
|
||||
it('should return the Id of the picture in the url', () => {
|
||||
const url = 'http://dummy.com?pic=3205340583&test'
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
href: url
|
||||
},
|
||||
writable: true // possibility to override
|
||||
})
|
||||
expect(getPicId()).toEqual('3205340583')
|
||||
})
|
||||
})
|
||||
|
||||
describe('title', () => {
|
||||
it('should return the formated title with instance name', () => {
|
||||
import.meta.env.VITE_INSTANCE_NAME = 'my instance'
|
||||
const myTitle = 'my title'
|
||||
expect(title(myTitle)).toEqual('my title my instance')
|
||||
})
|
||||
it('should return the formated title without instance name', () => {
|
||||
import.meta.env.VITE_INSTANCE_NAME = ''
|
||||
const myTitle = 'my title'
|
||||
expect(title(myTitle)).toEqual('my title')
|
||||
})
|
||||
})
|
||||
@@ -60,7 +60,7 @@ describe('Template', () => {
|
||||
await flushPromises()
|
||||
expect(axios.get).toHaveBeenCalledWith('api/users/me/catalog')
|
||||
expect(wrapper.vm.userSequences).toEqual([])
|
||||
expect(wrapper.html()).contains('general.header.contribute_text')
|
||||
expect(wrapper.html()).contains('general.header.upload_text')
|
||||
})
|
||||
|
||||
it('should render the view with a loader loading', async () => {
|
||||
@@ -75,7 +75,7 @@ describe('Template', () => {
|
||||
await flushPromises()
|
||||
expect(axios.get).toHaveBeenCalledWith('api/users/me/catalog')
|
||||
expect(wrapper.vm.userSequences).toEqual([])
|
||||
expect(wrapper.html()).contains('general.header.contribute_text')
|
||||
expect(wrapper.html()).contains('general.header.upload_text')
|
||||
})
|
||||
|
||||
it('should render the view with a sequence in the list', async () => {
|
||||
@@ -149,7 +149,6 @@ describe('Methods', () => {
|
||||
'[data-test="button-sort-title"]'
|
||||
)
|
||||
await buttonWrapper.vm.$emit('trigger')
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('title')
|
||||
expect(wrapper.vm.userSequences[0]).toEqual(
|
||||
mockResponseSequencesToSort[1]
|
||||
|
||||
@@ -46,14 +46,13 @@ describe('Template', () => {
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith('api/users/me/tokens')
|
||||
expect(wrapper.vm.userTokens).toEqual(mockResponseTokens)
|
||||
expect(wrapper.html()).contains('•••••••••••••••••••••••••••••••')
|
||||
expect(wrapper.html()).contains('icon="bi bi-eye"')
|
||||
expect(wrapper.html()).contains('look="button--rounded"')
|
||||
expect(wrapper.html()).contains('look="no-text"')
|
||||
expect(wrapper.html()).contains(
|
||||
'icon="bi bi-clipboard-plus" disabled="false" isloading="false" text="pages.upload.button_copy" tooltip="" look="button--white"'
|
||||
'icon="bi bi-clipboard-plus" disabled="false" isloading="false" text="pages.share_pictures.button_copy" tooltip="" look="button--white"'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { it, describe, expect } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import UploadView from '../../../views/UploadView.vue'
|
||||
import SharePicturesView from '../../../views/SharePicturesView.vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import fr from '../../../locales/fr.json'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
@@ -20,7 +20,7 @@ const router = createRouter({
|
||||
})
|
||||
describe('Template', () => {
|
||||
it('should render the view with the button link', async () => {
|
||||
const wrapper = shallowMount(UploadView, {
|
||||
const wrapper = shallowMount(SharePicturesView, {
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
@@ -34,12 +34,12 @@ describe('Template', () => {
|
||||
expect(wrapper.html()).contains('<link')
|
||||
expect(wrapper.html()).contains('path="')
|
||||
expect(wrapper.html()).contains('/auth/login')
|
||||
expect(wrapper.html()).contains('look="button"')
|
||||
expect(wrapper.html()).contains('look="button button--blue"')
|
||||
expect(wrapper.html()).contains('type="external"')
|
||||
})
|
||||
it('should render the view without the button link', async () => {
|
||||
import.meta.env.VITE_API_URL = 'api-url/'
|
||||
const wrapper = shallowMount(UploadView, {
|
||||
const wrapper = shallowMount(SharePicturesView, {
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
@@ -50,6 +50,6 @@ describe('Template', () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).not.toContain('pages.upload.sub_title')
|
||||
expect(wrapper.html()).not.toContain('pages.share_pictures.sub_title')
|
||||
})
|
||||
})
|
||||
50
src/tests/unit/views/UploadPicturesView.spec.js
Normal file
50
src/tests/unit/views/UploadPicturesView.spec.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { it, describe, expect, vi } from 'vitest'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import UploadPicturesView from '../../../views/UploadPicturesView.vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import i18n from '../config'
|
||||
import InputUpload from '../../../components/InputUpload.vue'
|
||||
import Button from '../../../components/Button.vue'
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: []
|
||||
})
|
||||
describe('Template', () => {
|
||||
it('should render the view with the input upload and the good wordings', () => {
|
||||
const wrapper = shallowMount(UploadPicturesView, {
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
$t: (msg) => msg
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.html()).contains('pages.upload.title')
|
||||
expect(wrapper.html()).contains('<input-upload-stub')
|
||||
expect(wrapper.html()).contains('pages.upload.input_label')
|
||||
expect(wrapper.html()).contains('<button-stub')
|
||||
expect(wrapper.html()).contains('text="pages.upload.button_text"')
|
||||
})
|
||||
describe('trigger addPictures', () => {
|
||||
it('should trigger to add pictures', async () => {
|
||||
const wrapper = shallowMount(UploadPicturesView, {
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
$t: (msg) => msg
|
||||
},
|
||||
components: {
|
||||
InputUpload
|
||||
}
|
||||
}
|
||||
})
|
||||
const spy = vi.spyOn(wrapper.vm, 'addPictures')
|
||||
const wrapperInputUpload = wrapper.findComponent(InputUpload)
|
||||
await wrapperInputUpload.trigger('trigger')
|
||||
await wrapperInputUpload.vm.$emit('trigger', [{}, {}])
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
expect(wrapper.html()).contains('2 fichiers')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import moment from 'moment'
|
||||
import 'moment/dist/locale/fr'
|
||||
|
||||
function formatDate(date: Date, formatType: string): string {
|
||||
const formatDate = moment(date)
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<template>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="https://cdn.jsdelivr.net/npm/geovisio@2.1.1/build/index.css"
|
||||
/>
|
||||
<main class="entry-page">
|
||||
<section id="viewer" class="entry-viewer">
|
||||
<div v-if="mapIsLoaded" class="entry-report-button">
|
||||
@@ -51,28 +46,29 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.entry-page {
|
||||
display: flex;
|
||||
}
|
||||
.entry-viewer {
|
||||
font-size: initial;
|
||||
width: 100vw;
|
||||
position: relative;
|
||||
min-height: calc(100vh - 8rem);
|
||||
min-height: calc(100vh - #{toRem(8)});
|
||||
}
|
||||
.gvs-has-map .entry-report-button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 2rem;
|
||||
right: toRem(1);
|
||||
top: toRem(2);
|
||||
z-index: 1;
|
||||
}
|
||||
.gvs-focus-map .entry-report-button {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.entry-page {
|
||||
padding-top: 11rem;
|
||||
padding-top: toRem(11);
|
||||
overflow: hidden;
|
||||
}
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
@@ -86,7 +82,7 @@ onMounted(async () => {
|
||||
@supports not (-webkit-touch-callout: none) {
|
||||
/* CSS for other than iOS devices */
|
||||
.entry-viewer {
|
||||
min-height: calc(100vh - 17rem);
|
||||
min-height: calc(100vh - #{toRem(17)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,30 +12,30 @@ const myAccountUrl = computed<string>(
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss" scoped>
|
||||
.entry-page {
|
||||
padding-right: 4rem;
|
||||
padding-left: 8rem;
|
||||
padding-right: toRem(4);
|
||||
padding-left: toRem(8);
|
||||
}
|
||||
.iframe {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 9rem);
|
||||
min-height: calc(100vh - #{toRem(9)});
|
||||
}
|
||||
|
||||
@media (max-width: 848px) {
|
||||
@media (max-width: toRem(84.8)) {
|
||||
.entry-page {
|
||||
padding-top: 4rem;
|
||||
padding-top: toRem(4);
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.entry-page {
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.entry-page {
|
||||
padding-top: 14rem;
|
||||
padding-top: toRem(14);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<template>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="https://cdn.jsdelivr.net/npm/geovisio@2.1.1/build/index.css"
|
||||
/>
|
||||
<main :class="['entry-page', { 'menu-is-open': menuIsOpen }]">
|
||||
<div class="button-close">
|
||||
<Button
|
||||
@@ -156,7 +151,7 @@
|
||||
:selected="photoToDeleteOrPatchSelected(item, picturesToDelete)"
|
||||
:selected-on-map="itemSelected === item.id"
|
||||
:status="
|
||||
imageStatus(item.properties['geovisio:status'], sequence)
|
||||
imageStatus(item.properties['geovisio:status'], sequence.status)
|
||||
"
|
||||
@trigger="selectImageAndMove(item)"
|
||||
/>
|
||||
@@ -176,7 +171,7 @@
|
||||
<Toast :text="toastText" :look="toastLook" @trigger="toastText = ''" />
|
||||
</div>
|
||||
<div v-else class="menu-right wrapper-loader">
|
||||
<Loader />
|
||||
<Loader look="sm" :is-loaded="false" />
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
@@ -554,11 +549,12 @@ async function patchOrDeleteCollectionItems(
|
||||
.entry-viewer {
|
||||
width: 50vw;
|
||||
position: relative;
|
||||
height: calc(100vh - 8rem);
|
||||
height: calc(100vh - #{toRem(8)});
|
||||
font-size: 137.5%;
|
||||
}
|
||||
.menu-right {
|
||||
width: 50vw;
|
||||
height: calc(100vh - 8rem);
|
||||
height: calc(100vh - #{toRem(8)});
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 4px 20px 0px #00000033;
|
||||
}
|
||||
@@ -572,13 +568,13 @@ async function patchOrDeleteCollectionItems(
|
||||
align-items: center;
|
||||
}
|
||||
.disable-button {
|
||||
margin-right: 1rem;
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
.collapse {
|
||||
&:first-child {
|
||||
@include text(s-regular);
|
||||
color: var(--grey-dark);
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: toRem(1);
|
||||
}
|
||||
&.hidden {
|
||||
opacity: 0.4;
|
||||
@@ -594,28 +590,28 @@ async function patchOrDeleteCollectionItems(
|
||||
.wrapper-info-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 1rem;
|
||||
margin-top: toRem(1);
|
||||
&:first-child {
|
||||
border-right: 0.1rem solid var(--grey-dark);
|
||||
padding-right: 2rem;
|
||||
border-right: toRem(0.1) solid var(--grey-dark);
|
||||
padding-right: toRem(2);
|
||||
}
|
||||
&:nth-child(2) {
|
||||
padding-left: 2rem;
|
||||
padding-left: toRem(2);
|
||||
}
|
||||
}
|
||||
.title {
|
||||
@include text(h2);
|
||||
color: var(--grey-dark);
|
||||
margin-right: 1rem;
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
.button-close {
|
||||
display: none;
|
||||
}
|
||||
.menu-top {
|
||||
margin: 2rem 2rem 0;
|
||||
padding: 1rem 2rem;
|
||||
border: 0.1rem solid var(--grey);
|
||||
border-radius: 0.5rem;
|
||||
margin: toRem(2) toRem(2) 0;
|
||||
padding: toRem(1) toRem(2);
|
||||
border: toRem(0.1) solid var(--grey);
|
||||
border-radius: toRem(0.5);
|
||||
background-color: var(--blue-semi);
|
||||
}
|
||||
.header-menu {
|
||||
@@ -623,21 +619,21 @@ async function patchOrDeleteCollectionItems(
|
||||
justify-content: space-between;
|
||||
}
|
||||
.sequence-status {
|
||||
border-radius: 3rem;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-right: 2rem;
|
||||
border-radius: toRem(3);
|
||||
padding: toRem(0.5) toRem(1);
|
||||
margin-right: toRem(2);
|
||||
color: var(--white);
|
||||
&.ready {
|
||||
background-color: var(--orange);
|
||||
border: 0.1rem solid var(--orange);
|
||||
border: toRem(0.1) solid var(--orange);
|
||||
}
|
||||
&.waiting-for-process {
|
||||
background-color: var(--yellow);
|
||||
border: 0.1rem solid var(--yellow);
|
||||
border: toRem(0.1) solid var(--yellow);
|
||||
}
|
||||
&.hidden {
|
||||
background-color: var(--blue-geovisio);
|
||||
border: 0.1rem solid var(--blue-geovisio);
|
||||
border: toRem(0.1) solid var(--blue-geovisio);
|
||||
}
|
||||
}
|
||||
.button-collapse {
|
||||
@@ -650,20 +646,20 @@ async function patchOrDeleteCollectionItems(
|
||||
.bi-plus,
|
||||
.bi-dash {
|
||||
color: var(--grey-dark);
|
||||
font-size: 3rem;
|
||||
font-size: toRem(3);
|
||||
}
|
||||
|
||||
.photos-wrapper {
|
||||
padding: 1rem 0rem 2rem 1rem;
|
||||
padding: toRem(1) 0rem toRem(2) toRem(1);
|
||||
height: calc(100vh - v-bind(menuHeight));
|
||||
}
|
||||
.delete-all {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-left: 1rem;
|
||||
margin-right: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-left: toRem(1);
|
||||
margin-right: toRem(2);
|
||||
margin-bottom: toRem(1);
|
||||
}
|
||||
.wrapper-select {
|
||||
display: flex;
|
||||
@@ -673,12 +669,12 @@ async function patchOrDeleteCollectionItems(
|
||||
@include text(xs-regular);
|
||||
}
|
||||
.photo-selected-separator {
|
||||
margin-right: 0.5rem;
|
||||
margin-left: 0.5rem;
|
||||
margin-right: toRem(0.5);
|
||||
margin-left: toRem(0.5);
|
||||
}
|
||||
.button-hidde {
|
||||
margin-right: 1rem;
|
||||
margin-left: 1rem;
|
||||
margin-right: toRem(1);
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
.delete-all-text {
|
||||
@include text(xs-r-regular);
|
||||
@@ -696,29 +692,29 @@ async function patchOrDeleteCollectionItems(
|
||||
padding: 0;
|
||||
}
|
||||
.photo-item {
|
||||
width: calc(33% - 2rem);
|
||||
width: calc(33% - #{toRem(2)});
|
||||
height: fit-content;
|
||||
margin: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin: toRem(1);
|
||||
border-radius: toRem(0.5);
|
||||
background-color: var(--grey);
|
||||
}
|
||||
.no-photo {
|
||||
@include text(s-regular);
|
||||
text-align: center;
|
||||
margin-top: 10rem;
|
||||
margin-top: toRem(10);
|
||||
color: var(--grey-dark);
|
||||
}
|
||||
.entry-pagination {
|
||||
margin-top: 2rem;
|
||||
margin-top: toRem(2);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
@media (max-width: toRem(102.4)) {
|
||||
.header-menu {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: toRem(1);
|
||||
}
|
||||
.block-collapse {
|
||||
flex-direction: column;
|
||||
@@ -735,10 +731,10 @@ async function patchOrDeleteCollectionItems(
|
||||
}
|
||||
}
|
||||
.photo-item {
|
||||
width: calc(50% - 2rem);
|
||||
width: calc(50% - #{toRem(2)});
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.photo-item {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
@@ -748,10 +744,10 @@ async function patchOrDeleteCollectionItems(
|
||||
margin-right: 0;
|
||||
}
|
||||
.wrapper-button {
|
||||
margin-top: 1rem;
|
||||
margin-top: toRem(1);
|
||||
}
|
||||
.photo-list {
|
||||
padding-right: 2rem;
|
||||
padding-right: toRem(2);
|
||||
}
|
||||
.delete-all {
|
||||
text-align: left;
|
||||
@@ -761,16 +757,16 @@ async function patchOrDeleteCollectionItems(
|
||||
align-items: initial;
|
||||
}
|
||||
.wrapper-photo-selected:nth-child(2) {
|
||||
margin-top: 0.5rem;
|
||||
margin-top: toRem(0.5);
|
||||
}
|
||||
.photo-selected-separator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.entry-page {
|
||||
height: calc(100vh - 11rem);
|
||||
height: calc(100vh - #{toRem(11)});
|
||||
overflow: hidden;
|
||||
}
|
||||
.entry-viewer {
|
||||
@@ -779,7 +775,7 @@ async function patchOrDeleteCollectionItems(
|
||||
z-index: 1;
|
||||
}
|
||||
.menu-right {
|
||||
padding-top: 11rem;
|
||||
padding-top: toRem(11);
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -794,15 +790,15 @@ async function patchOrDeleteCollectionItems(
|
||||
.button-close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 22rem;
|
||||
top: toRem(22);
|
||||
z-index: 3;
|
||||
background-color: var(--black);
|
||||
height: 5rem;
|
||||
height: toRem(5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
border-top-left-radius: toRem(0.5);
|
||||
border-bottom-left-radius: toRem(0.5);
|
||||
}
|
||||
.menu-is-open {
|
||||
.menu-right {
|
||||
@@ -812,19 +808,19 @@ async function patchOrDeleteCollectionItems(
|
||||
width: auto;
|
||||
}
|
||||
.button-close {
|
||||
left: calc(20vw - 3rem);
|
||||
left: calc(20vw - #{toRem(3)});
|
||||
right: initial;
|
||||
}
|
||||
}
|
||||
.entry-pagination {
|
||||
margin-top: 1rem;
|
||||
margin-top: toRem(1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1900px) {
|
||||
.menu-right {
|
||||
width: initial;
|
||||
max-width: 100rem;
|
||||
max-width: toRem(100);
|
||||
}
|
||||
.entry-viewer {
|
||||
width: 100%;
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
{{ $t('pages.sequences.no_sequences_text') }}
|
||||
</p>
|
||||
<Link
|
||||
:text="$t('general.header.contribute_text')"
|
||||
:text="$t('general.header.upload_text')"
|
||||
look="button"
|
||||
:route="{ name: 'share-pictures' }"
|
||||
/>
|
||||
@@ -194,19 +194,19 @@ onMounted(async () => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.entry-page {
|
||||
padding-right: 8rem;
|
||||
padding-left: 8rem;
|
||||
padding-top: 11rem;
|
||||
min-height: calc(100vh - 8rem);
|
||||
padding-right: toRem(8);
|
||||
padding-left: toRem(8);
|
||||
padding-top: toRem(11);
|
||||
min-height: calc(100vh - #{toRem(8)});
|
||||
}
|
||||
|
||||
.sequences-title {
|
||||
@include text(h1);
|
||||
margin-bottom: 4rem;
|
||||
margin-bottom: toRem(4);
|
||||
}
|
||||
.sequence-list {
|
||||
box-shadow: 0px 2px 30px 0px #0000000f;
|
||||
border-radius: 2rem;
|
||||
border-radius: toRem(2);
|
||||
padding: 0;
|
||||
}
|
||||
.sequence-item {
|
||||
@@ -217,18 +217,18 @@ onMounted(async () => {
|
||||
margin: auto;
|
||||
background-color: var(--blue-pale);
|
||||
&:last-child {
|
||||
border-bottom-right-radius: 2rem;
|
||||
border-bottom-left-radius: 2rem;
|
||||
border-bottom-right-radius: toRem(2);
|
||||
border-bottom-left-radius: toRem(2);
|
||||
.button-item {
|
||||
border-bottom-right-radius: 2rem;
|
||||
border-bottom-left-radius: 2rem;
|
||||
border-bottom-right-radius: toRem(2);
|
||||
border-bottom-left-radius: toRem(2);
|
||||
}
|
||||
}
|
||||
&:first-child {
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem 3rem;
|
||||
border-bottom: 0.1rem solid var(--grey);
|
||||
border-radius: 2rem 2rem 0rem 0rem;
|
||||
margin-bottom: toRem(1);
|
||||
padding: toRem(1) toRem(3);
|
||||
border-bottom: toRem(0.1) solid var(--grey);
|
||||
border-radius: toRem(2) toRem(2) 0rem 0rem;
|
||||
background-color: var(--white);
|
||||
}
|
||||
&:nth-child(2n) {
|
||||
@@ -244,11 +244,11 @@ onMounted(async () => {
|
||||
}
|
||||
.wrapper-thumb-hover {
|
||||
display: none;
|
||||
border-radius: 0.3rem;
|
||||
border: 1rem solid var(--grey);
|
||||
border-radius: toRem(0.3);
|
||||
border: toRem(1) solid var(--grey);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 15rem;
|
||||
height: toRem(15);
|
||||
z-index: 1;
|
||||
}
|
||||
.thumb-hover {
|
||||
@@ -263,30 +263,30 @@ onMounted(async () => {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 0.5rem;
|
||||
border-radius: toRem(0.5);
|
||||
position: relative;
|
||||
}
|
||||
.button-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 2rem 3rem;
|
||||
padding: toRem(2) toRem(3);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
& > * {
|
||||
padding: 1rem;
|
||||
padding: toRem(1);
|
||||
text-align: initial;
|
||||
width: 31%;
|
||||
color: var(--black);
|
||||
}
|
||||
> :first-child {
|
||||
color: var(--blue);
|
||||
width: 6rem;
|
||||
width: toRem(6);
|
||||
}
|
||||
& > :first-child {
|
||||
padding: 0;
|
||||
margin-right: 2rem;
|
||||
margin-right: toRem(2);
|
||||
}
|
||||
& > :nth-child(2) {
|
||||
color: var(--blue);
|
||||
@@ -299,48 +299,49 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
.bi-images {
|
||||
margin-right: 0.5rem;
|
||||
margin-right: toRem(0.5);
|
||||
}
|
||||
.sequence-header-item {
|
||||
width: 31%;
|
||||
&:first-child {
|
||||
margin-right: 2rem;
|
||||
margin-right: toRem(2);
|
||||
}
|
||||
&:first-child {
|
||||
width: 6rem;
|
||||
width: toRem(6);
|
||||
}
|
||||
}
|
||||
.no-sequence {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 4rem;
|
||||
padding-top: toRem(2);
|
||||
padding-bottom: toRem(4);
|
||||
margin: auto;
|
||||
width: fit-content;
|
||||
text-align: center;
|
||||
@include text(m-regular);
|
||||
}
|
||||
.no-sequence-text {
|
||||
margin-bottom: 4rem;
|
||||
margin-bottom: toRem(4);
|
||||
}
|
||||
.loader {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin-top: 20rem;
|
||||
margin-top: toRem(20);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.entry-page {
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
padding-top: 14rem;
|
||||
min-height: calc(100vh - 11rem);
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
padding-top: toRem(14);
|
||||
min-height: calc(100vh - #{toRem(11)});
|
||||
}
|
||||
.button-item,
|
||||
.sequence-item:first-child {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: toRem(1);
|
||||
padding-left: toRem(1);
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.button-item {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -356,8 +357,8 @@ onMounted(async () => {
|
||||
display: none;
|
||||
}
|
||||
.sequence-item {
|
||||
border-top-right-radius: 1rem;
|
||||
border-top-left-radius: 1rem;
|
||||
border-top-right-radius: toRem(1);
|
||||
border-top-left-radius: toRem(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="entry-button">
|
||||
<Button
|
||||
:data-test="`button-eye-${i}`"
|
||||
look="button--rounded"
|
||||
look="no-text"
|
||||
:tooltip="$t('pages.settings.setting_tooltip')"
|
||||
:icon="
|
||||
!item.token || item.isHidden ? 'bi bi-eye' : 'bi bi-eye-slash'
|
||||
@@ -26,7 +26,7 @@
|
||||
<Button
|
||||
:data-test="`button-copy-${i}`"
|
||||
look="button--white"
|
||||
:text="$t('pages.upload.button_copy')"
|
||||
:text="$t('pages.share_pictures.button_copy')"
|
||||
:icon="
|
||||
item.copied
|
||||
? 'bi bi-clipboard-check-fill'
|
||||
@@ -86,31 +86,31 @@ onMounted(async () => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.entry-page {
|
||||
padding-right: 8rem;
|
||||
padding-left: 8rem;
|
||||
padding-top: 11rem;
|
||||
min-height: calc(100vh - 8rem);
|
||||
padding-right: toRem(8);
|
||||
padding-left: toRem(8);
|
||||
padding-top: toRem(11);
|
||||
min-height: calc(100vh - #{toRem(8)});
|
||||
}
|
||||
.settings-title {
|
||||
@include text(h1);
|
||||
margin-bottom: 4rem;
|
||||
margin-bottom: toRem(4);
|
||||
}
|
||||
.settings-list {
|
||||
padding-left: 0;
|
||||
}
|
||||
.settings-item-title {
|
||||
font-size: 1.8rem;
|
||||
font-size: toRem(1.8);
|
||||
}
|
||||
.settings-item {
|
||||
font-size: 1.4rem;
|
||||
border: 0.1rem solid var(--grey);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
font-size: toRem(1.4);
|
||||
border: toRem(0.1) solid var(--grey);
|
||||
border-radius: toRem(0.5);
|
||||
padding: toRem(1.5);
|
||||
margin-bottom: toRem(2);
|
||||
}
|
||||
.item-information {
|
||||
height: 5rem;
|
||||
margin-top: 1rem;
|
||||
height: toRem(5);
|
||||
margin-top: toRem(1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -120,17 +120,18 @@ onMounted(async () => {
|
||||
width: 80%;
|
||||
}
|
||||
.token {
|
||||
margin-top: toRem(0.4);
|
||||
width: 100%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.entry-button {
|
||||
margin-right: 1rem;
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
@media (max-width: toRem(102.4)) {
|
||||
.settings-item {
|
||||
position: relative;
|
||||
height: 14rem;
|
||||
height: toRem(14);
|
||||
}
|
||||
.item-information {
|
||||
height: initial;
|
||||
@@ -138,21 +139,21 @@ onMounted(async () => {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.reveal-token {
|
||||
margin-right: 2rem;
|
||||
margin-right: toRem(2);
|
||||
}
|
||||
.entry-copy-button {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
bottom: toRem(1);
|
||||
right: toRem(1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.entry-page {
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
padding-top: 14rem;
|
||||
min-height: calc(100vh - 11rem);
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
padding-top: toRem(14);
|
||||
min-height: calc(100vh - #{toRem(11)});
|
||||
}
|
||||
.settings-item {
|
||||
height: initial;
|
||||
@@ -162,7 +163,7 @@ onMounted(async () => {
|
||||
}
|
||||
.token {
|
||||
max-width: initial;
|
||||
width: calc(100% - 3.5rem);
|
||||
width: calc(100% - #{toRem(3.5)});
|
||||
}
|
||||
.entry-copy-button {
|
||||
display: none;
|
||||
|
||||
@@ -1,9 +1,67 @@
|
||||
<template>
|
||||
<main class="entry-page">
|
||||
<section>
|
||||
<div class="information-wrapper">
|
||||
<div class="information">
|
||||
<h1 class="page-title">{{ $t('pages.share_pictures.title') }}</h1>
|
||||
<p
|
||||
class="page-description"
|
||||
v-html="$t('pages.share_pictures.description')"
|
||||
></p>
|
||||
</div>
|
||||
<img
|
||||
src="@/assets/images/person-map.png"
|
||||
alt=""
|
||||
class="information-image"
|
||||
/>
|
||||
</div>
|
||||
<ul class="card-list">
|
||||
<li class="card-list-item">
|
||||
<ShareCard
|
||||
:image="{
|
||||
url: 'building.png',
|
||||
alt: $t('pages.share_pictures.arg_alt1')
|
||||
}"
|
||||
:title="$t('pages.share_pictures.arg_title1')"
|
||||
:text="$t('pages.share_pictures.arg_text1')"
|
||||
/>
|
||||
</li>
|
||||
<li class="card-list-item">
|
||||
<ShareCard
|
||||
:image="{
|
||||
url: '360.png',
|
||||
alt: $t('pages.share_pictures.arg_alt2')
|
||||
}"
|
||||
:title="$t('pages.share_pictures.arg_title2')"
|
||||
:text="$t('pages.share_pictures.arg_text2')"
|
||||
/>
|
||||
</li>
|
||||
<li class="card-list-item">
|
||||
<ShareCard
|
||||
:image="{
|
||||
url: 'pointer-map.png',
|
||||
alt: $t('pages.share_pictures.arg_alt3')
|
||||
}"
|
||||
:title="$t('pages.share_pictures.arg_title3')"
|
||||
:text="$t('pages.share_pictures.arg_text3')"
|
||||
/>
|
||||
</li>
|
||||
<li class="card-list-item">
|
||||
<ShareCard
|
||||
:image="{
|
||||
url: 'pointer.png',
|
||||
alt: $t('pages.share_pictures.arg_alt4')
|
||||
}"
|
||||
:title="$t('pages.share_pictures.arg_title4')"
|
||||
:text="$t('pages.share_pictures.arg_text4')"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="sec-1" class="section-upload">
|
||||
<div class="wrapper-upload">
|
||||
<div class="wrapper-upload-text">
|
||||
<h1 class="upload-title">{{ $t('pages.upload.title') }}</h1>
|
||||
<h1 class="upload-title">{{ $t('pages.share_pictures.title') }}</h1>
|
||||
<div class="wrapper-check">
|
||||
<div class="wrapper-img-icon">
|
||||
<i class="bi bi-images img-icon"></i>
|
||||
@@ -11,41 +69,45 @@
|
||||
<div class="element-check">
|
||||
<span class="block-check"
|
||||
><span class="check-border">✔</span
|
||||
>{{ $t('pages.upload.photo_type1') }}</span
|
||||
>{{ $t('pages.share_pictures.photo_type1') }}</span
|
||||
>
|
||||
<span class="block-check"
|
||||
><span class="check-border">✔</span
|
||||
>{{ $t('pages.upload.photo_type2') }}</span
|
||||
>{{ $t('pages.share_pictures.photo_type2') }}</span
|
||||
>
|
||||
<span class="block-check"
|
||||
><span class="check-border">✔</span
|
||||
>{{ $t('pages.upload.photo_type3') }}</span
|
||||
>{{ $t('pages.share_pictures.photo_type3') }}</span
|
||||
>
|
||||
<span class="block-check"
|
||||
><span class="check-border">✔</span
|
||||
>{{ $t('pages.upload.photo_type4') }}</span
|
||||
>{{ $t('pages.share_pictures.photo_type4') }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="upload-text"
|
||||
v-html="
|
||||
$t('pages.upload.description', {
|
||||
$t('pages.share_pictures.description', {
|
||||
check: checkImg
|
||||
})
|
||||
"
|
||||
/>
|
||||
<p class="upload-text">{{ $t('pages.upload.footer_block') }}</p>
|
||||
<p class="upload-text">
|
||||
{{ $t('pages.share_pictures.footer_block') }}
|
||||
</p>
|
||||
<div v-if="!isLogged && authConf.enabled" class="wrapper-account">
|
||||
<h4 class="account-subtitle">
|
||||
{{ $t('pages.upload.sub_title') }}
|
||||
{{ $t('pages.share_pictures.sub_title') }}
|
||||
</h4>
|
||||
<div class="entry-link">
|
||||
<Link
|
||||
:text="$t('pages.upload.user_account_button')"
|
||||
:text="$t('pages.share_pictures.user_account_button')"
|
||||
type="external"
|
||||
look="button white"
|
||||
:path="getAuthRoute('auth/login', 'partager-des-photos')"
|
||||
look="button button--blue"
|
||||
:path-external="
|
||||
getAuthRoute('auth/login', 'partager-des-photos')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,7 +116,7 @@
|
||||
<div class="image">
|
||||
<img
|
||||
src="@/assets/images/upload.png"
|
||||
:alt="$t('pages.upload.alt_img_upload')"
|
||||
:alt="$t('pages.share_pictures.alt_img_upload')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,24 +142,26 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper-upload-text">
|
||||
<h2 class="upload-title">{{ $t('pages.upload.title_terminal') }}</h2>
|
||||
<h2 class="upload-title">
|
||||
{{ $t('pages.share_pictures.title_terminal') }}
|
||||
</h2>
|
||||
<p
|
||||
class="upload-text"
|
||||
v-html="$t('pages.upload.description_terminal')"
|
||||
v-html="$t('pages.share_pictures.description_terminal')"
|
||||
></p>
|
||||
<p
|
||||
class="upload-text grey"
|
||||
v-html="$t('pages.upload.footer_description_terminal')"
|
||||
v-html="$t('pages.share_pictures.footer_description_terminal')"
|
||||
></p>
|
||||
<div class="wrapper-account">
|
||||
<h4 class="account-subtitle">
|
||||
{{ $t('pages.upload.cli_title') }}
|
||||
{{ $t('pages.share_pictures.cli_title') }}
|
||||
</h4>
|
||||
<div class="entry-link">
|
||||
<Link
|
||||
:text="$t('pages.upload.button')"
|
||||
:text="$t('pages.share_pictures.button')"
|
||||
type="external"
|
||||
look="button"
|
||||
look="button button--blue"
|
||||
path="https://gitlab.com/geovisio/cli"
|
||||
/>
|
||||
</div>
|
||||
@@ -111,6 +175,7 @@
|
||||
<script lang="ts" setup>
|
||||
import Link from '@/components/Link.vue'
|
||||
import Terminal from '@/components/Terminal.vue'
|
||||
import ShareCard from '@/components/share-pictures/ShareCard.vue'
|
||||
import { useCookies } from 'vue3-cookies'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -125,17 +190,17 @@ const route = useRoute()
|
||||
let hrefSection = ref<string>('#sec-2')
|
||||
let icon = ref<string>('bi bi-chevron-down')
|
||||
const checkImg =
|
||||
"<span style='background:white; padding: 0.5rem 0.525rem;border-radius:50%; font-size:0.8rem;'>✔</span>"
|
||||
"<span style='background:white; padding: 5px 0.0525px;border-radius:50%; font-size:8px;'>✔</span>"
|
||||
|
||||
const isLogged = computed((): boolean => !!cookies.get('user_id'))
|
||||
const terminalText = computed((): string => {
|
||||
const url = import.meta.env.VITE_API_URL
|
||||
? import.meta.env.VITE_API_URL
|
||||
: 'https://panoramax.ign.fr/'
|
||||
return t('pages.upload.terminal_text', { url })
|
||||
return t('pages.share_pictures.terminal_text', { url })
|
||||
})
|
||||
const terminalTextInstall = computed((): string =>
|
||||
t('pages.upload.terminal_install')
|
||||
t('pages.share_pictures.terminal_install')
|
||||
)
|
||||
function triggerHref(): void {
|
||||
icon.value =
|
||||
@@ -145,6 +210,46 @@ function triggerHref(): void {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.entry-page {
|
||||
margin: auto;
|
||||
background-color: var(--beige-pale);
|
||||
padding: toRem(2) 5%;
|
||||
}
|
||||
.information-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: toRem(4);
|
||||
background-color: var(--white);
|
||||
border-radius: toRem(1);
|
||||
}
|
||||
.information {
|
||||
width: 50%;
|
||||
}
|
||||
.information-image {
|
||||
margin: auto;
|
||||
}
|
||||
.page-title {
|
||||
@include text(h1);
|
||||
color: var(--blue-dark);
|
||||
}
|
||||
.page-description {
|
||||
color: var(--grey-semi-dark);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.card-list {
|
||||
display: flex;
|
||||
padding: toRem(4);
|
||||
background-color: var(--white);
|
||||
border-radius: toRem(1);
|
||||
margin-top: toRem(2);
|
||||
}
|
||||
.card-list-item {
|
||||
margin-right: toRem(3);
|
||||
&:last-child {
|
||||
margin-right: toRem(0);
|
||||
}
|
||||
}
|
||||
|
||||
.upload-title {
|
||||
@include text(h1);
|
||||
}
|
||||
@@ -152,32 +257,32 @@ function triggerHref(): void {
|
||||
@include text(h4);
|
||||
}
|
||||
.entry-link {
|
||||
margin-top: 1rem;
|
||||
margin-top: toRem(1);
|
||||
}
|
||||
.upload-text {
|
||||
@include text(m-regular);
|
||||
margin-top: 5rem;
|
||||
margin-top: toRem(5);
|
||||
}
|
||||
.wrapper-check {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
padding: 3rem;
|
||||
padding: toRem(3);
|
||||
background-color: var(--white);
|
||||
border-radius: 1.5rem;
|
||||
font-size: 1.6rem;
|
||||
margin-top: 8rem;
|
||||
border-radius: toRem(1.4);
|
||||
font-size: toRem(1.6);
|
||||
margin-top: toRem(8);
|
||||
}
|
||||
.wrapper-img-icon {
|
||||
background-color: var(--black);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
height: 5.5rem;
|
||||
width: 5.5rem;
|
||||
height: toRem(5.5);
|
||||
width: toRem(5.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 3rem;
|
||||
top: -3rem;
|
||||
font-size: toRem(3);
|
||||
top: toRem(-3);
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
@@ -187,38 +292,38 @@ function triggerHref(): void {
|
||||
.element-check {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 3rem;
|
||||
margin-top: toRem(3);
|
||||
}
|
||||
.block-check {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: toRem(1);
|
||||
display: flex;
|
||||
}
|
||||
.check-border {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
width: toRem(2.2);
|
||||
height: toRem(2.2);
|
||||
border-radius: 50%;
|
||||
font-size: 0.8rem;
|
||||
font-size: toRem(0.8);
|
||||
background: var(--white);
|
||||
border: 0.1rem solid var(--black);
|
||||
margin-right: 0.5rem;
|
||||
border: toRem(0.1) solid var(--black);
|
||||
margin-right: toRem(0.5);
|
||||
}
|
||||
.block-check:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.section-upload {
|
||||
height: 100%;
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: 80vh;
|
||||
}
|
||||
.section-upload:first-child {
|
||||
background-color: rgba(236, 236, 236, 0.5);
|
||||
background-color: var(--beige-pale);
|
||||
}
|
||||
.wrapper-upload {
|
||||
display: flex;
|
||||
@@ -237,19 +342,19 @@ function triggerHref(): void {
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 3rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 0.1rem solid #e6e6e6;
|
||||
margin-top: toRem(3);
|
||||
padding-top: toRem(1);
|
||||
border-top: toRem(0.1) solid #e6e6e6;
|
||||
}
|
||||
.upload-button {
|
||||
display: flex;
|
||||
margin-top: 3rem;
|
||||
margin-top: toRem(3);
|
||||
}
|
||||
.image {
|
||||
background-color: var(--white);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid black;
|
||||
border-radius: toRem(1);
|
||||
padding: toRem(1);
|
||||
border: 1px solid var(--black);
|
||||
width: 75%;
|
||||
height: fit-content;
|
||||
}
|
||||
@@ -258,30 +363,47 @@ function triggerHref(): void {
|
||||
justify-content: center;
|
||||
width: 35%;
|
||||
overflow: hidden;
|
||||
margin-left: 6rem;
|
||||
margin-left: toRem(6);
|
||||
}
|
||||
.entry-terminal {
|
||||
margin-left: 0;
|
||||
margin-right: 6rem;
|
||||
margin-right: toRem(6);
|
||||
}
|
||||
.entry-image img {
|
||||
width: 100%;
|
||||
border-radius: 1rem;
|
||||
border-radius: toRem(1);
|
||||
}
|
||||
.entry-button-down {
|
||||
z-index: 1;
|
||||
position: fixed;
|
||||
right: 2rem;
|
||||
bottom: calc(20vh - 10.5rem);
|
||||
right: toRem(2);
|
||||
bottom: calc(20vh - #{toRem(10.5)});
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
@media (max-width: toRem(102.4)) {
|
||||
.information-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
.information {
|
||||
width: 100%;
|
||||
}
|
||||
.card-list {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.card-list-item {
|
||||
width: calc(50% - #{toRem(2)});
|
||||
margin-right: 0;
|
||||
&:nth-child(1),
|
||||
&:nth-child(3) {
|
||||
margin-right: toRem(2);
|
||||
}
|
||||
}
|
||||
.section-upload {
|
||||
height: initial;
|
||||
}
|
||||
.wrapper-upload {
|
||||
flex-direction: column-reverse;
|
||||
padding-top: 6rem;
|
||||
padding-bottom: 6rem;
|
||||
padding-top: toRem(6);
|
||||
padding-bottom: toRem(6);
|
||||
}
|
||||
.wrapper-upload-text {
|
||||
width: 100%;
|
||||
@@ -295,12 +417,23 @@ function triggerHref(): void {
|
||||
}
|
||||
.terminal {
|
||||
width: 100%;
|
||||
margin-top: 4rem;
|
||||
margin-top: toRem(4);
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.card-list-item {
|
||||
width: 100%;
|
||||
margin-bottom: toRem(2);
|
||||
&:nth-child(1),
|
||||
&:nth-child(3) {
|
||||
margin-right: 0;
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.entry-page {
|
||||
padding-top: 11rem;
|
||||
padding-top: toRem(11);
|
||||
}
|
||||
.entry-image {
|
||||
width: 60%;
|
||||
@@ -315,10 +448,10 @@ function triggerHref(): void {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: toRem(50)) {
|
||||
.entry-page {
|
||||
min-height: calc(100vh - 11rem);
|
||||
padding-top: 11rem;
|
||||
min-height: calc(100vh - #{toRem(11)});
|
||||
padding-top: toRem(11);
|
||||
}
|
||||
.entry-image {
|
||||
width: 100%;
|
||||
@@ -334,14 +467,14 @@ function triggerHref(): void {
|
||||
padding-left: 0;
|
||||
}
|
||||
.upload-text {
|
||||
margin-bottom: 3rem;
|
||||
margin-bottom: toRem(3);
|
||||
}
|
||||
.wrapper-account {
|
||||
padding-top: 3rem;
|
||||
padding-top: toRem(3);
|
||||
flex-direction: column;
|
||||
}
|
||||
.account-subtitle {
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: toRem(2);
|
||||
}
|
||||
.entry-button-down {
|
||||
display: none;
|
||||
341
src/views/UploadPicturesView.vue
Normal file
341
src/views/UploadPicturesView.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<main class="entry-page">
|
||||
<div class="entry-banner">
|
||||
<TooltipBanner />
|
||||
</div>
|
||||
<section class="upload-section">
|
||||
<div class="form-upload">
|
||||
<h1 class="settings-title">{{ $t('pages.upload.title') }}</h1>
|
||||
<p class="settings-text">{{ $t('pages.upload.text') }}</p>
|
||||
<form v-if="!isLoading" @submit.prevent="uploadPicture">
|
||||
<InputUpload
|
||||
:text="$t('pages.upload.input_label')"
|
||||
:text-second-part="$t('pages.upload.import_word')"
|
||||
:text-picture-type="$t('pages.upload.import_type')"
|
||||
accept="image/jpeg"
|
||||
data-test="input-add-pictures"
|
||||
@trigger="addPictures"
|
||||
/>
|
||||
<div class="footer-form">
|
||||
<span v-if="fileUploaded" class="number-file-text">{{
|
||||
fileUploaded
|
||||
}}</span>
|
||||
<span v-else class="number-file-text">{{
|
||||
t('pages.upload.no_uploaded_files')
|
||||
}}</span>
|
||||
<Button
|
||||
:text="$t('pages.upload.button_text')"
|
||||
:disabled="!pictures || pictures.length < 1"
|
||||
type="submit"
|
||||
look="button button--blue"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<UploadLoader
|
||||
v-else
|
||||
:load-percentage="loadPercentage"
|
||||
:load-text-size="loadTextSize"
|
||||
:is-loaded="isLoaded"
|
||||
:uploaded-sequences="uploadedSequences"
|
||||
:pictures-count="picturesCount"
|
||||
@triggerNewUpload="triggerNewUpload"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-upload">
|
||||
<h1 class="settings-title">{{ $t('pages.upload.title_uploading') }}</h1>
|
||||
<div v-if="!uploadedSequences.length" class="uploading-information">
|
||||
<img src="@/assets/images/uploading.png" class="uploading-img" />
|
||||
<p class="settings-text">
|
||||
{{ $t('pages.upload.no_picture_uploading_text') }}
|
||||
</p>
|
||||
</div>
|
||||
<template v-for="(sequence, i) in uploadedSequences">
|
||||
<ImportedSection
|
||||
v-if="i === 0"
|
||||
:index="i"
|
||||
:sequence="sequence"
|
||||
:pictures-count="picturesCount"
|
||||
:upload-errors="sequence.picturesOnError"
|
||||
:upload-pictures="sequence.pictures"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
<template v-for="(sequence, i) in uploadedSequences">
|
||||
<section v-if="i !== 0" class="information-section">
|
||||
<ImportedSection
|
||||
:index="i"
|
||||
:sequence="sequence"
|
||||
:pictures-count="picturesCount"
|
||||
:upload-errors="sequence.picturesOnError"
|
||||
:upload-pictures="sequence.pictures"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onUnmounted } from 'vue'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import InputUpload from '@/components/InputUpload.vue'
|
||||
import Button from '@/components/Button.vue'
|
||||
import ImportedSection from '@/components/upload/ImportedSection.vue'
|
||||
import TooltipBanner from '@/components/upload/TooltipBanner.vue'
|
||||
import UploadLoader from '@/components/upload/UploadLoader.vue'
|
||||
import type { sequenceInterface } from './interfaces/UploadPicturesView'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
import {
|
||||
createAPictureToASequence,
|
||||
createASequence
|
||||
} from '@/views/utils/upload/request'
|
||||
import { formatPictureSize, formatTextSize } from '@/views/utils/upload/index'
|
||||
const { t } = useI18n()
|
||||
let pictures = ref<FileList | []>([])
|
||||
let picturesCount = ref<number>(0)
|
||||
let isLoading = ref<boolean>(false)
|
||||
let isLoaded = ref<boolean>(false)
|
||||
let uploadingPictures = ref<FileList | []>([])
|
||||
let uploadedSequences = ref<sequenceInterface[] | []>([])
|
||||
let picturesUploadingSize = ref<number>(0)
|
||||
let picturesToUploadSize = ref<number>(0)
|
||||
let loadPercentage = ref<string>('0%')
|
||||
let loadTextSize = ref<string>('0 Mo')
|
||||
|
||||
watch(isLoading, () => {
|
||||
if (isLoading.value) {
|
||||
window.onbeforeunload = function (e) {
|
||||
e = e || window.event
|
||||
if (e) e.returnValue = 'Sure?'
|
||||
return 'Sure?'
|
||||
}
|
||||
} else {
|
||||
window.onbeforeunload = null
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.onbeforeunload = null
|
||||
})
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
if (isLoading.value) {
|
||||
const answer = window.confirm(t('pages.upload.leave_message'))
|
||||
if (answer) return next()
|
||||
return next(false)
|
||||
}
|
||||
next()
|
||||
})
|
||||
const fileUploaded = computed<string>(() => {
|
||||
return t('pages.upload.uploaded_files', { count: picturesCount.value })
|
||||
})
|
||||
|
||||
function picturesToUploadSizeText(): void {
|
||||
let fullSize = 0
|
||||
for (let i = 0; i < pictures.value.length; i++) {
|
||||
fullSize += pictures.value[i].size
|
||||
}
|
||||
picturesToUploadSize.value = fullSize
|
||||
loadTextSize.value = formatTextSize(fullSize, formatPictureSize(fullSize))
|
||||
}
|
||||
function calcPercentage(): void {
|
||||
if (picturesUploadingSize.value && picturesToUploadSize.value) {
|
||||
loadPercentage.value = `${(
|
||||
(picturesUploadingSize.value / picturesToUploadSize.value) *
|
||||
100
|
||||
).toFixed(0)}%`
|
||||
}
|
||||
}
|
||||
|
||||
function triggerNewUpload(): void {
|
||||
isLoading.value = false
|
||||
picturesCount.value = 0
|
||||
}
|
||||
function addPictures(value: FileList): void {
|
||||
pictures.value = value
|
||||
picturesCount.value = pictures.value.length
|
||||
picturesUploadingSize.value = 0
|
||||
picturesToUploadSize.value = 0
|
||||
loadPercentage.value = '0%'
|
||||
loadTextSize.value = '0 Mo'
|
||||
}
|
||||
function setPictureSizeValue(sequence: sequenceInterface, size: number): void {
|
||||
picturesUploadingSize.value = picturesUploadingSize.value + size
|
||||
sequence.pictureSize = formatTextSize(
|
||||
picturesUploadingSize.value,
|
||||
formatPictureSize(picturesUploadingSize.value)
|
||||
)
|
||||
}
|
||||
async function uploadPicture(): Promise<void> {
|
||||
if (!pictures.value || !pictures.value.length) {
|
||||
return
|
||||
}
|
||||
isLoaded.value = false
|
||||
isLoading.value = true
|
||||
picturesToUploadSizeText()
|
||||
const picturesToUpload = [...pictures.value]
|
||||
|
||||
const sequenceTitle = `${t('pages.upload.sequence_title')}${formatDate(
|
||||
new Date(),
|
||||
'Do MMMM YY, hh:mm:ss'
|
||||
)}`
|
||||
const { data } = await createASequence(sequenceTitle)
|
||||
const sequence: sequenceInterface = {
|
||||
title: sequenceTitle,
|
||||
id: data.id,
|
||||
pictures: [],
|
||||
picturesOnError: [],
|
||||
pictureCount: pictures.value.length,
|
||||
pictureSize: ''
|
||||
}
|
||||
uploadedSequences.value = [sequence, ...uploadedSequences.value]
|
||||
let i = 0
|
||||
for (let el of picturesToUpload) {
|
||||
const body = new FormData()
|
||||
i++
|
||||
body.append('position', i.toString())
|
||||
body.append('picture', el)
|
||||
try {
|
||||
const pictureUploaded = await createAPictureToASequence(data.id, body)
|
||||
const pictures = { ...pictureUploaded.data, name: el.name }
|
||||
setPictureSizeValue(sequence, el.size)
|
||||
sequence.pictures = [...sequence.pictures, pictures]
|
||||
const index = uploadedSequences.value.findIndex(
|
||||
(item: sequenceInterface) => item.id === data.id
|
||||
)
|
||||
uploadedSequences.value.splice(index, 1)
|
||||
uploadedSequences.value = [sequence, ...uploadedSequences.value]
|
||||
calcPercentage()
|
||||
} catch (err: any) {
|
||||
setPictureSizeValue(sequence, el.size)
|
||||
const picturesOnError = {
|
||||
message: err.response.data.message,
|
||||
name: el.name
|
||||
}
|
||||
sequence.picturesOnError = [...sequence.picturesOnError, picturesOnError]
|
||||
calcPercentage()
|
||||
}
|
||||
}
|
||||
isLoaded.value = true
|
||||
uploadingPictures.value = []
|
||||
pictures.value = []
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.entry-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: var(--beige-pale);
|
||||
min-height: calc(100vh - #{toRem(8)});
|
||||
padding-top: toRem(2);
|
||||
padding-bottom: toRem(2);
|
||||
overflow: hidden;
|
||||
}
|
||||
.entry-banner {
|
||||
width: 90%;
|
||||
margin-bottom: toRem(2);
|
||||
}
|
||||
.upload-section {
|
||||
display: flex;
|
||||
width: 90%;
|
||||
}
|
||||
.information-section {
|
||||
width: 90%;
|
||||
background-color: var(--white);
|
||||
border-radius: toRem(1);
|
||||
margin-top: toRem(2);
|
||||
padding: toRem(2);
|
||||
}
|
||||
.uploading-information {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.uploading-img {
|
||||
height: toRem(20);
|
||||
margin-bottom: toRem(2);
|
||||
}
|
||||
.form-upload {
|
||||
height: toRem(70);
|
||||
padding: toRem(2);
|
||||
width: 50%;
|
||||
background-color: var(--white);
|
||||
border-radius: toRem(1);
|
||||
&:first-child {
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
&:last-child {
|
||||
margin-left: toRem(1);
|
||||
}
|
||||
}
|
||||
.settings-title {
|
||||
color: var(--blue-dark);
|
||||
@include text(h3);
|
||||
margin-bottom: toRem(2);
|
||||
}
|
||||
.settings-text {
|
||||
color: var(--grey-semi-dark);
|
||||
margin-bottom: toRem(2);
|
||||
@include text(s-regular);
|
||||
}
|
||||
.footer-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: toRem(3);
|
||||
}
|
||||
.number-file-text {
|
||||
margin-bottom: toRem(2);
|
||||
color: var(--blue-dark);
|
||||
@include text(s-regular);
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--grey-pale);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--blue);
|
||||
border-radius: toRem(5);
|
||||
}
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.upload-section {
|
||||
flex-direction: column;
|
||||
}
|
||||
.form-upload {
|
||||
width: 100%;
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
margin: toRem(0);
|
||||
}
|
||||
&:first-child {
|
||||
margin-bottom: toRem(2);
|
||||
}
|
||||
}
|
||||
.information-section {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@media (max-width: toRem(50)) {
|
||||
.entry-page {
|
||||
margin-top: toRem(11);
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.upload-section,
|
||||
.information-section {
|
||||
width: 100%;
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
}
|
||||
.information-section {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
12
src/views/interfaces/UploadPicturesView.ts
Normal file
12
src/views/interfaces/UploadPicturesView.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface sequenceInterface {
|
||||
title: string
|
||||
id: string
|
||||
pictures: any[]
|
||||
picturesOnError: uploadErrorInterface[] | []
|
||||
pictureCount: number
|
||||
pictureSize: string
|
||||
}
|
||||
export interface uploadErrorInterface {
|
||||
message: string
|
||||
name: string
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import type {
|
||||
UserSequenceInterface,
|
||||
ResponseUserPhotoInterface,
|
||||
ResponseUserPhotoLinksInterface
|
||||
} from '@/views/interfaces/MySequenceView'
|
||||
|
||||
function imageStatus(
|
||||
imageStatus: string,
|
||||
userSequence: UserSequenceInterface
|
||||
): string {
|
||||
if (userSequence.status === 'hidden') return userSequence.status
|
||||
function imageStatus(imageStatus: string, sequenceStatus: string): string {
|
||||
if (sequenceStatus === 'hidden') return sequenceStatus
|
||||
return imageStatus
|
||||
}
|
||||
|
||||
|
||||
8
src/views/utils/upload/index.ts
Normal file
8
src/views/utils/upload/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
function formatPictureSize(size: number): number {
|
||||
return Math.floor(Math.log(size) / Math.log(1024))
|
||||
}
|
||||
function formatTextSize(size: number, i: number): string {
|
||||
const sizes = ['0', 'Ko', 'Mo', 'Go', 'To', 'Po', 'Eo', 'Zo', 'o']
|
||||
return `${parseFloat((size / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`
|
||||
}
|
||||
export { formatPictureSize, formatTextSize }
|
||||
11
src/views/utils/upload/request.ts
Normal file
11
src/views/utils/upload/request.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import axios from 'axios'
|
||||
|
||||
function createASequence(title: string): Promise<any> {
|
||||
return axios.post('api/collections', { title: title })
|
||||
}
|
||||
|
||||
async function createAPictureToASequence(id: string, body: any): Promise<any> {
|
||||
return await axios.post(`api/collections/${id}/items`, body)
|
||||
}
|
||||
|
||||
export { createASequence, createAPictureToASequence }
|
||||
@@ -19,7 +19,8 @@ export default defineConfig({
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@import "@/assets/font-size.scss";'
|
||||
additionalData:
|
||||
'@import "@/assets/font-size.scss"; @import "@/assets/rem-calc.scss";'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -19,5 +19,10 @@ export default defineConfig({
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
|
||||
}
|
||||
},
|
||||
test: {
|
||||
deps: {
|
||||
inline: ['moment']
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user