97 Commits

Author SHA1 Message Date
f047c30078 Restore Czech translation and default to English
All checks were successful
continuous-integration/drone Build is passing
2024-05-29 16:01:50 +02:00
bb660d0295 Merge 2.6.2 2024-05-29 15:58:01 +02:00
Florian Lainez
478b2f32ec Translated using Weblate (French)
Currently translated at 100.0% (206 of 206 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/fr/
2024-05-27 18:20:14 +00:00
Alexandre Badalo
7e936a1832 Translated using Weblate (Portuguese)
Currently translated at 64.5% (133 of 206 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/pt/
2024-05-25 18:20:14 +00:00
Florian Lainez
0e2b5629cf Translated using Weblate (French)
Currently translated at 95.6% (197 of 206 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/fr/
2024-05-25 18:20:14 +00:00
Alexandre Badalo
9f91b09919 Translated using Weblate (Portuguese)
Currently translated at 64.5% (133 of 206 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/pt/
2024-05-25 11:41:04 +00:00
Thibault Molleman
c831f007da Added translation using Weblate (Dutch) 2024-05-20 16:01:18 +00:00
Adrien Pavie
8cbf75161d Merge branch 'feature/mkdocs' into 'develop'
Add mkdocs config

See merge request panoramax/server/website!145
2024-05-15 17:03:56 +00:00
Adrien Pavie
a0cc0cd5f7 Add mkdocs config 2024-05-15 16:52:01 +02:00
Andreani Jean
ee5e32d290 Release 2.6.2 2024-05-15 15:05:00 +02:00
Jean Andreani
c07e6587c3 Merge branch 'tech/update-packages' into 'develop'
fix error upload img

See merge request panoramax/server/website!144
2024-05-15 12:45:46 +00:00
Jean Andreani
e5bc6323a3 fix error upload img 2024-05-15 12:45:46 +00:00
Jean Andreani
16bfbfd4da Merge branch 'tech/update-packages' into 'develop'
update packages and fix tests

See merge request panoramax/server/website!143
2024-05-15 09:25:16 +00:00
Andreani Jean
2492b4cffa update packages and fix tests 2024-05-15 10:50:10 +02:00
Jean Andreani
6c7e233d00 Merge branch 'fix/android-upload-pict' into 'develop'
fix/android-upload-pict

Closes #81

See merge request panoramax/server/website!142
2024-05-15 08:07:51 +00:00
Jean Andreani
79aaf29aed fix/android-upload-pict 2024-05-15 08:07:51 +00:00
Coehill
43fed2fb13 Translated using Weblate (English)
Currently translated at 100.0% (202 of 202 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/en/
2024-05-08 22:20:15 +00:00
Adrien Pavie
71ed9d4f33 Update docs & links to Panoramax 2024-05-07 14:24:56 +02:00
Adrien Pavie
715e234298 Change repo name to Panoramax 2024-05-07 09:27:09 +00:00
Adrien Pavie
77bd7604e4 Change repo name in CI 2024-05-07 08:20:57 +00:00
Coehill
783f079c95 Translated using Weblate (Portuguese)
Currently translated at 66.3% (134 of 202 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/pt/
2024-05-03 21:20:14 +00:00
Coehill
47630098ee Translated using Weblate (English)
Currently translated at 100.0% (202 of 202 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/en/
2024-05-03 21:20:14 +00:00
Andreani Jean
44d7e56dbc Release 2.6.1 2024-05-03 10:40:47 +02:00
Jean Andreani
4cf6b3d7e2 Merge branch 'fix/lang-switch' into 'develop'
fix warning + fix lang switcher

See merge request geovisio/website!141
2024-05-03 08:35:08 +00:00
Andreani Jean
58da8065e0 fix warning + fix lang switcher 2024-05-03 10:16:49 +02:00
Andreani Jean
67ebf3dd80 fix package lock 2024-05-02 15:54:57 +02:00
Andreani Jean
46e64b3cc1 Release 2.6.0 2024-05-02 15:19:25 +02:00
Jean Andreani
d814ba5855 Merge branch 'fix/various' into 'develop'
Various fixes on sequence page and list of sequences

See merge request geovisio/website!140
2024-05-02 12:03:05 +00:00
Adrien Pavie
4aa96cf243 Various fixes on sequence page and list of sequences 2024-05-02 12:03:05 +00:00
Jean Andreani
6690eecb81 Merge branch 'fix-viewer-bug' into 'develop'
Fix viewer bug

See merge request geovisio/website!139
2024-04-29 14:12:37 +00:00
Jean Andreani
f15d6c29cb Fix viewer bug 2024-04-29 14:12:37 +00:00
Alexandre Badalo
93389801e8 Translated using Weblate (Portuguese)
Currently translated at 68.1% (137 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/pt/
2024-04-24 19:20:14 +00:00
Alexandre Badalo
5568218ba4 Translated using Weblate (English)
Currently translated at 100.0% (201 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/en/
2024-04-24 19:20:14 +00:00
Alexandre Badalo
fde2bfd302 Translated using Weblate (Portuguese)
Currently translated at 26.8% (54 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/pt/
2024-04-24 12:20:14 +00:00
PanierAvide
70212f60cf Translated using Weblate (French)
Currently translated at 100.0% (201 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/fr/
2024-04-24 12:20:14 +00:00
PanierAvide
38db40a2ed Translated using Weblate (English)
Currently translated at 100.0% (201 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/en/
2024-04-24 12:20:14 +00:00
Coehill
3e29065494 Translated using Weblate (English)
Currently translated at 100.0% (201 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/en/
2024-04-24 04:20:14 +00:00
Alexandre Badalo
a9fb5957e9 Added translation using Weblate (Portuguese) 2024-04-23 17:56:27 +00:00
Coehill
e83b3cea4b Added translation using Weblate (German) 2024-04-23 15:55:31 +00:00
PanierAvide
6ede535f87 Translated using Weblate (Hungarian)
Currently translated at 98.0% (197 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/hu/
2024-04-23 12:35:38 +00:00
PanierAvide
f315b42e74 Translated using Weblate (French)
Currently translated at 98.0% (197 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/fr/
2024-04-23 12:35:38 +00:00
PanierAvide
f5176b5cae Translated using Weblate (English)
Currently translated at 100.0% (201 of 201 strings)

Translation: GeoVisio Website/translations-json
Translate-URL: http://weblate.panoramax.xyz/projects/geovisio-website/translations-json/en/
2024-04-23 12:35:38 +00:00
Jean Andreani
ea1b864a3b Merge branch 'fix-user-tests' into 'develop'
Fix user tests

Closes #75, #66, and #63

See merge request geovisio/website!136
2024-04-22 08:24:35 +00:00
Jean Andreani
fe54cd78a2 Fix user tests 2024-04-22 08:24:34 +00:00
Adrien Pavie
08ce274a40 Merge branch 'fix_docker' into 'develop'
Fix docker

See merge request geovisio/website!137
2024-04-22 06:52:34 +00:00
antoine-de
1a0f1bd60a Fix docker 2024-04-22 06:52:33 +00:00
Jean Andreani
6291f0567e Fix/port 5173 2024-04-17 12:51:38 +02:00
Jean Andreani
d493af65c3 Feat major version viewer 2024-04-17 12:51:36 +02:00
Jean Andreani
627d9d739f Merge branch 'fix/remove-edition-not-auth' into 'main'
fix isSequenceOwner for edition sequence

See merge request geovisio/website!135
2024-04-17 10:24:43 +00:00
Andreani Jean
e2dac72ba9 fix isSequenceOwner for edition sequence 2024-04-17 12:18:44 +02:00
5b6bfcdb3b Czech translation and default to English 2024-03-28 11:07:41 +01:00
Andreani Jean
3604909a37 new lockfile 2024-03-19 16:08:28 +01:00
Andreani Jean
14d7027aa2 remove getIgnTiles 2024-03-19 15:06:19 +01:00
Andreani Jean
523a6f98f0 Release 2.5.1 2024-03-19 15:04:25 +01:00
Jean Andreani
faf9fcc106 Merge branch 'feat/wordings-update-sequence' into 'develop'
fix typescript error & update pictures

Closes #64

See merge request geovisio/website!132
2024-03-19 14:03:07 +00:00
Andreani Jean
21a5793045 fix typescript error & update pictures 2024-03-19 14:56:07 +01:00
Jean Andreani
3dca1773cd Merge branch 'feat/edit-a-sequence' into 'develop'
Feat/edit a sequence

See merge request geovisio/website!126
2024-03-19 13:03:46 +00:00
Jean Andreani
3ced85eaf5 Feat/edit a sequence 2024-03-19 13:03:46 +00:00
Jean Andreani
e56c159146 Merge branch 'fix/ign-tiles' into 'main'
fix/ add new tiles IGN4

See merge request geovisio/website!131
2024-03-18 14:01:02 +00:00
Jean Andreani
ca6619b773 fix/ add new tiles IGN4 2024-03-18 14:01:01 +00:00
antoine-de
518f06c846 Release 2.5.0 2024-03-11 11:42:26 +01:00
antoine-de
50f1f7b472 Merge branch 'update_geovisio_2_5' into 'develop'
Update geovisio to 2.5

See merge request geovisio/website!128
2024-03-11 08:47:03 +00:00
antoine-de
2f4000291f Update geovisio to 2.5 2024-03-11 08:47:03 +00:00
Jean Andreani
6ad1d85604 Feat/improve meta tag 2024-03-05 13:30:13 +01:00
Jean Andreani
707420c69e Feat/add matomo analytics 2024-03-05 13:30:13 +01:00
Jean Andreani
1bd41002af Merge branch 'fix/ign-tiles-fallback' into 'main'
fix/ign-tiles-fallback

See merge request geovisio/website!125
2024-03-05 12:27:47 +00:00
Jean Andreani
c30eeb020d fix/ign-tiles-fallback 2024-03-05 12:27:47 +00:00
Andreani Jean
c37296066f Release 2.4.1 2024-02-01 10:17:17 +01:00
Andreani Jean
19f5f5a320 maj geovisio 2024-02-01 10:15:00 +01:00
Andreani Jean
589f7c64c0 Release 2.4.0 2024-01-31 15:24:10 +01:00
Jean Andreani
459efcfd57 Merge branch 'fix/auth-sequence-list' into 'develop'
Fix/auth sequence list

See merge request geovisio/website!122
2024-01-31 13:39:11 +00:00
Jean Andreani
966be42f19 Fix/auth sequence list 2024-01-31 13:39:11 +00:00
Jean Andreani
c4066930ba Merge branch 'fix/auth-sequence-list' into 'develop'
Fix/auth sequence list

See merge request geovisio/website!121
2024-01-31 09:00:25 +00:00
Jean Andreani
6322a75f42 Fix/auth sequence list 2024-01-31 09:00:25 +00:00
Andreani Jean
c37f814dc3 Release 2.3.1 2024-01-29 17:23:12 +01:00
Jean Andreani
fe68941ea9 Merge branch 'fix/ui-ux-fixs' into 'develop'
fix : UI / UX

See merge request geovisio/website!120
2024-01-29 16:20:50 +00:00
Jean Andreani
e2df50e18f fix : UI / UX 2024-01-29 16:20:50 +00:00
Jean Andreani
5d83e9df13 Merge branch 'feat-fullscreen-viewer' into 'develop'
feat: add fullscreen button to home

Closes #60

See merge request geovisio/website!118
2024-01-29 15:16:38 +00:00
Jean Andreani
d849f95013 feat: add fullscreen button to home 2024-01-29 15:16:38 +00:00
Jean Andreani
f53adeea28 Merge branch 'fix/pagination' into 'develop'
fix: remove condition pagination

See merge request geovisio/website!119
2024-01-24 13:56:08 +00:00
Jean Andreani
7685993710 fix: remove condition pagination 2024-01-24 13:56:08 +00:00
Jean Andreani
47dfd9bddc Merge branch 'feat/bbox-filter-sequence-list' into 'develop'
feat : bbox filter sequence

See merge request geovisio/website!117
2024-01-23 22:41:52 +00:00
Jean Andreani
b72fb7a0df feat : bbox filter sequence 2024-01-23 22:41:52 +00:00
Jean Andreani
136b3d629f Merge branch 'feat/filters-sequence-list' into 'develop'
Feat/filters sequence list

See merge request geovisio/website!116
2024-01-23 16:33:57 +00:00
Jean Andreani
9d82d73ca8 Feat/filters sequence list 2024-01-23 16:33:57 +00:00
Jean Andreani
503443f458 Merge branch 'feat/filters-sequence-list' into 'develop'
Feat/filters sequence list

Closes #57

See merge request geovisio/website!115
2024-01-22 14:08:46 +00:00
Jean Andreani
9b6bdc394b Feat/filters sequence list 2024-01-22 14:08:46 +00:00
Jean Andreani
cec383e424 Merge branch 'feat/link-to-sequence-from-viewer' into 'develop'
feat: add button to viewer to link to seuqence + refacto

Closes #48

See merge request geovisio/website!114
2023-12-19 12:53:06 +00:00
Jean Andreani
7e20788591 feat: add button to viewer to link to seuqence + refacto 2023-12-19 12:53:06 +00:00
Jean Andreani
390343916e Merge branch 'tech/add-more-test-e2e' into 'develop'
add e2e for upload test

See merge request geovisio/website!113
2023-12-18 10:27:31 +00:00
Jean Andreani
c768b714b9 add e2e for upload test 2023-12-18 10:27:31 +00:00
Jean Andreani
bf0bc4d91c Merge branch 'tech/perf-hover-sequence-list' into 'develop'
Tech/perf hover sequence list

See merge request geovisio/website!110
2023-12-12 15:55:46 +00:00
Jean Andreani
5d292b186c Tech/perf hover sequence list 2023-12-12 15:55:46 +00:00
Jean Andreani
93e434ecf9 Merge branch 'fix-e2e' into 'develop'
fix duration gitlab for e2e

See merge request geovisio/website!112
2023-12-12 15:43:21 +00:00
Jean Andreani
721bafbd3e fix duration gitlab for e2e 2023-12-12 15:43:21 +00:00
Jean Andreani
127550a19f Merge branch 'tech-add-test-e2e' into 'develop'
Tech add test e2e

See merge request geovisio/website!89
2023-12-12 15:01:50 +00:00
Jean Andreani
85abc46038 Tech add test e2e 2023-12-12 15:01:50 +00:00
131 changed files with 34027 additions and 3443 deletions

16
.drone.yml Normal file
View File

@@ -0,0 +1,16 @@
kind: pipeline
name: default
steps:
- name: docker
image: plugins/docker
settings:
registry: https://git.ivasoft.cz
username:
from_secret: repo_user
password:
from_secret: repo_pass
repo: git.ivasoft.cz/sw/geovisio-website
tags:
- latest
- ${DRONE_TAG:-latest}

7
.gitignore vendored
View File

@@ -90,6 +90,11 @@ sw.*
*.swp
# Cypress generated screen and videos files
cypress/downloads/*
cypress/screenshot/
cypress/videos/
*.cy.ts.mp4
*.cy.ts.mp4
# Mkdocs
env/
site/

View File

@@ -8,9 +8,11 @@ variables:
DOCKER_BUILDKIT: 1 # use buildkit for better performance
DOCKER_DRIVER: overlay2 # better docker driver to avoid copying too many files on each run
GITLAB_REGISTRY: registry.gitlab.com # We use docker.io for official images and gitlab's registry to store temporary images
IMAGE_NAME: geovisio/api
CI_IMAGE_CACHE: $GITLAB_REGISTRY/$IMAGE_NAME:build_cache
DOCKER_TLS_CERTDIR: ""
REPO_NAME: panoramax/server/website
DOCKER_IMAGE_NAME: geovisio/website
CI_IMAGE_CACHE: $GITLAB_REGISTRY/$REPO_NAME:build_cache
DOCKER_TLS_CERTDIR: ''
DOCKER_HOST: tcp://docker:2375
before_script:
## chmod is unfortunately currently mandatory : https://github.com/nodejs/docker-node/issues/661
@@ -22,28 +24,29 @@ cache:
install:
stage: Install
image: node:18.16.0
image: node:20.9.0
script:
- yarn install
- ls node_modules/.bin/cypress
test:unit:
stage: Test
image: node:18.16.0
image: node:20.9.0-alpine
script:
- yarn test:unit
coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
test:e2e:
stage: Test
image: cypress/browsers:node-18.16.0-chrome-113.0.5672.92-1-ff-113.0-edge-113.0.1774.35-1
image: node:20.9.0-alpine
services:
- docker:dind
script:
- yarn install
- ./node_modules/.bin/cypress install
- echo "VITE_API_URL=https://geovisio-proxy-dev.osc-fr1.scalingo.io/" > .env
- echo "VITE_ENV=dev" >> .env
- PORT=5173 yarn start &
- yarn test:e2e
- apk add --update --no-cache docker-cli docker-cli-compose git
- PROJECT_DIR=$PWD docker compose -f cypress/docker-compose-geovisio.yml -f cypress/docker-compose-gitlab-override.yml run --rm e2e
after_script:
- PROJECT_DIR=$PWD docker compose -f cypress/docker-compose-geovisio.yml -f cypress/docker-compose-gitlab-override.yml logs web || true
- PROJECT_DIR=$PWD docker compose -f cypress/docker-compose-geovisio.yml -f cypress/docker-compose-gitlab-override.yml down || true
coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
artifacts:
when: always
@@ -54,7 +57,7 @@ test:e2e:
deploy:
stage: Deploy
image: node:18.16.0
image: node:20.9.0
cache:
paths:
- node_modules
@@ -74,7 +77,7 @@ deploy:develop:
stage: Deploy
image: docker:latest
services:
- docker:dind
- docker:dind
before_script:
# login to the gitlab docker registry to use the cache and to publish
- echo $CI_DEPLOY_PASSWORD | docker login -u $CI_DEPLOY_USER --password-stdin $GITLAB_REGISTRY
@@ -86,7 +89,7 @@ deploy:develop:
- docker buildx build
--cache-from "type=registry,ref=$CI_IMAGE_CACHE"
--cache-to "type=registry,mode=max,ref=$CI_IMAGE_CACHE"
--tag "$CI_REGISTRY_IMAGE:develop"
--tag "$DOCKER_IMAGE_NAME:develop"
--label "org.opencontainers.image.title=$CI_PROJECT_TITLE"
--label "org.opencontainers.image.url=$CI_PROJECT_URL"
--label "org.opencontainers.image.created=$CI_JOB_STARTED_AT"
@@ -96,7 +99,7 @@ deploy:develop:
.
# publish image to dockerhub with the develop tag
- docker push "$CI_REGISTRY_IMAGE:develop"
- docker push "$DOCKER_IMAGE_NAME:develop"
deploy:latest:
# we consider that tag always land on main
@@ -106,7 +109,7 @@ deploy:latest:
stage: Deploy
image: docker:latest
services:
- docker:dind
- docker:dind
before_script:
# login to the gitlab docker registry to use the cache and to publish
- echo $CI_DEPLOY_PASSWORD | docker login -u $CI_DEPLOY_USER --password-stdin $GITLAB_REGISTRY
@@ -118,8 +121,8 @@ deploy:latest:
- docker buildx build
--cache-from "type=registry,ref=$CI_IMAGE_CACHE"
--cache-to "type=registry,mode=max,ref=$CI_IMAGE_CACHE"
--tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME"
--tag "$CI_REGISTRY_IMAGE:latest"
--tag "$DOCKER_IMAGE_NAME:$CI_COMMIT_REF_NAME"
--tag "$DOCKER_IMAGE_NAME:latest"
--label "org.opencontainers.image.title=$CI_PROJECT_TITLE"
--label "org.opencontainers.image.url=$CI_PROJECT_URL"
--label "org.opencontainers.image.created=$CI_JOB_STARTED_AT"
@@ -129,5 +132,5 @@ deploy:latest:
.
# publish image to dockerhub
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
- docker push $CI_REGISTRY_IMAGE:latest
- docker push $DOCKER_IMAGE_NAME:$CI_COMMIT_REF_NAME
- docker push $DOCKER_IMAGE_NAME:latest

View File

@@ -7,26 +7,119 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Before _0.1.0_, website development was on rolling release, meaning there are no version tags.
## [2.6.2] - 2024-15-03
### Changed
- Change Nodejs version to LTS 20.9.0
- Update dependencies (not prettier/typescript/eslint)
- Add tutorial on Android mobile for the picture upload
- Add tutorial on Android mobile when there is error on exif geoloc tag for the picture upload
- Add a server.js to serve statics files on deploy
### Fixed
- Fix orientation viewer on mobile
- Fix Header css
- Fix tests unit
## [2.6.1] - 2024-05-03
### Fixed
- Fix warning in browser console
- Fix lang switcher using cookie to set the locale
## [2.6.0] - 2024-05-02
### Changed
- Update Geovisio Viewer to 3.0.1
- Adapt the code for the new viewer version
- Change some features after user testing:
- Upload page tooltip
- Add an upload cancel button
- New mobile sequence list version
- Add a return home button in the header
- Improve the interaction in the Sequence page
- Improve the performance of the code
- Some refactoring
- Some bug fixes
## [2.5.1] - 2024-03-19
### Added
- Panel management to edit a sequence:
- 3 tabs to edit in a sequence page
- add the possibility to re-orient all the pictures of a sequence with a widget
- add the possibility to sort the pictures of a sequence
## [2.5.0] - 2024-03-11
### Changed
- GeoVisio web viewer updated to [2.5.0](https://gitlab.com/panoramax/clients/web-viewer/-/compare/2.4.0...2.5.0) to reduce tiles size.
## [2.4.1] - 2024-02-01
### Fixed
- Fix geovisio version yarn.lock
## [2.4.0] - 2024-01-31
### Added
- Possibility to edit a sequence title in the sequence page
### Changed
- GeoVisio web viewer updated to [2.4.0](https://gitlab.com/panoramax/clients/web-viewer/-/compare/2.3.1...2.4.0) to manage sequence by user
### Fixed
- Fix filter reset button to include bbox filter
- Fix fullscreen button added by the widget viewer
- Some UI and UX fixes before user tests
## [2.3.1] - 2024-01-29
### Added
- Add the possibility to fullscreen the viewer in the homepage : https://gitlab.com/panoramax/server/website/-/issues/60
- In the sequence list page add a filter by bbox in the map : https://gitlab.com/panoramax/server/website/-/issues/61
- In the sequence list page add a filter by date in the list : https://gitlab.com/panoramax/server/website/-/issues/57
- Add a cancel button to when a sequence is uploading to cancel the upload
### Changed
- GeoVisio web viewer updated to [2.3.1](https://gitlab.com/panoramax/clients/web-viewer/-/compare/2.3.0...2.3.1)
### Fixed
- Some UI and UX fixes before user tests
## [2.3.0] - 2023-12-06
### Added
- Add the possibility to an user to select a sequence in the list using the map : https://gitlab.com/geovisio/website/-/merge_requests/100
- For a selected sequence in the list, if the sequence is not displayed in the map, fly to the sequence on the map : https://gitlab.com/geovisio/website/-/merge_requests/108
- Add the pagination to the sequence with sort with API routes : https://gitlab.com/geovisio/website/-/merge_requests/107
- Add Hungarian translation : https://gitlab.com/geovisio/website/-/merge_requests/105
- Add the possibility to hide/delete a sequence in the sequence list : https://gitlab.com/geovisio/website/-/merge_requests/101
- Add the possibility for the user to change the title of the sequence before upload the pictures : https://gitlab.com/geovisio/website/-/merge_requests/101
- Add the possibility to resize the sequence page by dragging the blocs : https://gitlab.com/geovisio/website/-/merge_requests/101
- Add the possibility to an user to select a sequence in the list using the map : https://gitlab.com/panoramax/server/website/-/merge_requests/100
- For a selected sequence in the list, if the sequence is not displayed in the map, fly to the sequence on the map : https://gitlab.com/panoramax/server/website/-/merge_requests/108
- Add the pagination to the sequence with sort with API routes : https://gitlab.com/panoramax/server/website/-/merge_requests/107
- Add Hungarian translation : https://gitlab.com/panoramax/server/website/-/merge_requests/105
- Add the possibility to hide/delete a sequence in the sequence list : https://gitlab.com/panoramax/server/website/-/merge_requests/101
- Add the possibility for the user to change the title of the sequence before upload the pictures : https://gitlab.com/panoramax/server/website/-/merge_requests/101
- Add the possibility to resize the sequence page by dragging the blocs : https://gitlab.com/panoramax/server/website/-/merge_requests/101
### Changed
- GeoVisio web viewer updated to [2.3.0](https://gitlab.com/geovisio/web-viewer/-/compare/2.2.1...2.3.0)
- GeoVisio web viewer updated to [2.3.0](https://gitlab.com/panoramax/clients/web-viewer/-/compare/2.2.1...2.3.0)
### Fixed
- Nginx server in Docker container was not recognizing routes other than `/` on first loading.
- Fix the cookie bug by decoding flask cookie : https://gitlab.com/geovisio/website/-/merge_requests/102
- Fix the cookie bug by decoding flask cookie : https://gitlab.com/panoramax/server/website/-/merge_requests/102
## [2.2.3] - 2023-11-03
@@ -98,7 +191,7 @@ Before _0.1.0_, website development was on rolling release, meaning there are no
### Changed
- GeoVisio web viewer upgraded to 2.1.4, [with alls its changes embedded](https://gitlab.com/geovisio/web-viewer/-/blob/develop/CHANGELOG.md?ref_type=heads#213-2023-08-30).
- GeoVisio web viewer upgraded to 2.1.4, [with alls its changes embedded](https://gitlab.com/panoramax/clients/web-viewer/-/blob/develop/CHANGELOG.md?ref_type=heads#213-2023-08-30).
- Dockerfile creates smaller and faster containers, using pre-built website and Nginx for HTTP serving.
- In the upload input you can now choose between gallery and camera on mobile IOS
- Some CSS fix for the responsive of one Sequence
@@ -113,14 +206,14 @@ Before _0.1.0_, website development was on rolling release, meaning there are no
### Added
- A new page `/envoyer` to upload picture with an interface ([#13](https://gitlab.com/geovisio/website/-/issues/13)) :
- A new page `/envoyer` to upload picture with an interface ([#13](https://gitlab.com/panoramax/server/website/-/issues/13)) :
- the user can upload multiples pictures with the interface
- the pictures are sorted by name
- the user can see all the pictures uploaded and all the errors
### Changed
- Website releases now follow the synced `MAJOR.MINOR` API version rule, meaning that any version >= 2.1 of the website will be compatible with corresponding [GeoVisio API](https://gitlab.com/geovisio/api) version.
- Website releases now follow the synced `MAJOR.MINOR` API version rule, meaning that any version >= 2.1 of the website will be compatible with corresponding [GeoVisio API](https://gitlab.com/panoramax/server/api) version.
### Fixed
@@ -130,13 +223,13 @@ Before _0.1.0_, website development was on rolling release, meaning there are no
### Added
- A new page `/mes-sequences` to access to a list of sequences for a logged user ([#14](https://gitlab.com/geovisio/website/-/issues/14)) :
- A new page `/mes-sequences` to access to a list of sequences for a logged user ([#14](https://gitlab.com/panoramax/server/website/-/issues/14)) :
- the user can see all his sequences
- the user can filter sequences
- the user can enter to a specific sequence
- A new page `/sequence/:id` to access to a sequence of photos for a logged user ([#14](https://gitlab.com/geovisio/website/-/issues/14)) :
- A new page `/sequence/:id` to access to a sequence of photos for a logged user ([#14](https://gitlab.com/panoramax/server/website/-/issues/14)) :
- the user can see the sequence on the map and move on the map from photos to photos
- the user can see information about the sequence
- the user can see all the sequence's photos
@@ -147,14 +240,22 @@ Before _0.1.0_, website development was on rolling release, meaning there are no
- Header have now a new entry `Mes photos` when the user is logged to access to the sequence list
- The router guard for logged pages has been changed to not call the api to check the token
[unreleased]: https://gitlab.com/geovisio/website/-/compare/2.3.0...develop
[2.2.1]: https://gitlab.com/geovisio/website/-/compare/2.2.3...2.3.0
[2.2.1]: https://gitlab.com/geovisio/website/-/compare/2.2.2...2.2.3
[2.2.1]: https://gitlab.com/geovisio/website/-/compare/2.2.1...2.2.2
[2.2.1]: https://gitlab.com/geovisio/website/-/compare/2.2.0...2.2.1
[2.2.0]: https://gitlab.com/geovisio/website/-/compare/2.1.3...2.2.0
[2.1.3]: https://gitlab.com/geovisio/website/-/compare/2.1.2...2.1.3
[2.1.2]: https://gitlab.com/geovisio/website/-/compare/2.1.1...2.1.2
[2.1.1]: https://gitlab.com/geovisio/website/-/compare/2.1.0...2.1.1
[2.1.0]: https://gitlab.com/geovisio/website/-/compare/0.1.0...2.1.0
[0.1.0]: https://gitlab.com/geovisio/website/-/commits/0.1.0
[unreleased]: https://gitlab.com/panoramax/server/website/-/compare/2.6.2...develop
[2.6.2]: https://gitlab.com/panoramax/server/website/-/compare/2.6.1...2.6.2
[2.6.1]: https://gitlab.com/panoramax/server/website/-/compare/2.6.0...2.6.1
[2.6.0]: https://gitlab.com/panoramax/server/website/-/compare/2.5.1...2.6.0
[2.5.1]: https://gitlab.com/panoramax/server/website/-/compare/2.5.0...2.5.1
[2.5.0]: https://gitlab.com/panoramax/server/website/-/compare/2.4.1...2.5.0
[2.4.1]: https://gitlab.com/panoramax/server/website/-/compare/2.4.0...2.4.1
[2.4.0]: https://gitlab.com/panoramax/server/website/-/compare/2.3.1...2.4.0
[2.3.1]: https://gitlab.com/panoramax/server/website/-/compare/2.3.0...2.3.1
[2.3.0]: https://gitlab.com/panoramax/server/website/-/compare/2.2.3...2.3.0
[2.2.3]: https://gitlab.com/panoramax/server/website/-/compare/2.2.2...2.2.3
[2.2.2]: https://gitlab.com/panoramax/server/website/-/compare/2.2.1...2.2.2
[2.2.1]: https://gitlab.com/panoramax/server/website/-/compare/2.2.0...2.2.1
[2.2.0]: https://gitlab.com/panoramax/server/website/-/compare/2.1.3...2.2.0
[2.1.3]: https://gitlab.com/panoramax/server/website/-/compare/2.1.2...2.1.3
[2.1.2]: https://gitlab.com/panoramax/server/website/-/compare/2.1.1...2.1.2
[2.1.1]: https://gitlab.com/panoramax/server/website/-/compare/2.1.0...2.1.1
[2.1.0]: https://gitlab.com/panoramax/server/website/-/compare/0.1.0...2.1.0
[0.1.0]: https://gitlab.com/panoramax/server/website/-/commits/0.1.0

View File

@@ -2,7 +2,7 @@
#- Build image
#-
FROM node:18.16.0-alpine AS build
FROM node:20.9.0-alpine AS build
WORKDIR /opt/geovisio
@@ -19,6 +19,7 @@ COPY *.json *.js *.ts *.html ./
# Replace env variables by placeholder for dynamic change on container start
ENV VITE_INSTANCE_NAME=DOCKER_VITE_INSTANCE_NAME
ENV VITE_RASTER_TILE=DOCKER_VITE_RASTER_TILE
ENV VITE_API_URL=DOCKER_VITE_API_URL
ENV VITE_TILES=DOCKER_VITE_TILES
ENV VITE_MAX_ZOOM=DOCKER_VITE_MAX_ZOOM
@@ -26,7 +27,6 @@ ENV VITE_ZOOM=DOCKER_VITE_ZOOM
ENV VITE_CENTER=DOCKER_VITE_CENTER
# Build code
ENV PORT=3000
RUN yarn deploy
@@ -50,6 +50,7 @@ ENV VITE_INSTANCE_NAME="GeoVisio/Docker"
ENV VITE_API_URL="https://panoramax.openstreetmap.fr"
ENV VITE_TILES="https://tile-vect.openstreetmap.fr/styles/basic/style.json"
ENV VITE_MAX_ZOOM=""
ENV VITE_RASTER_TILE=""
ENV VITE_ZOOM=""
ENV VITE_CENTER=""

View File

@@ -1,35 +1,19 @@
# ![GeoVisio](https://gitlab.com/geovisio/api/-/raw/develop/images/logo_full.png)
# ![Panoramax](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Panoramax.svg/40px-Panoramax.svg.png) Panoramax
__GeoVisio__ is a complete solution for storing and __serving your own 📍📷 geolocated pictures__ (like [StreetView](https://www.google.com/streetview/) / [Mapillary](https://mapillary.com/)).
➡️ __Give it a try__ at [panoramax.ign.fr](https://panoramax.ign.fr/) or [geovisio.fr](https://geovisio.fr/viewer) !
## 📦 Components
GeoVisio is __modular__ and made of several components, each of them standardized and ♻️ replaceable.
![GeoVisio architecture](https://gitlab.com/geovisio/api/-/raw/develop/images/big_picture.png)
All of them are 📖 __open-source__ and available online:
| 🌐 Server | 💻 Client |
|:-----------------------------------------------------------------------:|:----------------------------------------------------:|
| [API](https://gitlab.com/geovisio/api) | [Website](https://gitlab.com/geovisio/website) |
| [Blur API](https://gitlab.com/geovisio/blurring) | [Web viewer](https://gitlab.com/geovisio/web-viewer) |
| [GeoPic Tag Reader](https://gitlab.com/geovisio/geo-picture-tag-reader) | [Command line](https://gitlab.com/geovisio/cli) |
__Panoramax__ is a digital resource for sharing and exploiting 📍📷 field photos. Anyone can take photographs of places visible from the public streets and contribute them to the Panoramax database. This data is then freely accessible and reusable by all. More information available at [gitlab.com/panoramax](https://gitlab.com/panoramax) and [panoramax.fr](https://panoramax.fr/).
# 💻 GeoVisio Website
This repository only contains __the web front-end of GeoVisio__.
Note that the 📷 __web viewer__ (component showing pictures and their location on a map) is in [a separate, dedicated repository](https://gitlab.com/geovisio/web-viewer).
Note that the 📷 __web viewer__ (component showing pictures and their location on a map) is in [a separate, dedicated repository](https://gitlab.com/panoramax/clients/web-viewer).
## ⚙️ Features
The website offers these functionalities:
- Display of pictures and their location (using the embed [web viewer](https://gitlab.com/geovisio/web-viewer))
- Display of pictures and their location (using the embed [web viewer](https://gitlab.com/panoramax/clients/web-viewer))
- Handle user authentication and account management
- Show simple to read documentation
@@ -39,22 +23,8 @@ The website offers these functionalities:
## 💁 Contributing
Pull requests are welcome. For major changes, please open an [issue](https://gitlab.com/geovisio/website/-/issues) first to discuss what you would like to change.
## 🤗 Special thanks
![Sponsors](https://gitlab.com/geovisio/api/-/raw/develop/images/sponsors.png)
GeoVisio was made possible thanks to a group of ✨ __amazing__ people ✨ :
- __[GéoVélo](https://geovelo.fr/)__ team, for 💶 funding initial development and for 🔍 testing/improving software
- __[Carto Cité](https://cartocite.fr/)__ team (in particular Antoine Riche), for 💶 funding improvements on viewer (map browser, flat pictures support)
- __[La Fabrique des Géocommuns (IGN)](https://www.ign.fr/institut/la-fabrique-des-geocommuns-incubateur-de-communs-lign)__ for offering long-term support and funding the [Panoramax](https://panoramax.fr/) initiative and core team (Camille Salou, Mathilde Ferrey, Christian Quest, Antoine Desbordes, Jean Andreani, Adrien Pavie)
- Many _many_ __wonderful people__ who worked on various parts of GeoVisio or core dependencies we use : 🧙 Stéphane Péneau, 🎚 Albin Calais & Cyrille Giquello, 📷 [Damien Sorel](https://www.strangeplanet.fr/), Pascal Rhod, Nick Whitelegg...
- __[Adrien Pavie](https://pavie.info/)__, for ⚙️ initial development of GeoVisio
- And you all ✨ __GeoVisio users__ for making this project useful !
Pull requests are welcome. For major changes, please open an [issue](https://gitlab.com/panoramax/server/website/-/issues) first to discuss what you would like to change.
## ⚖️ License
Copyright (c) GeoVisio team 2022-2023, [released under MIT license](./LICENSE).
Copyright (c) Panoramax team 2022-2024, [released under MIT license](./LICENSE).

View File

@@ -12,6 +12,6 @@ export default defineConfig({
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: 'http://localhost:5173'
baseUrl: 'http://localhost:5173/'
}
})

3
cypress.env.json Normal file
View File

@@ -0,0 +1,3 @@
{
"api_url": "http://localhost:5000/"
}

View File

@@ -0,0 +1,79 @@
services:
api:
image: geovisio/api:develop
command: api
restart: always
ports:
- 5000:5000
depends_on:
db:
condition: service_healthy
auth:
condition: service_healthy
environment:
DB_URL: postgres://gvs:gvspwd@db/geovisio
PICTURE_PROCESS_THREADS_LIMIT: 2
PICTURE_PROCESS_DERIVATES_STRATEGY: ON_DEMAND
API_FORCE_AUTH_ON_UPLOAD: 'true'
OAUTH_CLIENT_ID: geovisio
OAUTH_CLIENT_SECRET: what_a_secret
OAUTH_OIDC_URL: http://localhost:8183/realms/geovisio
OAUTH_PROVIDER: oidc
FLASK_SECRET_KEY: a_very_secret_key_never_to_be_used_in_production
healthcheck:
test: python -c "import requests; requests.get('http://localhost:5000/api').raise_for_status()"
interval: 5s
timeout: 5s
retries: 10
extra_hosts:
- 'localhost:host-gateway'
networks:
db: {}
geovisio:
aliases:
- api.localtest.me
db:
image: postgis/postgis:13-3.2
environment:
- POSTGRES_USER=gvs
- POSTGRES_PASSWORD=gvspwd
- POSTGRES_DB=geovisio
healthcheck:
test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER
interval: 5s
timeout: 5s
retries: 5
networks:
db: {}
auth:
command: start-dev --import-realm
environment:
GEOVISIO_BASE_URL: http://localhost:5000
GEOVISIO_CLIENT_SECRET: what_a_secret
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: password
KEYCLOAK_FRONTEND_URL: http://localhost:5000/api/auth/login
KC_HTTP_PORT: 8183
ports:
- '8183:8183'
healthcheck:
test: curl --fail http://localhost:8183/realms/geovisio
timeout: 5s
interval: 2s
retries: 20
start_period: 15s
image: quay.io/keycloak/keycloak:20.0.1
volumes:
- ./keycloak-realm.json:/opt/keycloak/data/import/geovisio_realm.json
networks:
geovisio:
aliases:
- keycloak.localtest.me
networks:
db: {}
geovisio: {}

View File

@@ -0,0 +1,55 @@
# Docker-compose used in gitlab-ci to run a container having access to all the other containers
services:
web:
image: node:20.9.0-alpine
volumes:
- $PROJECT_DIR:/src
working_dir: /src
command: >
sh -c "apk add --update --no-cache curl && yarn install && yarn dev"
environment:
PORT: 5173
VITE_API_URL: http://api.localtest.me:5000
VITE_ENV: dev
depends_on:
api:
condition: service_healthy
auth:
condition: service_healthy
ports:
- 5173:5173
healthcheck:
test: curl --fail http://0.0.0.0:5173
timeout: 10s
interval: 3s
retries: 20
start_period: 15s
networks:
geovisio:
aliases:
- front.localtest.me
e2e:
image: cypress/included:cypress-13.8.1-node-22.0.0-chrome-124.0.6367.60-1-ff-125.0.2-edge-124.0.2478.51-1
volumes:
- $PROJECT_DIR:/src
working_dir: /src
environment:
- CYPRESS_baseUrl=http://front.localtest.me:5173/
- CYPRESS_api_url=http://api.localtest.me:5000/
- LANG=fr
command: sleep 2 && yarn add --dev cypress && ./node_modules/.bin/cypress install && yarn test:e2e
depends_on:
web:
condition: service_healthy
networks:
geovisio: {}
api:
environment:
OAUTH_OIDC_URL: http://keycloak.localtest.me:8183/realms/geovisio
FLASK_SESSION_COOKIE_DOMAIN: localtest.me
auth:
environment:
GEOVISIO_BASE_URL: http://api.localtest.me:5000
KEYCLOAK_FRONTEND_URL: http://api.localtest.me:5000/api/auth/login

View File

@@ -0,0 +1,29 @@
describe('In the contribute page', () => {
it('go to the login page', () => {
cy.visit('/pourquoi-contribuer')
cy.get('.upload-text').scrollIntoView()
cy.fixture('contribute').then((contributeData: contributeInterface) => {
cy.contains(contributeData.textButtonContribute).click()
})
})
it('go to the doc pages', () => {
cy.visit('pourquoi-contribuer')
cy.fixture('contribute').then((contributeData: contributeInterface) => {
cy.get('.upload-text').scrollIntoView()
cy.contains(contributeData.textButtonDocPython).click()
cy.contains(contributeData.textButtonCli).click()
cy.contains(contributeData.textButtonDocCli).click()
cy.contains(contributeData.textButtonTiles).click()
cy.contains(contributeData.textButtonDoc).click()
})
})
})
interface contributeInterface {
textButtonContribute: string
textButtonDocPython: string
textButtonCli: string
textButtonDocCli: string
textButtonTiles: string
textButtonDoc: string
}
export {}

16
cypress/e2e/home.cy.ts Normal file
View File

@@ -0,0 +1,16 @@
describe('In the home page', () => {
it('click on the link in the footer to go to Panoramax.fr', () => {
cy.visit('/')
cy.fixture('home').then((homeData) => {
cy.contains(homeData.textLinkPanoramax).click()
})
})
it('click on the link in the footer to go to Gitlab', () => {
cy.visit('/')
cy.fixture('home').then((homeData) => {
cy.contains(homeData.textLinkGitlab).click()
})
})
})
export {}

View File

@@ -1,7 +1,25 @@
describe('In the login page', () => {
it('type in the form to login', () => {
cy.visit(`${Cypress.env('api_url')}api/auth/login`)
cy.get('#username').type('Elysee')
cy.get('#password').type('my password')
cy.contains('Sign In').click()
cy.visit('/')
})
it('go to the register form and create an account', () => {
cy.visit(`${Cypress.env('api_url')}api/auth/login`)
cy.fixture('login').then((loginData) => {
cy.contains(loginData.textLinkRegister).click()
cy.get('#firstName').type('Tom')
cy.get('#lastName').type('Tom')
cy.get('#email').type('test@test123.com')
cy.get('#username').type('Elysee12445')
cy.get('#password').type('my password1')
cy.get('#password-confirm').type('my password1')
cy.get('form').submit()
cy.visit('/')
})
})
})
export {}

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"types": ["reflect-metadata", "jest", "cypress"],
"module": "commonjs",
"target": "es5",
"sourceMap": true
},
"exclude": ["node_modules"]
}

32
cypress/e2e/upload.cy.ts Normal file
View File

@@ -0,0 +1,32 @@
describe('In the login page', () => {
it('login and go to the upload page to upload images', () => {
cy.visit(`${Cypress.env('api_url')}api/auth/login`)
cy.get('#username').type('Elysee')
cy.get('#password').type('my password')
cy.fixture('upload').then((uploadData: uploadInterface) => {
cy.contains(uploadData.textLinkLogin).click()
cy.visit('/envoyer')
cy.get('.edit-button').click()
cy.get('#upload-title').clear()
cy.get('#upload-title').type(uploadData.textTitle)
cy.contains(uploadData.textButtonTitle).click()
cy.contains(uploadData.textButtonUpload).click()
})
cy.get('.input-file').selectFile(
[
'cypress/fixtures/images/image1.jpg',
'cypress/fixtures/images/image2.jpg',
'cypress/fixtures/images/image3.jpg'
],
{ force: true }
)
})
})
interface uploadInterface {
textLinkLogin: string
textLinkUpload: string
textButtonUpload: string
textTitle: string
textButtonTitle: string
}
export {}

View File

@@ -0,0 +1,8 @@
{
"textButtonContribute": "Partager des photos",
"textButtonDocCli": "Voir la documentation",
"textButtonDoc": "Retrouvez sa documentation ici",
"textButtonDocPython": "de python (au moins la version 3.8)",
"textButtonCli": "L'outil en ligne de commande",
"textButtonTiles": "de tuiles vectorielles"
}

View File

@@ -0,0 +1,5 @@
{
"textLinkContribute": "Pourquoi contribuer ?",
"textLinkPanoramax": "Découvrir Panoramax",
"textLinkGitlab": "Voir le code"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -0,0 +1,4 @@
{
"textLinkRegister": "Register",
"textLinkLogin": "Sign In"
}

View File

@@ -0,0 +1,7 @@
{
"textLinkLogin": "Sign In",
"textLinkUpload": "Mes photos",
"textTitle": "My title",
"textButtonTitle": "Valider",
"textButtonUpload": "Glissez vos images ici ou cliquez sur"
}

1987
cypress/keycloak-realm.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
#!/bin/bash
ROOT_DIR=/usr/share/nginx/html
DOCKER_VARS=(VITE_INSTANCE_NAME VITE_API_URL VITE_TILES VITE_MAX_ZOOM VITE_ZOOM VITE_CENTER )
DOCKER_VARS=(VITE_INSTANCE_NAME VITE_API_URL VITE_TILES VITE_MAX_ZOOM VITE_ZOOM VITE_CENTER VITE_RASTER_TILE )
echo "Setting env variables in web files"
for file in $ROOT_DIR/assets/*.js $ROOT_DIR/index.html; do
for file in $ROOT_DIR/assets/*.js $ROOT_DIR/*.html; do
echo "Processing $file...";
for i in ${!DOCKER_VARS[@]}; do

View File

@@ -1,29 +0,0 @@
# GeoVisio Website hands-on guide
![GeoVisio logo](https://gitlab.com/geovisio/api/-/raw/develop/images/logo_full.png)
Welcome to GeoVisio __Website__ documentation ! It will help you through all phases of setup, run and develop on GeoVisio Website.
__Note that__ this only covers the Website / front-end component, if you're looking for docs on another component, you may go to [this page](https://gitlab.com/geovisio) instead.
Also, if at some point you're lost or need help, you can contact us through [issues](https://gitlab.com/geovisio/website/-/issues) or by [email](mailto:panieravide@riseup.net).
## Architecture
The website relies on the following technologies and components:
- Frontend website made in [Vue 3](https://vuejs.org/guide/introduction.html)
- Project use [Vite](https://vitejs.dev/guide/) who offer a fast development server and an optimized compilation for production (like webpack)
- The style is made with CSS/SASS and the [bootstrap library](https://getbootstrap.com/)
- [Typescript](https://www.typescriptlang.org/) used to type
- [Jest](https://jestjs.io/fr/) used for unit testing
## All the docs
You might want to dive into docs :
- [Install and setup](./02_Setup.md)
- [Change the settings](./03_Settings.md)
- [Work on the code](./09_Develop.md)

View File

@@ -2,81 +2,63 @@
GeoVisio website can be installed through classic method, or using Docker.
__Contents__
=== ":gear: Classic"
[[_TOC_]]
You need to have installed on your system:
* [NodeJS](https://nodejs.org/en/download) >= 18.13.0
* [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [Yarn](https://yarnpkg.com/)
The website can be installed locally by retrieving this repository and installing dependencies:
```bash
# Retrieve source code
git clone https://gitlab.com/panoramax/server/website.git
cd website/
# Install dependencies
npm install
```
Then, you need to define some settings. You have to create a `.env` file and edit its content.
```sh
cp env.example .env
```
See _Configuration_ for more details about available settings.
Then, building for production can be done with these commands:
```sh
npm run build
PORT=3000 npm run start
```
The website is now available at [localhost:3000](http://localhost:3000).
## Classic install
=== ":simple-docker: Docker"
### System requirements
The [Docker](https://docs.docker.com/get-docker/) deployment is a really convenient way to have a Geovisio website running in an easy and fast way. Note that this setup documentation only covers __GeoVisio front-end__ (website), if you also need an API running, please refer to [Docker API deployment](https://gitlab.com/panoramax/server/api/-/blob/develop/docs/14_Running_Docker.md).
**You need to have [Nodejs installed](https://nodejs.org/en/download)**
Node version : >=18.13.0
You can use the provided __Docker Hub__ `geovisio/website:latest` image directly:
**You need to have [Npm installed](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)**
```bash
docker run \
-e VITE_API_URL="https://your.geovisio.api/" \
-e VITE_INSTANCE_NAME="My Own GeoVisio" \
-e VITE_TILES="https://your.geovisio.api/vector/tiles/style.json" \
-p 3000:3000 \
--name geovisio-website \
-d \
geovisio/website:latest
```
You can use npm or [yarn](https://yarnpkg.com/) as package manager
This will run a container bound on [localhost:3000](http://localhost:3000).
### Install
You can also build the image from the local source with:
The website can be installed locally by retrieving this repository and installing dependencies:
```sh
# Retrieve source code
git clone https://gitlab.com/geovisio/website.git
cd website/
# Install dependencies
npm install
```
### Build for production
Before building, you need to define a bit of settings. At least, you have to create a `.env` file and edit its content.
```sh
cp env.example .env
```
More details about settings [can be found in docs here](./03_Settings.md).
Then, building for production can be done with these commands:
```sh
npm run build
PORT=3000 npm run start
```
The website is now available at [localhost:3000](http://localhost:3000).
## Docker setup
The [Docker](https://docs.docker.com/get-docker/) deployment is a really convenient way to have a Geovisio website running in an easy and fast way. Note that this setup documentation only covers __GeoVisio front-end__ (website), if you also need an API running, please refer to [Docker API deployment](https://gitlab.com/geovisio/api/-/blob/develop/docs/14_Running_Docker.md).
You can use the provided __Docker Hub__ `geovisio/website:latest` image directly:
```bash
docker run \
-e VITE_API_URL="https://your.geovisio.api/" \
-e VITE_INSTANCE_NAME="My Own GeoVisio" \
-e VITE_TILES="https://your.geovisio.api/vector/tiles/style.json" \
-p 3000:3000 \
--name geovisio-website \
-d \
geovisio/website:latest
```
This will run a container bound on [localhost:3000](http://localhost:3000).
You can also build the image from the local source with:
```bash
docker build -t geovisio/website:latest .
```
## Next steps
You can check out [the available settings for your instance](./03_Settings.md).
```bash
docker build -t geovisio/website:latest .
```

View File

@@ -8,16 +8,17 @@ Low-level settings can be changed through the `.env` file. An example is given i
Available parameters are:
- `VITE_API_URL`: the URL to the GeoVisio API (with trailing `/`, example: `https://geovisio.fr/`)
- `VITE_API_URL`: the URL to the GeoVisio API (example: `https://panoramax.ign.fr/`)
- `VITE_INSTANCE_NAME`: the name of the instance (example: `IGN`)
- `VITE_TILES`: the URL of your tiles : default tiles are the Open Street Map Tiles (example: `https://wxs.ign.fr/essentiels/static/vectorTiles/styles/PLAN.IGN/attenue.json`)
- `VITE_TILES`: the URL of your tiles : default tiles are the OpenStreetMap Tiles (example: `https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json`)
- `VITE_MAX_ZOOM`: the max zoom to use on the map (defaults to 24).
- `VITE_ZOOM`: the zoom to use at the initialization of the map (defaults to 0).
- `VITE_CENTER`: the center position to use at the initialization of the map (defaults to 0).
- `VITE_RASTER_TILE`: the raster tile. Example : `https://maplibre.org/maplibre-style-spec/sources/#raster`.
- Settings for the work environment:
- `NPM_CONFIG_PRODUCTION`: is it production environment (`true`, `false`)
- `YARN_PRODUCTION`: same as below, but if you use Yarn instead of NPM
- `VITE_ENV`: `dev`
- `NPM_CONFIG_PRODUCTION`: is it production environment (`true`, `false`)
- `YARN_PRODUCTION`: same as below, but if you use Yarn instead of NPM
- `VITE_ENV`: `dev`
More settings are available [in official Vite documentation](https://vitejs.dev/guide/env-and-mode.html#env-files)
@@ -27,21 +28,17 @@ Note that you can also change the _Vite_ server configuration in the `vite.confi
GeoVisio website can be customized to have wording reflecting your brand, licence and other elements.
All the wordings of the website are on this [locale file](./src/locales/fr.json). In there, you might want to change:
All the wordings of the website are on this [locale file](https://gitlab.com/panoramax/server/website/-/blob/develop/src/locales/en.json). In there, you might want to change:
- The website title (properties `title` and `meta.title`)
- The description (property `meta.description`)
- Links to help pages:
- `upload.description`
- `upload.footer_description_terminal`
- `upload.description`
- `upload.footer_description_terminal`
## Visuals
The following images can be changed to make the website more personal:
- Logo: [`src/assets/images/logo.jpeg`](../src/assets/images/logo.jpeg)
- Favicon: [`static/favicon.ico`](../static/favicon.ico)
## Next steps
You may be interested [in developing on the website](./09_Develop.md).
- Logo: [`src/assets/images/logo.jpeg`](https://gitlab.com/panoramax/server/website/-/blob/develop/src/assets/images/logo.jpeg)
- Favicon: [`static/favicon.ico`](https://gitlab.com/panoramax/server/website/-/blob/develop/static/favicon.ico)

View File

@@ -1,10 +1,16 @@
# Work on the code
## Available commands
## Architecture
Note that all the commands and packages used are available in the `package.json` file.
The website relies on the following technologies and components:
### Compile and Hot-Reload for Development
- Frontend website made in [Vue 3](https://vuejs.org/guide/introduction.html)
- Project use [Vite](https://vitejs.dev/guide/) who offer a fast development server and an optimized compilation for production (like webpack)
- The style is made with CSS/SASS and the [bootstrap library](https://getbootstrap.com/)
- [Typescript](https://www.typescriptlang.org/) used to type
- [Jest](https://jestjs.io/fr/) used for unit testing
## Compile and Hot-Reload for Development
Launch your dev server :
@@ -18,7 +24,7 @@ or
yarn dev
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
## Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
npm run test:unit
@@ -30,7 +36,7 @@ or
yarn test:unit
```
### Lint with [ESLint](https://eslint.org/)
## Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
@@ -41,3 +47,14 @@ or
```sh
yarn lint
```
## Documentation
Documentation is located `docs` folder, and can be served with [Mkdocs](https://www.mkdocs.org/):
```bash
python -m venv env
source ./env/bin/activate
pip install mkdocs-material
mkdocs serve
```

View File

@@ -1,8 +1,9 @@
# Make a release
The web site uses [semantic versioning](https://semver.org/) for its release numbers.
The website uses [semantic versioning](https://semver.org/) for its release numbers.
__Note__ : make sure that versions are in-sync with other Website components. Each component can have different `PATCH` versions, but compatibility __must__ be ensured between `MAJOR.MINOR` versions.
!!! note
Make sure that versions are in-sync with other GeoVisio components. Each component can have different `PATCH` versions, but compatibility __must__ be ensured between `MAJOR.MINOR` versions.
Run these commands in order to issue a new release:

15
docs/index.md Normal file
View File

@@ -0,0 +1,15 @@
# Panoramax instance Website
Welcome to GeoVisio __Website__ documentation ! It will help you through all phases of setup, run and develop on GeoVisio Website.
The website offers these functionalities:
- Display of pictures and their location (using the embed [web viewer](https://gitlab.com/panoramax/clients/web-viewer))
- Handle user authentication and account management
- Show simple to read documentation
!!! note
The 📷 __web viewer__ (component showing pictures and their location on a map) is in [a separate, dedicated repository](https://gitlab.com/panoramax/clients/web-viewer). If you're looking for docs on another component, you may go to [this page](https://gitlab.com/panoramax) instead.
!!! note
If at some point you're lost or need help, you can contact us through [issues](https://gitlab.com/panoramax/server/website/-/issues) or by [email](mailto:panieravide@riseup.net).

View File

@@ -4,6 +4,31 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/static/favicon.ico" />
<title>
Panoramax <%- instanceName %> : photo-cartographier les territoires
</title>
<meta
name="description"
content="L'instance Panoramax <%- instanceName %> permet la publication de photo de terrain pour cartographier le territoire. Panoramax favorise la réutilisation des photos pour de nombreux cas d'usages."
/>
<meta
name="twitter:title"
content="Panoramax <%- instanceName %> : photo-cartographier les territoires"
/>
<meta
name="og:title"
content="Panoramax <%- instanceName %> : photo-cartographier les territoires"
/>
<meta
name="twitter:description"
content="L'instance Panoramax <%- instanceName %> permet la publication de photo de terrain pour cartographier le territoire. Panoramax favorise la réutilisation des photos pour de nombreux cas d'usages."
/>
<meta
name="og:description"
content="L'instance Panoramax <%- instanceName %> permet la publication de photo de terrain pour cartographier le territoire. Panoramax favorise la réutilisation des photos pour de nombreux cas d'usages."
/>
<meta name="og:image" content="<%- frontUrl %>/static/meta-img.jpg" />
<meta name="twitter:image" content="<%- frontUrl %>/static/meta-img.jpg" />
</head>
<body>
<div id="app"></div>

42
mkdocs.yml Normal file
View File

@@ -0,0 +1,42 @@
site_name: 'Panoramax - Website'
site_url: https://docs.panoramax.fr
theme:
name: material
logo: https://gitlab.com/panoramax/gitlab-profile/-/raw/main/images/panoramax_favicon.svg
features:
- navigation.footer
- navigation.tracking
- navigation.sections
- search.suggest
- search.share
- content.code.annotate
- content.code.copy
- content.code.select
plugins:
- search
markdown_extensions:
- admonition
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- attr_list
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
nav:
- Home: 'index.md'
- Install: '02_Setup.md'
- Configuration: '03_Settings.md'
- Developping:
- 'Where to start': '09_Develop.md'
- 'Make a release': '90_Releases.md'

22711
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
{
"name": "geovisio-website",
"version": "2.3.0",
"version": "2.6.2",
"engines": {
"node": "18.16.0"
"node": "20.9.0"
},
"private": true,
"scripts": {
"dev": "vite",
"start": "vite --port $PORT",
"build": "run-p type-check build-only",
"start": "node server.js",
"build": "run-p build-only",
"preview": "vite preview",
"test:unit": "vitest --environment jsdom --root src/",
"test:e2e": "yarn cypress run --browser chrome",
@@ -20,55 +20,61 @@
"format": "prettier . --write"
},
"dependencies": {
"@popperjs/core": "^2.11.6",
"@vueuse/core": "^10.2.1",
"axios": "^1.2.3",
"bootstrap": "^5.2.3",
"bootstrap-icons": "^1.10.3",
"geovisio": "2.3.0",
"moment": "^2.29.4",
"@popperjs/core": "^2.11.8",
"@vueuse/core": "^10.9.0",
"axios": "^1.6.8",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"express": "4.19.2",
"geovisio": "3.0.1",
"moment": "^2.30.1",
"pako": "^2.1.0",
"pinia": "^2.1.4",
"vue": "^3.2.45",
"pinia": "^2.1.7",
"v-calendar": "^3.1.2",
"vue": "^3.4.21",
"vue-axios": "^3.5.2",
"vue-draggable-resizable-vue3": "^2.3.1-beta.13",
"vue-eslint-parser": "^9.1.0",
"vue-i18n": "9.2.2",
"vue-meta": "^3.0.0-alpha.10",
"vue-router": "^4.1.6",
"vue-i18n": "9.13.1",
"vue-matomo": "^4.2.0",
"vue-router": "^4.3.2",
"vue3-cookies": "^1.0.6",
"vue3-smooth-scroll": "^0.8.1"
},
"devDependencies": {
"@pinia/testing": "^0.1.2",
"@pinia/testing": "^0.1.3",
"@rushstack/eslint-patch": "^1.1.4",
"@types/jest": "^29.5.4",
"@types/jsdom": "^20.0.1",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.46.0",
"@typescript-eslint/parser": "^5.4.0",
"@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/cli-plugin-typescript": "~5.0.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/test-utils": "^2.2.4",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.1.3",
"cypress": "^12.12.0",
"cypress": "^13.9.0",
"eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-vue": "^9.8.0",
"jsdom": "^20.0.3",
"jest": "^29.7.0",
"jsdom": "^24.0.0",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"less-loader": "^12.2.0",
"npm-run-all": "^4.1.5",
"prettier": "2.8.1",
"sass": "^1.62.0",
"sass": "^1.77.1",
"terser": "^5.30.4",
"typescript": "~4.7.4",
"vite": "^3.2.4",
"vite": "^5.2.11",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^0.25.3",
"vue-tsc": "^1.0.9"
"vite-plugin-html": "^3.2.2",
"vitest": "^1.6.0",
"vue-eslint-parser": "^9.4.2",
"vue-tsc": "^2.0.17"
},
"eslintConfig": {
"extends": [

13
server.js Normal file
View File

@@ -0,0 +1,13 @@
const express = require('express')
const app = express()
const directory = '/' + (process.env.STATIC_DIR || 'dist')
app.use(express.static(__dirname + directory))
const port = process.env.PORT || 3003
app.listen(port, function () {
console.log('Listening on', port)
})
app.get('*', function (request, response) {
response.sendFile(__dirname + '/dist/index.html')
})

View File

@@ -1,37 +1,16 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref } from 'vue'
import { RouterView } from 'vue-router'
import { authConfig, isAuth } from './composables/auth'
const { authConf } = authConfig()
const { isLogged } = isAuth()
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import { RouterView } from 'vue-router'
import { useMeta } from 'vue-meta'
import { useI18n } from 'vue-i18n'
import { hasASessionCookieDecoded } from '@/utils/auth'
import { title } from '@/utils/index'
import authConfig from './composables/auth'
const { authConf } = authConfig()
const { t } = useI18n()
let focusMap = ref<string>('focus-map')
useMeta({
title: title(t('general.title')),
og: {
title: title(t('general.meta.title')),
description: title(t('general.meta.description'))
},
twitter: {
title: title(t('general.meta.title')),
description: title(t('general.meta.description'))
}
})
function setFocusMap(value: string) {
focusMap.value = value
}
const isLogged = computed((): boolean => {
const cookie = hasASessionCookieDecoded()
return !!(cookie && cookie.account)
})
</script>
<template>

View File

@@ -0,0 +1,14 @@
@mixin switch-button-view() {
position: fixed;
right: 0;
top: toRem(22);
z-index: 3;
height: toRem(5);
display: flex;
align-items: center;
justify-content: center;
border-top-left-radius: toRem(0.5);
border-bottom-left-radius: toRem(0.5);
background-color: var(--white);
border: toRem(0.1) solid var(--grey-pale);
}

View File

@@ -28,7 +28,7 @@
}
@if $size == h4 {
font-weight: normal;
font-size: toRem(1.6);
font-size: toRem(1.8);
}
@if $size == xxl-regular {
font-size: toRem(3.2);
@@ -42,10 +42,6 @@
font-size: toRem(1.4);
font-weight: normal;
}
@if $size == xss-regular {
font-size: toRem(0.9);
font-weight: normal;
}
@if $size == m-r-regular {
font-size: toRem(1.6);
@@ -54,13 +50,6 @@
font-size: toRem(1.4);
}
}
@if $size == xs-r-regular {
font-size: toRem(1.2);
font-weight: normal;
@media (max-width: toRem(50)) {
font-size: toRem(1);
}
}
@if $size == s-r-regular {
font-size: toRem(1.4);
font-weight: normal;
@@ -68,4 +57,15 @@
font-size: toRem(1.2);
}
}
@if $size == xs-r-regular {
font-size: toRem(1.2);
font-weight: normal;
@media (max-width: toRem(50)) {
font-size: toRem(1);
}
}
@if $size == xss-regular {
font-size: toRem(1);
font-weight: normal;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

37
src/assets/images/car.svg Normal file
View File

@@ -0,0 +1,37 @@
<svg width="58" height="114" viewBox="0 0 58 114" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M54.5335 97.255C53.702 100.976 51.4048 104.696 50.7988 106.514C50.0096 108.882 39.7427 110.087 29.2996 110.136C29.1939 110.136 29.0953 110.136 28.9896 110.136C28.8839 110.136 28.7852 110.136 28.6795 110.136C18.2365 110.087 7.96957 108.882 7.18035 106.514C6.57434 104.696 4.2701 100.976 3.44565 97.255L2.7269 99.221C3.42451 103.238 6.02471 107.395 6.68004 109.361C7.4904 111.785 17.9898 113.018 28.6725 113.06C28.7782 113.06 28.8839 113.06 28.9896 113.06C29.0953 113.06 29.201 113.06 29.3067 113.06C39.9893 113.011 50.4887 111.778 51.2991 109.354C51.9544 107.381 54.5546 103.23 55.2522 99.2139L54.5335 97.2479V97.255Z" fill="#160302"/>
<path d="M6.30663 32.0951C7.65253 25.2599 10.2386 11.8713 10.2386 11.8713L10.288 11.7304C11.9439 6.12836 27.087 5.0291 28.8276 5.08547C28.8839 5.08547 28.9333 5.08547 28.9896 5.08547C29.046 5.08547 29.0953 5.08547 29.1517 5.08547C30.8852 5.0291 46.0353 6.12836 47.6913 11.7304L47.7406 11.8713C47.7406 11.8713 50.3197 25.2528 51.6726 32.0951C54.5265 27.2752 55.203 21.4759 53.5611 16.1204C52.2786 11.9348 51.0807 8.97519 51.0807 8.97519L51.0173 8.81312C49.0654 2.2175 31.2234 0.920929 29.1799 0.984348C29.1165 0.984348 29.053 0.984348 28.9896 0.984348C28.9262 0.984348 28.8628 0.984348 28.7994 0.984348C26.7559 0.920929 8.91387 2.2175 6.95492 8.81312L6.8915 8.97519C6.8915 8.97519 5.69358 11.9277 4.4111 16.1204C2.76924 21.4759 3.44571 27.2752 6.29958 32.0951H6.30663Z" fill="#160302"/>
<path d="M55.0126 70.3581L53.6879 66.3627L53.0396 48.5559C52.9621 43.6022 53.0466 41.8405 53.2017 37.373C53.2228 36.7881 54.7308 33.7863 54.7519 33.2155C55.1324 23.5969 49.5797 9.87012 49.5797 9.87012L49.5233 9.72214C47.7053 3.57046 31.0683 2.35845 29.1657 2.42187C29.1023 2.42187 29.0459 2.42187 28.9895 2.42187C28.9332 2.42187 28.8698 2.42187 28.8134 2.42187C26.9038 2.35845 10.2738 3.57046 8.44871 9.72214L8.39234 9.87012C8.39234 9.87012 2.83962 23.5969 3.22014 33.2155C3.24128 33.7863 4.74925 36.7881 4.77039 37.373C4.92541 41.8405 5.00997 43.6022 4.93246 48.5559L4.29122 66.257L2.95941 70.2595V93.8162L3.21309 95.9302V95.9795C3.7134 100.095 6.50385 104.485 7.17328 106.507C7.9625 108.875 18.2294 110.08 28.6724 110.129C28.7781 110.129 28.8768 110.129 28.9825 110.129C29.0882 110.129 29.1868 110.129 29.2925 110.129C39.7356 110.08 50.0025 108.875 50.7917 106.507C51.4682 104.478 54.2516 100.095 54.7519 95.9795V95.9302L55.0056 93.9149V70.3581H55.0126Z" fill="#17347A"/>
<path d="M45.8168 47.795L26.0018 27.9799C24.7898 12.8016 31.9843 6.36803 39.1226 3.68328C34.5845 2.66857 30.1029 2.3867 29.1657 2.42194C29.1023 2.42194 29.0459 2.42194 28.9895 2.42194C28.9332 2.42194 28.8698 2.42194 28.8134 2.42194C26.9038 2.35852 10.2738 3.57053 8.44871 9.72221L8.39234 9.87019C8.39234 9.87019 2.83962 23.597 3.22014 33.2156C3.24128 33.7863 4.74925 36.7882 4.77039 37.3731C4.92541 41.8406 5.00997 43.6023 4.93246 48.556L4.29122 66.2571L2.95941 70.2595V93.8163L3.21309 95.9303V95.9796C3.64293 99.5029 5.74282 103.216 6.76458 105.485C9.08291 100.003 12.268 92.1674 12.268 92.1674L12.8881 51.276C20.139 52.9742 28.6302 52.6642 28.6302 52.6642C45.6054 52.8192 45.8239 47.7879 45.8239 47.7879L45.8168 47.795Z" fill="#1E4084"/>
<path d="M43.9213 10.8637L49.0583 18.9673L51.0031 22.0396C49.6572 17.0788 48.0929 13.2173 48.0929 13.2173L48.0436 13.0763C47.4939 11.2231 45.4856 9.85605 42.9771 8.84839C43.2096 9.55305 43.5196 10.2295 43.9142 10.8637H43.9213Z" fill="#C1D1D0"/>
<path d="M14.0508 10.8637C14.4524 10.2366 14.7555 9.55305 14.988 8.84839C12.4724 9.84901 10.4711 11.216 9.92149 13.0763L9.87216 13.2173C9.87216 13.2173 8.30781 17.0788 6.96191 22.0396L14.0508 10.8637Z" fill="#C1D1D0"/>
<path d="M14.4102 10.2225C14.6427 9.77854 14.833 9.32051 14.988 8.84839C12.4724 9.84901 10.4711 11.216 9.92151 13.0763L9.87218 13.2173C9.87218 13.2173 9.40006 14.387 8.75177 16.2544C9.22389 16.1134 9.66078 15.8809 10.0554 15.5286C11.4154 14.3165 13.1488 12.2589 14.4102 10.2225Z" fill="#DFF3F4"/>
<path d="M43.562 10.2225C44.8233 12.266 46.5638 14.3165 47.9167 15.5286C48.3114 15.8809 48.7482 16.1064 49.2204 16.2544C48.5721 14.38 48.1 13.2173 48.1 13.2173L48.0506 13.0763C47.501 11.2231 45.4927 9.85605 42.9841 8.84839C43.1392 9.32051 43.3294 9.78559 43.562 10.2225Z" fill="#DFF3F4"/>
<path d="M45.3729 106.655C39.0521 107.578 33.1189 107.578 28.9826 107.578C24.8462 107.578 18.913 107.578 12.5922 106.655C7.37065 105.894 8.97727 104.097 8.97727 104.097L12.3455 97.6073C18.3633 98.8052 39.5877 98.8052 45.6055 97.6073L48.9738 104.097C48.9738 104.097 50.5874 105.894 45.3589 106.655H45.3729Z" fill="#373637"/>
<path d="M28.9896 107.656C24.9871 107.656 18.9482 107.656 12.5922 106.733C10.3161 106.401 9.01955 105.838 8.73769 105.048C8.55448 104.534 8.87862 104.125 8.92795 104.055L12.3174 97.5227L12.3737 97.5368C18.4268 98.7488 39.5454 98.7488 45.6055 97.5368L45.6619 97.5227L45.69 97.572L49.0583 104.055C49.1147 104.118 49.4388 104.534 49.2486 105.048C48.9667 105.838 47.6701 106.401 45.3941 106.733C39.0381 107.656 32.9921 107.656 28.9967 107.656H28.9896ZM12.4019 97.6989L9.04774 104.154C9.04774 104.154 8.7095 104.541 8.87862 104.999C9.06183 105.499 9.8581 106.183 12.6133 106.585C18.9623 107.508 24.9942 107.508 28.9896 107.508C32.985 107.508 39.024 107.508 45.3659 106.585C48.1211 106.183 48.9174 105.499 49.1006 104.999C49.2697 104.534 48.9385 104.161 48.9315 104.154L48.9174 104.139L45.5773 97.6989C39.4538 98.8968 18.5254 98.8968 12.4019 97.6989Z" fill="#160302"/>
<path d="M44.6753 105.774C38.6223 106.627 32.9427 106.641 28.9896 106.641C25.0365 106.641 19.3499 106.627 13.3039 105.774C8.30079 105.07 9.84399 103.407 9.84399 103.407L12.8177 97.861C19.223 98.9532 38.7562 98.9532 45.1545 97.861L48.1282 103.407C48.1282 103.407 49.6714 105.07 44.6683 105.774H44.6753Z" fill="#DFF3F4"/>
<path d="M48.1282 103.407L47.8674 102.92C47.346 102.765 46.6907 102.624 45.8662 102.512C39.3551 101.596 33.2457 101.582 28.9826 101.582C24.7194 101.582 18.61 101.596 12.0989 102.512C11.2745 102.624 10.6191 102.765 10.0977 102.92L9.83694 103.407C9.83694 103.407 8.29374 105.07 13.2968 105.774C19.3499 106.627 25.0294 106.641 28.9826 106.641C32.9357 106.641 38.6223 106.627 44.6683 105.774C49.6714 105.07 48.1282 103.407 48.1282 103.407Z" fill="#C1D1D0"/>
<path d="M35.8953 109.03L35.8741 108.988L35.8389 108.931L35.7473 108.769C35.5571 108.713 35.3245 108.67 35.0567 108.628C32.6609 108.29 30.406 108.29 28.9192 108.29C27.4323 108.29 25.1774 108.29 22.7816 108.628C22.5138 108.663 22.2883 108.713 22.091 108.769L22.0135 108.917C22.0135 108.917 21.9853 108.952 21.9712 108.981L21.9501 109.023H21.9007C18.2717 108.868 15.0162 108.537 11.9509 108.022C9.66785 107.635 8.33604 106.986 7.98371 106.084C7.71594 105.394 8.13874 104.837 8.15283 104.816L11.9791 97.3396C12.0919 97.1141 12.4301 96.9872 12.7543 97.0577C19.2724 98.3684 38.6787 98.3684 45.1968 97.0577C45.5209 96.9943 45.8592 97.1141 45.9719 97.3396L49.8053 104.816C49.8546 104.879 50.2281 105.422 49.9744 106.077C49.6221 106.979 48.2832 107.628 46.0072 108.015C42.9137 108.537 39.6159 108.868 35.9376 109.023H35.8882L35.8953 109.03ZM35.9658 108.847C35.9658 108.847 35.9728 108.861 35.9869 108.875C39.6441 108.72 42.9137 108.396 45.9931 107.874C48.7624 107.402 49.615 106.613 49.8476 106.035C50.0871 105.429 49.6996 104.922 49.6996 104.915V104.901L45.8521 97.4171C45.7676 97.255 45.4928 97.1634 45.2391 97.2127C38.7069 98.5234 19.2653 98.5234 12.7331 97.2127C12.4794 97.1634 12.2046 97.255 12.1201 97.4171L8.28672 104.901C8.28672 104.901 7.89211 105.429 8.13169 106.035C8.35718 106.613 9.21687 107.409 11.9862 107.874C15.0303 108.389 18.2647 108.713 21.8655 108.868C21.8796 108.847 21.8937 108.833 21.8937 108.833L21.9923 108.642H22.0205C22.2249 108.572 22.4715 108.523 22.7604 108.48C25.1704 108.142 27.4253 108.142 28.9192 108.142C30.413 108.142 32.6679 108.142 35.0779 108.48C35.3668 108.523 35.6134 108.572 35.8178 108.628H35.846L35.9517 108.84L35.9658 108.847Z" fill="#160302"/>
<path d="M45.3166 26.8032C39.0169 24.513 33.1119 24.4778 28.9826 24.4778C24.8533 24.4778 18.9482 24.513 12.6486 26.8032C7.44113 28.6987 9.04775 32.8844 9.04775 32.8844L12.1483 47.7879C13.7478 52.939 44.161 52.8262 45.8099 47.7879L48.9104 32.8844C48.9104 32.8844 50.517 28.6987 45.3095 26.8032H45.3166Z" fill="#160302"/>
<path d="M44.6753 29.0792C38.6223 27.0427 32.9427 27.0145 28.9896 27.0145C25.0364 27.0145 19.3498 27.0427 13.3038 29.0792C8.30076 30.7633 9.84396 34.7376 9.84396 34.7376L12.8176 47.9922C14.3538 52.5725 43.569 52.4668 45.1545 47.9922L48.1281 34.7376C48.1281 34.7376 49.6713 30.7633 44.6683 29.0792H44.6753Z" fill="#DFF3F4"/>
<path d="M44.6753 29.0792C38.6223 27.0427 32.9427 27.0145 28.9896 27.0145C25.0364 27.0145 19.3498 27.0427 13.3038 29.0792C8.30076 30.7633 9.84396 34.7376 9.84396 34.7376L10.3654 37.0489C10.8728 37.4083 11.514 37.7324 12.2962 38.0002C12.3314 38.9233 12.4583 39.783 12.7401 40.5299C12.1693 40.6356 11.6549 40.7554 11.2321 40.8963L11.7606 43.2428C13.2193 43.6234 15.3403 43.87 17.7009 43.87C22.1121 43.87 25.6847 43.0314 25.6847 41.9956C25.6847 41.4037 24.515 40.8752 22.6829 40.5369C22.7463 40.389 22.7956 40.241 22.8449 40.093C25.3394 40.2903 27.5943 40.2974 29.4899 40.2974C33.8094 40.2974 39.9964 40.2692 46.599 38.0425C46.9161 37.9368 47.205 37.817 47.4728 37.6972L48.1352 34.7446C48.1352 34.7446 49.6784 30.7704 44.6753 29.0862V29.0792Z" fill="#C1D1D0"/>
<path d="M6.10921 33.7792C6.1233 33.2789 6.8632 33.2014 6.99708 33.6806C8.54733 39.3037 11.8381 51.29 11.824 51.8185L10.76 92.428C10.7318 93.6823 9.49862 94.2813 8.95604 93.2032C8.95604 93.2032 5.36932 83.4718 5.35522 80.3431L6.10921 33.7792Z" fill="#17347A"/>
<path d="M9.72417 93.8443C9.38594 93.8443 9.08293 93.6259 8.88563 93.2383C8.84335 93.1326 5.29186 83.4506 5.27777 80.3431L6.03176 33.7791C6.0388 33.4761 6.27134 33.3141 6.49683 33.2859C6.71528 33.2577 6.99009 33.3634 7.07465 33.6593C8.88563 40.2127 11.9227 51.297 11.9016 51.8184L10.8375 92.428C10.8164 93.2102 10.3513 93.7175 9.90739 93.8162C9.84397 93.8303 9.78759 93.8373 9.72417 93.8373V93.8443ZM6.18678 33.7862L5.4328 80.3501C5.44689 83.4295 8.99133 93.0833 9.02656 93.182C9.22387 93.5695 9.52687 93.7527 9.86511 93.6752C10.2527 93.5907 10.6614 93.1326 10.6755 92.435L11.7395 51.8255C11.7536 51.4591 9.94967 44.6873 6.91963 33.7087C6.86326 33.4973 6.673 33.4268 6.51093 33.448C6.36295 33.462 6.18678 33.5677 6.17974 33.7932L6.18678 33.7862Z" fill="#160302"/>
<path d="M6.75751 34.822L10.8304 50.1624C10.9855 50.7473 11.0559 51.3462 11.0348 51.9452L9.87915 91.7303C9.86506 92.294 6.79274 84.1129 6.79274 82.3935L6.75751 34.8079V34.822Z" fill="#373637"/>
<path d="M9.87217 91.8431C9.62554 91.8431 8.33601 88.1506 8.18803 87.7278C7.75114 86.4665 6.72234 83.4153 6.72234 82.4006L6.6871 34.223L10.908 50.1413C11.063 50.7332 11.1335 51.3392 11.1194 51.9452L9.96377 91.7303C9.96377 91.8149 9.9074 91.836 9.87217 91.836V91.8431ZM6.83508 35.4139L6.87031 82.4006C6.87031 83.9297 9.26616 90.3421 9.80875 91.5189L10.9573 51.9452C10.9714 51.3463 10.908 50.7543 10.753 50.1836L6.83508 35.4139Z" fill="#160302"/>
<path d="M9.70307 82.5698L10.5205 52.3046C10.5346 51.7409 10.4782 51.1772 10.3443 50.6275L7.25085 37.6759L7.31427 75.4245C7.31427 77.4399 7.84982 79.427 8.86453 81.1675C9.13934 81.6396 9.42121 82.1188 9.70307 82.5768V82.5698Z" fill="#C1D1D0"/>
<path d="M9.70304 82.5698L10.5204 52.3046C10.5345 51.7409 10.4782 51.1772 10.3443 50.6275L9.69599 47.9005C8.25144 54.0592 7.3847 61.9585 7.3847 70.5765C7.3847 73.0921 7.46222 75.5514 7.60315 77.9261C7.85683 79.0606 8.27962 80.1528 8.87154 81.1675C9.14635 81.6396 9.42822 82.1188 9.71008 82.5768L9.70304 82.5698Z" fill="#DFF3F4"/>
<path d="M6.75049 69.3011C6.87028 70.6752 9.42819 75.2554 10.4147 76.0728L10.5275 72.2043C9.7312 70.4638 7.18033 67.2787 6.74344 65.5593L6.75753 69.294L6.75049 69.3011Z" fill="#160302"/>
<path d="M6.7928 34.8502C6.7928 34.8502 0.373348 38.3383 0.373348 39.4587C0.373348 40.5791 -0.00012207 43.2075 -0.00012207 43.2075L5.2355 41.3472L7.66658 38.0705L6.78575 34.8502H6.7928Z" fill="#160302"/>
<path d="M5.62918 66.4665L5.49713 66.5477L10.3844 74.4949L10.5164 74.4137L5.62918 66.4665Z" fill="#160302"/>
<path d="M51.8629 33.8778C51.8488 33.3775 51.1089 33.3 50.975 33.7792C49.4248 39.4024 46.134 51.3886 46.1481 51.9171L47.2122 92.5267C47.2403 93.781 48.4735 94.3799 49.0161 93.3018C49.0161 93.3018 52.6028 83.5705 52.6169 80.4418L51.8629 33.8778Z" fill="#17347A"/>
<path d="M48.2479 93.943C48.1916 93.943 48.1282 93.943 48.0647 93.9219C47.6138 93.8232 47.1487 93.3159 47.1275 92.5337L46.0705 51.9241C46.0494 51.3957 49.0865 40.3184 50.8975 33.7651C50.975 33.4691 51.2568 33.3634 51.4753 33.3916C51.7008 33.4198 51.9333 33.5818 51.9404 33.8849L52.6943 80.4488C52.6803 83.5563 49.1217 93.2383 49.0865 93.337C48.8892 93.7316 48.5862 93.95 48.2479 93.95V93.943ZM51.4119 33.5325C51.2639 33.5325 51.1018 33.61 51.0525 33.8003C48.0225 44.7789 46.2185 51.5507 46.2326 51.9171L47.2896 92.5337C47.3107 93.2313 47.7124 93.6893 48.1 93.7739C48.4382 93.8514 48.7483 93.6682 48.9456 93.2736C48.9808 93.182 52.5252 83.5281 52.5393 80.4488L51.7853 33.8849C51.7783 33.6594 51.6021 33.5607 51.4541 33.5466C51.4401 33.5466 51.4189 33.5466 51.4048 33.5466L51.4119 33.5325Z" fill="#160302"/>
<path d="M51.2145 34.9207L47.1416 50.2612C46.9866 50.846 46.9161 51.445 46.9373 52.0439L48.0929 91.829C48.107 92.3928 51.1793 84.2117 51.1793 82.4923L51.2145 34.9066V34.9207Z" fill="#373637"/>
<path d="M48.1 91.9418C48.1 91.9418 48.0154 91.9207 48.0083 91.8361L46.8598 52.051C46.8457 51.438 46.9091 50.832 47.0712 50.24L51.2921 34.3218V34.9137L51.2568 82.4923C51.2568 83.507 50.228 86.5582 49.7911 87.8195C49.6432 88.2423 48.3536 91.9277 48.107 91.9348L48.1 91.9418ZM51.137 35.5127L47.2191 50.2823C47.0641 50.8601 47.0007 51.4521 47.0148 52.044L48.1634 91.6177C48.713 90.4479 51.1018 84.0285 51.1018 82.4994L51.137 35.5127Z" fill="#160302"/>
<path d="M48.2691 82.6615L47.4517 52.3963C47.4376 51.8326 47.494 51.2689 47.6279 50.7192L50.7213 37.7676L50.6579 75.5162C50.6579 77.5315 50.1224 79.5187 49.1076 81.2592C48.8328 81.7313 48.551 82.2105 48.2691 82.6685V82.6615Z" fill="#C1D1D0"/>
<path d="M48.2691 82.5698L47.4517 52.3046C47.4376 51.7409 47.494 51.1772 47.6279 50.6275L48.2761 47.9005C49.7207 54.0592 50.5874 61.9585 50.5874 70.5765C50.5874 73.0921 50.5099 75.5514 50.369 77.9261C50.1153 79.0606 49.6925 80.1528 49.1006 81.1675C48.8258 81.6396 48.5439 82.1188 48.2621 82.5768L48.2691 82.5698Z" fill="#DFF3F4"/>
<path d="M51.2217 69.3998C51.1019 70.7739 48.544 75.3542 47.5574 76.1716L47.4447 72.303C48.241 70.5625 50.7918 67.3775 51.2287 65.6581L51.2146 69.3928L51.2217 69.3998Z" fill="#160302"/>
<path d="M51.1864 34.9489C51.1864 34.9489 57.6058 38.4299 57.6058 39.5573C57.6058 40.6848 57.9793 43.3061 57.9793 43.3061L52.7437 41.4458L50.3126 38.1691L51.1934 34.9489H51.1864Z" fill="#160302"/>
<path d="M52.342 66.5611L47.4576 74.51L47.5897 74.5912L52.4741 66.6423L52.342 66.5611Z" fill="#160302"/>
<path d="M7.87093 29.7697L7.80047 29.509C7.26492 27.4584 6.55322 24.6821 6.25021 23.0754V23.0472L6.25726 23.0191L13.9803 10.8425C14.6498 9.78555 15.0866 8.62286 15.2699 7.38266L15.5024 5.82536L15.5376 5.80422C15.5729 5.78308 19.3428 3.95097 27.3971 3.38019C27.5944 3.35905 27.7987 3.34496 28.0242 3.33791H28.1158C28.1158 3.33791 28.3906 3.31677 28.7852 3.31677H28.9684H29.1446C29.5533 3.31677 29.8211 3.33087 29.8211 3.33087H29.9127C30.1382 3.35201 30.3425 3.3661 30.5398 3.38019C38.5871 3.95097 42.364 5.78308 42.3993 5.80422L42.4345 5.82536L42.667 7.38266C42.8573 8.62286 43.2871 9.78555 43.9566 10.8425L51.7008 23.0472V23.0754C51.3837 24.6821 50.679 27.4584 50.1435 29.509L50.073 29.7697L49.9955 29.516C49.5093 27.9799 48.2338 26.8454 46.2044 26.1337C39.5243 23.8083 33.1682 23.8083 28.9614 23.8083C24.7546 23.8083 18.3985 23.8083 11.7184 26.1337C9.68895 26.8383 8.41352 27.9799 7.9273 29.516L7.84979 29.7697H7.87093ZM28.9896 23.6603C33.2035 23.6603 39.5806 23.6603 46.2819 25.9927C48.255 26.6762 49.5304 27.7685 50.0871 29.2271C50.6086 27.2259 51.2709 24.6257 51.5669 23.0825L43.8579 10.9271C43.1744 9.84897 42.7305 8.67219 42.5402 7.41085L42.3147 5.93811C41.913 5.74785 38.1502 4.08485 30.5469 3.54226C30.3496 3.52112 30.1452 3.50703 29.9197 3.49998H29.8281C29.8281 3.49998 29.5604 3.47884 29.1658 3.47884H28.9825H28.8134C28.4118 3.47884 28.144 3.49294 28.1369 3.49294H28.0453C27.8199 3.51408 27.6155 3.52817 27.4252 3.54226C19.822 4.08485 16.0591 5.74785 15.6574 5.93811L15.4319 7.41085C15.2417 8.67219 14.7977 9.85602 14.1142 10.9271L6.40524 23.0825C6.7012 24.6186 7.36358 27.2188 7.88502 29.2271C8.44171 27.7614 9.71714 26.6762 11.6902 25.9927C18.3915 23.6603 24.7687 23.6603 28.9825 23.6603H28.9896Z" fill="#160302"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,3 @@
<svg width="202" height="135" viewBox="0 0 202 135" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.18153 39.6234C17.011 27.3107 32.2015 17.7116 48.8458 11.4109C66.2695 4.81515 84.892 1.98332 103.49 3.10152C122.087 4.21972 140.239 9.26262 156.751 17.8989C172.525 26.149 186.463 37.4998 197.732 51.2619L95.8009 131.621L4.18153 39.6234Z" fill="#2954E9" fill-opacity="0.55" stroke="white" stroke-width="5"/>
</svg>

After

Width:  |  Height:  |  Size: 424 B

1
src/assets/images/en.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

1
src/assets/images/fr.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" version="1.1"><path d="M 341 256 L 341 424.064 412.272 423.782 L 483.545 423.500 490.127 420.259 C 498.055 416.355, 505.078 409.201, 508.820 401.218 L 511.500 395.500 511.500 256 L 511.500 116.500 508.820 110.782 C 505.078 102.799, 498.055 95.645, 490.127 91.741 L 483.545 88.500 412.272 88.218 L 341 87.936 341 256" stroke="none" fill="#ff4b55" fill-rule="evenodd"/><path d="M 171 256 L 171 424 256 424 L 341 424 341 256 L 341 88 256 88 L 171 88 171 256" stroke="none" fill="#f4f4f4" fill-rule="evenodd"/><path d="M 27.929 89.367 C 17.637 92.142, 8.008 100.484, 3.160 110.825 L 0.500 116.500 0.500 256 L 0.500 395.500 3.180 401.218 C 6.922 409.201, 13.945 416.355, 21.873 420.259 L 28.455 423.500 99.728 423.782 L 171 424.064 171 256.032 L 171 88 101.750 88.067 C 50.201 88.118, 31.332 88.450, 27.929 89.367" stroke="none" fill="#44449c" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

1
src/assets/images/hu.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" version="1.1"><path d="M 50 17.886 C 41.197 20.732, 35.982 23.858, 29.912 29.928 C 23.834 36.006, 20.197 42.216, 17.612 50.932 C 16.208 55.662, 16 64.083, 16 116.182 L 16 176 256 176 L 496 176 496 116.182 C 496 51.184, 496.061 51.984, 490.272 40.554 C 487.059 34.209, 477.791 24.941, 471.446 21.728 C 459.317 15.586, 474.887 15.995, 255.568 16.054 C 66.632 16.105, 55.194 16.206, 50 17.886" stroke="none" fill="#c0392b" fill-rule="evenodd"/><path d="M 16 256 L 16 336 256 336 L 496 336 496 256 L 496 176 256 176 L 16 176 16 256" stroke="none" fill="#fcfcfc" fill-rule="evenodd"/><path d="M 16 395.818 C 16 460.816, 15.939 460.016, 21.728 471.446 C 24.941 477.791, 34.209 487.059, 40.554 490.272 C 52.678 496.412, 37.187 496, 256 496 C 474.813 496, 459.322 496.412, 471.446 490.272 C 477.791 487.059, 487.059 477.791, 490.272 471.446 C 496.061 460.016, 496 460.816, 496 395.818 L 496 336 256 336 L 16 336 16 395.818" stroke="none" fill="#24ac64" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,18 @@
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_193_56)">
<circle cx="16.8921" cy="14.0595" r="12" transform="rotate(2.04823 16.8921 14.0595)" fill="white" fill-opacity="0.45"/>
<path d="M12.542 9.32783C12.2352 9.04959 11.7609 9.07278 11.4826 9.37962C11.2044 9.68647 11.2276 10.1608 11.5344 10.439L12.542 9.32783ZM22.446 20.0368C22.8597 20.0165 23.1787 19.6648 23.1584 19.251L22.8288 12.5091C22.8086 12.0954 22.4568 11.7764 22.0431 11.7966C21.6294 11.8168 21.3104 12.1686 21.3306 12.5823L21.6236 18.5752L15.6308 18.8682C15.217 18.8884 14.8981 19.2402 14.9183 19.6539C14.9385 20.0676 15.2903 20.3866 15.704 20.3664L22.446 20.0368ZM11.5344 10.439L21.9055 19.8433L22.9131 18.7321L12.542 9.32783L11.5344 10.439Z" fill="#0A1F69"/>
</g>
<defs>
<filter id="filter0_d_193_56" x="0.891907" y="0.0593262" width="32.0003" height="32.0004" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_193_56"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_193_56" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="48"
height="48"
viewBox="0 0 12.7 12.7"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
d="M-3.007-.005a5.978 5.978 0 0 1-5.979 5.978V-.005z"
style="fill:#1a237e;fill-opacity:1;stroke:#fff;stroke-width:0.661458;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="rotate(-135)"
id="path1" />
<circle
cx="6.35"
cy="6.545"
r="2.64"
style="fill:#1e88e5;fill-opacity:1;stroke:#fff;stroke-width:0.660027;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle1" />
</svg>

After

Width:  |  Height:  |  Size: 721 B

1
src/assets/images/pt.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" version="1.1"><path d="M 170.377 245.461 C 170.056 246.296, 169.957 252.497, 170.157 259.240 C 170.443 268.882, 170.911 272.141, 172.345 274.500 C 178.068 283.914, 190.051 284.114, 195.500 274.887 C 197.234 271.950, 197.500 269.704, 197.500 258 L 197.500 244.500 184.230 244.221 C 173.107 243.987, 170.865 244.187, 170.377 245.461" stroke="none" fill="#004cba" fill-rule="evenodd"/><path d="M 153.189 250.776 L 153.500 276.551 156.787 283.223 C 164.622 299.127, 181.991 306.147, 195.922 299.040 C 202.361 295.755, 208.630 289.450, 211.728 283.143 L 214.500 277.500 214.824 251.250 L 215.147 225 184.012 225 L 152.877 225 153.189 250.776 M 170.377 245.461 C 170.056 246.296, 169.953 252.497, 170.147 259.240 C 170.453 269.872, 170.801 271.984, 172.766 275.144 C 176.832 281.682, 184.484 283.718, 190.656 279.903 C 196.539 276.268, 197.480 273.250, 197.491 258 L 197.500 244.500 184.230 244.221 C 173.107 243.987, 170.865 244.187, 170.377 245.461" stroke="none" fill="#ffffff" fill-rule="evenodd"/><path d="M 173.500 148.570 C 140.174 153.092, 115.707 166.576, 97.570 190.413 C 61.447 237.890, 70.878 305.962, 118.472 341.285 C 153.937 367.605, 199.632 369.859, 238.444 347.202 C 249.571 340.707, 267.651 322.620, 274.392 311.242 C 279.888 301.965, 285.403 288.090, 287.784 277.546 C 290.041 267.558, 290.033 243.939, 287.771 233.765 C 283.057 212.569, 274.214 196.340, 259 180.962 C 242.241 164.022, 226.242 155.179, 203.819 150.462 C 196.553 148.933, 178.928 147.834, 173.500 148.570 M 128.750 201.080 C 127.133 202.022, 127 204.912, 127 239 C 127 261.889, 127.417 277.914, 128.097 281.201 C 130.603 293.305, 140.064 308.220, 149.714 315.277 C 164.345 325.975, 179.016 329.755, 193.451 326.543 C 211.705 322.482, 226.548 310.919, 234.893 294.260 C 240.551 282.965, 240.969 279.203, 240.985 239.405 C 240.999 205.696, 240.879 203.201, 239.171 201.655 C 237.517 200.158, 232.254 200.003, 183.921 200.030 C 149.501 200.050, 129.878 200.423, 128.750 201.080" stroke="none" fill="#ffe600" fill-rule="evenodd"/><path d="M 75 1.553 C 40.842 8.877, 14.816 32.647, 3.340 67 L 0.500 75.500 0.236 253.351 C -0.024 429.355, -0.006 431.291, 2.013 439.791 C 9.839 472.741, 35.014 498.784, 69.067 509.155 C 76.325 511.366, 77.765 511.427, 130.250 511.758 L 184 512.097 184 437.765 L 184 363.434 172.750 362.225 C 158.801 360.727, 149.219 358.088, 137.148 352.421 C 81.316 326.207, 59.078 258.161, 88.643 204 C 106.061 172.094, 138.074 151.693, 175.750 148.493 L 184 147.792 184 73.896 L 184 0 132.750 0.079 C 92.950 0.141, 80.048 0.470, 75 1.553" stroke="none" fill="#04a206" fill-rule="evenodd"/><path d="M 184 73.898 L 184 147.841 190.750 148.526 C 210.176 150.497, 226.827 156.307, 242.910 166.727 C 251.981 172.603, 268.750 190.300, 274.476 200.037 C 280.234 209.831, 285.314 222.719, 287.771 233.765 C 290.033 243.939, 290.041 267.558, 287.784 277.546 C 283.274 297.513, 274.210 314.428, 260.101 329.208 C 241.790 348.391, 220.088 359.179, 193.793 362.169 L 184 363.282 184 437.641 L 184 512 306.542 512 C 414.592 512, 429.996 511.812, 436.792 510.412 C 473.570 502.834, 503.058 473.237, 510.450 436.484 C 511.895 429.301, 512.027 410.846, 511.773 252.500 L 511.490 76.500 509.204 69 C 499.478 37.085, 474.881 12.491, 443 2.803 L 435.500 0.523 309.750 0.239 L 184 -0.045 184 73.898 M 128.750 201.080 C 127.133 202.022, 127 204.912, 127 239 C 127 261.889, 127.417 277.914, 128.097 281.201 C 130.603 293.305, 140.064 308.220, 149.714 315.277 C 164.345 325.975, 179.016 329.755, 193.451 326.543 C 211.705 322.482, 226.548 310.919, 234.893 294.260 C 240.551 282.965, 240.969 279.203, 240.985 239.405 C 240.999 205.696, 240.879 203.201, 239.171 201.655 C 237.517 200.158, 232.254 200.003, 183.921 200.030 C 149.501 200.050, 129.878 200.423, 128.750 201.080 M 153.189 251.250 L 153.500 277.500 156.272 283.143 C 161.388 293.558, 171.743 300.889, 182.568 301.758 C 190.722 302.412, 199.009 298.836, 205.555 291.838 C 214.130 282.670, 214.456 281.242, 214.811 251.250 L 215.121 225 184 225 L 152.879 225 153.189 251.250" stroke="none" fill="#f3042b" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,6 @@
<svg width="173" height="360" viewBox="0 0 173 360" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="173" height="360" fill="#D5DAE8"/>
<line x1="6.5" y1="-1.09278e-07" x2="6.50002" y2="360" stroke="white" stroke-width="5"/>
<line x1="166.5" y1="1.3381e-07" x2="166.5" y2="358.776" stroke="white" stroke-width="5"/>
<line x1="85.0001" y1="5.35242e-08" x2="85" y2="358.776" stroke="white" stroke-width="2" stroke-dasharray="4 4"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -34,10 +34,11 @@ h5 {
--grey-pale: #cfd2cf;
--grey-semi-dark: #808080;
--grey-dark: #3e3e3e;
--blue: #2954e9;
--blue-dark: #0a1f69;
--blue-semi: #d7dffc;
--blue: #2954e9;
--blue-semi-pale: #f2f5ff;
--blue-pale: #f2f5ff;
--blue-very-pale: #f9faff;
--blue-geovisio: #34495e;
--beige: #f5f3ec;
--yellow: #fec868;

View File

@@ -1,3 +1,45 @@
export function createLink(href: string, text: string): string {
return `<a href='mailto:signalement.ign@panoramax.fr${href}' target='_blank' title='${text}' class='gvs-btn gvs-widget-bg gvs-btn-large' style='font-size: 1.6em;display: block'><i class="bi bi-exclamation-triangle"></i></a>`
return `<a href='mailto:signalement.ign@panoramax.fr${href}' target='_blank' title='${text}' class='gvs-btn gvs-widget-bg gvs-btn-large' style='font-size: 1.6em;display: block; margin-top: 0.5em;'><i class="bi bi-exclamation-triangle"></i></a>`
}
export function createSequenceLink(href: string, title: string): string {
return `<a href='${href}' title='${title}' class='gvs-btn gvs-widget-bg gvs-btn-large' style='font-size: 1.6em;display: block; position: relative; margin-top: 0.5em;'>
<i class="bi bi-images"></i>
</a>`
}
export function createFullScreenButton(): string {
return `<button type='button' onClick="
const header = document.getElementById('navHeader')
const footer = document.getElementById('navFooter')
const icon = document.getElementById('iconScreen')
const home = document.getElementById('homePage')
if (header) {
const isHiddenHeader = header.classList.contains('hidden')
if (isHiddenHeader) header.classList.remove('hidden')
else header.classList.add('hidden')
}
if (footer) {
const isHiddenHeader = footer.classList.contains('hidden')
if (isHiddenHeader) footer.classList.remove('hidden')
else footer.classList.add('hidden')
}
if (icon) {
const isIconNotFull = icon.classList.contains('bi-fullscreen')
if(isIconNotFull) {
icon.classList.remove('bi-fullscreen')
icon.classList.add('bi-fullscreen-exit')
} else {
icon.classList.remove('bi-fullscreen-exit')
icon.classList.add('bi-fullscreen')
}
}
if (home) {
const isHomeFull = home.classList.contains('full-viewer')
if(isHomeFull) home.classList.remove('full-viewer')
else home.classList.add('full-viewer')
}
"
class='gvs-btn gvs-widget-bg gvs-btn-large'
>
<i id='iconScreen' class="bi bi-fullscreen"></i>
</button>`
}

View File

@@ -55,13 +55,34 @@ defineProps({
visibility: visible;
}
}
.row-reverse {
flex-direction: row-reverse;
justify-content: flex-start;
width: 100%;
padding: 0;
font-size: toRem(1.2);
.icon {
margin-left: toRem(0.5);
margin-right: 0;
}
}
.button--black {
color: var(--white);
background-color: var(--black);
}
.button-border--black {
font-size: toRem(1.4);
background-color: var(--white);
border: toRem(0.1) solid var(--black);
.icon {
font-size: toRem(1.4);
color: var(--black);
}
}
.button--blue {
color: var(--white);
background-color: var(--blue);
height: toRem(4);
&.disabled {
opacity: 0.6;
color: var(--white);
@@ -75,28 +96,37 @@ defineProps({
color: var(--white);
}
.button--red {
color: var(--red);
background-color: transparent;
border: toRem(0.1) solid var(--red);
color: var(--white);
background-color: var(--red-pale);
border: toRem(0.1) solid var(--red-pale);
.icon {
margin-right: 0;
font-size: toRem(1.4);
color: var(--red);
color: var(--white);
}
.text {
margin-left: toRem(1);
}
&.background-white {
color: var(--red-pale);
.icon {
color: var(--red-pale);
}
&.disabled {
.icon {
color: var(--grey-pale);
}
}
}
}
.button--white {
color: var(--blue);
background-color: var(--white);
border: toRem(0.1) solid var(--blue);
.icon {
font-size: toRem(1.4);
color: var(--blue);
margin-right: 0;
}
.text {
margin-left: toRem(1);
margin-right: toRem(1);
}
}
.no-text {
@@ -104,7 +134,6 @@ defineProps({
width: toRem(3);
padding: 0;
.icon {
color: var(--black);
font-size: toRem(1.8);
margin-right: 0;
}
@@ -112,10 +141,29 @@ defineProps({
.no-text-white .icon {
color: var(--white);
margin-right: 0;
font-size: toRem(1.4);
}
.no-text-blue-dark .icon {
color: var(--blue-dark);
margin-right: 0;
font-size: toRem(1.4);
}
.no-text-blue .icon {
color: var(--blue);
margin-right: 0;
font-size: toRem(1.6);
}
.background-white {
background-color: var(--white);
}
.link--blue {
color: var(--blue);
text-decoration: underline;
.icon {
font-size: toRem(1.4);
color: var(--blue);
}
}
.link--grey {
color: var(--grey-semi-dark);
.icon {
@@ -123,12 +171,20 @@ defineProps({
color: var(--grey-semi-dark);
}
}
.link--red {
color: var(--red);
background-color: var(--white);
.link--black {
color: var(--black);
text-decoration: underline;
.icon {
font-size: toRem(1.4);
color: var(--red);
color: var(--black);
}
}
.link--red {
color: var(--red-pale);
text-decoration: underline;
.icon {
font-size: toRem(1.4);
color: var(--red-pale);
}
}
@@ -143,8 +199,10 @@ defineProps({
align-items: center;
border-radius: 50%;
padding: 0;
height: toRem(2.5);
width: toRem(2.5);
height: toRem(4);
width: toRem(4);
background-color: var(--white);
border: toRem(0.1) solid var(--grey-pale);
.icon {
color: var(---black);
font-size: toRem(1.8);
@@ -162,6 +220,7 @@ defineProps({
visibility: hidden;
width: toRem(20);
right: 0;
z-index: 9;
@include text(xss-regular);
}

View File

@@ -1,42 +1,48 @@
<template>
<div class="entry-edit">
<form
v-if="isEditTitle && !isDisabled"
@submit.prevent="isEditTitle = false"
class="edit-form"
>
<div class="wrapper-input">
<Input
:text="text || ''"
:placeholder="$t('pages.upload.edit_placeholder_input')"
@input="changeTextValue"
<div :class="['entry-edit', { 'edit-mode': isEditTitle }]">
<span v-if="isEditTitle && formTitle" class="form-title">{{
formTitle
}}</span>
<div class="entry-form">
<form
v-if="isEditTitle && !isDisabled"
@submit.prevent="isEditTitle = false"
class="edit-form"
>
<div class="wrapper-input">
<Input
id="upload-title"
:text="text || ''"
:placeholder="$t('pages.upload.edit_placeholder_input')"
@input="changeTextValue"
/>
</div>
<Button
id="valid-button"
:text="$t('pages.upload.ok_button')"
type="submit"
look="button--white"
@trigger="validNewName"
/>
<div class="close-button">
<Button
id="close-button"
look="no-text-white"
icon="bi bi-x"
look="no-text-blue-dark"
icon="bi bi-x-lg"
@trigger="closeEdition"
/>
</div>
</form>
<span v-else class="title">{{ text }}</span>
<div v-if="!isEditTitle" class="edit-button">
<Button
:text="$t('pages.upload.edit_title')"
look="link--black row-reverse"
icon="bi bi-pen"
:disabled="isDisabled"
@trigger="goToEditMode"
/>
</div>
<Button
id="valid-button"
:text="$t('pages.upload.ok_button')"
type="submit"
look="button button--blue"
@trigger="validNewName"
/>
</form>
<span v-else class="title">{{ text }}</span>
<div v-if="!isEditTitle" class="edit-button">
<Button
look="no-text"
icon="bi bi-pen"
:tooltip="$t('pages.upload.edit_title_tooltip')"
:disabled="isDisabled"
@trigger="goToEditMode"
/>
</div>
</div>
</template>
@@ -52,7 +58,8 @@ const emit = defineEmits<{
const props = defineProps({
defaultText: { type: String, default: null },
isLoading: { type: Boolean, default: false },
isLoaded: { type: Boolean, default: false }
isLoaded: { type: Boolean, default: false },
formTitle: { type: String, default: null }
})
let titleToEdit = ref<string | null>(null)
let isEditTitle = ref<boolean>(false)
@@ -85,13 +92,28 @@ const isDisabled = computed<boolean>(() => props.isLoading && !props.isLoaded)
<style scoped lang="scss">
.title {
color: var(--blue-dark);
text-align: left;
}
.form-title {
text-align: left;
width: 100%;
@include text(xs-r-regular);
margin-bottom: toRem(0.3);
}
.entry-edit {
display: flex;
flex-direction: column;
position: relative;
}
.entry-form {
display: flex;
align-items: center;
margin-bottom: toRem(2);
}
.edit-mode {
background-color: var(--blue-very-pale);
padding: toRem(1);
border-radius: toRem(0.4);
width: 100%;
height: toRem(4.7);
}
.wrapper-edit {
display: flex;
@@ -100,15 +122,9 @@ const isDisabled = computed<boolean>(() => props.isLoading && !props.isLoaded)
width: 100%;
}
.edit-button {
background-color: var(--grey);
border-radius: 50%;
height: toRem(3.5);
width: toRem(3.5);
padding: toRem(1);
display: flex;
justify-content: center;
align-items: center;
margin-left: toRem(1.5);
min-width: toRem(11);
margin-left: toRem(1);
z-index: 2;
}
.wrapper-input {
position: relative;
@@ -127,10 +143,8 @@ const isDisabled = computed<boolean>(() => props.isLoading && !props.isLoaded)
align-items: center;
height: toRem(2);
width: toRem(2);
top: toRem(-1);
right: toRem(-1);
background-color: var(--blue-dark);
color: var(--white);
border-radius: 50%;
top: toRem(0.5);
right: toRem(0.5);
border-radius: toRem(0.5);
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<footer class="footer">
<footer id="navFooter" class="footer">
<ul class="link-list">
<li class="link-item">
<div class="link">
@@ -62,6 +62,9 @@ ul {
padding: toRem(1.5) toRem(3);
border-top: toRem(0.1) solid var(--grey);
}
.hidden {
display: none;
}
.link-list {
display: flex;
justify-content: center;

View File

@@ -1,5 +1,5 @@
<template>
<header class="header">
<header id="navHeader" class="header">
<div class="responsive entry-instance">
<InstanceName />
</div>
@@ -31,6 +31,15 @@
@triggerClose="closeModal"
/>
<div class="wrapper-right-entries">
<div>
<Link
:text="$t('general.header.viewer')"
:route="{ name: 'home' }"
look="link--blue"
class="desktop"
@click.native="closeModal"
/>
</div>
<div>
<Link
v-if="isLogged"
@@ -73,7 +82,10 @@
:menu-is-closed="menuIsClosed"
@triggerToggleMenu="toggleMenu"
/>
<div v-else-if="!isLogged && authEnabled">
<div
v-else-if="!isLogged && authEnabled"
class="button-login-responsive"
>
<div class="desktop" data-test="button-login-desktop">
<Link
type="external"
@@ -98,6 +110,9 @@
/>
</div>
</div>
<div class="entry-lang-switcher">
<LangSwitcher />
</div>
</div>
</div>
</nav>
@@ -110,10 +125,13 @@ import { onClickOutside } from '@vueuse/core'
import { useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { getAuthRoute, hasASessionCookieDecoded } from '@/utils/auth'
import { isAuth } from '../composables/auth'
import Link from '@/components/Link.vue'
import InstanceName from '@/components/InstanceName.vue'
import HeaderOpen from '@/components/header/HeaderOpen.vue'
import AccountButton from '@/components/header/AccountButton.vue'
import LangSwitcher from '@/components/header/LangSwitcher.vue'
const { isLogged } = isAuth()
const { t } = useI18n()
const route = useRoute()
defineProps({
@@ -128,14 +146,9 @@ onClickOutside(list, () => closeModal())
function closeModal(): void {
menuIsClosed.value = true
}
function toggleMenu(): void {
menuIsClosed.value = !menuIsClosed.value
}
const isLogged = computed((): boolean => {
const cookie = hasASessionCookieDecoded()
return !!(cookie && cookie.account)
})
const ariaLabel = computed((): string =>
menuIsClosed.value
? t('general.header.burger_menu_aria_label_open')
@@ -161,6 +174,9 @@ const userName = computed((): string => {
background-color: var(--white);
border-bottom: toRem(0.1) solid var(--grey-pale);
}
.hidden {
display: none;
}
.nav {
width: 100%;
display: flex;
@@ -185,13 +201,11 @@ const userName = computed((): string => {
.wrapper-right-entries {
display: flex;
align-items: center;
div:first-child {
div {
margin-right: toRem(2);
}
div:last-child {
.desktop {
margin-right: 0;
}
margin-right: 0;
}
}
.link-upload {
@@ -206,7 +220,21 @@ const userName = computed((): string => {
padding-left: toRem(0.5);
color: var(--blue);
}
@media (max-width: toRem(76.8)) {
.entry-lang-switcher {
margin-left: toRem(2);
}
@media (max-width: toRem(102.4)) {
.nav {
padding-right: toRem(2);
padding-left: toRem(2);
}
.wrapper-right-entries {
div {
margin-right: toRem(1.5);
}
}
}
@media (max-width: toRem(84.8)) {
.nav {
padding-right: toRem(2);
padding-left: toRem(2);
@@ -232,5 +260,8 @@ const userName = computed((): string => {
.responsive {
display: flex;
}
.wrapper-right-entries .button-login-responsive {
margin-right: toRem(-1);
}
}
</style>

View File

@@ -2,7 +2,7 @@
<div :class="status">
<div class="wrapper-image">
<button
:class="[{ selected }, 'button-image-item']"
:class="[{ selected: selectedOnMap }, 'button-image-item']"
:disabled="status === 'waiting-for-process'"
type="button"
@click="$emit('trigger')"
@@ -33,18 +33,8 @@
$t('pages.sequence.waiting_process')
}}</span>
</div>
<div
v-if="selectedOnMap && !selected"
class="icon-img pointer-map"
></div>
<div v-if="selected && !selectedOnMap" class="icon-img button-check">
<i class="bi bi-check-lg" />
</div>
<div
v-if="selected && selectedOnMap"
class="icon-img button-check-pointer"
>
<i class="bi bi-check-lg" />
<div v-if="selectedOnMap" class="pointer-map">
<img src="@/assets/images/logo-geovisio.svg" alt="" />
</div>
<div
v-if="status.length && (status === 'ready' || status === 'hidden')"
@@ -64,6 +54,9 @@
</div>
</div>
</button>
<div class="geovisio-pointer">
<slot name="checkbox"></slot>
</div>
</div>
</div>
</template>
@@ -75,7 +68,6 @@ defineProps({
created: { type: String, default: null },
href: { type: String, default: null },
hrefHd: { type: String, default: null },
selected: { type: Boolean, default: false },
selectedOnMap: { type: Boolean, default: false },
status: {
type: String,
@@ -95,12 +87,16 @@ defineProps({
position: relative;
}
.selected {
border: toRem(0.1) solid var(--blue);
border-radius: toRem(0.5);
border: toRem(0.2) solid var(--blue);
border-radius: toRem(1.5);
box-shadow: 0px 4px 4px 0px #00000040;
.photo-img-wrapper {
padding: toRem(0.2);
}
}
.wrapper-image {
display: flex;
position: relative;
}
.photo-img-wrapper {
display: flex;
@@ -115,7 +111,7 @@ defineProps({
.photo-img {
height: 100%;
width: 100%;
border-radius: toRem(0.5);
border-radius: toRem(1.5);
}
.icon-hidden {
color: var(--grey-dark);
@@ -146,7 +142,7 @@ defineProps({
}
.info {
@include text(xs-r-regular);
@include text(xss-regular);
padding: toRem(0.5) toRem(0.8);
background-color: var(--white);
border-radius: toRem(0.5);
@@ -164,9 +160,15 @@ defineProps({
align-items: center;
font-size: toRem(1.3);
}
.pointer-map,
.button-check-pointer {
background-color: var(--orange);
.geovisio-pointer {
position: absolute;
top: toRem(1.5);
right: toRem(1.5);
}
.pointer-map {
position: absolute;
top: toRem(1);
left: 0;
}
.delete-checked {
opacity: 1;

View File

@@ -10,9 +10,7 @@
alt=""
class="icon-block-img"
/>
<h3 v-if="title" class="subtitle">
{{ title }}
</h3>
<slot name="title"></slot>
</div>
<p v-if="text" v-html="text" class="information-text"></p>
</div>
@@ -23,7 +21,6 @@
<script setup lang="ts">
defineProps({
text: { type: String, default: null },
title: { type: String, default: null },
look: { type: String, default: '' }
})
</script>
@@ -36,11 +33,11 @@ h3 {
.information-block {
position: relative;
border-left: toRem(1.4) solid var(--blue);
padding: toRem(2) toRem(2) toRem(1.5);
padding: toRem(1.8) toRem(1.8) toRem(1.3);
background-color: var(--white);
border-radius: toRem(1.5);
display: flex;
align-items: flex-end;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
@@ -59,11 +56,7 @@ h3 {
}
.icon-block-img {
margin-right: toRem(0.5);
height: toRem(2.2);
}
.subtitle {
@include text(h2);
color: var(--blue-dark);
height: toRem(2);
}
.information-text {
margin-top: toRem(1);

View File

@@ -3,16 +3,21 @@
:value="text"
:required="true"
:placeholder="placeholder"
class="input"
type="text"
:type="type"
:min="min"
:max="max"
@input="emitValue"
class="input"
/>
</template>
<script lang="ts" setup>
const emit = defineEmits<{ (e: 'input', value: string): void }>()
defineProps({
text: { type: String, default: null },
type: { type: String, default: 'text' },
min: { type: String, default: '' },
max: { type: String, default: '' },
text: { type: [String, Number], default: null },
placeholder: { type: String, default: '' }
})
@@ -24,10 +29,11 @@ function emitValue(event: Event): void {
<style scoped lang="scss">
.input {
padding: toRem(1);
padding: toRem(0.5) toRem(1);
border-radius: toRem(0.5);
border: toRem(0.1) solid var(--blue-dark);
color: var(--blue-dark);
width: 100%;
@include text(s-r-regular);
}
</style>

View File

@@ -1,18 +1,18 @@
<template>
<div class="wrapper-checkbox">
<div :class="['wrapper-checkbox', { checked: isChecked || isIndeterminate }]">
<div class="input-checkbox">
<i v-if="isChecked && !isIndeterminate" class="icon bi bi-check-square" />
<i v-if="!isChecked && !isIndeterminate" class="icon bi bi-square" />
<i v-if="isIndeterminate && !isChecked" class="icon bi bi-dash-square" />
<input
id="checkbox"
:id="name"
v-model="inputValue"
type="checkbox"
@input="updateValue(!inputValue)"
class="input"
/>
</div>
<label v-if="label && label.length" for="checkbox" class="label">{{
<label v-if="label && label.length" :for="name" class="label">{{
label
}}</label>
</div>
@@ -35,6 +35,7 @@ watchEffect(async () => {
if (htmlCheckbox) {
htmlCheckbox.indeterminate = props.isIndeterminate
}
inputValue.value = props.isChecked
})
function updateValue(value: boolean): void {
@@ -42,7 +43,11 @@ function updateValue(value: boolean): void {
htmlCheckbox.indeterminate = false
}
inputValue.value = value
emit('trigger', { isChecked: value, isIndeterminate: false })
emit('trigger', {
isChecked: value,
isIndeterminate: false,
name: props.name
})
}
</script>
@@ -54,6 +59,8 @@ function updateValue(value: boolean): void {
align-items: center;
height: toRem(2);
width: toRem(2);
background-color: var(--white);
border-radius: toRem(0.5);
}
.input {
-webkit-appearance: none;
@@ -68,7 +75,7 @@ function updateValue(value: boolean): void {
.icon {
font-size: toRem(2);
position: absolute;
color: var(--grey-semi-dark);
color: var(--blue);
}
.wrapper-checkbox {
display: flex;
@@ -77,5 +84,14 @@ function updateValue(value: boolean): void {
.label {
cursor: pointer;
margin-left: toRem(0.5);
@include text(s-r-regular);
}
.checked {
.input-checkbox {
background-color: var(--blue);
}
.icon {
color: var(--white);
}
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<div class="input-radio">
<input
:id="id"
:name="name"
:value="id"
type="radio"
:checked="props.value === id"
@change="updateValue"
class="input"
/>
<label v-if="label && label.length" :for="id" class="label">{{
label
}}</label>
</div>
</template>
<script lang="ts" setup>
const emit = defineEmits<{ (e: 'trigger', value: string): void }>()
const props = defineProps({
name: { type: String, default: null },
id: { type: String, default: '' },
label: { type: String, default: '' },
value: { type: String, default: null }
})
function updateValue(): void {
emit('trigger', props.id)
}
</script>
<style lang="scss" scoped>
.label {
margin-left: toRem(0.5);
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<label class="toggle">
<input
type="checkbox"
v-model="inputValue"
@input="updateValue(inputValue)"
class="input"
/>
<div :class="['labels increase', { increaseChecked: !inputValue }]">
<span>{{ $t('pages.sequence.sort_panel_settings_order_increase') }}</span>
<i class="bi bi-sort-up"></i>
</div>
<div :class="['labels decrease', { decreaseChecked: inputValue }]">
<span>{{ $t('pages.sequence.sort_panel_settings_order_decrease') }}</span>
<i class="bi bi-sort-down"></i>
</div>
</label>
</template>
<script setup lang="ts">
import { ref, watchEffect } from 'vue'
const emit = defineEmits<{
(e: 'trigger', value: { isIncreaseChecked: boolean }): void
}>()
let inputValue = ref<boolean>(false)
const props = defineProps({
increased: { type: Boolean, default: false }
})
watchEffect(() => {
inputValue.value = !props.increased
})
function updateValue(value: boolean): void {
inputValue.value = value
emit('trigger', { isIncreaseChecked: value })
}
</script>
<style scoped lang="scss">
.toggle {
position: relative;
width: 14.5rem;
background-color: var(--blue-very-pale);
padding: toRem(0.7);
border-radius: toRem(0.7);
height: toRem(4.5);
cursor: pointer;
}
.input {
width: 100%;
border: none;
appearance: none;
}
.labels {
position: absolute;
top: toRem(0.7);
@include text(s-r-regular);
padding: toRem(0.5) toRem(1);
color: var(--grey-semi-dark);
span {
margin-right: toRem(0.5);
}
}
.increase {
left: toRem(0.7);
}
.decrease {
right: toRem(0.7);
}
.increaseChecked,
.decreaseChecked {
background-color: var(--blue-dark);
color: var(--white);
border-radius: toRem(0.7);
}
</style>

View File

@@ -9,8 +9,8 @@
ref="upload"
type="file"
multiple
:accept="accept"
@change="changeFile"
@cancel="cancelUpload"
class="input-file"
/>
<i class="bi bi-cloud-upload-fill"></i>
@@ -26,19 +26,24 @@
<script lang="ts" setup>
import { ref } from 'vue'
const emit = defineEmits<{ (e: 'trigger', value: FileList): void }>()
const emit = defineEmits<{
(e: 'trigger', value: FileList): void
(e: 'triggerCancel'): 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: '' }
textSecondPart: { type: String, default: null }
})
interface HTMLInputChangeEvent extends Event {
target: HTMLInputElement & EventTarget
}
function cancelUpload(): void {
if (navigator.userAgent.includes('Android')) emit('triggerCancel')
}
function changeFile(event: Event): void {
const { target } = event as HTMLInputChangeEvent
if (target && target.files) {
@@ -59,8 +64,9 @@ function drop(event: DragEvent): void | boolean {
event.preventDefault()
const { dataTransfer } = event
if (dataTransfer && dataTransfer.files) {
if (!checkPicturesType(dataTransfer.files))
if (!checkPicturesType(dataTransfer.files)) {
return (isDragging.value = false)
}
emit('trigger', dataTransfer.files)
isDragging.value = false
}

View File

@@ -8,8 +8,9 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { getEnv } from '@/utils'
const instanceName = computed((): string | null => {
const instanceName = import.meta.env.VITE_INSTANCE_NAME
const instanceName = getEnv('VITE_INSTANCE_NAME')
if (instanceName) return instanceName
return null
})
@@ -43,7 +44,7 @@ const instanceName = computed((): string | null => {
position: absolute;
top: toRem(0.3);
margin-left: toRem(0.5);
width: toRem(7);
width: toRem(7.3);
text-align: center;
}
</style>

View File

@@ -55,7 +55,7 @@ const props = defineProps({
})
const titleImg = computed<string>(() =>
props.disabled ? t('general.header.contribute_text') : ''
props.disabled ? t('general.header.about_text') : ''
)
function triggerButton() {
if (props.disabled) return
@@ -79,6 +79,10 @@ function triggerButton() {
&:hover {
opacity: 0.8;
}
&.disabled {
pointer-events: none;
cursor: default;
}
}
.button {
height: toRem(4);
@@ -117,6 +121,23 @@ function triggerButton() {
color: var(--grey-semi-dark);
text-decoration: underline;
font-weight: inherit;
font-size: toRem(1.4);
.icon {
color: var(--grey-semi-dark);
font-size: toRem(1.4);
margin-right: toRem(0.5);
}
}
.link--grey-dark {
color: var(--grey-dark);
text-decoration: underline;
font-weight: inherit;
font-size: toRem(1.4);
.icon {
color: var(--grey-dark);
font-size: toRem(1.4);
margin-right: toRem(0.5);
}
}
.link--blue-dark {
color: var(--blue-dark);
@@ -163,7 +184,7 @@ function triggerButton() {
}
}
.button--blue-bleu {
background-color: var(--blue-semi);
background-color: var(--blue-semi-pale);
color: var(--blue);
}
.disabled {

View File

@@ -13,15 +13,10 @@
<i class="bi bi-x-circle-fill"></i>
</button>
<div class="modal-header">
<h5>{{ $t('pages.upload.modal_error_title') }}</h5>
<h5>{{ title }}</h5>
</div>
<div class="modal-body">
<ul>
<li v-for="item in uploadErrors" class="error-item">
<span>{{ item.name }} - </span>
<span>{{ item.details.error }}</span>
</li>
</ul>
<slot name="body"></slot>
</div>
</div>
</div>
@@ -30,15 +25,13 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { PropType } from 'vue'
import { Modal } from 'bootstrap'
let bsModal = ref()
import type { uploadErrorInterface } from '@/views/interfaces/UploadPicturesView'
defineProps({
uploadErrors: {
type: Array as PropType<uploadErrorInterface[]>,
default: []
title: {
type: String,
default: ''
}
})
@@ -62,12 +55,6 @@ ul {
.modal {
background: rgba(10, 31, 105, 0.6);
}
.error-item {
padding: toRem(1);
&:nth-child(odd) {
background-color: var(--grey);
}
}
.modal-content {
border-radius: toRem(1.5);
}

View File

@@ -0,0 +1,56 @@
<template>
<div class="tab">
<button
:class="['tablinks', { selected: panelSelected === 'photos' }]"
:disabled="isLoading"
@click="$emit('trigger', 'photos')"
>
{{ $t('pages.sequence.button_panel_photos') }}
</button>
<button
v-if="isSequenceOwner"
:class="['tablinks', { selected: panelSelected === 'orientation' }]"
:disabled="isLoading"
@click="$emit('trigger', 'orientation')"
>
{{ $t('pages.sequence.button_panel_orientation') }}
</button>
<button
v-if="isSequenceOwner"
:class="['tablinks', { selected: panelSelected === 'sort' }]"
:disabled="isLoading"
@click="$emit('trigger', 'sort')"
>
{{ $t('pages.sequence.button_panel_sort') }}
</button>
</div>
</template>
<script setup lang="ts">
defineProps({
panelSelected: { type: String, default: '' },
isLoading: { type: Boolean, default: false },
isSequenceOwner: { type: Boolean, default: false }
})
</script>
<style scoped lang="scss">
.tab {
display: flex;
width: 100%;
@include text(s-regular);
}
.tablinks {
border: none;
border-top-right-radius: toRem(0.5);
border-top-left-radius: toRem(0.5);
margin-right: toRem(0.3);
background-color: var(--blue-very-pale);
padding: toRem(1);
}
.selected {
background-color: var(--blue-semi-pale);
color: var(--blue-dark);
font-weight: 600;
}
</style>

View File

@@ -39,6 +39,7 @@ defineProps({
min-width: toRem(10);
padding-right: toRem(1);
padding-left: toRem(1);
z-index: 3;
}
.button-close {
position: absolute;

View File

@@ -1,150 +0,0 @@
<template>
<div id="viewer" class="entry-viewer"></div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import type { ViewerInterface, MapInterface } from '@/views/interfaces/common'
import { getIgnTiles } from '@/utils/mapAndViewer'
import { Viewer, StandaloneMap } from 'geovisio'
import { createUrlLink } from '@/utils'
import { createLink } from '@/components-viewer/reportLink'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
let mapIsLoaded = ref<boolean>(false)
let viewer = ref()
const props = defineProps({
fetchOptions: { type: Object, default: {} },
geovisioViewer: { type: Boolean, default: true },
bbox: { type: Array, default: null },
userId: { type: String, default: '' }
})
defineExpose({
viewer
})
onMounted(async () => {
const tiles = import.meta.env.VITE_TILES
const maxZoom = import.meta.env.VITE_MAX_ZOOM
const zoom = import.meta.env.VITE_ZOOM
const center = import.meta.env.VITE_CENTER
let paramsViewer: ViewerInterface
let paramsMap: MapInterface
try {
if (props.geovisioViewer) {
paramsViewer = { map: { startWide: true } }
if (center && center !== '') {
const centerMap = center.split(',').map((el: string) => parseInt(el))
paramsViewer = {
map: {
...paramsViewer.map,
center: centerMap
}
}
}
if (zoom && zoom !== '') {
paramsViewer = {
map: {
...paramsViewer.map,
zoom: parseFloat(zoom)
}
}
}
if (maxZoom && maxZoom !== '') {
paramsViewer = {
map: {
...paramsViewer.map,
maxZoom: parseInt(maxZoom)
}
}
}
if (tiles) {
const style = tiles.includes('wxs.ign.fr') ? await getIgnTiles() : tiles
paramsViewer = {
map: {
...paramsViewer.map,
style
}
}
}
if (props.fetchOptions) {
paramsViewer = {
...paramsViewer,
...props.fetchOptions
}
}
const reportLink = document.createElement('div')
reportLink.className = 'gvs-group gvs-group-large gvs-group-btnpanel'
viewer.value = new Viewer(
'viewer', // Div ID
`${import.meta.env.VITE_API_URL}/api/search`,
{
...paramsViewer,
widgets: { customWidget: reportLink }
}
)
if (viewer.value && viewer.value.addEventListener) {
viewer.value.addEventListener(
'picture-loaded',
async (e: { detail: { picId: string } }): Promise<void> => {
const href = t('pages.home.report_mail', {
picId: e.detail.picId,
link: createUrlLink(e.detail.picId)
})
reportLink.innerHTML = createLink(
href,
t('pages.home.report_button_text')
)
}
)
}
} else {
paramsMap = { minZoom: 7 }
if (tiles) {
const style = tiles.includes('wxs.ign.fr') ? await getIgnTiles() : tiles
paramsMap = {
...paramsMap,
style
}
}
const bbox = [props.bbox[0], props.bbox[1], props.bbox[2], props.bbox[3]]
viewer.value = new StandaloneMap(
'viewer', // Div ID
`${import.meta.env.VITE_API_URL}/api/search`,
{
...paramsMap,
bounds: bbox,
zoom: 14
}
)
viewer.value.addEventListener('ready', () => {
viewer.value.setFilters({ user: props.userId }, true)
viewer.value.fitBounds(bbox, {
padding: { top: 70, bottom: 70, left: 70, right: 70 },
maxZoom: 14,
speed: 10
})
})
}
mapIsLoaded.value = true
} catch (err) {
console.log(err)
}
})
onUnmounted(() => {
if (viewer.value && props.geovisioViewer) viewer.value.destroy()
})
</script>
<style scoped lang="scss">
.entry-viewer {
font-size: initial;
position: relative;
width: 100%;
height: 100%;
}
.gvs-focus-map .entry-report-button {
display: none;
}
</style>

View File

@@ -0,0 +1,245 @@
<template>
<div :id="id" class="entry-viewer"></div>
</template>
<script setup lang="ts">
import axios from 'axios'
import { onMounted, onUnmounted, ref, computed } from 'vue'
import { useSequenceStore } from '@/store/sequence'
import { Viewer, StandaloneMap, Editor } from 'geovisio'
import { createUrlLink, manageSlashUrl, getEnv } from '@/utils'
import { isAuth } from '../composables/auth'
import {
createLink,
createSequenceLink,
createFullScreenButton
} from '@/components-viewer/reportLink'
import { useI18n } from 'vue-i18n'
import { hasASessionCookieDecoded } from '@/utils/auth'
import type {
ViewerInterface,
EditorInterface,
StandAloneInterface,
ParamsViewerInterface,
ParamsEditorStandaloneInterface
} from '@/views/interfaces/common'
const sequenceStore = useSequenceStore()
const { isLogged } = isAuth()
const { t, locale } = useI18n()
let mapIsLoaded = ref<boolean>(false)
let viewer = ref()
const props = defineProps({
id: { type: String, default: 'viewer' },
fetchOptions: { type: Object, default: {} },
viewerType: { type: String, default: 'viewer' },
bbox: { type: Array, default: null },
picId: { type: String, default: null },
seqId: { type: String, default: null },
userId: { type: String, default: '' },
seqBruteDeg: { type: Number, default: 0 },
roadDegrees: { type: Number, default: 0 }
})
const userName = computed((): string => {
const cookie = hasASessionCookieDecoded()
if (cookie && cookie.account) return cookie.account.name
return ''
})
defineExpose({
viewer
})
async function getSequenceId(imgId: string): Promise<{
sequenceId: string
username: string
}> {
const { data } = await axios.get(`api/search?ids=${imgId}`)
return {
sequenceId: data.features[0].collection,
username: data.features[0].properties['geovisio:producer']
}
}
function createViewerButton(link: HTMLDivElement): void {
link.innerHTML = `<div id='instanceBtn'>${createFullScreenButton()}</div>`
viewer.value.addEventListener(
'psv:picture-loaded',
async (e: { detail: { picId: string } }): Promise<void> => {
const sequenceInformation = await getSequenceId(e.detail.picId)
let href: string
if (isLogged.value && sequenceInformation.username === userName.value) {
href = `${window.location.origin}/sequence/${sequenceInformation.sequenceId}?currentPic=${e.detail.picId}`
link.innerHTML = `<div id='instanceBtn'>
${createFullScreenButton()}
${createSequenceLink(
href,
t('pages.home.sequence_title')
)}
</div>`
sequenceStore.addSequence(e.detail.picId)
} else {
href = t('pages.home.report_mail', {
picId: e.detail.picId,
link: createUrlLink(e.detail.picId)
})
link.innerHTML = `<div id='instanceBtn'>
${createFullScreenButton()}
${createLink(
href,
t('pages.home.report_button_text')
)}
</div>`
}
}
)
}
function setupViewer(params: ParamsViewerInterface): void {
const maxZoom = getEnv('VITE_MAX_ZOOM')
const zoom = getEnv('VITE_ZOOM')
const center = getEnv('VITE_CENTER')
const reportLink = document.createElement('div')
reportLink.className = 'gvs-group gvs-group-large gvs-group-btnpanel'
let paramsViewer: ViewerInterface = {
...params,
widgets: { customWidget: reportLink },
map: { startWide: true }
}
if (params.map && params.map.raster) {
paramsViewer = {
...paramsViewer,
map: {
...paramsViewer.map,
raster: params.map.raster
}
}
}
if (center && center.length > 0) {
const centerMap = center.split(',').map((el: string) => parseInt(el))
paramsViewer = {
...paramsViewer,
map: {
...paramsViewer.map,
center: centerMap
}
}
}
if (zoom && zoom.length > 0) {
paramsViewer = {
...paramsViewer,
map: {
...paramsViewer.map,
zoom: parseFloat(zoom)
}
}
}
if (maxZoom && maxZoom.length > 0) {
paramsViewer = {
...paramsViewer,
map: {
...paramsViewer.map,
maxZoom: parseInt(maxZoom)
}
}
}
viewer.value = new Viewer(
'viewer', // Div ID
`${manageSlashUrl()}api`,
{
...paramsViewer
}
)
if (viewer.value && viewer.value.addEventListener) {
createViewerButton(reportLink)
}
}
function setupStandAlone(params: ParamsViewerInterface): void {
let paramsMap: StandAloneInterface = {
...params,
padding: { top: 70, bottom: 70, left: 70, right: 70 },
minZoom: 7,
maxZoom: 14,
speed: 10,
zoom: 14
}
if (props.userId) paramsMap = { ...paramsMap, users: [props.userId] }
const bbox = [props.bbox[0], props.bbox[1], props.bbox[2], props.bbox[3]]
viewer.value = new StandaloneMap(
props.id, // Div ID
`${manageSlashUrl()}api`,
{
...paramsMap,
bounds: bbox
}
)
}
function setupEditor(params: ParamsEditorStandaloneInterface): void {
let paramsMap: EditorInterface = { ...params, minZoom: 7 }
if (props.seqId) paramsMap = { ...paramsMap, selectedSequence: props.seqId }
if (props.userId) paramsMap = { ...paramsMap, users: [props.userId] }
try {
viewer.value = new Editor(
'viewer', // Div ID
`${manageSlashUrl()}api`,
{
...paramsMap
}
)
} catch (e) {
console.log(e)
}
}
async function initViewer(): Promise<void> {
const tiles = getEnv('VITE_TILES')
const rasterTile = getEnv('VITE_RASTER_TILE')
let params: ParamsViewerInterface | ParamsEditorStandaloneInterface = {
lang: locale.value
}
if (rasterTile && rasterTile.length > 0) {
const raster = JSON.parse(rasterTile)
if (props.viewerType !== 'viewer') params = { ...params, raster }
else params = { ...params, map: { raster } }
}
if (tiles && tiles.length) {
params = { ...params, style: await fetchTiles(tiles) }
}
if (props.picId && props.picId.length) {
params = { ...params, selectedPicture: props.picId }
}
if (props.fetchOptions && props.fetchOptions.credentials) {
params = { ...params, fetchOptions: { ...props.fetchOptions } }
}
try {
if (props.viewerType === 'standAlone') setupStandAlone(params)
else if (props.viewerType === 'editor') setupEditor(params)
else setupViewer(params)
mapIsLoaded.value = true
} catch (err) {
mapIsLoaded.value = true
}
}
onMounted(() => {
initViewer()
})
onUnmounted((): void => {
if (viewer.value && props.viewerType) {
viewer.value.addEventListener('psv:picture-loaded', (): void => {
viewer.value.destroy()
})
}
})
async function fetchTiles(tiles: string): Promise<void> {
return fetch(tiles)
.then((res) => res.json())
.then((style) => style)
}
</script>
<style scoped lang="scss">
.entry-viewer {
font-size: initial;
position: relative;
width: 100%;
height: 100%;
}
.gvs-focus-map .entry-report-button {
display: none;
}
</style>

View File

@@ -0,0 +1,166 @@
<template>
<div class="wrapper-calendar">
<div class="inputs-wrapper">
<div class="input-wrapper">
<i class="bi bi-calendar-plus"></i>
<input
:value="
range && range.start
? formatDate(new Date(range.start), 'YYYY-MM-DD')
: null
"
:placeholder="$t('pages.sequences.radio_date_placeholder')"
@input="setStartValue"
class="input"
/>
</div>
<i class="bi bi-arrow-right"></i>
<div class="input-wrapper">
<i class="bi bi-calendar-plus"></i>
<input
:value="
range && range.end
? formatDate(new Date(range.end), 'YYYY-MM-DD')
: null
"
:placeholder="$t('pages.sequences.radio_date_placeholder')"
@input="setEndValue"
class="input"
/>
</div>
</div>
</div>
<v-date-picker
ref="datePicker"
v-model="range"
mode="date"
:masks="{
input: 'YYYY-MM-DD'
}"
is-range
expanded
:max-date="new Date()"
/>
<div class="footer-modal">
<Button
:text="$t('pages.sequences.filter_date_close_button')"
look="button--transparent"
@trigger="$emit('triggerCloseModal')"
/>
<Button
v-if="range && (range.start || range.end)"
:text="$t('pages.sequences.filter_date_reset_button')"
icon="bi bi-trash"
look="button--red"
@trigger="resetCalendar"
/>
</div>
</template>
<script setup lang="ts">
import moment from 'moment'
import { ref, watch } from 'vue'
import type { PropType } from 'vue'
import { formatDate } from '@/utils/dates'
import Button from '@/components/Button.vue'
interface CalendarDateInterface {
start: Date | string | null
end: Date | string | null
type: string
}
const emit = defineEmits(['triggerDate', 'triggerCloseModal'])
const props = defineProps({
type: { type: String, default: '' },
rangeSelected: {
type: Object as PropType<CalendarDateInterface>,
default: { start: null, end: null, type: '' }
}
})
let range = ref<CalendarDateInterface>({
start: props.rangeSelected.start,
end: props.rangeSelected.end,
type: props.rangeSelected.type
})
const datePicker = ref()
function checkValidityDate(dateToValid: string): boolean {
const date = moment(dateToValid, 'YYYY-MM-DD', true)
return date.isValid() && date.format('YYYY-MM-DD') === dateToValid
}
function setStartValue(event: Event): void {
const value = (event.target as HTMLInputElement).value
if (checkValidityDate(value)) {
range.value.start = new Date(value)
const startDate = `${formatDate(new Date(value), 'YYYY-MM-DD')} 12:00 AM`
if (range && range.value.end && range.value.start) {
range.value = {
start: new Date(startDate),
end: range.value.end,
type: props.type
}
datePicker.value.updateValue(range.value)
}
}
}
function setEndValue(event: Event): void {
const value = (event.target as HTMLInputElement).value
if (checkValidityDate(value)) {
range.value.end = new Date(value)
const endDate = `${formatDate(new Date(value), 'YYYY-MM-DD')} 11:59 PM`
if (range && range.value.end && range.value.start) {
range.value = {
end: new Date(endDate),
start: range.value.start,
type: props.type
}
datePicker.value.updateValue(range.value)
}
}
}
function resetCalendar(): void {
range.value = { start: null, end: null, type: '' }
emit('triggerDate', { start: null, end: null, type: props.type })
}
watch(range, (range) => {
if (range && range.start && range.end) {
const startDate = `${formatDate(range.start, 'YYYY-MM-DD')} 12:00 AM`
const endDate = `${formatDate(range.end, 'YYYY-MM-DD')} 11:59 PM`
range.type = props.type
emit('triggerDate', { start: startDate, end: endDate, type: props.type })
}
})
</script>
<style scoped lang="scss">
.wrapper-calendar {
margin-bottom: toRem(2);
}
.inputs-wrapper {
display: flex;
align-items: center;
@include text(xs-r-regular);
}
.input-wrapper {
position: relative;
width: 100%;
}
.footer-modal {
margin-top: toRem(2);
display: flex;
justify-content: space-between;
}
.bi-arrow-right {
margin-right: toRem(1);
margin-left: toRem(1);
}
.bi-calendar-plus {
position: absolute;
left: 5%;
top: 20%;
}
.input {
padding: toRem(0.5) toRem(0.5) toRem(0.5) toRem(2.5);
border-radius: toRem(0.3);
border: toRem(0.1) solid var(--grey-pale);
width: 100%;
}
</style>

View File

@@ -53,7 +53,7 @@ defineProps({
justify-content: center;
align-items: center;
color: var(--blue);
background-color: var(--blue-semi);
background-color: var(--blue-semi-pale);
height: toRem(3);
width: toRem(3);
border-radius: 50%;

View File

@@ -0,0 +1,113 @@
<template>
<div class="dropdown">
<img :src="img(`${locale}.svg`)" loading="lazy" class="img-lang" />
<span class="desktop">{{ locale.toUpperCase() }}</span>
<div class="image-chevron desktop">
<i class="bi bi-chevron-down" />
</div>
<div class="dropdown-block">
<button
v-for="lang in allLocales"
@click.native="changeLocale(lang)"
class="item-lang"
>
<img :src="formatLangListUrl(lang)" loading="lazy" class="img-lang" />
<span>{{ lang.toUpperCase() }}</span>
</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { img } from '../../utils/image'
import { useCookies } from 'vue3-cookies'
import i18n from '../../i18n'
const { locale, messages } = useI18n()
const { cookies } = useCookies()
const displayed = ref<boolean>(true)
const allLocales = ref<string[]>([])
onMounted(() => {
Object.keys(messages.value).forEach(function (key) {
if (locale.value !== key) allLocales.value = [...allLocales.value, key]
})
})
function changeLocale(lang: string): void {
const index = allLocales.value.findIndex((el) => el === lang)
allLocales.value[index] = locale.value
cookies.set('lang', lang)
locale.value = cookies.get('lang')
const html = document.querySelector('html')
if (html) html.setAttribute('lang', locale.value)
localStorage.setItem('user-locale', locale.value)
i18n.global.locale.value = locale.value
displayed.value = false
location.reload()
}
function formatLangListUrl(lang: string): string {
return img(`${lang}.svg`)
}
</script>
<style scoped lang="scss">
.dropdown {
display: flex;
align-items: center;
cursor: pointer;
height: toRem(2);
}
.dropdown:hover > .dropdown-block {
display: block;
}
.dropdown:hover > .image-chevron {
transform: rotate(180deg);
}
.dropdown-block {
display: none;
position: absolute;
z-index: 2;
background-color: var(--white);
border-radius: toRem(0.5);
width: fit-content;
left: toRem(-1);
top: toRem(2);
padding: toRem(0.5);
}
.img-lang {
height: toRem(1.5);
margin-right: toRem(0.5);
}
.desktop {
@include text(xs-r-regular);
}
.image-chevron {
margin-left: toRem(0.5);
}
.item-lang {
display: flex;
align-items: center;
justify-content: flex-start;
background-color: var(--white);
border: none;
cursor: pointer;
@include text(xs-r-regular);
margin-bottom: toRem(0.5);
}
@media (max-width: toRem(50)) {
.desktop {
display: none;
}
.img-lang {
height: toRem(2);
}
.dropdown-block {
left: toRem(-2);
top: toRem(3);
padding: toRem(0.5);
}
.item-lang {
margin-bottom: toRem(1);
}
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<div class="wrapper-orientation">
<h2 class="orientation-title">
{{ $t('pages.sequence.orientation_panel_title') }}
</h2>
<div class="entry-information-card">
<InformationCard>
<template v-slot:title>
<h3 class="subtitle">
{{ $t('pages.sequence.orientation_panel_tooltip') }}
</h3>
</template>
</InformationCard>
<div class="wrapper-input-angle">
<label for="inputAngle" name="inputAngle">{{
$t('pages.sequence.orientation_input_label')
}}</label>
<Input
id="inputAngle"
name="inputAngle"
type="number"
min="-180"
max="180"
:text="Number(angleInputValue)"
:placeholder="$t('pages.sequence.orientation_input_placeholder')"
@input="captureAngle"
/>
<span v-if="errorAngleValue" class="errorValue">{{
$t('pages.sequence.orientation_input_error_value')
}}</span>
</div>
</div>
<div class="entry-compass">
<WidgetOrientation
:road-degrees="roadDegrees"
:seq-brute-deg="angleValue"
@triggerAngle="captureAngle"
@triggerMovingAngle="triggerMovingAngle"
/>
</div>
<div class="entry-button">
<Button
look="button--blue"
:text="$t('pages.sequence.orientation_panel_button')"
:disabled="isDisabled(Number(angleInputValue)) || isLoading"
@trigger="triggerAngle"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watchEffect, onMounted, onUnmounted } from 'vue'
import InformationCard from '@/components/InformationCard.vue'
import Button from '@/components/Button.vue'
import Input from '@/components/Input.vue'
import WidgetOrientation from '@/components/sequence/WidgetOrientation.vue'
import { modulo180 } from '@/utils/calc'
const emit = defineEmits<{
(e: 'triggerAngle', value: number): void
(e: 'triggerMovingAngle', value: number): void
}>()
let angleValue = ref<number>(0)
let angleInputValue = ref<number>(0)
let errorAngleValue = ref<boolean>(false)
const props = defineProps({
roadDegrees: { type: Number, default: 0 },
seqBruteDeg: { type: Number, default: 0 },
isLoading: { type: Boolean, default: false }
})
onMounted(() => {
triggerMovingAngle(angleInputValue.value)
})
onUnmounted(() => {
triggerMovingAngle(angleInputValue.value)
})
watchEffect(() => {
angleValue.value = props.seqBruteDeg
angleInputValue.value = Math.round(props.seqBruteDeg - props.roadDegrees)
})
function isDisabled(value: number): boolean {
return value < -180 || value > 180
}
function captureAngle(value: number | string) {
errorAngleValue.value = false
const valueNum = Number(value)
angleInputValue.value = valueNum
angleValue.value = valueNum + props.roadDegrees
const movingAngle = modulo180(angleValue.value, Math.round(props.roadDegrees))
emit('triggerMovingAngle', movingAngle)
if (isDisabled(valueNum)) return (errorAngleValue.value = true)
}
function triggerMovingAngle(value: number) {
emit('triggerMovingAngle', value)
}
function triggerAngle() {
const valueToSend = angleValue.value - Number(props.roadDegrees)
if (isDisabled(valueToSend)) return
emit('triggerAngle', valueToSend)
}
</script>
<style scoped lang="scss">
.wrapper-orientation {
background-color: var(--blue-semi-pale);
}
.orientation-title {
@include text(h4);
margin-bottom: toRem(2);
}
.subtitle {
@include text(m-r-regular);
color: var(--blue-dark);
margin-bottom: 0;
}
.entry-compass {
width: 100%;
margin-bottom: toRem(2);
}
.entry-information-card {
margin-top: toRem(2);
margin-bottom: toRem(2.5);
display: flex;
}
.wrapper-input-angle {
margin-left: toRem(1);
width: 45%;
background-color: var(--white);
padding: toRem(1);
border-radius: toRem(1);
}
.entry-button {
width: 100%;
display: flex;
justify-content: center;
}
.errorValue {
color: var(--red);
@include text(xs-r-regular);
text-align: center;
}
@media (max-width: toRem(102.4)) {
.entry-information-card {
flex-direction: column;
}
.wrapper-input-angle {
margin-left: 0;
width: 100%;
margin-top: toRem(1);
}
}
@media (max-width: toRem(50)) {
.orientation-title {
@include text(m-regular);
margin-bottom: toRem(2);
}
}
</style>

View File

@@ -0,0 +1,296 @@
<template>
<div v-if="pictures && pictures.length">
<div v-if="isSequenceOwner" class="delete-all">
<div class="wrapper-select">
<InputCheckbox
:is-checked="pictures.length === picturesToDelete.length"
:is-indeterminate="isIndeterminate"
:label="selectedText"
name="picture-selected"
@trigger="triggerInputCheck"
/>
<div v-if="picturesToDelete.length" class="wrapper-photo-selected">
<span class="photo-selected-separator">-</span>
<span>{{
$t('pages.sequence.picture_selected', picturesToDelete.length)
}}</span>
</div>
</div>
<div class="action-buttons">
<Button
look="button--white background-white no-text"
:icon="
picturesToDeleteStatus === 'hidden' ||
imagesSelectedHaveDifferentStatus
? 'bi bi-eye'
: 'bi bi-eye-slash'
"
:tooltip="$t('pages.sequence.hide_photo_tooltip')"
:disabled="!picturesToDelete.length || sequence.status === 'hidden'"
@trigger="triggerPatchOrDeleteCollectionItems('PATCH')"
/>
<div class="button-hidde">
<Button
look="button--red background-white no-text"
icon="bi bi-trash"
:tooltip="$t('pages.sequence.delete_photo_tooltip')"
:disabled="!picturesToDelete.length"
@trigger="triggerPatchOrDeleteCollectionItems('DELETE')"
/>
</div>
</div>
</div>
<div v-if="informationCardDisplayed" class="entry-info-card">
<InformationCard look="blue">
<template v-slot:title>
<span class="msg-info">
{{ $t('pages.sequence.info_msg_maj') }}
</span>
</template>
<template v-slot:cross>
<Button
icon="bi bi-x-lg"
look="no-text-blue-dark"
@trigger="informationCardDisplayed = false"
/>
</template>
</InformationCard>
</div>
<ul class="photo-list">
<li v-for="(item, i) in pictures" :id="`el-list${i}`" class="photo-item">
<ImageItem
:href="item.assets.thumb.href"
:href-hd="item.assets.hd.href"
:created="formatDate(item.properties.datetime, 'HH:mm:ss')"
:selected="photoToDeleteOrPatchSelected(item, picturesToDelete)"
:selected-on-map="itemSelected === item.id"
:status="
imageStatus(item.properties['geovisio:status'], sequence.status)
"
@trigger="triggerSelectImageAndMove(item)"
>
<template v-slot:checkbox>
<InputCheckbox
:is-checked="photoToDeleteOrPatchSelected(item, picturesToDelete)"
:name="item.id"
:is-indeterminate="false"
@trigger.self="triggerInputCheckItem"
/>
</template>
</ImageItem>
</li>
<div class="entry-pagination">
<Pagination
v-for="item in paginationLinks"
:type="item.rel"
:href="item.href"
:self-link="selfLink[0]"
@trigger="triggerGoToNextPage"
/>
</div>
</ul>
</div>
<p v-else class="no-photo">{{ $t('pages.sequence.no_image') }}</p>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import type { PropType } from 'vue'
import Pagination from '@/components/Pagination.vue'
import ImageItem from '@/components/ImageItem.vue'
import Button from '@/components/Button.vue'
import InputCheckbox from '@/components/InputCheckbox.vue'
import InformationCard from '@/components/InformationCard.vue'
import { formatDate } from '@/utils/dates'
import {
imageStatus,
photoToDeleteOrPatchSelected
} from '@/views/utils/sequence/index'
import type {
ResponseUserPhotoLinksInterface,
ResponseUserPhotoInterface,
UserSequenceInterface,
CheckboxInterface
} from '../../views/interfaces/MySequenceView'
const { t } = useI18n()
const emit = defineEmits<{
(e: 'triggerInputCheck', value: CheckboxInterface): void
(e: 'triggerInputCheckItem', value: string): void
(e: 'triggerGoToNextPage', value: string): void
(e: 'triggerSelectImageAndMove', value: ResponseUserPhotoInterface): void
(e: 'triggerPatchOrDeleteCollectionItems', value: string): void
}>()
let informationCardDisplayed = ref<boolean>(true)
const props = defineProps({
pictures: {
type: Array as PropType<ResponseUserPhotoInterface[]>,
default: []
},
picturesToDelete: {
type: Array as PropType<string[]>,
default: []
},
sequence: {
type: Object as PropType<UserSequenceInterface>,
default: {}
},
paginationLinks: {
type: Array as PropType<ResponseUserPhotoLinksInterface[]>,
default: []
},
selfLink: {
type: Array as PropType<ResponseUserPhotoLinksInterface[]>,
default: []
},
fullImagesToDelete: {
type: Array as PropType<ResponseUserPhotoInterface[]>,
default: []
},
menuHeight: {
type: String,
default: '0'
},
isSequenceOwner: { type: Boolean, default: false },
imagesSelectedHaveDifferentStatus: { type: Boolean, default: false },
itemSelected: { type: String, default: '' }
})
const isIndeterminate = computed(
(): boolean =>
!!props.picturesToDelete.length &&
!!props.sequence &&
props.pictures.length !== props.picturesToDelete.length
)
const selectedText = computed((): string =>
props.picturesToDelete.length === props.pictures.length
? t('pages.sequence.unselect_text')
: t('pages.sequence.select_text')
)
const picturesToDeleteStatus = computed((): string => {
if (props.fullImagesToDelete.length) {
return props.fullImagesToDelete[0].properties['geovisio:status']
}
return 'hidden'
})
function triggerInputCheck(value: CheckboxInterface): void {
emit('triggerInputCheck', value)
}
function triggerGoToNextPage(value: string): void {
emit('triggerGoToNextPage', value)
}
function triggerSelectImageAndMove(value: ResponseUserPhotoInterface): void {
emit('triggerSelectImageAndMove', value)
}
function triggerPatchOrDeleteCollectionItems(value: string): void {
emit('triggerPatchOrDeleteCollectionItems', value)
}
function triggerInputCheckItem(value: CheckboxInterface): void {
emit('triggerInputCheckItem', value.name)
}
</script>
<style scoped lang="scss">
.delete-all {
display: flex;
justify-content: space-between;
align-items: center;
margin-left: toRem(1);
margin-right: toRem(2);
margin-bottom: toRem(1);
@include text(xs-r-regular);
}
.wrapper-select {
display: flex;
align-items: center;
}
.wrapper-photo-selected {
@include text(xs-regular);
}
.photo-selected-separator {
margin-right: toRem(0.5);
margin-left: toRem(0.5);
}
.action-buttons {
display: flex;
align-items: center;
}
.button-hidde {
margin-right: toRem(1);
margin-left: toRem(1);
}
.msg-info {
@include text(xs-r-regular);
}
.entry-info-card {
padding: toRem(1);
}
.photo-list {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
height: 100%;
width: 100%;
padding: 0;
}
.photo-item {
width: calc(33% - #{toRem(2)});
height: fit-content;
margin: toRem(1);
border-radius: toRem(1.5);
background-color: var(--grey);
}
.no-photo {
@include text(s-regular);
text-align: center;
margin-top: toRem(10);
color: var(--grey-dark);
}
.entry-pagination {
margin-top: toRem(2);
margin-bottom: toRem(2);
width: 100%;
display: flex;
justify-content: center;
}
@media (max-width: toRem(102.4)) {
.photo-item {
width: calc(50% - #{toRem(2)});
}
}
@media (max-width: toRem(76.8)) {
.entry-info-card {
display: none;
}
.delete-all {
text-align: left;
}
.wrapper-select {
flex-direction: column;
align-items: initial;
}
.wrapper-photo-selected:nth-child(2) {
margin-top: toRem(0.5);
}
.photo-selected-separator {
display: none;
}
.entry-pagination {
margin-top: toRem(0.5);
margin-bottom: toRem(1);
}
.photo-list {
padding-right: toRem(2);
}
.photo-item {
width: 100%;
margin-right: 0;
}
}
@media (max-width: toRem(50)) {
.entry-pagination {
margin-top: toRem(1);
}
}
</style>

View File

@@ -0,0 +1,118 @@
<template>
<form class="wrapper-sort" @submit.prevent="triggerSort">
<h2 class="sort-title">
{{ $t('pages.sequence.sort_panel_title') }}
</h2>
<span class="sort-subtitle">
{{ $t('pages.sequence.sort_panel_settings') }}
</span>
<div class="wrapper-checkboxs">
<div class="checkbox-item">
<InputRadio
:value="sortValue"
:label="$t('pages.sequence.sort_panel_check_gps')"
name="sort"
id="gpsdate"
@trigger="triggerInputCheck"
/>
</div>
<div class="checkbox-item">
<InputRadio
:value="sortValue"
:label="$t('pages.sequence.sort_panel_check_file')"
name="sort"
id="filedate"
@trigger="triggerInputCheck"
/>
</div>
<div class="checkbox-item">
<InputRadio
:value="sortValue"
:label="$t('pages.sequence.sort_panel_check_name')"
name="sort"
id="filename"
@trigger="triggerInputCheck"
/>
</div>
</div>
<span class="sort-subtitle">
{{ $t('pages.sequence.sort_panel_settings_order') }}
</span>
<div class="wrapper-checkboxs">
<InputSwitch @trigger="triggerInputSwitch" :increased="increaseChecked" />
</div>
<Button
look="button--blue"
type="submit"
:text="$t('pages.sequence.sort_panel_button')"
:disabled="sortValue.length === 0"
/>
</form>
</template>
<script setup lang="ts">
import { watchEffect, ref } from 'vue'
import InputRadio from '@/components/InputRadio.vue'
import InputSwitch from '@/components/InputSwitch.vue'
import Button from '@/components/Button.vue'
const props = defineProps({
sequenceSorted: { type: String, default: null }
})
const emit = defineEmits<{
(e: 'triggerSort', value: string): void
}>()
let sortValue = ref<string>('')
let increaseChecked = ref<boolean>(true)
watchEffect(() => {
if (props.sequenceSorted) {
const filtersType = ['gpsdate', 'filedate', 'filename']
const filtersValue = ['+', '-']
filtersType.map((e: string) => {
if (props.sequenceSorted.includes(e)) sortValue.value = e
})
filtersValue.map((e: string) => {
if (props.sequenceSorted.includes(e) && e === '+') {
increaseChecked.value = true
}
if (props.sequenceSorted.includes(e) && e === '-') {
increaseChecked.value = false
}
})
}
})
function triggerInputSwitch(value: { isIncreaseChecked: boolean }): void {
increaseChecked.value = value.isIncreaseChecked
}
function triggerInputCheck(value: string): void {
sortValue.value = value
}
function triggerSort() {
const value = increaseChecked.value
? `+${sortValue.value}`
: `-${sortValue.value}`
emit('triggerSort', value)
}
</script>
<style scoped lang="scss">
.sort-title {
@include text(h4);
color: var(--blue-dark);
margin-bottom: toRem(2);
}
.sort-subtitle {
color: var(--grey-semi-dark);
}
.wrapper-checkboxs {
display: flex;
flex-wrap: wrap;
margin-top: toRem(1.5);
margin-bottom: toRem(2.5);
}
.checkbox-item {
margin-right: toRem(4);
margin-bottom: toRem(1);
}
</style>

View File

@@ -0,0 +1,301 @@
<template>
<div class="wrapper-widget-orientation">
<div
class="wrapper-img-road"
:style="{
transform: `rotate(${roadDegrees}deg)`
}"
></div>
<div class="wrapper-widget">
<div class="wrapper-car">
<div class="wrapper-elements">
<div class="rounded-transparent"></div>
<img
src="@/assets/images/car.svg"
alt=""
:style="{
transform: `rotate(${roadDegrees}deg)`
}"
class="car-img"
/>
<div
:style="{
transform: `rotate(${angle}deg)`
}"
id="rotateWrapper"
class="rotate-wrapper"
>
<div
id="rotate"
@mousedown="mousedown"
@mousemove="handleMouseMove"
@mouseup="mouseup"
@mouseout="mouseup"
class="cursor-img desktop"
>
<button class="arrow-img arrow-img-1" @click="clickAndMove(45)">
<img src="@/assets/images/icon/cursor-arrow.svg" alt="" />
</button>
<button class="arrow-img arrow-img-2" @click="clickAndMove(-45)">
<img src="@/assets/images/icon/cursor-arrow.svg" alt="" />
</button>
</div>
<div
id="rotate"
@touchstart="touchdown"
@touchmove="handleTouchMove"
@touchend="mouseup"
class="cursor-img responsive"
>
<button class="arrow-img arrow-img-1" @click="clickAndMove(45)">
<img src="@/assets/images/icon/cursor-arrow.svg" alt="" />
</button>
<button class="arrow-img arrow-img-2" @click="clickAndMove(-45)">
<img src="@/assets/images/icon/cursor-arrow.svg" alt="" />
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watchEffect } from 'vue'
import { modulo180 } from '@/utils/calc'
let angleValue = ref<number>(0)
let angle = ref<number>(0)
let prevRotation = ref<number>(0)
let active = ref<boolean>(false)
let rotation = ref<number>(0)
let startAngle = ref<number>(0)
let rotateWrapper = ref<HTMLElement | null>(null)
let rotate = ref<HTMLElement | null>(null)
let center: { x: number; y: number } = { x: 0, y: 0 }
const R2D: number = 180 / Math.PI
const emit = defineEmits<{
(e: 'triggerAngle', value: number): void
(e: 'triggerMovingAngle', value: number): void
}>()
const props = defineProps({
roadDegrees: { type: Number, default: 0 },
seqBruteDeg: { type: Number, default: 0 }
})
watchEffect(() => {
angle.value = Math.round(props.seqBruteDeg)
})
onMounted(() => {
const app = document.getElementById('app')
rotateWrapper.value = document.getElementById('rotateWrapper')
rotate.value = document.getElementById('rotate')
if (app) app.addEventListener('mouseup', () => (active.value = false))
})
function touchdown(e: TouchEvent): void {
e.preventDefault()
if (!rotateWrapper.value) return
const bb = rotateWrapper.value.getBoundingClientRect()
const { top: t, left: l, height: h, width: w } = bb
center = { x: l + w / 2, y: t + h / 2 }
let x = e.changedTouches[0].clientX - center.x
let y = e.changedTouches[0].clientY - center.y
startAngle.value = R2D * Math.atan2(y, x)
active.value = true
}
function mousedown(e: MouseEvent): void {
e.preventDefault()
if (!rotateWrapper.value) return
const bb = rotateWrapper.value.getBoundingClientRect()
const { top: t, left: l, height: h, width: w } = bb
center = { x: l + w / 2, y: t + h / 2 }
let x = e.clientX - center.x
let y = e.clientY - center.y
startAngle.value = R2D * Math.atan2(y, x)
active.value = true
}
function handleTouchMove(e: TouchEvent): void {
if (!active.value || !rotateWrapper.value) return
const x = e.changedTouches[0].clientX - center.x
const y = e.changedTouches[0].clientY - center.y
const d = R2D * Math.atan2(y, x)
rotation.value = d - startAngle.value
const calc = angle.value + rotation.value
rotateWrapper.value.style.transform = `rotate(${calc}deg)`
}
function handleMouseMove(e: MouseEvent): void {
if (!active.value || !rotateWrapper.value) return
const x = e.clientX - center.x
const y = e.clientY - center.y
const d = R2D * Math.atan2(y, x)
rotation.value = d - startAngle.value
const calc = angle.value + rotation.value
rotateWrapper.value.style.transform = `rotate(${calc}deg)`
}
function mouseup(): void {
if (!active.value || !rotate.value) return
angle.value += Math.round(rotation.value)
if (rotation.value !== prevRotation.value) {
prevRotation.value = rotation.value
angleValue.value = angle.value
if (angleValue.value !== 0) {
emit('triggerMovingAngle', modulo180(angle.value, props.roadDegrees))
emit('triggerAngle', modulo180(angle.value, props.roadDegrees))
}
}
active.value = false
}
function clickAndMove(value: number): void {
const moduloAngle = modulo180(angle.value, Math.round(props.roadDegrees))
if (moduloAngle % 45 === 0) {
let angleToEmit = moduloAngle + value
if (angleToEmit > 180) angleToEmit = -135
if (angleToEmit < -180) angleToEmit = 135
return emit('triggerAngle', angleToEmit)
}
let closestMultiple = Math.ceil(moduloAngle / 45) * value
return emit('triggerAngle', closestMultiple)
}
</script>
<style scoped lang="scss">
.wrapper-widget-orientation {
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: toRem(40);
width: 100%;
border-radius: toRem(1);
}
.wrapper-img-road {
background-image: url('@/assets/images/road.svg');
background-repeat: no-repeat;
background-size: contain;
background-position: center;
height: 210%;
width: 250%;
}
.wrapper-widget {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.wrapper-car {
height: toRem(40);
width: toRem(40);
display: flex;
justify-content: center;
position: relative;
}
.wrapper-elements {
width: 100%;
height: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.rotate-wrapper {
width: 100%;
height: 100%;
border-radius: 50%;
transform: rotate(0);
background-color: transparent;
display: flex;
justify-content: center;
position: relative;
z-index: 0;
.cursor-img {
background-image: url('@/assets/images/cursor.svg');
content: '';
display: block;
position: absolute;
left: toRem(2);
right: 0;
top: 0;
margin: auto;
height: 50%;
width: 74%;
background-repeat: no-repeat;
background-size: cover;
background-position: center;
cursor: move;
}
.desktop {
display: block;
}
.responsive {
display: none;
}
}
.arrow-img {
position: absolute;
border: none;
background-color: transparent;
height: toRem(5);
width: toRem(5);
padding: 0;
img {
height: 100%;
&:hover {
opacity: 0.8;
}
}
}
.arrow-img-1 {
right: toRem(4);
top: toRem(6);
}
.arrow-img-2 {
left: toRem(3.2);
bottom: toRem(10.5);
transform: rotate(90deg);
}
.car-img {
position: absolute;
height: 45%;
z-index: 1;
pointer-events: none;
}
.rounded-transparent {
background-color: rgba(white, 0.4);
height: toRem(25);
width: toRem(25);
position: absolute;
border-radius: 50%;
}
@media (max-width: toRem(76.8)) {
.rotate-wrapper {
.desktop {
display: none;
}
.responsive {
display: block;
}
}
.rotate-wrapper {
width: 75%;
height: 75%;
}
.arrow-img {
height: toRem(4);
width: toRem(4);
}
.arrow-img-1 {
right: toRem(3);
top: toRem(4.5);
}
.arrow-img-2 {
left: toRem(2.4);
bottom: toRem(7.88);
}
.car-img {
height: 40%;
}
}
</style>

View File

@@ -34,33 +34,64 @@
<span>{{ $t('pages.upload.images_count_text') }}</span>
</div>
</div>
<div v-else class="loaded-block">
<div v-else-if="uploadError" class="loaded-block error">
<div class="error-block">
<i class="bi bi-x-octagon"></i>
<p>{{ uploadError }}</p>
</div>
</div>
<div v-else class="loaded-block success">
<img src="@/assets/images/success.svg" alt="" />
<p>{{ $t('pages.upload.upload_done') }}</p>
<span
v-if="!uploadedSequence.picturesOnError.length && otherFilesCount > 0"
class="other-files"
>{{
$t('pages.upload.upload_other_files', { count: otherFilesCount })
}}</span
>
</div>
</div>
<div
v-if="uploadedSequence && uploadedSequence.picturesOnError.length"
class="error-wrapper"
>
<div v-if="uploadedSequence?.picturesOnError?.length" class="error-wrapper">
<div class="error-corpus">
<i class="bi bi-exclamation-triangle"></i>
<span class="error-text">{{
$t('pages.upload.pictures_error', {
count: uploadedSequence.picturesOnError.length
})
}}</span>
<div>
<i class="bi bi-exclamation-triangle"></i>
<span class="error-text">{{
$t('pages.upload.pictures_error', {
count: uploadedSequence.picturesOnError.length
})
}}</span>
</div>
<div v-if="userAgentAndroid" class="entry-tutorial-error-exif">
<span class="other-files">{{
$t('pages.sequence.sequence_tutorial_exif_text')
}}</span>
<img
src="@/assets/images/tutorial-upload-loc.jpg"
alt=""
class="img-tutorial-error-exif"
/>
<img
src="@/assets/images/tutorial-upload-loc-2.jpg"
alt=""
class="img-tutorial-error-exif first-tutorial-img-error"
/>
</div>
</div>
<Button
:text="$t('pages.upload.error_button')"
look="button button--red"
@trigger="$emit('triggerModal')"
/>
<span v-if="otherFilesCount > 0" class="other-files">{{
$t('pages.upload.upload_other_files', { count: otherFilesCount })
}}</span>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import Loader from '@/components/Loader.vue'
import Link from '@/components/Link.vue'
import Button from '@/components/Button.vue'
@@ -74,8 +105,14 @@ defineProps({
type: Object as PropType<SequenceInterface | null>,
default: null
},
picturesCount: { type: Number, default: null }
picturesCount: { type: Number, default: null },
otherFilesCount: { type: Number, default: null },
uploadError: { type: String, default: null }
})
const userAgentAndroid = computed<boolean>(() =>
navigator.userAgent.includes('Android')
)
</script>
<style scoped lang="scss">
@@ -151,8 +188,12 @@ defineProps({
margin-bottom: toRem(1);
}
.error-text {
font-weight: bold;
margin-left: toRem(0.5);
}
.entry-tutorial-error-exif {
display: none;
}
.loading-block,
.loaded-block {
height: 100%;
@@ -161,12 +202,25 @@ defineProps({
justify-content: center;
align-items: center;
}
.loaded-block {
.success {
color: var(--green);
img {
margin-bottom: toRem(2);
}
}
.other-files {
@include text(s-regular);
color: var(--red-pale);
}
.error-block {
display: flex;
i {
margin-right: toRem(1);
}
}
.error {
color: var(--red);
}
@media (max-width: toRem(102.4)) {
.loader-title {
flex-direction: column;
@@ -176,4 +230,33 @@ defineProps({
margin-bottom: toRem(1.5);
}
}
@media (max-width: toRem(76.8)) {
.entry-tutorial-error-exif {
display: block;
padding-top: toRem(1);
}
.other-files {
margin-top: toRem(1);
}
.img-tutorial-error-exif {
width: fit-content;
height: toRem(27);
border-radius: toRem(1);
margin-top: toRem(1);
margin-bottom: toRem(1);
}
.first-tutorial-img-error {
margin-right: toRem(1);
}
}
@media (max-width: toRem(50)) {
.first-tutorial-img-error {
margin-right: 0;
margin-bottom: 0;
}
.img-tutorial-error-exif {
width: 100%;
height: initial;
}
}
</style>

View File

@@ -1,8 +1,9 @@
import axios from 'axios'
import { onMounted, ref } from 'vue'
import { onMounted, watchEffect, ref } from 'vue'
import { hasASessionCookieDecoded } from '@/utils/auth'
import type { AuthConfigInterface } from './interfaces/Auth'
export default function authConfig() {
export function authConfig() {
const authConf = ref<AuthConfigInterface>()
async function getConfig(): Promise<AuthConfigInterface> {
@@ -15,3 +16,12 @@ export default function authConfig() {
onMounted(async () => (authConf.value = await getConfig()))
return { authConf }
}
export function isAuth() {
const isLogged = ref<boolean>(false)
watchEffect(() => {
const cookie = hasASessionCookieDecoded()
isLogged.value = !!(cookie && cookie.account)
})
return { isLogged }
}

28
src/i18n/index.js Normal file
View File

@@ -0,0 +1,28 @@
import { createI18n } from 'vue-i18n'
import { useCookies } from 'vue3-cookies'
import fr from '../locales/fr.json'
import en from '../locales/en.json'
import hu from '../locales/hu.json'
import pt from '../locales/pt.json'
import cs from '../locales/cs.json'
const { cookies } = useCookies()
const locale = cookies.get('lang')
? cookies.get('lang')
: navigator.language.split('-')[0]
const i18n = createI18n({
locale,
fallbackLocale: 'en',
warnHtmlMessage: false,
globalInjection: true,
legacy: false,
messages: {
cs,
fr,
en,
hu,
pt
}
})
export default i18n

224
src/locales/cs.json Normal file
View File

@@ -0,0 +1,224 @@
{
"general": {
"title": "Instance Panoramax",
"meta": {
"title": "Instance Panoramax",
"description": "Panoramax, bezplatná alternativa k fotomapování území"
},
"header": {
"login_text": "Připojit",
"register_text": "Registrovat",
"contribute_text": "Proč přispívat?",
"my_account": "Můj účet",
"upload_text": "+ Sdílejte obrázky",
"sequences_text": "Moje obrázky",
"alt_logo": "Logo instance",
"alt_photos": "Ikona obrázků",
"alt_information": "Ikona uživatele",
"alt_settings": "Ikona parametrů",
"alt_logout": "Ikona odhlášení",
"title": "Panoramax",
"beta_text": "Beta verze",
"logout_text": "Odhlásit se",
"my_information_text": "Moje detaily",
"my_settings_text": "Moje parametry",
"burger_menu_aria_label_open": "Zobrazit menu",
"burger_menu_aria_label_closed": "Skrýt nabídku"
},
"footer": {
"panoramax_site": "Objevte Panoramax",
"information_gitlab": "Zobrazit zdrojový kód",
"gitlab_logo": "Logo Gitlab",
"ay11_text": "Přístupnost: nevyhovuje"
},
"error_text": "Nastala chyba",
"success_text": "Aktualizace provedena"
},
"pages": {
"home": {
"report_mail": "?subject=⚠️ Nahlásit k obrázku {picId}&body=Hello, %0D%0A%0D%0A Problém na obrázku (typ problému ponechat nahlášený) : %0D%0A%0D%0A %0D%0A%0D%0A nevhodné obsah / chybějící rozmazání na prvku, který má být anonymizován nebo rozmazán z bezpečnostních důvodů / nadměrné rozmazání (příliš velké rozmazání) %0D%0A%0D%0A Odkaz na dotčenou fotografii: {link} %0D%0A%0D%0A Podrobnosti o dotčených prvky (zejména pro problémy s rozmazáním - co by mělo být rozmazané nebo nerozmazané?):",
"report_button_text": "Nahlásit tento obrázek",
"sequence_title": "Podívejte se na sekvenci",
"open_fullscreen": "Režim celé obrazovky",
"close_fullscreen": "Normální mód"
},
"settings": {
"title": "Moje tokeny",
"setting_tooltip": "Zobrazit nebo skrýt token"
},
"sequence": {
"sequence_published": "Publikováno",
"sequence_waiting": "Stále zpracovávám",
"sequence_hidden": "Skrytý",
"sequence_form_title": "Upravte název",
"hide_sequence_tooltip": "Skryjte tyto sekvence",
"back_button": "Zpět k mému seznamu sekvencí",
"delete_sequence_tooltip": "Trvale odstranit tuto sekvenci",
"hide_photo_tooltip": "Skrýt vybrané obrázky",
"delete_photo_tooltip": "Trvale odstranit vybrané snímky",
"conf_pic_msg": "⚠️ Vybrané fotografie budou trvale smazány",
"conf_sequence_msg": "⚠️ Tato sekvence bude trvale smazána",
"button_panel_photos": "Správa obrázků",
"button_panel_orientation": "Nastavte orientaci",
"button_panel_sort": "Posloupnost řazení",
"orientation_panel_title": "Úprava orientace všech fotografií v sekvenci",
"orientation_panel_tooltip": "Přetáhněte modrý rámeček požadovaným směrem\"",
"orientation_input_label": "nebo zde změňte úhel",
"orientation_input_placeholder": "Hodnota mezi -180 a 180",
"orientation_input_error_value": "Hodnota musí být mezi -180 a 180",
"orientation_panel_button": "Potvrďte pozici",
"orientation_updated": "Orientace aktualizována",
"sort_updated": "Sekvence seřazena",
"sort_panel_title": "Nastavení sekvenčního řazení",
"sort_panel_settings": "Seřadit sekvenci podle:",
"sort_panel_settings_order": "Objednat :",
"sort_panel_settings_order_increase": "Vzestupně",
"sort_panel_settings_order_decrease": "Klesající",
"sort_panel_check_gps": "Datum GPS",
"sort_panel_check_file": "Datum souboru",
"sort_panel_check_name": "Název souboru",
"created": "Nahráno:",
"taken": "Vyfoceno dne:",
"duration": "Doba trvání :",
"duration_begin": "Start :",
"duration_end": "Konec :",
"camera": "Fotoaparát :",
"button_delete": "Vymazat",
"button_disable": "Skrýt",
"button_enable": "Ukázat",
"picture_selected": "{count} obrázek vybrán| Počet vybraných obrázků: {count}",
"hours": "{count} hodina| {count} hodin",
"minutes": "{count} minut| {count} minut",
"seconds": "{count} sekund| {count} sekund",
"select_text": "Vybrat vše",
"unselect_text": "Odznačit vše",
"select_shift_text": "Vyberte více fotografií pomocí posunu",
"waiting_process": "Foto v procesu",
"broken": "Chyba zpracování fotografie",
"no_image": "V této sekvenci není žádný obrázek"
},
"sequences": {
"title": "Moje sekvence",
"filter_date_upload_title": "Filtrujte podle data nahrání",
"filter_date_title": "Filtrovat podle data focení:",
"radio_date_placeholder": "03/01/2024",
"radio_date": "datum",
"hide_button": "Skrýt",
"show_button": "Ukázat",
"delete_button": "Vymazat",
"filter_date_reset_button": "Resetovat",
"filter_date_close_button": "Zavřít",
"no_sequence_found": "Nebyla nalezena žádná sekvence",
"sequence_name": "název",
"sequence_photos": "Fotky",
"sequence_date": "Vyfoceno dne",
"sequence_creation": "nahrát",
"sequence_creation_tooltip": "Filtrujte podle data nahrání",
"sequence_date_tooltip": "Filtrujte podle data pořízení",
"reset_filter_button": "Resetujte filtry",
"filter_bbox_button": "Hledejte v této oblasti",
"sequence_status": "Postavení",
"sequence_published": "Publikováno",
"sequence_waiting": "Stále zpracovávám",
"sequence_hidden": "Skrytý",
"no_sequences_text": "Ještě nemáte zveřejněné žádné fotky 😢",
"button_upload": "Nahrajte obrázky",
"sequence_deleted": "Sekvence byla smazána"
},
"share_pictures": {
"title": "Proč přispívat na Panoramax?",
"description": "Přispívat do Panoramax znamená podílet se na vývoji geo-common, suverénního, bezplatného a opakovaně použitelného digitálního zdroje. Každou geolokalizovanou fotografii zveřejněnou na Panoramaxu může kdokoli použít k různým účelům, například místní úřad, který potřebuje sledovat stav svých silnic, nebo telekomunikační operátor k přípravě zásahu. Každý přispěvatel může posílat své obrazové sekvence, upravovat je a konzultovat je, stejně jako všechny pohledy 360° nebo ne přidané komunitou. Povinné rozmazání obličejů a SPZ je na platformě automatizované.",
"alt_img_map": "Ilustrace ženy při pohledu na mapu se svým geolokovaným smartphonem",
"card_photo1": "Místa viditelná z veřejné komunikace",
"card_photo2": "Fotografie zveřejněné ve formátu 360° nebo ne",
"card_photo3": "Snadno znovu použitelné fotografie",
"card_photo4": "Rychlý a snadný příspěvek k obrázku",
"card_alt_photo1": "Obrázek budovy",
"card_alt_photo2": "Obrázek ukazuje 360 stupňů",
"card_alt_photo3": "Obrázek zobrazující mapu s ukazatelem",
"card_alt_photo4": "Obrázek představující ukazatel",
"card_description1": "Všechny fotografie pořízené z veřejné komunikace jsou přijímány, pokud jsou geolokovány a pozorovány ze země.",
"card_description2": "360° snímky nejsou povinné: stačí pouze fotografie pořízené chytrým telefonem. Data, umístění a formát jpg jsou jedinými předpoklady.",
"card_description3": "Všechny fotografie jsou snadno dostupné a znovu použitelné bez účtu: přes webové stránky nebo standardní API (standard STAC).",
"card_description4": "Pro usnadnění příspěvků je k dispozici několik nástrojů, včetně příkazového řádku a webového rozhraní.",
"upload_subtitle": "Jednoduše nahrajte své obrázky online",
"upload_illustration_alt": "Ilustrace zobrazující online nahrávání fotografií",
"upload_description": "Webová aplikace Panoramax vám umožňuje nahrát všechny vaše terénní fotografie ve formátu JPEG jediným kliknutím na tlačítko. Nejsou vyžadovány žádné znalosti programování. Pro větší čísla však doporučujeme použít nástroj příkazového řádku",
"upload_button": "+ Nahrát obrázky",
"command_line_subtitle": "Nástroj příkazového řádku",
"comment_install": "Nainstalujte nástroj příkazového řádku geovisio",
"comment_upload": "LanceSpusťte příkaz pro nahrání obrázku ve vybrané složce",
"description_terminal": "<a href='https://gitlab.com/geovisio/cli' target='_blank' style='color:black'>CLI</a> vám umožňuje sdílet velké objemy fotografií. Postup je jednoduchý a vyžaduje <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>python (verze 3.8 nebo vyšší)</a>. Nástroj se před importem zeptá na vaše přihlašovací údaje. Po nahrání obrázků je před zveřejněním vyžadována doba zpracování.",
"terminal_install": "pip install geovisio_cli",
"terminal_text": "upload geovisio --api-url {url} <DOSSIER_PHOTOS>",
"button_copy": "kopírovat",
"information_subtitle": "Zde jsou vaše fotografie přístupné všem:",
"information_text1": "Automaticky rozmazané v souladu s legislativou.",
"information_text2": "Nahrané obrázky budou zveřejněny pod {word}",
"information_text3": "V původním formátu a rozlišení pro různé opětovné použití.",
"information_about_title": "Potřebujete přístup k obrázkům?",
"information_about_description": "Pro načtení všech metadat a obrázků je k dispozici API. <a href='{docLink}' target='_blank' style='color:#0a1f69'> Více informací naleznete zde</a> Data se také zobrazují ve tvaru <a href='{docTiles}' target=' _blank' style='color:#0a1f69'>vektorových dlaždic</a>",
"doc_subtitle": "Potřebujete pomoci s přispěním do Panoramax?",
"doc_description": "Dokumentace Panoramax je k dispozici u nás a výukové programy můžete získat na fóru geo-commons.",
"doc_button": "Viz dokumentace",
"doc_illustration_alt": "Ilustrace postavy s listem dokumentů"
},
"upload": {
"title": "Přispějte na projekt Panoramax",
"description": "Pro velké objemy obrázků je vhodnější nástroj příkazového řádku.",
"know_more_button": "Přečtěte si více",
"input_label": "Přetáhněte své obrázky sem nebo klikněte na",
"import_word": "nahrát",
"import_type": "Pouze formát JPEG",
"subtitle_import": "Nahrání obrázku",
"title_sequence": "Název sekvence",
"description_title_sequence": "Ve výchozím nastavení bude názvem sekvence datum dne. Pokud chcete, můžete zde upravit název.",
"text_import": "Zde nahrajte své soubory jpg. Každý obrázek nebo série obrázků tvoří \"sekvenci\". Poté je můžete najít v sekci „moje obrázky“ a zvolit, zda je chcete skrýt, zobrazit nebo odstranit.",
"subtitle_process": "Zpracování nahrávání",
"uploading_process": "Nahrávání probíhá...",
"uploading_cancel": "Zrušit odesílání fotografií",
"cancel_message": "⚠️ Upozorňujeme, že stahování bude přerušeno, pokud potvrdíte a sekvence bude smazána.",
"sequence_title": "Sekvence",
"import": "Nahrávání",
"upload_pending": "Nahrávání probíhá...",
"images_count_text": "Obrázky byly nahrány",
"no_img_text": "zatím nebyl nahrán žádný obrázek",
"upload_done": "Nahrání sekvence bylo dokončeno",
"sequence_link": "Ukažte tuto sekvenci",
"edit_title_tooltip": "Upravte název sekvence",
"edit_placeholder_input": "Upravte název sekvence",
"ok_button": "OK",
"pictures_error": "{count} obrázek se nepodařilo nahrát| {count} obrázků se nepodařilo nahrát",
"sequence_loading_information": "Po nahrání bude sekvence zpracována a poté zveřejněna na Panoramaxu (obvykle během několika minut).",
"sequence_loaded_information": "Sekvence byly nahrány a zpracovává se. Během pár minut by měl být veřejně dostupný na Panoramaxu.",
"leave_message": "⚠️ VAROVÁNÍ, pokud stránku opustíte před koncem, stahování se přeruší.",
"error_button": "Zobrazit chyby",
"modal_error_title": "Chybné obrázky"
},
"ay11": {
"title": "Déclaration d'accessibilité",
"date": "Établie le 18 září 2023.",
"introduction": "Služba IGN je přístupná, v souladu s článkem 47 ze zákona č. 2005-102 z 11. února 2005. Prohlášení o přístupu k aplikaci pro Panoramax Instance IGN.ign: https://panora. fr",
"subtitle_conformity": "État de conformité",
"conformity_text": "Instance Panoramax IGN není konformní průměr",
"conformity_text2": "Le site n'a encore pas été audité.",
"subtitle_conformity2": "Nepřístupný obsah",
"subtitle_increase": "Améliorace a kontakt",
"increase_text": "Pokud se k vám připojíte a připojíte se k servisu, je odpovědný kontaktér společnosti Panoramax Instance IGN nalézající se v jiné orientaci nebo alternativě, která je přístupná nebo získaná v souladu s vlastní formou.",
"phone": "Telefon: +33 14 398 84 61",
"email_text": "E-mailem :",
"email": "signalement.ign@panoramax.fr",
"address": "Adresa: IGN, Saint-Mandé",
"increase_info": "Nous essayons de répondre dans les 5 jours ouvrés.",
"subtitle_to_do": "Voie de recours",
"to_do_text": "Postup uživatele v daném případu : vous avez signalé au odpovědný z webu internet a défaut d'accessibilité qui vous empêche d'accessider à un contenu ou à un des services du satiss a vous obtenu.n'a pasis vous pouvez:",
"write_message": "Écrire un message au",
"defenseur_droits": "Défenseur des droits",
"contact": "Kontaktní osoba",
"contact_text": "le délégué du Défenseur des droits dans votre région",
"send_letter": "Envoyer un courier par la poste (gratuit, ne pas mettre de timbre): Défenseur des droits Libre réponse 71120 75342 Paris CEDEX 07",
"end": "Cette déclaration d'accessibilité a été créé le 18. září 2023 grâce au",
"generator_betagouv": "Générateur de Déclaration d'Accessibilité de BetaGouv"
}
}
}

1
src/locales/de.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -1,67 +1,96 @@
{
"general": {
"title": "Panoramax instance",
"title": "Panoramax Instance",
"meta": {
"title": "Panoramax instance",
"title": "Panoramax Instance",
"description": "Panoramax, the free alternative to photo-mapping territories"
},
"header": {
"login_text": "Connect",
"login_text": "Login",
"register_text": "Register",
"contribute_text": "Why contribute ?",
"my_account": "My account",
"upload_text": "+ Share pictures",
"sequences_text": "My pictures",
"contribute_text": "Why Contribute?",
"viewer": "Viewer",
"my_account": "My Account",
"upload_text": "+ Share Pictures",
"sequences_text": "My Pictures",
"alt_logo": "Instance logo",
"alt_photos": "Pictures icon",
"alt_information": "User icon",
"alt_settings": "Parameters icon",
"alt_settings": "Settings icon",
"alt_logout": "Logout icon",
"title": "Panoramax",
"beta_text": "Beta version",
"beta_text": "Beta Version",
"logout_text": "Logout",
"my_information_text": "My details",
"my_settings_text": "My parameters",
"my_settings_text": "My Settings",
"burger_menu_aria_label_open": "Show menu",
"burger_menu_aria_label_closed": "Hide menu"
},
"footer": {
"panoramax_site": "Discover Panoramax",
"information_gitlab": "Show source code",
"gitlab_logo": "Gitlab logo",
"gitlab_logo": "GitLab logo",
"ay11_text": "Accessibility: not compliant"
},
"error_text": "An error occured",
"success_text": "Update done"
"success_text": "Update complete"
},
"pages": {
"home": {
"report_mail": "?subject=⚠️ Report on picture {picId}&body=HEllo, %0D%0A%0D%0A Problem on image (keep type of problem reported) : %0D%0A%0D%0A %0D%0A%0D%0A inappropriate content / lack of blurring on an element to be anonymized or blurred for security reasons / overblurring (too much blurring) %0D%0A%0D%0A Link to affected photo : {link} %0D%0A%0D%0A Details of affected elements (especially for blurring problems - what should be blurred or unblurred?) :",
"report_button_text": "Report this picture"
"report_mail": "?subject=⚠️ Report on picture {picId}&body=Hello, %0D%0A%0D%0A Problem on image (keep type of problem reported): %0D%0A%0D%0A %0D%0A%0D%0A inappropriate content / lack of blurring on an element to be anonymized or blurred for security reasons / overblurring (too much blurring) %0D%0A%0D%0A Link to affected photo: {link} %0D%0A%0D%0A Details of affected elements (especially for blurring problems - what should be blurred or unblurred?):",
"report_button_text": "Report this picture",
"sequence_title": "See the sequence",
"open_fullscreen": "Fullscreen mode",
"close_fullscreen": "Normal mode"
},
"settings": {
"title": "My tokens",
"title": "My Tokens",
"setting_tooltip": "Show or hide token"
},
"sequence": {
"sequence_published": "Published",
"sequence_waiting": "Still processing",
"sequence_waiting": "Processing",
"sequence_hidden": "Hidden",
"sequence_form_title": "Edit the title",
"sequence_tutorial_text": "⚠ File selection has been canceled. If this was not intentional, it is possible that your smartphone does not handle sending photos correctly.\nGoing through “My Files” (see image below) to select photos might resolve this issue.",
"sequence_tutorial_exif_text": "If the photo upload errors are related to geolocation, please make sure « Location Tags » is enabled on your phone's camera (see images below).",
"hide_sequence_tooltip": "Hide this sequences",
"back_button": "Back to my sequence list",
"delete_sequence_tooltip": "Permanently delete this sequence",
"hide_photo_tooltip": "Hide selected pictures",
"delete_photo_tooltip": "Permanently delete selected pictures",
"confirm_pictures_dialog": "⚠️ Selected photos will be permanently deleted",
"confirm_sequence_dialog": "⚠️ This sequence will be permanently deleted",
"info_msg_maj": "Use the SHIFT key to select many pictures",
"conf_pic_msg": "⚠️ Selected photos will be permanently deleted",
"conf_sequence_msg": "⚠️ This sequence will be permanently deleted",
"button_panel_photos": "Manage pictures",
"button_panel_orientation": "Set orientation",
"button_panel_sort": "Sort sequence",
"orientation_panel_title": "Adjusting the orientation of all photos in the sequence",
"orientation_panel_tooltip": "Drag the blue box in the desired direction\"",
"orientation_input_label": "or change the angle here",
"orientation_input_placeholder": "Value between -180 and 180",
"orientation_input_error_value": "Value must be between -180 and 180",
"orientation_panel_button": "Validate position",
"orientation_updated": "Orientation updated",
"sort_updated": "Sequence sorted",
"sort_panel_title": "Sequence sort setting",
"sort_panel_settings": "Sort sequence by:",
"sort_panel_settings_order": "Order :",
"sort_panel_settings_order_increase": "Ascending",
"sort_panel_settings_order_decrease": "Decreasing",
"sort_panel_check_gps": "GPS Date",
"sort_panel_check_file": "File date",
"sort_panel_check_name": "File name",
"sort_panel_button": "Validate sort",
"created": "Uploaded :",
"taken": "Shot on :",
"duration": "Duration :",
"duration_begin": "Start :",
"duration_end": "End :",
"camera": "Camera :",
"button_delete": "Delete",
"button_disable": "Hide",
"button_enable": "Show",
"button_delete": "Delete the sequence",
"button_disable": "Hide the sequence",
"button_enable": "Show the sequence",
"picture_selected": "{count} picture selected| {count} pictures selected",
"hours": "{count} hour| {count} hours",
"minutes": "{count} minute| {count} minutes",
@@ -75,15 +104,29 @@
},
"sequences": {
"title": "My sequences",
"filter_date_upload_title": "Filter by upload date :",
"filter_date_title": "Filter by shooting date :",
"radio_date_placeholder": "03/01/2024",
"radio_date": "date",
"hide_button": "Hide",
"show_button": "Show",
"delete_button": "Delete",
"filter_date_reset_button": "Reset",
"filter_date_close_button": "Close",
"no_sequence_found": "No sequence found",
"sequence_name": "Name",
"sequence_photos": "Photos",
"sequence_date": "Shot on",
"sequence_creation": "Upload",
"sequence_creation_tooltip": "Filter by uploaded date",
"sequence_date_tooltip": "Filter by shooting date",
"reset_filter_button": "Reset the filters",
"filter_bbox_button": "Search on this area",
"sequence_status": "Status",
"sequence_published": "Published",
"sequence_waiting": "Still processing",
"sequence_waiting": "Processing",
"sequence_hidden": "Hidden",
"no_sequences_text": "You have no photos published yet \uD83D\uDE22",
"no_sequences_text": "You have no photos published yet 😢",
"button_upload": "Upload pictures",
"sequence_deleted": "The sequence has been deleted"
},
@@ -96,7 +139,7 @@
"card_photo3": "Easily reusable photos",
"card_photo4": "A quick and easy image contribution",
"card_alt_photo1": "Image of a building",
"card_alt_photo2": "Image showing 360-degree ",
"card_alt_photo2": "Image showing 360-degree",
"card_alt_photo3": "Image showing a map with a pointer",
"card_alt_photo4": "Image representing a pointer",
"card_description1": "All photos taken from the public highway are accepted, as long as they are geolocated and viewed from the ground.",
@@ -109,10 +152,10 @@
"upload_button": "+ Upload pictures",
"command_line_subtitle": "Command line tool",
"comment_install": "Install the geovisio command-line tool",
"comment_upload": "LanceStart the image upload command on the chosen folder",
"description_terminal": "<a href='https://gitlab.com/geovisio/cli' target='_blank' style='color:black'>The CLI</a> lets you share large volumes of photos. The procedure is simple and requires <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>python (version 3.8 or above)</a>.\n\nThe tool will ask for your login details before importing. Once the pictures have been uploaded, a processing time is required before publication.",
"comment_upload": "Start the image upload command on the chosen folder",
"description_terminal": "<a href='https://gitlab.com/panoramax/clients/cli' target='_blank' style='color:black'>The CLI</a> lets you share large volumes of photos. The procedure is simple and requires <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>python (version 3.8 or above)</a>.\n\nThe tool will ask for your login details before importing. Once the pictures have been uploaded, a processing time is required before publication.",
"terminal_install": "pip install geovisio_cli",
"terminal_text": "geovisio upload --api-url {url} <DOSSIER_PHOTOS>",
"terminal_text": "geovisio upload --api-url {url} <PHOTOS_FOLDER>",
"button_copy": "Copy",
"information_subtitle": "Here, your photos are accessible to all : ",
"information_text1": "Automatically blurred in compliance with legislation.",
@@ -133,19 +176,24 @@
"import_word": "upload",
"import_type": "JPEG format only",
"subtitle_import": "Picture upload",
"title_sequence": "Séquence title",
"title_sequence": "Sequence title",
"description_title_sequence": "By default, the sequence title will be the date of the day. You can, if you want, edit the title here.",
"text_import": "Upload your jpg files here. Each picture or series of pictures constitutes a \"sequence\". You can then find them in the \"my pictures\" section and choose to hide, show or delete them.",
"subtitle_process": "Upload processing",
"uploading_process": "Upload in progress...",
"uploading_process": "Upload in progress",
"uploading_cancel": "Cancel sending photos",
"cancel_message": "⚠️ Please note, the download will be interrupted if you validate and the sequence will be deleted.",
"sequence_title": "Sequence ",
"error_upload": "Sequence creation error",
"error_upload_img": "An error has occurred",
"import": "Uploads",
"upload_pending": "Upload in progress...",
"upload_pending": "Upload in progress",
"images_count_text": "Pictures uploaded",
"no_img_text": "no picture upload so far",
"upload_done": "Sequence upload done",
"upload_other_files": "{count} photo in wrong format was removed from the list| {count} wrong format photos have been removed from the list",
"sequence_link": "Show this sequence",
"edit_title_tooltip": "Edit the sequence's title",
"edit_title": "Edit the title",
"edit_placeholder_input": "Edit the sequence's title",
"ok_button": "OK",
"pictures_error": "{count} picture could not be uploaded| {count} pictures could not be uploaded",
@@ -156,29 +204,29 @@
"modal_error_title": "Pictures in error"
},
"ay11": {
"title": "Déclaration daccessibilité",
"date": "Établie le 18 septembre 2023.",
"introduction": "IGN sengage à rendre son service accessible, conformément à larticle 47 de la loi n° 2005-102 du 11 février 2005.\n Cette déclaration daccessibilité sapplique à Panoramax Instance IGN : https://panoramax.ign.fr",
"subtitle_conformity": "État de conformité",
"conformity_text": "Panoramax Instance IGN est non conforme avec le ",
"conformity_text2": "Le site na encore pas été audité.",
"subtitle_conformity2": "Contenus non accessibles",
"subtitle_increase": "Amélioration et contact",
"title": "Accessibility Statement",
"date": "Issued on September 18, 2023.",
"introduction": "IGN aims to make its services accessible, in accordance with article 47 of Act No. 2005-102 on 11 February 2005.\nThis accessibility statement applies to Panoramax Instance IGN: https://panoramax.ign.fr",
"subtitle_conformity": "Compliance status",
"conformity_text": "Panoramax Instance IGN is non-compliant with the ",
"conformity_text2": "The site has not yet been audited.",
"subtitle_conformity2": "Content not accessible",
"subtitle_increase": "Improvement and contact",
"increase_text": "Si vous narrivez pas à accéder à un contenu ou à un service, vous pouvez\n contacter le responsable de Panoramax Instance IGN pour être orienté vers une alternative accessible ou obtenir le contenu sous une autre forme.",
"phone": "Téléphone : +33 14 398 84 61",
"email_text": "E-mail :",
"phone": "Telephone: +33 14 398 84 61",
"email_text": "E-mail:",
"email": "signalement.ign@panoramax.fr",
"address": "Adresse : IGN, Saint-Mandé",
"increase_info": "Nous essayons de répondre dans les 5 jours ouvrés.",
"address": "Address: IGN, Saint-Mandé",
"increase_info": "We try to respond within 5 business days.",
"subtitle_to_do": "Voie de recours",
"to_do_text": "Cette procédure est à utiliser dans le cas suivant : vous avez signalé au responsable du site internet un défaut daccessibilité qui vous\n empêche daccéder à un contenu ou à un des services du portail et vous navez pas obtenu de réponse satisfaisante. \n vous pouvez :",
"write_message": "Écrire un message au",
"defenseur_droits": "Défenseur des droits",
"contact": "Contacter",
"contact_text": "le délégué du Défenseur des droits dans votre région",
"write_message": "Write a message to",
"defenseur_droits": "Defender of Rights",
"contact": "Contact",
"contact_text": "the delegate of the Defender of Rights in your region",
"send_letter": "Envoyer un courrier par la poste (gratuit, ne pas mettre de\n timbre):\n Défenseur des droits\n Libre réponse 71120 75342 Paris CEDEX 07",
"end": "Cette déclaration daccessibilité a été créé le\n 18 septembre 2023 grâce au",
"generator_betagouv": "Générateur de Déclaration dAccessibilité de BetaGouv"
"generator_betagouv": "BetaGouv Accessibility Statement Generator"
}
}
}

View File

@@ -1,184 +1,232 @@
{
"general": {
"title": "Instance Panoramax",
"meta": {
"title": "Instance Panoramax",
"description": "Panoramax, lalternative libre pour photo-cartographier les territoires"
"general": {
"title": "Instance Panoramax",
"meta": {
"title": "Instance Panoramax",
"description": "Panoramax, lalternative libre pour photo-cartographier les territoires"
},
"header": {
"login_text": "Connexion",
"register_text": "Inscription",
"contribute_text": "Pourquoi contribuer?",
"viewer": "Visionneuse",
"my_account": "Mon compte",
"upload_text": "+ Partager des photos",
"sequences_text": "Mes photos",
"alt_logo": "Logo de l'instance",
"alt_photos": "Icone représentant des photos",
"alt_information": "Icone représentant un utilisateur",
"alt_settings": "Icône des paramètres",
"alt_logout": "Icone représentant un bouton de déconnexion",
"title": "Panoramax",
"beta_text": "Version bêta",
"logout_text": "Déconnexion",
"my_information_text": "Mes informations",
"my_settings_text": "Mes paramètres",
"burger_menu_aria_label_open": "Afficher le menu",
"burger_menu_aria_label_closed": "Masquer le menu"
},
"footer": {
"panoramax_site": "Découvrir Panoramax",
"information_gitlab": "Voir le code",
"gitlab_logo": "Logo GitLab",
"ay11_text": "Accessibilité : non conforme"
},
"error_text": "Une erreur est survenue",
"success_text": "Mise à jour réussie"
},
"header": {
"login_text": "Connexion",
"register_text": "Inscription",
"contribute_text": "Pourquoi contribuer ?",
"my_account": "Mon compte",
"upload_text": "+ Partager des photos",
"sequences_text": "Mes photos",
"alt_logo": "Logo de l'instance",
"alt_photos": "Icone représentant des photos",
"alt_information": "Icone représentant un utilisateur",
"alt_settings": "Icone des paramètres",
"alt_logout": "Icone représentant un bouton de déconnexion",
"title": "Panoramax",
"beta_text": "Version beta",
"logout_text": "Déconnexion",
"my_information_text": "Mes informations",
"my_settings_text": "Mes paramètres",
"burger_menu_aria_label_open": "Afficher le menu",
"burger_menu_aria_label_closed": "Masquer le menu"
},
"footer": {
"panoramax_site": "Découvrir Panoramax",
"information_gitlab": "Voir le code",
"gitlab_logo": "Logo Gitlab",
"ay11_text": "Accessibilité : non conforme"
},
"error_text": "Une erreur est survenue",
"success_text": "Mise à jour réussie"
},
"pages": {
"home": {
"report_mail": "?subject=⚠️ Signalement sur l`image {picId}&body=Bonjour, %0D%0A%0D%0A Problème sur l`image (garder le type de problème signalé) : %0D%0A%0D%0A contenu inapproprié / absence de floutage sur un élément à anonymiser ou flouter pour des raisons de sécurité /surfloutage (floutage en trop) %0D%0A%0D%0A Lien vers la photo concernée : {link} %0D%0A%0D%0A Précision sur les éléments concernés (en particulier pour les problèmes de floutage - que faut-il flouter ou déflouter?) :",
"report_button_text": "Signaler la photo"
},
"settings": {
"title": "Mes Tokens",
"setting_tooltip": "Afficher ou masquer le token"
},
"sequence": {
"sequence_published": "Publiée",
"sequence_waiting": "En cours de publication",
"sequence_hidden": "Masquée",
"hide_sequence_tooltip": "Masque la séquence sur la carte",
"delete_sequence_tooltip": "Supprime définitivement la séquence",
"hide_photo_tooltip": "Masque les photos sur la carte",
"delete_photo_tooltip": "Supprime définitivement les photos",
"confirm_pictures_dialog": "⚠️ Les photos sélectionnées vont être définitivement supprimées",
"confirm_sequence_dialog": "⚠️ La séquence va être définitivement supprimée",
"created": "Versement :",
"taken": "Prise de vue :",
"duration": "Durée :",
"duration_begin": "Début :",
"duration_end": "Fin :",
"camera": "Matériel :",
"button_delete": "Supprimer",
"button_disable": "Masquer",
"button_enable": "Afficher",
"picture_selected": "{count} photo sélectionnée| {count} photos sélectionnées",
"hours": "{count} heure| {count} heures",
"minutes": "{count} minute| {count} minutes",
"seconds": "{count} seconde| {count} secondes",
"select_text": "Tout sélectionner",
"unselect_text": "Tout désélectionner",
"select_shift_text": "Sélectionnez plusieurs photos avec shift",
"waiting_process": "Photo en cours de traitement",
"broken": "Traitement de la photo en erreur",
"no_image": "Aucune photo dans cette séquence"
},
"sequences": {
"title": "Mes séquences de photos",
"sequence_name": "Nom",
"sequence_photos": "Photos",
"sequence_date": "Prise de vue",
"sequence_creation": "Versement",
"sequence_status": "Statut",
"sequence_published": "Publiée",
"sequence_waiting": "En cours de publication",
"sequence_hidden": "Masquée",
"no_sequences_text": "Vous n'avez pas encore de photos publiées \uD83D\uDE22",
"button_upload": "Partager vos photos",
"sequence_deleted": "La séquence a bien été supprimée"
},
"share_pictures": {
"title": "Pourquoi contribuer à la base de photographies de Panoramax ?",
"description": "Contribuer à Panoramax, c'est participer au développement d'un géo-commun, une ressource numérique souveraine, libre et réutilisable. Chaque photo géolocalisée publiée sur Panoramax est utilisable par tous et pour des usages variés, par exemple par une collectivité territoriale qui a besoin d'observer létat de la voirie ou par un opérateur de télécommunications pour préparer une intervention.\n\nChaque contributeur peut envoyer ses séquences d'images, les modifier et les consulter tout comme l'ensemble des vues - 360° ou non - versées par la communauté. Le floutage obligatoire des visages et plaques d'immatriculation est automatisé sur la plateforme.",
"alt_img_map": "Illustration d'une femme qui regarde une carte avec son smartphone geolocalisé",
"card_photo1": "Des lieux visibles depuis la voie publique",
"card_photo2": "Des photos publiées au format 360° ou non",
"card_photo3": "Des photos facilement réutilisables",
"card_photo4": "Une contribution en images facile et rapide",
"card_alt_photo1": "Image qui représente un immeuble",
"card_alt_photo2": "Image qui représente des photos 360 degrés",
"card_alt_photo3": "Image qui représente une carte avec un pointeur",
"card_alt_photo4": "Image qui représente un pointeur",
"card_description1": "Toutes les photos prises depuis la voie publique sont concernées, dès lors qu'elles sont géolocalisées et vues du sol.",
"card_description2": "Le format 360° n'est pas obligatoire : des photos prises via un smartphone suffisent, dates, lieux et format jpg sont les seuls pre-requis.",
"card_description3": "Toutes les photos facilement accessible et réutilisables sans compte : via le site web ou une API standard (format STAC).",
"card_description4": "Plusieurs outils sont mis à disposition pour faciliter les contributions dont une ligne de commande et une interface web.",
"upload_subtitle": "Chargez vos images simplement en ligne",
"upload_illustration_alt": "Illustration qui représente l'envoie de photo en ligne",
"upload_description": "L'application web de Panoramax vous permet de déposer toutes vos photos de terrain au format jpg d'un simple clic. Aucune notion de programmation n'est nécessaire. Pour les envois en grand nombre, il est toutefois conseillé de faire appel à l'outil en ligne de commande",
"upload_button": "+ Partager des images",
"command_line_subtitle": "L'outil en ligne de commande",
"comment_install": "Installer loutil en ligne de commande geovisio",
"comment_upload": "Lancez la commande de versement dimages sur le dossier choisi",
"description_terminal": "<a href='https://gitlab.com/geovisio/cli' target='_blank' style='color:black'>L'outil en ligne de commande</a> vous permet de partager de grands volumes de photos. La procédure est simple et vous devez disposer <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>de python (au moins la version 3.8)</a>.\n\nLoutil demandera vos informations de connexion avant l'import. Une fois les données chargées, un temps de traitement est nécessaire pour les rendre disponibles.",
"terminal_install": "pip install geovisio_cli",
"terminal_text": "geovisio upload --api-url {url} <DOSSIER_PHOTOS>",
"button_copy": "Copier",
"information_subtitle": "Ici, vos photos sont accessibles à tous : ",
"information_text1": "Automatiquement floutées dans le respect de la législation.",
"information_text2": "Les données déposées seront publiées sous {word}",
"information_text3": "Sous forme «brute» pour des réutilisations variées (ex: préparation des chantiers).",
"information_about_title": "Besoin de récupérer les données ?",
"information_about_description": "Une API est mise à disposition pour récupérer toutes les données. <a href='{docLink}' target='_blank' style='color:#0a1f69'>\nRetrouvez sa documentation ici</a>\nLes données sont aussi exposées sous forme <a href='{docTiles}' target='_blank' style='color:#0a1f69'>de tuiles vectorielles</a>",
"doc_subtitle": "Besoin d'aide pour contribuer à Panoramax ?",
"doc_description": "Nous mettons à disposition lensemble de la documentation relative à Panoramax et vous pouvez accéder aux tutoriels sur le forum des géo-communs.",
"doc_button": "Voir la documentation",
"doc_illustration_alt": "Illustration représentant un personnage avec un feuillet de documents"
},
"upload": {
"title": "Contribuez à la base de photographies de Panoramax",
"description": "Pour le versement d'un grand volume de photographies, l'outil en ligne de commande est plus adapté.",
"know_more_button": "En savoir plus",
"input_label": "Glissez vos images ici ou cliquez sur ",
"import_word": "importer",
"import_type": "Format JPEG uniquement",
"subtitle_import": "Dépôt des images",
"title_sequence": "Titre de ma séquence",
"description_title_sequence": "Le titre d'une séquence est par défaut la date du jour. Vous pouvez, si vous le souhaitez le modifier ci-dessous.",
"text_import": "Déposez ici vos fichiers jpg. Chaque image ou série dimages constitue une « séquence ». Vous pourrez ensuite les retrouver dans la section « mes images » et choisir de les masquer, les afficher ou les supprimer.",
"subtitle_process": "Traitements de l'import",
"uploading_process": "Envoi en cours...",
"sequence_title": "Séquence du ",
"import": "Imports",
"upload_pending": "Transfert en cours...",
"images_count_text": "Images chargées",
"no_img_text": "aucune image chargée actuellement",
"upload_done": "Le chargement de la séquence est terminé",
"sequence_link": "Accéder à cette séquence",
"edit_title_tooltip": "Modifier le titre de la séquence",
"edit_placeholder_input": "Modifier le titre de la séquence",
"ok_button": "Valider",
"pictures_error": "{count} image n'a pas pu être chargée| {count} images n'ont pas pu être chargées",
"sequence_loading_information": "Une fois chargée, la séquence sera en traitement et accessible sur Panoramax dans les prochaines minutes.",
"sequence_loaded_information": "La séquence est chargée et est en cours de traitement. Elle sera accessible sur Panoramax dans quelques minutes.",
"leave_message": "⚠️ Attention, le téléchargement sera interrompu si vous quittez la page avant la fin.",
"error_button": "Afficher les erreurs",
"modal_error_title": "Liste des photos non chargés"
},
"ay11": {
"title": "Déclaration daccessibilité",
"date": "Établie le 18 septembre 2023.",
"introduction": "IGN sengage à rendre son service accessible, conformément à larticle 47 de la loi n° 2005-102 du 11 février 2005.\n Cette déclaration daccessibilité sapplique à Panoramax Instance IGN : https://panoramax.ign.fr",
"subtitle_conformity": "État de conformité",
"conformity_text": "Panoramax Instance IGN est non conforme avec le ",
"conformity_text2": "Le site na encore pas été audité.",
"subtitle_conformity2": "Contenus non accessibles",
"subtitle_increase": "Amélioration et contact",
"increase_text": "Si vous narrivez pas à accéder à un contenu ou à un service, vous pouvez\n contacter le responsable de Panoramax Instance IGN pour être orienté vers une alternative accessible ou obtenir le contenu sous une autre forme.",
"phone": "Téléphone : +33 14 398 84 61",
"email_text": "E-mail :",
"email": "signalement.ign@panoramax.fr",
"address": "Adresse : IGN, Saint-Mandé",
"increase_info": "Nous essayons de répondre dans les 5 jours ouvrés.",
"subtitle_to_do": "Voie de recours",
"to_do_text": "Cette procédure est à utiliser dans le cas suivant : vous avez signalé au responsable du site internet un défaut daccessibilité qui vous\n empêche daccéder à un contenu ou à un des services du portail et vous navez pas obtenu de réponse satisfaisante. \n vous pouvez :",
"write_message": "Écrire un message au",
"defenseur_droits": "Défenseur des droits",
"contact": "Contacter",
"contact_text": "le délégué du Défenseur des droits dans votre région",
"send_letter": "Envoyer un courrier par la poste (gratuit, ne pas mettre de\n timbre):\n Défenseur des droits\n Libre réponse 71120 75342 Paris CEDEX 07",
"end": "Cette déclaration daccessibilité a été créé le\n 18 septembre 2023 grâce au",
"generator_betagouv": "Générateur de Déclaration dAccessibilité de BetaGouv"
"pages": {
"home": {
"report_mail": "?subject=⚠️ Signalement sur l'image {picId}&body=Bonjour, %0D%0A%0D%0A Problème sur l'image (garder le type de problème signalé): %0D%0A%0D%0A contenu inapproprié / absence de floutage sur un élément à anonymiser ou flouter pour des raisons de sécurité /surfloutage (floutage en trop) %0D%0A%0D%0A Lien vers la photo concernée: {link} %0D%0A%0D%0A Précision sur les éléments concernés (en particulier pour les problèmes de floutage - que faut-il flouter ou déflouter?):",
"report_button_text": "Signaler la photo",
"sequence_title": "Voir la séquence",
"open_fullscreen": "Mode plein écran",
"close_fullscreen": "Mode normal"
},
"settings": {
"title": "Mes jetons",
"setting_tooltip": "Afficher ou masquer le token"
},
"sequence": {
"sequence_published": "Publiée",
"sequence_waiting": "En cours de publication",
"sequence_hidden": "Masquée",
"sequence_form_title": "Modifier le titre",
"sequence_tutorial_text": "⚠ La sélection des fichiers a été annulée. Si ce n'était pas intentionnel, il est possible que votre smartphone gère mal l'envoi de photos.\nPasser par « Mes fichiers » (voir l'image ci-dessous) pour sélectionner les photos pourrait résoudre ce problème.",
"sequence_tutorial_exif_text": "Si les erreurs de téléchargement de photos concernent la géolocalisation, veuillez vous assurer que l'option « Tags de localisation » est bien activée sur l'appareil photo de votre téléphone (voir les images ci-dessous).",
"hide_sequence_tooltip": "Masque la séquence sur la carte",
"back_button": "Retourner à la liste de mes séquences",
"delete_sequence_tooltip": "Supprime définitivement la séquence",
"hide_photo_tooltip": "Masque les photos sur la carte",
"delete_photo_tooltip": "Supprime définitivement les photos",
"info_msg_maj": "Utilisez la touche MAJ pour sélectionner plusieurs photos",
"conf_pic_msg": "⚠️ Les photos sélectionnées vont être définitivement supprimées",
"conf_sequence_msg": "⚠️ La séquence va être définitivement supprimée",
"button_panel_photos": "Gérer les photos",
"button_panel_orientation": "Régler l'orientation",
"button_panel_sort": "Trier la séquence",
"orientation_panel_title": "Définir l'orientation de la caméra sur le véhicule",
"orientation_panel_tooltip": "Faites glisser la zone bleu dans la direction souhaitée",
"orientation_input_label": "ou modifiez l'angle ici",
"orientation_input_placeholder": "Valeur entre -180 et 180",
"orientation_input_error_value": "La valeur doit être entre -180 et 180",
"orientation_panel_button": "Valider la position",
"orientation_updated": "L'orientation a bien été modifiée",
"sort_updated": "La séquence a bien triée",
"sort_panel_title": "Réglage du tri de la séquence",
"sort_panel_settings": "Trier la séquence par :",
"sort_panel_settings_order": "Ordre :",
"sort_panel_settings_order_increase": "Croissant",
"sort_panel_settings_order_decrease": "Décroissant",
"sort_panel_check_gps": "Date du GPS",
"sort_panel_check_file": "Date de la caméra",
"sort_panel_check_name": "Nom du fichier",
"sort_panel_button": "Valider le tri",
"created": "Versement :",
"taken": "Prise de vue :",
"duration": "Durée :",
"duration_begin": "Début :",
"duration_end": "Fin :",
"camera": "Matériel :",
"button_delete": "Supprimer la séquence",
"button_disable": "Masquer la séquence",
"button_enable": "Afficher la séquence",
"picture_selected": "{count} photo sélectionnée| {count} photos sélectionnées",
"hours": "{count} heure| {count} heures",
"minutes": "{count} minute| {count} minutes",
"seconds": "{count} seconde| {count} secondes",
"select_text": "Tout sélectionner",
"unselect_text": "Tout sélectionner",
"select_shift_text": "Sélectionnez plusieurs photos avec shift",
"waiting_process": "Photo en cours de traitement",
"broken": "Traitement de la photo en erreur",
"no_image": "Aucune photo dans cette séquence"
},
"sequences": {
"title": "Mes séquences de photos",
"filter_date_upload_title": "Filtrer par date de versement:",
"filter_date_title": "Filtrer par date de prise de vue :",
"radio_date_placeholder": "2024-01-03",
"radio_date": "date",
"hide_button": "Masquer",
"show_button": "Afficher",
"delete_button": "Supprimer",
"filter_date_reset_button": "Réinitialiser",
"filter_date_close_button": "Fermer",
"no_sequence_found": "Aucune séquence trouvée",
"sequence_name": "Nom",
"sequence_photos": "Photos",
"sequence_date": "Prise de vue",
"sequence_creation": "Versement",
"sequence_creation_tooltip": "Filtre par date de versement",
"sequence_date_tooltip": "Filtre par date de prise de vue",
"reset_filter_button": "Réinitialiser les filtres",
"filter_bbox_button": "Chercher dans cette zone",
"sequence_status": "Statut",
"sequence_published": "Publiée",
"sequence_waiting": "En cours de publication",
"sequence_hidden": "Masquée",
"no_sequences_text": "Vous n'avez pas encore de photos publiées 😢",
"button_upload": "Partager vos photos",
"sequence_deleted": "La séquence a bien été supprimée"
},
"share_pictures": {
"title": "Pourquoi contribuer à la base de photographies de Panoramax ?",
"description": "Contribuer à Panoramax, c'est participer au développement d'un géo-commun, une ressource numérique souveraine, libre et réutilisable. Chaque photo géolocalisée publiée sur Panoramax est utilisable par tous et pour des usages variés, par exemple par une collectivité territoriale qui a besoin d'observer létat de la voirie ou par un opérateur de télécommunications pour préparer une intervention.\n\nChaque contributeur peut envoyer ses séquences d'images, les modifier et les consulter tout comme l'ensemble des vues - 360° ou non - versées par la communauté. Le floutage obligatoire des visages et plaques d'immatriculation est automatisé sur la plateforme.",
"alt_img_map": "Illustration d'une femme qui regarde une carte avec son smartphone geolocalisé",
"card_photo1": "Des lieux visibles depuis la voie publique",
"card_photo2": "Des photos publiées au format 360° ou non",
"card_photo3": "Des photos facilement réutilisables",
"card_photo4": "Une contribution en images facile et rapide",
"card_alt_photo1": "Image qui représente un immeuble",
"card_alt_photo2": "Image qui représente des photos 360 degrés",
"card_alt_photo3": "Image qui représente une carte avec un pointeur",
"card_alt_photo4": "Image qui représente un pointeur",
"card_description1": "Toutes les photos prises depuis la voie publique sont concernées, dès lors qu'elles sont géolocalisées et vues du sol.",
"card_description2": "Le format 360° n'est pas obligatoire : des photos prises via un smartphone suffisent, dates, lieux et format jpg sont les seuls pre-requis.",
"card_description3": "Toutes les photos facilement accessible et réutilisables sans compte : via le site web ou une API standard (format STAC).",
"card_description4": "Plusieurs outils sont mis à disposition pour faciliter les contributions dont une ligne de commande et une interface web.",
"upload_subtitle": "Chargez vos images simplement en ligne",
"upload_illustration_alt": "Illustration qui représente l'envoie de photo en ligne",
"upload_description": "L'application web de Panoramax vous permet de déposer toutes vos photos de terrain au format jpg d'un simple clic. Aucune notion de programmation n'est nécessaire. Pour les envois en grand nombre, il est toutefois conseillé de faire appel à l'outil en ligne de commande",
"upload_button": "+ Partager des photos",
"command_line_subtitle": "L'outil en ligne de commande",
"comment_install": "Installer loutil en ligne de commande geovisio",
"comment_upload": "Lancez la commande de versement dimages sur le dossier choisi",
"description_terminal": "<a href='https://gitlab.com/panoramax/clients/cli' target='_blank' style='color:black'>L'outil en ligne de commande</a> vous permet de partager de grands volumes de photos. La procédure est simple et vous devez disposer <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>de python (au moins la version 3.8)</a>.\n\nLoutil demandera vos informations de connexion avant l'import. Une fois les données chargées, un temps de traitement est nécessaire pour les rendre disponibles.",
"terminal_install": "pip install geovisio_cli",
"terminal_text": "geovisio upload --api-url {url} <DOSSIER_PHOTOS>",
"button_copy": "Copier",
"information_subtitle": "Ici, vos photos sont accessibles à tous : ",
"information_text1": "Automatiquement floutées dans le respect de la législation.",
"information_text2": "Les données déposées seront publiées sous {word}",
"information_text3": "Sous forme «brute» pour des réutilisations variées (ex: préparation des chantiers).",
"information_about_title": "Besoin de récupérer les données ?",
"information_about_description": "Une API est mise à disposition pour récupérer toutes les données. <a href='{docLink}' target='_blank' style='color:#0a1f69'>\nRetrouvez sa documentation ici</a>\nLes données sont aussi exposées sous forme <a href='{docTiles}' target='_blank' style='color:#0a1f69'>de tuiles vectorielles</a>",
"doc_subtitle": "Besoin d'aide pour contribuer à Panoramax ?",
"doc_description": "Nous mettons à disposition lensemble de la documentation relative à Panoramax et vous pouvez accéder aux tutoriels sur le forum des géo-communs.",
"doc_button": "Voir la documentation",
"doc_illustration_alt": "Illustration représentant un personnage avec un feuillet de documents"
},
"upload": {
"title": "Contribuez à la base de photographies de Panoramax",
"description": "Pour le versement d'un grand volume de photographies, l'outil en ligne de commande est plus adapté.",
"know_more_button": "En savoir plus",
"input_label": "Glissez vos images ici ou cliquez sur ",
"import_word": "importer",
"import_type": "Format JPEG uniquement",
"subtitle_import": "Dépôt des images",
"title_sequence": "Titre de ma séquence",
"description_title_sequence": "Le titre d'une séquence est par défaut la date du jour. Vous pouvez, si vous le souhaitez le modifier ci-dessous.",
"text_import": "Déposez ici vos fichiers jpg. Chaque image ou série dimages constitue une « séquence ». Vous pourrez ensuite les retrouver dans la section « mes images » et choisir de les masquer, les afficher ou les supprimer.",
"subtitle_process": "Traitements de l'import",
"uploading_process": "Envoi en cours…",
"uploading_cancel": "Annuler l'envoi des photos",
"cancel_message": "⚠️ Attention, le téléchargement sera interrompu si vous validez et la séquence sera supprimée.",
"sequence_title": "Séquence du ",
"error_upload": "Erreur de création de la séquence",
"error_upload_img": "Une erreur est survenue",
"import": "Imports",
"upload_pending": "Transfert en cours…",
"images_count_text": "Images chargées",
"no_img_text": "aucune image chargée actuellement",
"upload_done": "Le chargement de la séquence est terminé",
"upload_other_files": "{count} photo au mauvais format a été retirée de la liste| {count} photos au mauvais format ont été retirées de la liste",
"sequence_link": "Accéder à cette séquence",
"edit_title": "Modifier le titre",
"edit_placeholder_input": "Modifier le titre de la séquence",
"ok_button": "Valider",
"pictures_error": "{count} photo n'a pas pu être chargée| {count} photos n'ont pas pu être chargées",
"sequence_loading_information": "Une fois chargée, la séquence sera en traitement et accessible sur Panoramax dans les prochaines minutes.",
"sequence_loaded_information": "La séquence est chargée et est en cours de traitement. Elle sera accessible sur Panoramax dans quelques minutes.",
"leave_message": "⚠️ Attention, le téléchargement sera interrompu si vous quittez la page avant la fin.",
"error_button": "Afficher les erreurs",
"modal_error_title": "Liste des photos non chargés"
},
"ay11": {
"title": "Déclaration daccessibilité",
"date": "Établie le 18 septembre 2023.",
"introduction": "IGN sengage à rendre son service accessible, conformément à larticle 47 de la loi n° 2005-102 du 11 février 2005.\n Cette déclaration daccessibilité sapplique à Panoramax Instance IGN: https://panoramax.ign.fr",
"subtitle_conformity": "État de conformité",
"conformity_text": "Panoramax Instance IGN est non conforme avec le ",
"conformity_text2": "Le site na encore pas été audité.",
"subtitle_conformity2": "Contenus non accessibles",
"subtitle_increase": "Amélioration et contact",
"increase_text": "Si vous narrivez pas à accéder à un contenu ou à un service, vous pouvez\n contacter le responsable de Panoramax Instance IGN pour être orienté vers une alternative accessible ou obtenir le contenu sous une autre forme.",
"phone": "Téléphone: +33 14 398 84 61",
"email_text": "E-mail:",
"email": "signalement.ign@panoramax.fr",
"address": "Adresse: IGN, Saint-Mandé",
"increase_info": "Nous essayons de répondre dans les 5 jours ouvrés.",
"subtitle_to_do": "Voie de recours",
"to_do_text": "Cette procédure est à utiliser dans le cas suivant : vous avez signalé au responsable du site internet un défaut daccessibilité qui vous\n empêche daccéder à un contenu ou à un des services du portail et vous navez pas obtenu de réponse satisfaisante. \n vous pouvez :",
"write_message": "Écrire un message au",
"defenseur_droits": "Défenseur des droits",
"contact": "Contacter",
"contact_text": "le délégué du Défenseur des droits dans votre région",
"send_letter": "Envoyer un courrier par la poste (gratuit, ne pas mettre de\n timbre):\n Défenseur des droits\n Libre réponse 71120 75342 Paris CEDEX 07",
"end": "Cette déclaration daccessibilité a été créé le\n 18 septembre 2023 grâce au",
"generator_betagouv": "Générateur de Déclaration dAccessibilité de BetaGouv"
}
}
}
}

View File

@@ -9,6 +9,7 @@
"login_text": "Kapcsolódás",
"register_text": "Regisztráció",
"contribute_text": "Miért működjön közre?",
"viewer": "Néző",
"my_account": "Saját fiók",
"upload_text": "+ Fényképek megosztása",
"sequences_text": "Saját fényképek",
@@ -37,7 +38,10 @@
"pages": {
"home": {
"report_mail": "?subject=⚠️ Report on picture {picId}&body=Hello, %0D%0A%0D%0A Problem on image (keep type of problem reported) : %0D%0A%0D%0A %0D%0A%0D%0A inappropriate content / lack of blurring on an element to be anonymized or blurred for security reasons / overblurring (too much blurring) %0D%0A%0D%0A Link to affected photo : {link} %0D%0A%0D%0A Details of affected elements (especially for blurring problems - what should be blurred or unblurred?) :",
"report_button_text": "Fénykép jelentése"
"report_button_text": "Fénykép jelentése",
"sequence_title": "lásd a sorrendet",
"open_fullscreen": "Teljes képernyős mód",
"close_fullscreen": "Normál mód"
},
"settings": {
"title": "Saját tokenek",
@@ -47,21 +51,45 @@
"sequence_published": "Közzétéve",
"sequence_waiting": "Feldolgozás alatt",
"sequence_hidden": "Rejtett",
"sequence_form_title": "Szerkessze a címet",
"sequence_tutorial_text": "⚠ A fájl kiválasztása megszakadt. Ha ez nem volt szándékos, akkor lehetséges, hogy az okostelefon nem kezeli megfelelően a fényképek küldését.\nA „Saját fájlok” (lásd az alábbi képet) segítségével kiválaszthatja a képeket, és megoldhatja ezt a problémát.",
"sequence_tutorial_exif_text": "Ha a fotófeltöltési hibák a földrajzi helyhez kapcsolódnak, kérjük, győződjön meg arról, hogy a „Helycímkék” engedélyezve van a telefon kameráján (lásd az alábbi képeket).",
"hide_sequence_tooltip": "A sorozat elrejtése",
"back_button": "Vissza a sorozatlistámhoz",
"delete_sequence_tooltip": "A sorozat végleges törlése",
"hide_photo_tooltip": "A kiválasztott fényképek elrejtése",
"delete_photo_tooltip": "A kiválasztottt fényképek végleges törlése",
"confirm_pictures_dialog": "⚠️ A kiválasztott fényképek véglegesen elvesznek",
"confirm_sequence_dialog": "⚠️ A kiválasztott sorozat véglegesen elvész",
"info_msg_maj": "Használja a SHIFT billentyűt sok kép kiválasztásához",
"conf_pic_msg": "⚠️ A kiválasztott fényképek véglegesen elvesznek",
"conf_sequence_msg": "⚠️ A kiválasztott sorozat véglegesen elvész",
"button_panel_photos": "Fényképek kezelése",
"button_panel_orientation": "Tájolás beállítása",
"button_panel_sort": "Rendezési sorrend",
"orientation_panel_title": "Az összes kép tájolásának beállítása a sorozatban",
"orientation_panel_tooltip": "Húzza a kék dobozt a kívánt irányba",
"orientation_input_label": "vagy módosítsa a szöget itt",
"orientation_input_placeholder": "-180 és 180 közötti érték",
"orientation_input_error_value": "Az értéknek -180 és 180 között kell lennie",
"orientation_panel_button": "Pozíció ellenőrzése",
"orientation_updated": "A tájolás megváltozott",
"sort_updated": "A sorozat jól van rendezve",
"sort_panel_title": "Szekvenciás rendezés beállítása",
"sort_panel_settings": "Sorrend rendezése:",
"sort_panel_settings_order": "Rendelés :",
"sort_panel_settings_order_increase": "Növekvő",
"sort_panel_settings_order_decrease": "Csökkenő",
"sort_panel_check_gps": "GPS dátum",
"sort_panel_check_file": "Fájl dátuma",
"sort_panel_check_name": "Fájlnév",
"created": "Feltöltés ideje:",
"taken": "Elkészítés ideje:",
"duration": "Hossz:",
"duration_begin": "Kezdet:",
"duration_end": "Vég:",
"camera": "Kamera:",
"button_delete": "Törlés",
"button_disable": "Elrejtés",
"button_enable": "Megjelenítés",
"button_delete": "Törlés szekvenciás",
"button_disable": "Elrejtés szekvenciás",
"button_enable": "Megjelenítés szekvenciás",
"picture_selected": "{count} fénykép kiválasztva| {count} fénykép kiválasztva",
"hours": "{count} óra| {count} óra",
"minutes": "{count} perc| {count} perc",
@@ -75,15 +103,29 @@
},
"sequences": {
"title": "Saját fényképsorozatok",
"filter_date_upload_title": "Szűrés feltöltés dátuma szerint :",
"filter_date_title": "Szűrés forgatás dátuma szerint :",
"radio_date_placeholder": "03/01/2024",
"radio_date": "dátum",
"hide_button": "Elrejt",
"show_button": "Előadás",
"delete_button": "Töröl",
"filter_date_reset_button": "Visszaállítás",
"filter_date_close_button": "Bezárás",
"no_sequence_found": "Nem található felvétel",
"sequence_name": "Név",
"sequence_photos": "Fényképek",
"sequence_date": "Elkészítés ideje",
"sequence_creation": "Feltöltés ideje",
"sequence_creation_tooltip": "Szűrés feltöltés dátuma szerint",
"sequence_date_tooltip": "Szűrés forgatás dátuma szerint",
"reset_filter_button": "Szűrők visszaállítása",
"filter_bbox_button": "Keresés ezen a területen",
"sequence_status": "Állapot",
"sequence_published": "Közzétéve",
"sequence_waiting": "Feldolgozás alatt",
"sequence_hidden": "Rejtett",
"no_sequences_text": "Még nincsenek közzétett fényképei \uD83D\uDE22",
"no_sequences_text": "Még nincsenek közzétett fényképei 😢",
"button_upload": "Fényképek feltöltése",
"sequence_deleted": "A sorozat törlésre került"
},
@@ -110,7 +152,7 @@
"command_line_subtitle": "Parancssoros eszköz",
"comment_install": "A geovisio parancssoros eszköz telepítése",
"comment_upload": "A képfeltöltési parancs indítása a kiválasztott mappán",
"description_terminal": "<a href='https://gitlab.com/geovisio/cli' target='_blank' style='color:black'>A parancssor</a> lehetővé teszi, hogy nagy számú képet osszon meg. A folyamat egyszerű, és <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>Pythont (3.8-as vagy újabb verzió)</a> igényli.\n\nAz importálás előtt az eszköz bekéri a bejelentkezési adatait. Amint a képek felöltésre kerültek, azok közzététele előtt feldolgozási idő szükséges.",
"description_terminal": "<a href='https://gitlab.com/panoramax/clients/cli' target='_blank' style='color:black'>A parancssor</a> lehetővé teszi, hogy nagy számú képet osszon meg. A folyamat egyszerű, és <a target='_blank' href='https://www.python.org/downloads/' style='color:black'>Pythont (3.8-as vagy újabb verzió)</a> igényli.\n\nAz importálás előtt az eszköz bekéri a bejelentkezési adatait. Amint a képek felöltésre kerültek, azok közzététele előtt feldolgozási idő szükséges.",
"terminal_install": "pip install geovisio_cli",
"terminal_text": "geovisio upload --api-url {webcím} <FÉNYKÉPMAPPA>",
"button_copy": "Másolás",
@@ -138,14 +180,19 @@
"text_import": "Ide töltse fel a JPG-fájljait. Minden kép vagy képsorozat egy „sorozatot” alkot. Megtalálhatja azokat a „saját fényképek” szakaszban, és elrejtheti, megjelenítheti vagy törölheti azokat.",
"subtitle_process": "Feltöltés feldolgozása",
"uploading_process": "Feltöltés folyamatban…",
"uploading_cancel": "Fényképek küldésének megszakítása",
"cancel_message": "⚠️ Felhívjuk figyelmét, hogy a letöltés megszakad, ha érvényesíti, és a sorozat törlődik.",
"sequence_title": "Sorozat ",
"error_upload": "Sorozat létrehozási hiba",
"error_upload_img": "Hiba történt",
"import": "Feltöltések",
"upload_pending": "Feltöltés folyamatban…",
"images_count_text": "Feltöltött képek",
"no_img_text": "még nem volt képfeltöltés",
"upload_done": "A sorozat feltöltése elkészült",
"upload_other_files": "{count} rossz formátumú fényképet eltávolítottunk a listáról| {count} rossz formátumú fényképet eltávolítottunk a listáról",
"sequence_link": "A sorozat megjelenítése",
"edit_title_tooltip": "A sorozat címének szerkesztése",
"edit_title": "A sorozat címének",
"edit_placeholder_input": "A sorozat címének szerkesztése",
"ok_button": "OK",
"pictures_error": "{count} kép feltöltése nem sikerült| {count} kép feltöltése nem sikerült",
@@ -156,8 +203,8 @@
"modal_error_title": "A hibában érintett képek"
},
"ay11": {
"title": "Déclaration daccessibilité",
"date": "Établie le 18 septembre 2023.",
"title": "Hozzáférhetőségi nyilatkozat",
"date": "Létrehozva 2023. szeptember 18-án.",
"introduction": "IGN sengage à rendre son service accessible, conformément à larticle 47 de la loi n° 2005-102 du 11 février 2005.\n Cette déclaration daccessibilité sapplique à Panoramax Instance IGN : https://panoramax.ign.fr",
"subtitle_conformity": "État de conformité",
"conformity_text": "Panoramax Instance IGN est non conforme avec le ",

1
src/locales/nl.json Normal file
View File

@@ -0,0 +1 @@
{}

162
src/locales/pt.json Normal file
View File

@@ -0,0 +1,162 @@
{
"general": {
"meta": {
"title": "Instância Panoramax",
"description": "Panoramax, a alternativa livre para foto-mapear território."
},
"header": {
"contribute_text": "Porquê Contribuir?",
"viewer": "Visualizador",
"my_account": "Minha Conta",
"upload_text": "+ Partilhar Imagens",
"alt_logo": "Logo da Instância",
"alt_photos": "Icon das Imagens",
"alt_information": "Icon do Utilizador",
"alt_settings": "Icon das Definições",
"alt_logout": "Icon de Terminar Sessão",
"title": "Panoramax",
"beta_text": "Versão Beta",
"logout_text": "Terminar Sessão",
"my_information_text": "Meus Detalhes",
"my_settings_text": "Minhas Definições",
"burger_menu_aria_label_open": "Mostrar Menu",
"burger_menu_aria_label_closed": "Esconder menu",
"sequences_text": "Minhas Imagens",
"login_text": "Iniciar sessão",
"register_text": "Registar"
},
"footer": {
"panoramax_site": "Descobrir Panoramax",
"information_gitlab": "Mostrar código fonte",
"gitlab_logo": "Logo do GitLab",
"ay11_text": "Acessibilidade: Não conforme"
},
"error_text": "Um erro ocorreu",
"success_text": "Atualização concluida",
"title": "Instância Panoramax"
},
"pages": {
"home": {
"report_button_text": "Reportar esta imagem",
"sequence_title": "Ver a sequencia",
"open_fullscreen": "Modo ecrã inteiro",
"close_fullscreen": "Modo normal",
"report_mail": "?subject=⚠️ Denuncia na imagem {picId}&body=Olá, %0D%0A%0D%0A Problema na imagem (manter tipo de problema reportado): %0D%0A%0D%0A %0D%0A%0D%0A conteúdo inapropriado / falta de desfoque num elemento a ser anonimizado ou desfocado por razões de segurançans / sobredesfocamento (demasiado desfocamento) %0D%0A%0D%0A Link para a imagem afetada: {link} %0D%0A%0D%0A Detalhes dos elementos afetados (especialmente para problemas de desfocamento - o que deveria ou não estar defocado?):"
},
"settings": {
"title": "Meus tokens",
"setting_tooltip": "Mostrar ou esconder token"
},
"sequence": {
"sequence_published": "Publicado",
"sequence_waiting": "Ainda a processar",
"sequence_hidden": "Escondido",
"sequence_form_title": "Editar o titulo",
"back_button": "Voltar à lista das minhas sequencias",
"delete_sequence_tooltip": "Permanentemente apagar esta sequencia",
"hide_photo_tooltip": "Esconder imagens selecionadas",
"delete_photo_tooltip": "Apagar permanentemente as imagens selecionadas",
"conf_pic_msg": "⚠️ As imagens selecionadas irão ser apagadas permanentemente",
"conf_sequence_msg": "⚠️ Esta sequencia vai ser apagada permanentemente",
"button_panel_orientation": "Definir orientação",
"button_panel_sort": "Ordenar sequencia",
"orientation_panel_tooltip": "Arraste a caixa azul para a direção desejada\"",
"orientation_input_label": "ou altere o ângulo aqui",
"orientation_input_placeholder": "Valor entre -180 e 180",
"orientation_input_error_value": "O valor tem de estar entre -180 e 180",
"orientation_panel_button": "Validar posição",
"orientation_updated": "Orientação atualizada",
"sort_updated": "Sequencia ordenada",
"hide_sequence_tooltip": "Esconder estas sequencias",
"info_msg_maj": "Use a tecla SHIFT para selecionar várias imagens",
"button_panel_photos": "Gerir imagens",
"orientation_panel_title": "Ajustar a orientação de todas as fotos na sequencia",
"duration_end": "Fim :",
"camera": "Camera :",
"button_delete": "Apagar a sequencia",
"button_disable": "Ocultar a sequencia",
"button_enable": "Mostrar a sequencia",
"picture_selected": "{count} imagem selecionada| {count} imagens selecionadas",
"hours": "{count} hora| {count} horas",
"minutes": "{count} minuto| {count} minutos",
"seconds": "{count} segundo| {count} segundos",
"select_text": "Selecionar tudo",
"unselect_text": "Desselecionar tudo",
"select_shift_text": "Selecione múltiplas imagens com shift",
"waiting_process": "Foto em progresso",
"no_image": "Sem imagens na sequencia",
"sort_panel_title": "Definição de ordenamento da sequencia",
"sort_panel_settings": "Ordenar sequencia por:",
"sort_panel_settings_order": "Ordenar:",
"sort_panel_settings_order_increase": "Ascendente",
"sort_panel_settings_order_decrease": "Descendente",
"sort_panel_check_gps": "Data GPS",
"sort_panel_check_file": "Data do ficheiro",
"sort_panel_check_name": "Nome do ficheiro",
"created": "Enviada :",
"taken": "Tirada em :",
"duration": "Duração :",
"duration_begin": "Inicio :"
},
"sequences": {
"no_sequence_found": "Nenhuma sequencia encontrada",
"filter_bbox_button": "Procurar nesta área",
"filter_date_close_button": "Fechar",
"title": "Minhas sequencias",
"filter_date_upload_title": "Filtrar por data de envio:",
"radio_date_placeholder": "03/01/2024",
"radio_date": "data",
"hide_button": "Ocultar",
"show_button": "Mostrar",
"delete_button": "Apagar",
"filter_date_reset_button": "Restaurar",
"filter_date_title": "Filtrar por data da foto :",
"sequence_name": "Nome",
"sequence_photos": "Fotos",
"sequence_date": "Tirada em",
"sequence_creation": "Enviar",
"sequence_creation_tooltip": "Filtrar por data de envio",
"sequence_date_tooltip": "Filtrar por data da foto",
"reset_filter_button": "Restaurar filtros",
"sequence_status": "Estado",
"sequence_published": "Publicado",
"sequence_waiting": "Ainda a processar",
"sequence_hidden": "Oculto",
"no_sequences_text": "Ainda não tem fotos publicadas 😢",
"button_upload": "Enviar imagens",
"sequence_deleted": "A sequencia foi apagada"
},
"share_pictures": {
"title": "Porquê contribuir para o Panoramax?",
"upload_description": "A aplicação web Panoramax permite o envio de todas as fotos de campo em formato JPEG com apenas um clique. Não são necessárias habilidades de programação. Para grandes números de fotos, recomendamos o uso da ferramenta em linha comandos",
"information_about_description": "Uma API está disponivel para recolher todos os metadados e fotos. <a href='{docLink}' target='_blank' style='color:#0a1f69'>\nDescubra mais aqui </a>\nOs dados também são exibidos na forma de <a href='{docTiles}' target='_blank' style='color:#0a1f69'> vector tiles </a>",
"card_description2": "Fotos 360º não são obrigatórias: fotos tiradas com o smartphone são tudo o que é necessário. Datas, localização e formato jpg são os únicos pré-requisitos.",
"description": "Contribuir para o Panoramax significa participar no desenvolvimento de um recurso digital geo-comum, livre, soberano e reutilizavel. Cada foto geolocalizada publicada no panoramax pode ser usada por qualquer um para uma grande variedade de propósitos, por exemplo, por uma autoridade local que precise de observar o estado das estradas, ou por um operador de telecomunicações para preparar uma intrevenção.\n\nCada contribuidor pode enviar as suas sequencias de imagens, modificar e consulta-las, tal como ver todas as outras - 360º ou não - contribuidas pela comunidade. O desfocamento das caras e matriculas é automático na plataforma.",
"alt_img_map": "Ilustração de uma mulher a olhar para um mapa com o seu smartphone geolocalizado",
"card_photo1": "Locais visíveis de uma estrada publica",
"card_photo2": "Fotos publicadas em formato 360º ou não",
"card_photo3": "Fotos facilmente reutilizáveis",
"card_photo4": "Uma fácil e rápida contribuição de imagens",
"card_alt_photo1": "Imagem de um edificio",
"card_alt_photo2": "Imagem a mostrar 360-graus",
"card_alt_photo3": "Imagem a mostrar um mapa com um ponteiro",
"card_alt_photo4": "Imagem a representar um ponteiro",
"card_description1": "Todas as fotos tiradas de uma estrada publica são aceites, desde que sejam geolocalizadas e vistas desde o solo.",
"card_description3": "Todas as fotos são acessíveis e reutilizáveis sem uma conta: via website ou API standard (standard STAC).",
"card_description4": "Várias ferramentas estão disponíveis para facilitar contribuições, incluindo em linhas de comandos e no site.",
"upload_subtitle": "Envie as suas fotos facilmente online",
"upload_illustration_alt": "Ilustração a mostrar o envio de fotos online",
"upload_button": "+ Enviar fotos",
"command_line_subtitle": "Ferramenta de linha de comandos",
"comment_install": "Instalar a ferramenta de linha de comandos geovisio",
"comment_upload": "Iniciar o comando de envio de imagens na pasta escolhida",
"button_copy": "Copiar",
"information_subtitle": "Aqui, todas as suas fotos são acessíveis para todos: ",
"information_text1": "Automaticamente desfocado em conformidade com a legislação.",
"information_text3": "No seu formato e resolução original para diversas reutilizações.",
"information_about_title": "Precisa acesso a fotos?",
"terminal_text": "geovisio upload --api-url {url} <PASTA_FOTOS>",
"information_text2": "As fotos enviadas serão publicadas como {word}"
}
}
}

View File

@@ -1,43 +1,47 @@
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import i18n from './i18n'
import VueMatomo from 'vue-matomo'
import App from './App.vue'
import router from './router'
import axios from 'axios'
import VueAxios from 'vue-axios'
import { createMetaManager } from 'vue-meta'
import { VueDraggableResizable } from 'vue-draggable-resizable-vue3'
import VCalendar from 'v-calendar'
import 'v-calendar/style.css'
import { pinia } from './store'
import fr from './locales/fr.json'
import en from './locales/en.json'
import hu from './locales/hu.json'
import './assets/main.scss'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-icons/font/bootstrap-icons.css'
import 'geovisio/build/index.css'
import { getEnv } from '@/utils'
axios.defaults.baseURL = import.meta.env.VITE_API_URL
axios.defaults.withCredentials = true
const i18n = createI18n({
locale: navigator.language.split('-')[0],
fallbackLocale: 'fr',
warnHtmlMessage: false,
globalInjection: true,
legacy: false,
messages: {
fr,
en,
hu
declare global {
interface Window {
_paq: any[]
}
})
}
axios.defaults.baseURL = getEnv('VITE_API_URL')
axios.defaults.withCredentials = true
const matomoHost = getEnv('VITE_MATOMO_HOST')
const matomoSiteId = getEnv('VITE_MATOMO_SITE_ID')
const matomoExist = matomoHost && matomoSiteId
const app = createApp(App)
app.use(pinia)
app.use(i18n)
app.use(router)
app.use(VueAxios, axios)
app.provide('axios', app.config.globalProperties.axios)
app.use(createMetaManager())
app.use(VueDraggableResizable)
app.use(VCalendar)
if (matomoExist) {
app.use(VueMatomo, {
host: matomoHost,
siteId: matomoExist
})
}
app.mount('#app')
if (matomoExist) {
window._paq.push(['trackPageView']) // Pour suivre les visites sur vos pages
}

View File

@@ -3,7 +3,8 @@ import { defineStore } from 'pinia'
export const useSequenceStore = defineStore('sequence', {
state: () => ({
toastText: <string>'',
toastLook: <string>''
toastLook: <string>'',
picId: <string>''
}),
actions: {
addToastText(text: string, look: string): void {
@@ -12,6 +13,9 @@ export const useSequenceStore = defineStore('sequence', {
setTimeout(() => {
this.toastText = ''
}, 3000)
},
addSequence(id: string): void {
this.picId = id
}
}
})

View File

@@ -40,7 +40,6 @@ describe('Template', () => {
await wrapper.vm.$nextTick()
expect(wrapper.html()).contains('text="title to edit"')
expect(wrapper.html()).contains('icon="bi bi-x"')
expect(wrapper.html()).contains('text="Valider"')
})
test('should valid the name and emit', async () => {

View File

@@ -5,7 +5,11 @@ import { createRouter, createWebHistory } from 'vue-router'
import { useCookies } from 'vue3-cookies'
import fr from '../../../locales/fr.json'
import Header from '../../../components/Header.vue'
vi.mock('vue-router')
vi.mock('vue-router', () => ({
useRoute: () => ({
path: '/mock-path'
})
}))
vi.mock('vue3-cookies', () => {
const mockCookies = {
get: vi.fn()
@@ -26,11 +30,6 @@ const i18n = createI18n({
}
})
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/', component: { template: '<div></div>' } }]
})
describe('Template', () => {
describe('When the user is not logged', () => {
describe('When the user is in desktop', () => {
@@ -38,7 +37,7 @@ describe('Template', () => {
import.meta.env.VITE_API_URL = 'api-url/'
const wrapper = mount(Header, {
global: {
plugins: [i18n, router],
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
@@ -61,7 +60,7 @@ describe('Template', () => {
import.meta.env.VITE_API_URL = 'api-url/'
const wrapper = mount(Header, {
global: {
plugins: [i18n, router],
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
@@ -84,7 +83,7 @@ describe('Template', () => {
import.meta.env.VITE_API_URL = 'api-url/'
const wrapper = mount(Header, {
global: {
plugins: [i18n, router],
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
@@ -107,7 +106,7 @@ describe('Template', () => {
userProfileUrl: 'profil'
},
global: {
plugins: [i18n, router],
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
@@ -125,17 +124,10 @@ describe('Template', () => {
describe('Methods', () => {
describe('toggleMenu', () => {
let wrapper
beforeEach(async () => {
const VueRouter = await import('vue-router')
VueRouter.useRoute.mockReturnValueOnce({
path: 'my-path'
})
})
it('Menu should be closed by default', () => {
wrapper = shallowMount(Header, {
const wrapper = shallowMount(Header, {
global: {
plugins: [i18n, router],
plugins: [i18n],
mocks: {
$t: (msg) => msg
}

View File

@@ -16,7 +16,6 @@ describe('Template', () => {
expect(wrapper.vm.created).toBe(null)
expect(wrapper.vm.href).toBe(null)
expect(wrapper.vm.hrefHd).toBe(null)
expect(wrapper.vm.selected).toBe(false)
expect(wrapper.vm.selectedOnMap).toBe(false)
expect(wrapper.vm.status).toBe('')
})
@@ -42,21 +41,6 @@ describe('Template', () => {
expect(wrapper.html()).contains('10 mars 2023')
})
})
describe('When the component is selected', () => {
it('should render the component with the selected class', () => {
const wrapper = shallowMount(ImageItem, {
global: {
mocks: {
$t: (msg) => msg
}
},
props: {
selected: true
}
})
expect(wrapper.html()).contains('class="selected button-image-item"')
})
})
describe('When the component is ready', () => {
it('should render the component with the status class', () => {
const wrapper = shallowMount(ImageItem, {
@@ -93,7 +77,7 @@ describe('Template', () => {
expect(wrapper.html()).contains('class="bi bi-card-image icon-waiting"')
})
})
describe('When the component is selected on the map but not selected', () => {
describe('When the component is selected on the map', () => {
it('should render the component with the map pointer', () => {
const wrapper = shallowMount(ImageItem, {
global: {
@@ -102,11 +86,11 @@ describe('Template', () => {
}
},
props: {
selectedOnMap: true,
selected: false
selectedOnMap: true
}
})
expect(wrapper.html()).contains('class="icon-img pointer-map"')
expect(wrapper.html()).contains('class="pointer-map"')
expect(wrapper.html()).contains('class="selected button-image-item"')
})
})
describe('When the component is selected but not selected on the map', () => {
@@ -118,29 +102,10 @@ describe('Template', () => {
}
},
props: {
selectedOnMap: false,
selected: true
selectedOnMap: false
}
})
expect(wrapper.html()).contains('class="bi bi-check-lg"')
expect(wrapper.html()).contains('class="icon-img button-check"')
})
})
describe('When the component is selected and selected on the map', () => {
it('should render the component with the check icon', () => {
const wrapper = shallowMount(ImageItem, {
global: {
mocks: {
$t: (msg) => msg
}
},
props: {
selectedOnMap: true,
selected: true
}
})
expect(wrapper.html()).contains('class="icon-img button-check-pointer"')
expect(wrapper.html()).contains('class="bi bi-check-lg"')
expect(wrapper.html()).contains('class="button-image-item"')
})
})
describe('When the button is trigger', () => {

View File

@@ -11,7 +11,6 @@ describe('Template', () => {
}
})
expect(wrapper.vm.text).toBe(null)
expect(wrapper.vm.title).toBe(null)
expect(wrapper.vm.look).toBe('')
})
test('should have all the props filled', () => {
@@ -21,11 +20,9 @@ describe('Template', () => {
},
props: {
text: 'my text',
title: 'my title',
look: 'my-look'
}
})
expect(wrapper.html()).contains('my title</h3>')
expect(wrapper.html()).contains('my text</p>')
expect(wrapper.html()).contains('class="information-block my-look"')
})

View File

@@ -13,7 +13,6 @@ describe('Template', () => {
expect(wrapper.vm.text).toBe(null)
expect(wrapper.vm.textPictureType).toBe(null)
expect(wrapper.vm.textSecondPart).toBe(null)
expect(wrapper.vm.accept).toBe('')
})
test('should have all the props filled', () => {
const wrapper = shallowMount(InputUpload, {
@@ -23,11 +22,9 @@ describe('Template', () => {
props: {
text: 'my text',
textPictureType: 'my textPictureType',
textSecondPart: 'my textSecondPart',
accept: 'accept'
textSecondPart: 'my textSecondPart'
}
})
expect(wrapper.html()).contains('accept="accept"')
expect(wrapper.html()).contains('my text')
expect(wrapper.html()).contains('my textSecondPart')
expect(wrapper.html()).contains('my textPictureType')

View File

@@ -1,24 +1,13 @@
import { test, describe, vi, expect } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import Link from '../../../components/Link.vue'
import { createI18n } from 'vue-i18n'
import { createRouter, createWebHistory } from 'vue-router'
import fr from '../../../locales/fr.json'
vi.mock('vue-i18n')
import * as img from '../../../utils/image'
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key) => key
})
}))
const i18n = createI18n({
locale: 'fr',
fallbackLocale: 'fr',
globalInjection: true,
legacy: false,
messages: {
fr
}
})
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/', component: { template: '<div></div>' } }]
})
const stubs = {
'router-link': {
template: '<router-link></router-link>'
@@ -28,9 +17,7 @@ describe('Template', () => {
describe('Props', () => {
test('should have default props', () => {
const wrapper = shallowMount(Link, {
global: {
plugins: [i18n, router]
}
global: {}
})
expect(wrapper.vm.text).toBe(null)
@@ -49,8 +36,7 @@ describe('Template', () => {
test('should render the component as a <a>', () => {
const wrapper = shallowMount(Link, {
global: {
stubs,
plugins: [i18n]
stubs
},
props: {
type: 'external'
@@ -61,8 +47,7 @@ describe('Template', () => {
test('should render an icon inside the link', () => {
const wrapper = shallowMount(Link, {
global: {
stubs,
plugins: [i18n]
stubs
},
props: {
type: 'external',
@@ -74,8 +59,7 @@ describe('Template', () => {
test('should render the text inside the link', () => {
const wrapper = shallowMount(Link, {
global: {
stubs,
plugins: [i18n]
stubs
},
props: {
type: 'external',
@@ -85,28 +69,29 @@ describe('Template', () => {
expect(wrapper.html()).contains('my-text')
})
test('should render an image inside the link', () => {
const imgSrc = 'my-url.png'
const imgPath = vi.spyOn(img, 'img')
imgPath.mockReturnValue(imgSrc)
const wrapper = shallowMount(Link, {
global: {
stubs,
plugins: [i18n]
stubs
},
props: {
type: 'external',
image: {
url: 'my-url.png',
url: imgSrc,
alt: 'my-alt'
}
}
})
expect(wrapper.html()).contains('<img')
expect(wrapper.html()).contains('my-url.png')
expect(wrapper.html()).contains(imgSrc)
expect(wrapper.html()).contains('my-alt')
})
test('should render the text inside the link', () => {
const wrapper = shallowMount(Link, {
global: {
stubs,
plugins: [i18n]
stubs
},
props: {
type: 'external',
@@ -120,17 +105,13 @@ describe('Template', () => {
describe('When the component is an internal link', () => {
test('should render the component as an internal link', () => {
const wrapper = shallowMount(Link, {
global: {
plugins: [i18n, router]
}
global: {}
})
expect(wrapper.html()).contains('<router-link')
})
test('should render an icon inside the link', () => {
const wrapper = shallowMount(Link, {
global: {
plugins: [i18n]
},
global: {},
props: {
type: 'internal',
icon: 'my-icon'
@@ -140,9 +121,7 @@ describe('Template', () => {
})
test('should render the text inside the link', () => {
const wrapper = shallowMount(Link, {
global: {
plugins: [i18n, router]
},
global: {},
props: {
text: 'my-text'
}
@@ -151,9 +130,7 @@ describe('Template', () => {
})
test('should render the disabled attribute', () => {
const wrapper = shallowMount(Link, {
global: {
plugins: [i18n, router]
},
global: {},
props: {
disabled: true
}

View File

@@ -26,17 +26,10 @@ describe('Template', () => {
}
}
})
expect(wrapper.vm.uploadErrors).toEqual([])
expect(wrapper.vm.title).toEqual('')
})
it('should render the props filled', async () => {
document.body.innerHTML = '<div id="bs-modal"></div>'
const uploadErrors = [
{
details: { error: 'my error' },
message: 'my message',
name: 'my name'
}
]
const wrapper = shallowMount(Modal, {
global: {
plugins: [i18n],
@@ -45,13 +38,12 @@ describe('Template', () => {
}
},
props: {
uploadErrors
title: 'My title'
}
})
expect(wrapper.vm.uploadErrors).toEqual(uploadErrors)
expect(wrapper.html()).contains('my name - ')
expect(wrapper.html()).contains('my error')
expect(wrapper.vm.title).toEqual('My title')
expect(wrapper.html()).contains('My title')
})
})
})

View File

@@ -3,18 +3,19 @@ import { shallowMount } from '@vue/test-utils'
import HeaderOpen from '../../../../components/header/HeaderOpen.vue'
import i18n from '../../config'
import { createRouter, createWebHistory } from 'vue-router/dist/vue-router'
vi.mock('vue-router')
vi.mock('vue-router', () => ({
useRoute: () => ({
path: '/mock-path'
})
}))
import.meta.env.VITE_API_URL = 'api-url/'
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/', component: { template: '<div></div>' } }]
})
describe('Template', () => {
describe('Props', () => {
it('should have default props', () => {
const wrapper = shallowMount(HeaderOpen, {
global: {
plugins: [i18n, router]
plugins: [i18n]
}
})
expect(wrapper.vm.menuIsClosed).toBe(true)
@@ -25,7 +26,7 @@ describe('Template', () => {
it('should render the desktop entries', () => {
const wrapper = shallowMount(HeaderOpen, {
global: {
plugins: [i18n, router]
plugins: [i18n]
},
props: {
menuIsClosed: false,
@@ -33,14 +34,14 @@ describe('Template', () => {
}
})
expect(wrapper.html()).contains('class="logged-link desktop"')
expect(wrapper.html()).contains('text="Pourquoi contribuer ?"')
expect(wrapper.html()).contains('text="Pourquoi contribuer?"')
})
})
describe('When the user is in mobile', () => {
it('should render the responsive entries', () => {
const wrapper = shallowMount(HeaderOpen, {
global: {
plugins: [i18n, router]
plugins: [i18n]
},
props: {
menuIsClosed: false,
@@ -53,17 +54,16 @@ describe('Template', () => {
})
})
it('should render all the commons entries', () => {
import.meta.env.VITE_API_URL = 'api-url/'
const wrapper = shallowMount(HeaderOpen, {
global: {
plugins: [i18n, router]
plugins: [i18n]
},
props: {
menuIsClosed: false,
userProfileUrlLength: 5
}
})
expect(wrapper.html()).contains('text="Pourquoi contribuer ?"')
expect(wrapper.html()).contains('text="Pourquoi contribuer?"')
expect(wrapper.html()).contains('text="+ Partager des photos"')
expect(wrapper.html()).contains('text="Mes informations"')
expect(wrapper.html()).contains('text="Mes paramètres"')

View File

@@ -0,0 +1,74 @@
import { it, describe, expect } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import PanelOrientationManagement from '../../../../components/sequence/PanelOrientationManagement.vue'
import i18n from '../../config'
describe('Template', () => {
describe('Props', () => {
it('should have default props', () => {
const wrapper = shallowMount(PanelOrientationManagement, {
global: {
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
}
})
expect(wrapper.vm.roadDegrees).toBe(0)
expect(wrapper.vm.seqBruteDeg).toBe(0)
})
describe('When the component have props filled', () => {
it('should render the component all the element', () => {
const wrapper = shallowMount(PanelOrientationManagement, {
global: {
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
},
props: {
roadDegrees: 45,
seqBruteDeg: 22
}
})
expect(wrapper.html()).contains(
'pages.sequence.orientation_panel_title'
)
expect(wrapper.html()).contains(
'pages.sequence.orientation_input_label'
)
expect(wrapper.html()).contains(
'pages.sequence.orientation_input_placeholder'
)
expect(wrapper.html()).contains('<widget-orientation')
expect(wrapper.html()).contains('<information-card')
expect(wrapper.html()).contains('<input')
expect(wrapper.html()).contains('<button')
expect(wrapper.html()).contains(
'type="number" min="-180" max="180" text="-23"'
)
})
})
describe('Emit functions', () => {
it('should emit triggerAngle event', async () => {
const wrapper = shallowMount(PanelOrientationManagement, {
global: {
plugins: [i18n],
mocks: {
$t: (msg) => msg
}
},
props: {
roadDegrees: 45,
seqBruteDeg: 22
}
})
const valueToEmit = 22 - 45
await wrapper.vm.triggerAngle(valueToEmit)
expect(wrapper.emitted('triggerAngle')).toHaveLength(1)
expect(wrapper.emitted('triggerAngle')[0]).toEqual([valueToEmit])
})
})
})
})

Some files were not shown because too many files have changed in this diff Show More