forked from Ivasoft/geovisio-website
Feat/upload interface
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -90,5 +90,5 @@ sw.*
|
||||
*.swp
|
||||
|
||||
# Cypress generated screen and videos files
|
||||
src/tests/cypress/screenshot/
|
||||
src/tests/cypress/videos/
|
||||
cypress/screenshot/
|
||||
cypress/videos/
|
||||
@@ -12,11 +12,6 @@ export default defineConfig({
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
baseUrl: 'http://localhost:5173',
|
||||
supportFile: 'src/tests/cypress/support/e2e.{js,jsx,ts,tsx}',
|
||||
specPattern: 'src/tests/cypress/**/*.cy.{js,jsx,ts,tsx}',
|
||||
fixturesFolder: 'src/tests/cypress/fixtures',
|
||||
videosFolder: 'src/tests/cypress/videos',
|
||||
screenshotsFolder: 'src/tests/cypress/screenshot'
|
||||
baseUrl: 'http://localhost:5173'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('In the upload page', () => {
|
||||
describe('In the login 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": "À propos"
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
@mixin text($size) {
|
||||
@if $size == h1 {
|
||||
font-weight: normal;
|
||||
font-size: 4rem;
|
||||
font-size: toRem(4);
|
||||
|
||||
@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 == 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
html {
|
||||
font-size: 62.5%; /* 1rem = 10px */
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
body {
|
||||
@@ -25,17 +24,18 @@ 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;
|
||||
@@ -43,15 +43,16 @@ h5 {
|
||||
--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(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(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(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,12 +6,9 @@
|
||||
<nav class="nav">
|
||||
<div class="wrapper-logo desktop">
|
||||
<Link
|
||||
:image="{
|
||||
url: 'logo.jpeg',
|
||||
alt: $t('general.header.alt_logo')
|
||||
}"
|
||||
:image="{ url: 'logo.jpeg', alt: $t('general.header.alt_logo') }"
|
||||
:text="title($t('general.header.title'))"
|
||||
path="/"
|
||||
:route="{ name: 'home' }"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper-logo responsive">
|
||||
@@ -20,7 +17,7 @@
|
||||
url: 'logo.jpeg',
|
||||
alt: $t('general.header.alt_logo')
|
||||
}"
|
||||
path="/"
|
||||
:route="{ name: 'home' }"
|
||||
/>
|
||||
</div>
|
||||
<div ref="list" class="wrapper-entries">
|
||||
@@ -32,13 +29,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 +43,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 +54,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 +93,7 @@
|
||||
<Link
|
||||
type="external"
|
||||
icon="bi bi-person-circle"
|
||||
:path="getAuthRoute('auth/login', route.path)"
|
||||
:path-external="getAuthRoute('auth/login', route.path)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -147,13 +151,13 @@ 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;
|
||||
}
|
||||
@@ -173,7 +177,7 @@ const userName = computed((): string =>
|
||||
.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 +190,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 +233,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 +258,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 +267,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,11 +57,6 @@ 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;
|
||||
@@ -66,58 +64,77 @@ const titleImg = computed<string>(() =>
|
||||
color: var(--black);
|
||||
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);
|
||||
}
|
||||
.white .icon {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
.button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.white:hover {
|
||||
border: toRem(0.1) solid var(--black);
|
||||
.icon {
|
||||
font-size: toRem(1.6);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.white:hover > .icon {
|
||||
.icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.logo {
|
||||
height: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
.button--white-blue {
|
||||
background-color: var(--white);
|
||||
border: toRem(0.1) solid var(--blue);
|
||||
.icon {
|
||||
font-size: toRem(1.4);
|
||||
color: var(--blue);
|
||||
}
|
||||
}
|
||||
.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 {
|
||||
.icon {
|
||||
color: grey;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.disabled:hover {
|
||||
}
|
||||
&: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 {
|
||||
|
||||
131
src/components/upload/ImportedSection.vue
Normal file
131
src/components/upload/ImportedSection.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<section :class="['information-section', { 'first-sequence': index === 0 }]">
|
||||
<div class="uploaded-pictures">
|
||||
<p v-if="index === 0" class="title-current-upload">
|
||||
{{ $t('pages.upload.sequence_uploading_title') }}
|
||||
</p>
|
||||
<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>
|
||||
</section>
|
||||
</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">
|
||||
.information-section {
|
||||
display: flex;
|
||||
margin-bottom: toRem(4);
|
||||
padding-top: toRem(2);
|
||||
padding-bottom: toRem(2);
|
||||
width: 80%;
|
||||
box-shadow: 0px 6.45694px 8.60925px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.first-sequence {
|
||||
background-color: var(--blue-pale);
|
||||
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-left: toRem(2);
|
||||
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: 0rem toRem(2) toRem(2);
|
||||
overflow-y: auto;
|
||||
max-height: toRem(35);
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
src/components/upload/PictureItem.vue
Normal file
52
src/components/upload/PictureItem.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<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(s-regular);
|
||||
}
|
||||
.success {
|
||||
background-color: var(--white);
|
||||
border: toRem(1) solid var(--grey);
|
||||
color: var(--black);
|
||||
}
|
||||
.error {
|
||||
background-color: var(--white);
|
||||
border: toRem(1) solid var(--red-pale);
|
||||
color: var(--red);
|
||||
}
|
||||
.uploaded-information {
|
||||
margin-right: toRem(1);
|
||||
}
|
||||
</style>
|
||||
108
src/components/upload/UploadLoader.vue
Normal file
108
src/components/upload/UploadLoader.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<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;
|
||||
margin-top: toRem(4);
|
||||
}
|
||||
.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": "À propos",
|
||||
"upload_text": "Contribuer",
|
||||
"sequences_text": "Mes photos",
|
||||
"alt_logo": "Logo de l'instance",
|
||||
"title": "Instance Panoramax",
|
||||
"title": "Instance\nPanoramax",
|
||||
"beta_text": "Version beta",
|
||||
"logout_text": "Déconnexion",
|
||||
"my_information_text": "Mes informations",
|
||||
@@ -73,7 +74,7 @@
|
||||
"button_upload": "Partager vos photos",
|
||||
"sequence_deleted": "La séquence a bien été supprimée"
|
||||
},
|
||||
"upload": {
|
||||
"share_pictures": {
|
||||
"title": "Partagez vos photos",
|
||||
"sub_title": "Un compte utilisateur est obligatoire pour partager des photos",
|
||||
"photo_type1": "Des lieux visibles depuis la voie publique",
|
||||
@@ -92,6 +93,26 @@
|
||||
"terminal_install": "pip install geovisio_cli",
|
||||
"terminal_text": "geovisio upload --api-url {url} <DOSSIER_PHOTOS>",
|
||||
"button_copy": "Copier"
|
||||
},
|
||||
"upload": {
|
||||
"title": "Déposez vos photos",
|
||||
"input_label": "Déposez des photos dans la zone ou ",
|
||||
"import_word": "importez",
|
||||
"import_type": "Format JPEG uniquement",
|
||||
"sequence_title": "Séquence du ",
|
||||
"button_text": "Envoyer",
|
||||
"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": "Nouvel envoi",
|
||||
"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)
|
||||
|
||||
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="Nouvel envoi"')
|
||||
})
|
||||
})
|
||||
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="Nouvel envoi"')
|
||||
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')
|
||||
})
|
||||
})
|
||||
127
src/tests/unit/views/UploadPicturesView.spec.js
Normal file
127
src/tests/unit/views/UploadPicturesView.spec.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { it, describe, expect, vi } from 'vitest'
|
||||
import { shallowMount, mount, flushPromises } 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 * as createAPictureToASequence from '@/views/utils/upload/request'
|
||||
import * as createASequence from '@/views/utils/upload/request'
|
||||
import { formatDate } from '../../../utils/dates'
|
||||
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')
|
||||
})
|
||||
describe('submit uploadPicture', () => {
|
||||
it('should trigger to uploadPictures', async () => {
|
||||
const wrapper = mount(UploadPicturesView, {
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
$t: (msg) => msg
|
||||
},
|
||||
components: {
|
||||
InputUpload
|
||||
}
|
||||
}
|
||||
})
|
||||
const spy = vi.spyOn(wrapper.vm, 'uploadPicture')
|
||||
const wrapperInputUpload = wrapper.findComponent(InputUpload)
|
||||
await wrapperInputUpload.trigger('trigger')
|
||||
await wrapperInputUpload.vm.$emit('trigger', [{}, {}])
|
||||
const buttonWrapper = await wrapper.find('[data-test="button-upload"]')
|
||||
await buttonWrapper.trigger('submit.prevent')
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
expect(wrapper.html()).contains('class="loader-percentage"')
|
||||
expect(wrapper.html()).contains('class="lds-ring lg"')
|
||||
})
|
||||
})
|
||||
describe('one sequence has been imported', () => {
|
||||
it('should render a sequence with a list of uploaded pictures', async () => {
|
||||
const wrapper = mount(UploadPicturesView, {
|
||||
global: {
|
||||
plugins: [i18n, router],
|
||||
mocks: {
|
||||
$t: (msg) => msg
|
||||
}
|
||||
}
|
||||
})
|
||||
const spyASequence = vi.spyOn(createASequence, 'createASequence')
|
||||
const spyPicture = vi.spyOn(
|
||||
createAPictureToASequence,
|
||||
'createAPictureToASequence'
|
||||
)
|
||||
const sequenceId = 'my-id'
|
||||
spyASequence.mockReturnValue({ data: { id: sequenceId } })
|
||||
spyPicture.mockReturnValue({ data: {} })
|
||||
const sequenceTitle = `Séquence du ${formatDate(
|
||||
new Date(),
|
||||
'Do MMMM YY, hh:mm:ss'
|
||||
)}`
|
||||
const picture = {
|
||||
lastModified: 1599133968750,
|
||||
name: '100MSDCF_DSC02790.JPG',
|
||||
size: 2345,
|
||||
type: 'image/jpeg'
|
||||
}
|
||||
const body = new FormData()
|
||||
body.append('position', '1')
|
||||
body.append('picture', picture)
|
||||
const wrapperInputUpload = wrapper.findComponent(InputUpload)
|
||||
await wrapperInputUpload.trigger('trigger')
|
||||
await wrapperInputUpload.vm.$emit('trigger', [picture])
|
||||
const buttonWrapper = await wrapper.find('[data-test="button-upload"]')
|
||||
await buttonWrapper.trigger('submit.prevent')
|
||||
|
||||
expect(spyASequence).toHaveBeenCalledWith(sequenceTitle)
|
||||
expect(spyPicture).toHaveBeenCalledWith(sequenceId, body)
|
||||
expect(wrapper.html()).contains('class="uploaded-picture-list"')
|
||||
expect(wrapper.html()).contains('class="uploaded-picture-item success"')
|
||||
expect(wrapper.html()).contains(
|
||||
'100MSDCF_DSC02790.JPG - pages.upload.uploaded_word'
|
||||
)
|
||||
expect(wrapper.html()).contains('<router-link')
|
||||
expect(wrapper.html()).contains(
|
||||
'class="default button button--white" title="pages.upload.sequence_link"'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import moment from 'moment'
|
||||
import 'moment/dist/locale/fr'
|
||||
|
||||
function formatDate(date: Date, formatType: string): string {
|
||||
const formatDate = moment(date)
|
||||
|
||||
@@ -47,29 +47,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);
|
||||
font-size: 160%; /* Cancels the rule set on HTML 62.5% */
|
||||
min-height: calc(100vh - #{toRem(8)});
|
||||
}
|
||||
.gvs-has-map .entry-report-button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 120px;
|
||||
top: 10px;
|
||||
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) {
|
||||
@@ -83,7 +83,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>
|
||||
|
||||
@@ -151,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)"
|
||||
/>
|
||||
@@ -171,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>
|
||||
@@ -550,11 +550,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;
|
||||
}
|
||||
@@ -568,13 +569,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;
|
||||
@@ -590,28 +591,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 {
|
||||
@@ -619,21 +620,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 {
|
||||
@@ -646,20 +647,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;
|
||||
@@ -669,12 +670,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);
|
||||
@@ -692,29 +693,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;
|
||||
@@ -731,10 +732,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;
|
||||
@@ -744,10 +745,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;
|
||||
@@ -757,16 +758,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 {
|
||||
@@ -775,7 +776,7 @@ async function patchOrDeleteCollectionItems(
|
||||
z-index: 1;
|
||||
}
|
||||
.menu-right {
|
||||
padding-top: 11rem;
|
||||
padding-top: toRem(11);
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -790,15 +791,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 {
|
||||
@@ -808,19 +809,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;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<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 +11,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 +58,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 +84,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>
|
||||
@@ -125,17 +131,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 =
|
||||
@@ -152,32 +158,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,31 +193,31 @@ 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;
|
||||
@@ -237,19 +243,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 +264,30 @@ 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)) {
|
||||
.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 +301,12 @@ function triggerHref(): void {
|
||||
}
|
||||
.terminal {
|
||||
width: 100%;
|
||||
margin-top: 4rem;
|
||||
margin-top: toRem(4);
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.entry-page {
|
||||
padding-top: 11rem;
|
||||
padding-top: toRem(11);
|
||||
}
|
||||
.entry-image {
|
||||
width: 60%;
|
||||
@@ -315,10 +321,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 +340,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;
|
||||
261
src/views/UploadPicturesView.vue
Normal file
261
src/views/UploadPicturesView.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<main class="entry-page">
|
||||
<section class="upload-section">
|
||||
<h1 class="settings-title">{{ $t('pages.upload.title') }}</h1>
|
||||
<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"
|
||||
data-test="button-upload"
|
||||
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"
|
||||
/>
|
||||
</section>
|
||||
<ImportedSection
|
||||
v-for="(sequence, i) in uploadedSequences"
|
||||
:index="i"
|
||||
:sequence="sequence"
|
||||
:pictures-count="picturesCount"
|
||||
:upload-errors="sequence.picturesOnError"
|
||||
:upload-pictures="sequence.pictures"
|
||||
/>
|
||||
</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 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;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.upload-section {
|
||||
width: 80%;
|
||||
padding: toRem(4);
|
||||
margin-top: toRem(6);
|
||||
margin-bottom: toRem(4);
|
||||
box-shadow: 0px 6.45694px 8.60925px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.settings-title {
|
||||
text-align: center;
|
||||
@include text(h1);
|
||||
margin-bottom: toRem(4);
|
||||
}
|
||||
.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 {
|
||||
width: toRem(2);
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--grey-pale);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--blue);
|
||||
border-radius: toRem(5);
|
||||
}
|
||||
@media (max-width: toRem(76.8)) {
|
||||
.entry-page {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.information-section {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@media (max-width: toRem(50)) {
|
||||
.upload-section {
|
||||
margin-top: toRem(15);
|
||||
width: 100%;
|
||||
padding-right: toRem(2);
|
||||
padding-left: toRem(2);
|
||||
}
|
||||
.information-section {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</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