Compare commits
90 commits
revert-664
...
main
Author | SHA1 | Date | |
---|---|---|---|
15156a624a | |||
486da7450d | |||
c58e22dd2a | |||
facddd3fe6 | |||
|
95a57110e4 | ||
|
4e5c085d5d | ||
|
32297c2834 | ||
|
b922df3766 | ||
|
f8813411ae | ||
|
b5fb75e82d | ||
|
35265fe807 | ||
|
f46f4c7e65 | ||
|
db60c13147 | ||
|
b27f6ceffd | ||
|
427bb51d30 | ||
|
2b805b1ee1 | ||
|
7882ec28e7 | ||
|
5b848b0456 | ||
|
00a1d07ae2 | ||
7a1ac02c99 | |||
e39e4f2406 | |||
9fd01826f7 | |||
d07bbb52f7 | |||
d833cf1ea9 | |||
|
cb211c9b77 | ||
|
c5a5efbf01 | ||
d19acda808 | |||
3869ea5e19 | |||
|
9d17f846f8 | ||
|
adf008a6be | ||
3c4499cb66 | |||
760d868dc2 | |||
eeccb24a92 | |||
6317422824 | |||
|
b0d6784462 | ||
|
d6013d5fa2 | ||
763a5babba | |||
2c7bfa7ab1 | |||
b43eacd22f | |||
|
3794690cef | ||
0785bc440c | |||
9cbd3472d4 | |||
bb148086ba | |||
fcf226c4ec | |||
f13ecf92ab | |||
09028f31ac | |||
|
cea70a1d42 | ||
|
19ba190ced | ||
|
ff5333c715 | ||
|
b570367142 | ||
d006c75872 | |||
d75174d064 | |||
2564098b6d | |||
|
82470ecf6b | ||
|
e2075b96d4 | ||
ee905cd277 | |||
|
82e1e4e86a | ||
|
240701ed7a | ||
|
0f8b04bf15 | ||
|
381b2088ac | ||
b89ec4b279 | |||
ffeb499b91 | |||
|
725a999e9a | ||
|
2141a26dbd | ||
|
fe568132a1 | ||
fab2a6d2b6 | |||
|
58fcf52cd4 | ||
9cff6bd7ec | |||
419d6d9813 | |||
6853d106d8 | |||
196531b342 | |||
48e29ee9fd | |||
19b4d18941 | |||
b9918347c1 | |||
8e6f0b1d10 | |||
56fc6bca3c | |||
53b593086c | |||
|
f5630ba590 | ||
|
930eb2dbde | ||
|
44c8350384 | ||
2ebc4f571d | |||
d769d44e60 | |||
|
c59eaff193 | ||
|
01011763e7 | ||
a371a84426 | |||
004c9a697c | |||
eb55b258d9 | |||
54caf6d3ef | |||
|
cab24874f7 | ||
|
e7356b3c3c |
8
.env.example
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# OAuth 2.0 Client ID. Create one at Google Cloud Console > APIs & Services > Credentials
|
||||||
|
VITE_CLIENT_ID=
|
||||||
|
|
||||||
|
# OpenRouteService API key. Create one at OpenRouteService dev dashboard > Tokens
|
||||||
|
VITE_OPENROUTESERVICE_API_KEY=
|
||||||
|
|
||||||
|
# Specify backend URL here
|
||||||
|
VITE_BACKEND_URL=
|
3
.gitignore
vendored
|
@ -22,3 +22,6 @@ dist-ssr
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
#env
|
||||||
|
.env
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
stages:
|
|
||||||
- prepare
|
|
||||||
- build
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
remove-old-services:
|
|
||||||
stage: prepare
|
|
||||||
script:
|
|
||||||
- podman stop little-lines
|
|
||||||
- podman rm little-lines
|
|
||||||
|
|
||||||
container-build:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- sed -i "s/DATE/$(date -I)/g" ${CI_PROJECT_DIR}/src/views/About.vue
|
|
||||||
- sed -i "s/VERSION/$(git log -1 --oneline | awk '{print $1}')/g" ${CI_PROJECT_DIR}/src/views/About.vue
|
|
||||||
- podman build -t little-lines .
|
|
||||||
|
|
||||||
container-deploy:
|
|
||||||
stage: deploy
|
|
||||||
script:
|
|
||||||
- podman run --name little-lines -p 8081:80 -d little-lines
|
|
||||||
- podman generate systemd little-lines > ~/.config/systemd/user/little-lines.service
|
|
||||||
- systemctl --user daemon-reload
|
|
||||||
- systemctl --user enable little-lines
|
|
23
.woodpecker.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
when:
|
||||||
|
- branch: main
|
||||||
|
event: push
|
||||||
|
- event: tag
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: deploy
|
||||||
|
image: node
|
||||||
|
commands:
|
||||||
|
- npm i
|
||||||
|
- npm run build
|
||||||
|
- rm -rf /mnt/caddy-sites/little-lines.techtransthai.org/*
|
||||||
|
- cp -r dist/* /mnt/caddy-sites/little-lines.techtransthai.org/
|
||||||
|
volumes:
|
||||||
|
- /media/core/Data1/Apps/caddy/sites:/mnt/caddy-sites
|
||||||
|
environment:
|
||||||
|
VITE_BACKEND_URL: https://little-lines-backend.techtransthai.org
|
||||||
|
VITE_CLIENT_ID:
|
||||||
|
from_secret: client_id
|
||||||
|
VITE_OPENROUTESERVICE_API_KEY:
|
||||||
|
from_secret: ors_api_key
|
||||||
|
|
||||||
|
|
19
Dockerfile
|
@ -1,16 +1,15 @@
|
||||||
FROM nginx:alpine
|
FROM docker.io/library/alpine:latest
|
||||||
|
|
||||||
# Set up environment for building
|
# Set up environment for building
|
||||||
RUN apk add yarn nodejs
|
RUN apk add nodejs npm
|
||||||
|
|
||||||
# Copy files to build environment
|
# Copy files to build environment
|
||||||
RUN mkdir /opt/micromobility-navigation
|
RUN mkdir /opt/little-lines-frontend
|
||||||
COPY . /opt/micromobility-navigation
|
COPY . /opt/little-lines-frontend
|
||||||
|
|
||||||
|
# Start the app
|
||||||
|
WORKDIR /opt/little-lines-frontend
|
||||||
|
RUN npm i
|
||||||
|
CMD npm run dev -- --host
|
||||||
|
|
||||||
# Run Vite production build
|
|
||||||
WORKDIR /opt/micromobility-navigation
|
|
||||||
RUN yarn
|
|
||||||
RUN yarn run build
|
|
||||||
|
|
||||||
# Copy files to nginx path
|
|
||||||
RUN cp -r dist/* icons /usr/share/nginx/html
|
|
||||||
|
|
43
README.md
|
@ -6,22 +6,22 @@
|
||||||
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">
|
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">
|
||||||
<img alt="License: AGPLv3" src="https://shields.io/badge/License-AGPLv3-blueviolet.svg">
|
<img alt="License: AGPLv3" src="https://shields.io/badge/License-AGPLv3-blueviolet.svg">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://gitlab.com/openKMITL/micromobility-navigation/-/pipelines">
|
<img alt="Service Status" src="https://status.techtransthai.org/api/badge/7/status">
|
||||||
<img alt="Build Status" src="https://gitlab.com/openkmitl/micromobility-navigation/badges/main/pipeline.svg">
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3>ระบบนำทางสำหรับล้อขนาดเล็ก</h3>
|
<h3>ระบบนำทางสำหรับล้อขนาดเล็ก</h3>
|
||||||
|
|
||||||
<a href="https://little-lines.techtransthai.org">เข้าใช้งานเว็บแอป</a>
|
<a href="https://little-lines.techtransthai.org">เข้าใช้งานเว็บแอป</a>
|
||||||
•
|
•
|
||||||
<a href="https://gitlab.com/openKMITL/micromobility-navigation/-/wikis/home">เอกสาร</a>
|
<a href="https://www.techtransthai.org/webapps/little-lines/">โฮมเพจ</a>
|
||||||
|
|
||||||
<h5>ร่วมพูดคุยกับเรา:</h5>
|
<h5>ร่วมพูดคุยกับเรา:</h5>
|
||||||
<a href="https://discord.gg/6aPemyuSzx">
|
<a href="https://t.me/techtransthai">
|
||||||
<img alt="Discord" src="https://img.shields.io/discord/1053041544845861015?label=Discord&color=blueviolet">
|
<img alt="Telegram" src="https://img.shields.io/badge/Telegram-TechTransThai Community-blue">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -41,37 +41,8 @@ Littles Lines คือระบบนำทางสำหรับล้อข
|
||||||
สำหรับการพัฒนาด้วย NPM
|
สำหรับการพัฒนาด้วย NPM
|
||||||
|
|
||||||
```
|
```
|
||||||
$ git clone https://gitlab.com/openKMITL/micromobility-navigation.git
|
$ git clone https://gitlab.com/little-lines/frontend.git
|
||||||
$ cd micromobility-navigation
|
$ cd little-lines
|
||||||
$ npm i
|
$ npm i
|
||||||
$ npm run dev
|
$ npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
สำหรับการพัฒนาด้วย Yarn
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git clone https://gitlab.com/openKMITL/micromobility-navigation.git
|
|
||||||
$ cd micromobility-navigation
|
|
||||||
$ yarn
|
|
||||||
$ yarn dev
|
|
||||||
```
|
|
||||||
|
|
||||||
สำหรับ Production ด้วย Podman/Docker
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git clone https://gitlab.com/openKMITL/micromobility-navigation.git
|
|
||||||
$ cd micromobility-navigation
|
|
||||||
$ podman build -t littlelines:20230720 .
|
|
||||||
$ podman run --name littlelines -p 8080:80 -d littlelines:20230720
|
|
||||||
```
|
|
||||||
|
|
||||||
## Special Thanks
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: center; align-items: center;">
|
|
||||||
<a href="https://discord.gg/6aPemyuSzx">
|
|
||||||
<img src="assets/openKMITL-Community.png" alt=openKMITL" height="40">
|
|
||||||
<a/>
|
|
||||||
<a href="https://techtransthai.org">
|
|
||||||
<img src="assets/ttt-org-wide-grey.svg" alt="TTT Logo" height="45">
|
|
||||||
<a/>
|
|
||||||
</div>
|
|
BIN
assets/flag-filled-symbolic.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
2
assets/flag-filled-symbolic.svg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><path d="m 1.980469 1.003906 v 14 h 2 v -6 h 2.382812 l 0.722657 1.445313 c 0.171874 0.339843 0.519531 0.554687 0.894531 0.554687 h 5 c 0.554687 0 1 -0.449218 1 -1 v -6 c 0 -0.550781 -0.445313 -1 -1 -1 h -3.378907 l -0.726562 -1.449218 c -0.167969 -0.335938 -0.515625 -0.550782 -0.894531 -0.550782 z m 0 0" fill="#222222"/><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -580 -600)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -580 -600)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -580 -600)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g></svg>
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/map-marker-symbolic.png
Normal file
After Width: | Height: | Size: 14 KiB |
2
assets/map-marker-symbolic.svg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><path d="m 8 0 c -3.589844 0 -6.5 2.910156 -6.5 6.5 s 2.910156 6.496094 6.5 6.496094 c 3.589844 0.003906 6.5 -2.90625 6.5 -6.496094 s -2.910156 -6.5 -6.5 -6.5 z m 0 4 c 1.378906 0 2.5 1.117188 2.5 2.5 c 0 1.378906 -1.121094 2.5 -2.5 2.496094 c -1.378906 0 -2.5 -1.117188 -2.5 -2.496094 s 1.117188 -2.5 2.5 -2.5 z m 0 0" fill="#222222"/><path d="m 14.097656 8.746094 l -5.660156 0.230468 l -6.535156 -0.230468 c 0.6875 2.152344 4.097656 5.25 6.097656 7.25 v 0.003906 v -0.003906 c 2 -2 5.410156 -5.101563 6.097656 -7.25 z m 0 0" fill="#222222"/><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -700 -60)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -700 -60)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -700 -60)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g></svg>
|
After Width: | Height: | Size: 2.3 KiB |
2
icons/Adwaita/no-wheelchair.svg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -320 -60)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -320 -60)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -320 -60)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g><g fill="#222222"><path d="m 2 1 c -0.265625 0 -0.519531 0.105469 -0.707031 0.292969 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 l 12 12 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 s 0.390625 -1.023437 0 -1.414062 l -12 -12 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 0"/><path d="m 5.949219 0.125 c -0.753907 0.019531 -1.433594 0.460938 -1.753907 1.140625 c -0.207031 0.191406 -0.320312 0.457031 -0.320312 0.734375 v 0.5 c 0 0.128906 0.023438 0.253906 0.074219 0.371094 l 4.066406 6.238281 c 0.152344 0.378906 0.519531 0.625 0.929687 0.628906 l 2.234376 1.261719 l 2.046874 3.191406 c 0.144532 0.386719 0.40625 0.808594 0.824219 0.808594 h 0.824219 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 h -0.804688 l -1.257812 -3.351562 c -0.148438 -0.390626 -0.519531 -0.648438 -0.9375 -0.648438 h -3.324219 l -0.398437 -1 h 2.847656 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 h -3.648438 l -0.78125 -1.957031 c 0.847657 -0.253907 1.429688 -1.03125 1.429688 -1.917969 c 0 -1.105469 -0.894531 -2 -2 -2 c -0.015625 0 -0.03125 0 -0.046875 0 z m 0 0"/><path d="m 2.855469 6.671875 c -0.261719 0.246094 -0.5 0.519531 -0.707031 0.8125 l 1.398437 1.402344 c 0.179687 -0.320313 0.394531 -0.617188 0.660156 -0.859375 z m -1.273438 1.839844 c -0.164062 0.394531 -0.277343 0.8125 -0.339843 1.25 l 2.207031 2.203125 c -0.210938 -0.441406 -0.339844 -0.929688 -0.339844 -1.453125 c 0 -0.148438 0.027344 -0.289063 0.042969 -0.429688 z m -0.324219 2.851562 c 0.363282 2.234375 2.136719 4.007813 4.375 4.375 z m 7.710938 1.421875 c -0.246094 0.269532 -0.539062 0.484375 -0.855469 0.664063 l 1.371094 1.371093 c 0.289063 -0.210937 0.554687 -0.453124 0.796875 -0.722656 z m -3.9375 0.761719 l 2.199219 2.203125 c 0.4375 -0.066406 0.851562 -0.183594 1.238281 -0.351562 l -1.554688 -1.558594 c -0.144531 0.019531 -0.28125 0.042968 -0.429687 0.042968 c -0.523437 0 -1.015625 -0.125 -1.453125 -0.335937 z m 0 0"/></g></svg>
|
After Width: | Height: | Size: 3.6 KiB |
2
icons/Adwaita/wheelchair-limited.svg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -360 -60)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -360 -60)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -360 -60)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g><g fill="#222222"><path d="m 4.875 1 c -0.550781 0 -1 0.449219 -1 1 v 0.5 c 0 0.128906 0.023438 0.253906 0.074219 0.371094 l 3 7.5 c 0.148437 0.378906 0.515625 0.628906 0.925781 0.628906 h 3.304688 l 1.257812 3.351562 c 0.148438 0.390626 0.519531 0.648438 0.9375 0.648438 h 1.5 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 h -0.804688 l -1.257812 -3.351562 c -0.148438 -0.390626 -0.519531 -0.648438 -0.9375 -0.648438 h -3.324219 l -2.675781 -6.691406 v -0.308594 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0"/><path d="m 8 2.125 c 0 1.105469 -0.894531 2 -2 2 s -2 -0.894531 -2 -2 s 0.894531 -2 2 -2 s 2 0.894531 2 2 z m 0 0"/><path d="m 4.25 5.707031 c -1.808594 0.847657 -3.066406 2.683594 -3.066406 4.800781 c 0 2.917969 2.382812 5.304688 5.300781 5.304688 c 1.933594 0 3.582031 -1.066406 4.488281 -2.628906 l -0.488281 -1.226563 h -0.957031 c -0.539063 1.140625 -1.6875 1.925781 -3.042969 1.925781 c -1.875 0 -3.375 -1.5 -3.375 -3.375 c 0 -1.308593 0.742187 -2.421874 1.824219 -2.984374 z m 0 0" fill-opacity="0.34902"/><path d="m 7 6 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 4 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/></g></svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -1,2 +1,2 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 8 0.0625 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 s 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m -1 4.9375 s -1 0 -1 1 v 3 c 0 2 2 2 2 2 h 2 v -0.0625 h 0.371094 l 2.710937 4.515625 c 0.28125 0.472656 0.894531 0.625 1.367188 0.34375 c 0.476562 -0.285156 0.628906 -0.898437 0.34375 -1.375 l -3.289063 -5.484375 h -1.503906 v -0.9375 h 3 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 h -2.585938 l -0.707031 -0.707031 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m -2 1.972656 c -1.613281 0.183594 -3.027344 1.21875 -3.65625 2.742188 c -0.699219 1.679687 -0.308594 3.621094 0.976562 4.90625 c 1.28125 1.28125 3.222657 1.671875 4.902344 0.972656 c 1.523438 -0.628906 2.558594 -2.042969 2.738282 -3.65625 h -2.015626 c -0.164062 0.804688 -0.710937 1.488281 -1.488281 1.8125 c -0.9375 0.386719 -2.007812 0.171875 -2.726562 -0.546875 c -0.714844 -0.714844 -0.929688 -1.785156 -0.542969 -2.722656 c 0.324219 -0.777344 1.007812 -1.324219 1.8125 -1.484375 z m 0 0"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -340 -60)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -340 -60)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -340 -60)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g><g fill="#222222"><path d="m 4.875 1 c -0.550781 0 -1 0.449219 -1 1 v 0.5 c 0 0.128906 0.023438 0.253906 0.074219 0.371094 l 3 7.5 c 0.148437 0.378906 0.515625 0.628906 0.925781 0.628906 h 3.304688 l 1.257812 3.351562 c 0.148438 0.390626 0.519531 0.648438 0.9375 0.648438 h 1.5 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 h -0.804688 l -1.257812 -3.351562 c -0.148438 -0.390626 -0.519531 -0.648438 -0.9375 -0.648438 h -3.324219 l -2.675781 -6.691406 v -0.308594 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0"/><path d="m 8 2.125 c 0 1.105469 -0.894531 2 -2 2 s -2 -0.894531 -2 -2 s 0.894531 -2 2 -2 s 2 0.894531 2 2 z m 0 0"/><path d="m 4.25 5.707031 c -1.808594 0.847657 -3.066406 2.683594 -3.066406 4.800781 c 0 2.917969 2.382812 5.304688 5.300781 5.304688 c 1.933594 0 3.582031 -1.066406 4.488281 -2.628906 l -0.488281 -1.226563 h -0.957031 c -0.539063 1.140625 -1.6875 1.925781 -3.042969 1.925781 c -1.875 0 -3.375 -1.5 -3.375 -3.375 c 0 -1.308593 0.742187 -2.421874 1.824219 -2.984374 z m 0 0"/><path d="m 7 6 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 4 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/></g></svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.9 KiB |
14
little-lines-frontend.container.example
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Little Lines frontend container
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=little-lines-frontend
|
||||||
|
Image=localhost/little-lines-frontend
|
||||||
|
PublishPort=5173:5173
|
||||||
|
Volume=/path/to/env:/opt/little-lines-frontend/.env
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target default.target
|
1075
package-lock.json
generated
15
package.json
|
@ -9,18 +9,21 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.6.2",
|
||||||
"ol": "^7.4.0",
|
"dotenv": "^16.3.1",
|
||||||
"ol-contextmenu": "^5.2.1",
|
"ol": "^9.1.0",
|
||||||
|
"ol-contextmenu": "^5.4.0",
|
||||||
"ol-ext": "^4.0.10",
|
"ol-ext": "^4.0.10",
|
||||||
"ol-geocoder": "^4.3.0",
|
"ol-geocoder": "^4.3.0",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-router": "^4.2.4",
|
"vue-router": "^4.2.4",
|
||||||
"vue3-openlayers": "^1.0.0",
|
"vue3-google-login": "^2.0.25",
|
||||||
|
"vue3-google-oauth2": "^1.0.7",
|
||||||
|
"vue3-openlayers": "^6.3.0",
|
||||||
"vuetify": "^3.3.8"
|
"vuetify": "^3.3.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"vite": "^4.4.0"
|
"vite": "^5.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,432 @@
|
||||||
<template>
|
<template>
|
||||||
<v-img
|
|
||||||
class="image"
|
|
||||||
src="https://cdn.vuetifyjs.com/images/cards/sunshine.jpg"
|
|
||||||
cover
|
|
||||||
></v-img>
|
|
||||||
|
|
||||||
<v-card
|
<v-card
|
||||||
class="stick-bottom card-height"
|
class="stick-bottom card-height destination-card"
|
||||||
width="100%"
|
width="100%"
|
||||||
height="60vh"
|
:height="cardHeight"
|
||||||
style="padding-top: 15px; font-weight:bold;"
|
style="padding-top: 15px;"
|
||||||
:title="destination.title"
|
|
||||||
:subtitle="destination.subTitle"
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<v-list lines="one">
|
<div v-if="!showRoute">
|
||||||
|
<v-list>
|
||||||
|
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<template v-slot:prepend>
|
<v-list-item-title class="title-text">{{nearestStructureData.display_name}}</v-list-item-title>
|
||||||
<v-avatar>
|
|
||||||
<img :src="detial[0].icon"
|
|
||||||
class="iconCheck"
|
|
||||||
/>
|
|
||||||
</v-avatar>
|
|
||||||
</template>
|
|
||||||
<v-list-item-title class="textCheck">{{detial[0].title}}</v-list-item-title>
|
|
||||||
<v-list-item-subtitle ><a href="https://wiki.openstreetmap.org/wiki/Key:wheelchair" class="knowMore">{{detial[0].subtitle}}</a></v-list-item-subtitle>
|
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-avatar>
|
<v-avatar>
|
||||||
<img :src="detial[1].icon"
|
<img :src="wheelchairIcon" :class="wheelchairIconClass"/>
|
||||||
|
</v-avatar>
|
||||||
|
</template>
|
||||||
|
<v-list-item-title :class="wheelchairTextColorClass">{{ wheelchairAccessText }}</v-list-item-title>
|
||||||
|
<v-list-item-subtitle ><a href="https://wiki.openstreetmap.org/wiki/Key:wheelchair" class="knowMore">เรียนรู้เพิ่มเติม</a></v-list-item-subtitle>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-avatar>
|
||||||
|
<img :src="findLocation"
|
||||||
class="icon"
|
class="icon"
|
||||||
/>
|
/>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title class="text-decoration-underline">{{detial[1].title}}</v-list-item-title>
|
<v-list-item-title class="text-decoration-underline">{{nearestStructureData.lon}} , {{nearestStructureData.lat}}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
||||||
<v-list-item>
|
<!-- <v-list-item>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-avatar>
|
<v-avatar>
|
||||||
<img :src="detial[2].icon"
|
<img :src="clock"
|
||||||
class="icon"
|
class="icon"
|
||||||
/>
|
/>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title>{{detial[2].title}}</v-list-item-title>
|
<v-list-item-title class="text-decoration-underline">{{nearestStructureData.infoWheelchair}}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item> -->
|
||||||
|
|
||||||
|
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<v-card-actions class="stick-bottom btnlist-height justify-sa">
|
<v-card-actions class="stick-bottom btnlist-height justify-sa">
|
||||||
<v-btn rounded="xl" variant="tonal" width="45vw" height="44px">
|
<v-btn @click="addToFavorites" rounded="xl" variant="tonal" width="45vw" height="44px">
|
||||||
เพิ่มลงในสถานที่โปรด</v-btn>
|
เพิ่มลงในสถานที่โปรด</v-btn>
|
||||||
<v-btn rounded="xl" variant="flat" class="text-white" width="45vw" height="44px" color="#f16322">
|
<v-btn @click="viewRoute" rounded="xl" variant="flat" class="text-white" width="45vw" height="44px" color="#f16322">
|
||||||
ดูเส้นทาง</v-btn>
|
ดูเส้นทาง</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
|
||||||
<v-btn :to="{name: 'favorite'}" variant="tonal" icon="mdi-close" style="position: absolute; top: 15px; right: 15px;" />
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
|
||||||
|
<v-list-item class="d-flex justify-center">
|
||||||
|
<v-btn-toggle
|
||||||
|
class="btn-toggle"
|
||||||
|
mandatory
|
||||||
|
>
|
||||||
|
<v-btn class="btn"><v-icon class="icon-walk"></v-icon></v-btn>
|
||||||
|
<v-btn class="btn"><v-icon class="icon-wheelchair"></v-icon></v-btn>
|
||||||
|
</v-btn-toggle>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item class="d-flex justify-center">
|
||||||
|
<v-toolbar
|
||||||
|
dense
|
||||||
|
floating
|
||||||
|
style="width: 90vw; background-color: transparent;"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
style="
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 12vw;
|
||||||
|
height: 100%;
|
||||||
|
">
|
||||||
|
<v-icon
|
||||||
|
class="icon-flag"
|
||||||
|
style="background-color: transparent;"
|
||||||
|
>
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
hide-details
|
||||||
|
prepend-icon="mdi-magnify"
|
||||||
|
style="
|
||||||
|
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(230, 230, 230, 1);
|
||||||
|
padding-left: 2vw;"
|
||||||
|
variant="invert-solo"
|
||||||
|
:model-value="formattedLocation"
|
||||||
|
flat
|
||||||
|
></v-text-field>
|
||||||
|
<v-btn
|
||||||
|
hide-details
|
||||||
|
style="
|
||||||
|
background-color: rgba(230, 230, 230, 1);
|
||||||
|
margin-left: 2vw;
|
||||||
|
margin-right: 1vw;
|
||||||
|
border-radius: 10px;
|
||||||
|
height: 86%;"
|
||||||
|
flat
|
||||||
|
>
|
||||||
|
<v-icon class="icon-plus"></v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item class="d-flex justify-center"
|
||||||
|
style="overflow-x:hidden"
|
||||||
|
>
|
||||||
|
<v-toolbar
|
||||||
|
dense
|
||||||
|
floating
|
||||||
|
style="
|
||||||
|
width: 90vw;
|
||||||
|
background-color: transparent;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
overflow-y: hidden;"
|
||||||
|
>
|
||||||
|
|
||||||
|
<v-icon
|
||||||
|
style="
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 12vw;
|
||||||
|
height: 100%;
|
||||||
|
">
|
||||||
|
<v-icon
|
||||||
|
class="icon-flag"
|
||||||
|
style="background-color: rgba(241, 99, 34, 1);"
|
||||||
|
>
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
</v-icon>
|
||||||
|
<v-text-field
|
||||||
|
@click="viewPopup"
|
||||||
|
readonly
|
||||||
|
hide-details
|
||||||
|
prepend-icon="mdi-magnify"
|
||||||
|
style="
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(230, 230, 230, 1);
|
||||||
|
padding-left: 2vw;
|
||||||
|
"
|
||||||
|
variant="invert-solo"
|
||||||
|
flat
|
||||||
|
:model-value="nearestStructureData.display_name"
|
||||||
|
>
|
||||||
|
|
||||||
|
</v-text-field>
|
||||||
|
<v-btn
|
||||||
|
hide-details
|
||||||
|
style="
|
||||||
|
background-color: rgba(230, 230, 230, 1);
|
||||||
|
margin-left: 2vw;
|
||||||
|
margin-right: 1vw;
|
||||||
|
border-radius: 10px;
|
||||||
|
height: 86%;
|
||||||
|
"
|
||||||
|
flat
|
||||||
|
>
|
||||||
|
|
||||||
|
<v-icon class="icon-vertical-arrows"></v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item class="d-flex justify-center">
|
||||||
|
<v-card-text>0 นาที</v-card-text>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-card-actions class="stick-bottom btnlist-height justify-sa">
|
||||||
|
<v-btn @click="viewPopup" rounded="xl" variant="tonal" width="45vw" height="44px">
|
||||||
|
ย้อนกลับ</v-btn>
|
||||||
|
<v-btn @click="enterRoute" rounded="xl" variant="flat" class="text-white" width="45vw" height="44px" color="#f16322">
|
||||||
|
เริ่มการนำทาง</v-btn>
|
||||||
|
|
||||||
|
</v-card-actions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
@click="closePopup"
|
||||||
|
variant="tonal" icon="mdi-close" style="position: absolute; top: 15px; right: 15px;" />
|
||||||
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import findLocation from '../../icons/Material/find-location.svg';
|
import findLocation from '../../icons/Adwaita/find-location.svg';
|
||||||
import clock from '../../icons/Material/clock.svg';
|
import clock from '../../icons/Adwaita/clock.svg';
|
||||||
import check from '../../icons/Material/check-round.svg';
|
import check from '../../icons/Adwaita/check-round.svg';
|
||||||
const destination = {
|
import cross from '../../icons/Adwaita/cross.svg';
|
||||||
title: "อนุสาวรีย์ชัยสมรภูมิ",
|
import wheelchair from '../../icons/Adwaita/wheelchair.svg'
|
||||||
subTitle: "ราชเทวี , กรุงเทพมหานคร"
|
import wheelchairlimited from '../../icons/Adwaita/wheelchair-limited.svg'
|
||||||
|
import nowheelchair from '../../icons/Adwaita/no-wheelchair.svg'
|
||||||
|
|
||||||
|
|
||||||
|
// import DestinationInfoCard from '@/components/DestinationInfoCard.vue';
|
||||||
|
// const destination = {
|
||||||
|
// title: "อนุสาวรีย์ชัยสมรภูมิ",
|
||||||
|
// subTitle: "ราชเทวี , กรุงเทพมหานคร"
|
||||||
|
// }
|
||||||
|
// const detial = [
|
||||||
|
// {
|
||||||
|
// icon: check,
|
||||||
|
// subtitle: 'เรียนรู้เพิ่มเติม',
|
||||||
|
// title: 'Unrestricted Wheelchair access',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// icon: findLocation,
|
||||||
|
// subtitle: '',
|
||||||
|
// title: '13.76493, 100.53828',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// icon: clock,
|
||||||
|
// subtitle: '',
|
||||||
|
// title: 'Mo-Su 11:30-22:00',
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
nearestStructureData: Object,
|
||||||
|
onClose: Function,
|
||||||
|
infoWheelchair: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPopup: true,
|
||||||
|
showRoute: false,
|
||||||
|
userLocation: null,
|
||||||
|
isLocationRequested: false,
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
cardHeight() {
|
||||||
|
return this.showRoute ? '45vh' : '50vh';
|
||||||
|
},
|
||||||
|
formattedLocation() {
|
||||||
|
if (this.isLocationRequested && !this.userLocation) {
|
||||||
|
return 'Requesting location...';
|
||||||
|
} else if (this.userLocation) {
|
||||||
|
return `${this.userLocation.lon.toFixed(6)}, ${this.userLocation.lat.toFixed(6)}`;
|
||||||
}
|
}
|
||||||
const detial = [
|
return 'Location not available';
|
||||||
{
|
|
||||||
icon: check,
|
|
||||||
subtitle: 'เรียนรู้เพิ่มเติม',
|
|
||||||
title: 'Unrestricted Wheelchair access',
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: findLocation,
|
//icon
|
||||||
subtitle: '',
|
wheelchairIcon() {
|
||||||
title: '13.76493, 100.53828',
|
switch(this.nearestStructureData.infoWheelchair) {
|
||||||
|
case 'yes':
|
||||||
|
return wheelchair;
|
||||||
|
case 'limited':
|
||||||
|
return wheelchairlimited;
|
||||||
|
case 'no':
|
||||||
|
return nowheelchair;
|
||||||
|
default:
|
||||||
|
return cross;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: clock,
|
//icon-color
|
||||||
subtitle: '',
|
wheelchairIconClass() {
|
||||||
title: 'Mo-Su 11:30-22:00',
|
// return this.nearestStructureData.infoWheelchair === 'limited' ? 'iconCheckLimited' : 'iconCheck';
|
||||||
|
switch(this.nearestStructureData.infoWheelchair) {
|
||||||
|
case 'yes':
|
||||||
|
return 'iconCheck';
|
||||||
|
case 'limited':
|
||||||
|
return 'iconCheckLimited';
|
||||||
|
case 'no':
|
||||||
|
return 'iconCheckNo';
|
||||||
|
default:
|
||||||
|
return 'iconCheckDefault';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]
|
|
||||||
|
//text
|
||||||
|
wheelchairAccessText() {
|
||||||
|
switch(this.nearestStructureData.infoWheelchair) {
|
||||||
|
case 'yes':
|
||||||
|
return 'วีลแชร์สามารถเข้าถึงได้';
|
||||||
|
case 'limited':
|
||||||
|
return 'วีลแชร์เข้าถึงได้อย่างจำกัด';
|
||||||
|
case 'no':
|
||||||
|
return 'วีลแชร์ไม่สามารถเข้าถึงได้';
|
||||||
|
default:
|
||||||
|
return 'ไม่มีข้อมูลว่าวีลแชร์สามารถเข้าถึงได้หรือไม่';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
//text-color
|
||||||
|
|
||||||
|
wheelchairTextColorClass() {
|
||||||
|
switch(this.nearestStructureData.infoWheelchair) {
|
||||||
|
case 'yes':
|
||||||
|
return 'textColorYes';
|
||||||
|
case 'limited':
|
||||||
|
return 'textColorLimited';
|
||||||
|
case 'no':
|
||||||
|
return 'textColorNo';
|
||||||
|
default:
|
||||||
|
return 'textColorDefault';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closePopup() {
|
||||||
|
this.showPopup = false;
|
||||||
|
|
||||||
|
this.$emit('updateRouting', {route:null,isRouting:false});
|
||||||
|
this.isRouting = false;
|
||||||
|
|
||||||
|
this.onClose();
|
||||||
|
},
|
||||||
|
viewRoute() {
|
||||||
|
this.getUserLocation();
|
||||||
|
this.showRoute = true;
|
||||||
|
},
|
||||||
|
enterRoute() {
|
||||||
|
console.log('Entering Route!');
|
||||||
|
this.Routing();
|
||||||
|
},
|
||||||
|
getUserLocation() {
|
||||||
|
this.isLocationRequested = true;
|
||||||
|
if (navigator.geolocation) {
|
||||||
|
navigator.geolocation.getCurrentPosition(this.setLocation, this.handleLocationError)
|
||||||
|
} else {
|
||||||
|
console.error("Geolocation is not supported by this browser.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setLocation(position) {
|
||||||
|
this.userLocation = {
|
||||||
|
lat: position.coords.latitude,
|
||||||
|
lon: position.coords.longitude
|
||||||
|
};
|
||||||
|
console.log('User Location:', this.userLocation);
|
||||||
|
},
|
||||||
|
handleLocationError(error) {
|
||||||
|
console.error('Error getting location:', error);
|
||||||
|
},
|
||||||
|
viewPopup(){
|
||||||
|
this.showPopup = true;
|
||||||
|
this.showRoute = false;
|
||||||
|
|
||||||
|
this.$emit('updateRouting', {route:null,isRouting:false});
|
||||||
|
this.isRouting = false;
|
||||||
|
},
|
||||||
|
addToFavorites() {
|
||||||
|
const currentUser = JSON.parse(sessionStorage.getItem('current_user'));
|
||||||
|
|
||||||
|
if (currentUser) {
|
||||||
|
console.log('Logged in. Proceed to add to favorites.');
|
||||||
|
|
||||||
|
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/favorites/create`, {
|
||||||
|
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
user_id: currentUser.id, // ใช้ userId ของผู้ใช้ที่ล็อกอินอยู่
|
||||||
|
place_name: this.nearestStructureData.display_name,
|
||||||
|
location: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates: [parseFloat(this.nearestStructureData.lon), parseFloat(this.nearestStructureData.lat)]
|
||||||
|
},
|
||||||
|
wheelchair_access: 'Accessible',
|
||||||
|
highway_type: 'Highway'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
console.log('Add to favorites success');
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
throw Error(`Add to favorites failed (${res.status})`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('User not logged in. Unable to add to favorites.');
|
||||||
|
this.$router.push({ name: 'login' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Routing(){
|
||||||
|
if(!this.isRouting){
|
||||||
|
console.log('Start routing!!');
|
||||||
|
console.log(`nearestStructureData : ${this.nearestStructureData.lon},${this.nearestStructureData.lat}`);
|
||||||
|
console.log(`userLocation : ${this.userLocation.lon},${this.userLocation.lat}`);
|
||||||
|
// Make a request to OpenRouteService API for a sample route
|
||||||
|
const apiKey = import.meta.env.VITE_OPENROUTESERVICE_API_KEY;
|
||||||
|
const startCoord = `${this.userLocation.lon},${this.userLocation.lat}`;//'100.53860,13.76410';
|
||||||
|
const endCoord = `${this.nearestStructureData.lon},${this.nearestStructureData.lat}`;//'100.53928,13.76526';
|
||||||
|
|
||||||
|
axios.get(`https://api.openrouteservice.org/v2/directions/wheelchair?api_key=${apiKey}&start=${startCoord}&end=${endCoord}`)
|
||||||
|
.then(response => {
|
||||||
|
const route = response.data.features[0].geometry.coordinates;
|
||||||
|
console.log('This is route :',{route:route})
|
||||||
|
this.$emit('updateRouting', {route:route,isRouting:true});
|
||||||
|
this.isRouting = true;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert(`Can't routing`)
|
||||||
|
console.error('Error fetching route:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -129,5 +466,120 @@ const detial = [
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 45vh;
|
height: 45vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title-text
|
||||||
|
{
|
||||||
|
color: rgba(0, 0, 0, 1);
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sutitle-text{
|
||||||
|
color: rgba(155, 155, 155, 1);
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-walk {
|
||||||
|
background-color: #000000;
|
||||||
|
-webkit-mask: url(icons/Adwaita/walk.svg) no-repeat center;
|
||||||
|
mask: url(icons/Adwaita/walk.svg) no-repeat center;
|
||||||
|
}
|
||||||
|
.icon-wheelchair {
|
||||||
|
background-color: #000000;
|
||||||
|
-webkit-mask: url(icons/Adwaita/wheelchair.svg) no-repeat center;
|
||||||
|
mask: url(icons/Adwaita/wheelchair.svg) no-repeat center;
|
||||||
|
}
|
||||||
|
.icon-plus {
|
||||||
|
background-color: #000000;
|
||||||
|
-webkit-mask: url(icons/Adwaita/plus.svg) no-repeat center;
|
||||||
|
mask: url(icons/Adwaita/plus.svg) no-repeat center;
|
||||||
|
}
|
||||||
|
.icon-vertical-arrows {
|
||||||
|
background-color: #000000;
|
||||||
|
-webkit-mask: url(icons/Adwaita/vertical-arrows.svg) no-repeat center;
|
||||||
|
mask: url(icons/Adwaita/vertical-arrows.svg) no-repeat center;
|
||||||
|
}
|
||||||
|
.icon-flag {
|
||||||
|
background-color: #000000;
|
||||||
|
-webkit-mask: url(icons/Adwaita/flag.svg) no-repeat center;
|
||||||
|
mask: url(icons/Adwaita/flag.svg) no-repeat center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-toggle
|
||||||
|
{
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(230, 230, 230, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-toggle .btn
|
||||||
|
{
|
||||||
|
border-radius: 0px;
|
||||||
|
background-color: rgba(230, 230, 230, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.destination-card .v-input__control
|
||||||
|
{
|
||||||
|
|
||||||
|
max-height: 6vh;
|
||||||
|
max-width: 50vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.destination-card .v-field__input
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconCheckYes {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(45%) sepia(85%) saturate(380%) hue-rotate(100deg) brightness(98%) contrast(87%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconCheckLimited {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(59%) sepia(84%) saturate(813%) hue-rotate(5deg) brightness(99%) contrast(92%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconCheckDefault {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(59%) sepia(84%) saturate(813%) hue-rotate(5deg) brightness(99%) contrast(92%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconCheckNo {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(25%) sepia(20%) saturate(5539%) hue-rotate(326deg) brightness(86%) contrast(109%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.textColorLimited {
|
||||||
|
filter: brightness(0) saturate(100%) invert(59%) sepia(84%) saturate(813%) hue-rotate(5deg) brightness(99%) contrast(92%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.textColorNo {
|
||||||
|
filter: brightness(0) saturate(100%) invert(25%) sepia(20%) saturate(5539%) hue-rotate(326deg) brightness(86%) contrast(109%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.textColorDefault {
|
||||||
|
filter: brightness(0) saturate(100%) invert(59%) sepia(84%) saturate(813%) hue-rotate(5deg) brightness(99%) contrast(92%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.textColorYes {
|
||||||
|
filter: brightness(0) saturate(100%) invert(45%) sepia(85%) saturate(380%) hue-rotate(100deg) brightness(98%) contrast(87%);
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
38
src/components/NavigationMapCard.vue
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<v-card
|
||||||
|
class="stick-bottom card-height"
|
||||||
|
width="100%"
|
||||||
|
height="60vh"
|
||||||
|
style="padding-top: 15px; font-weight:bold;"
|
||||||
|
:title="nearestStructureData.display_name"
|
||||||
|
:subtitle="destination.subTitle"
|
||||||
|
>
|
||||||
|
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
nearestStructureData: Object,
|
||||||
|
onClose: Function,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPopup: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closePopup() {
|
||||||
|
this.showPopup = false;
|
||||||
|
this.onClose();
|
||||||
|
},
|
||||||
|
|
||||||
|
viewRoute() {
|
||||||
|
this.$emit('changeComponent', { userLocation: this.userLocation, destination: this.destination });
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -8,6 +8,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import DestinationInfoCard from '@/components/DestinationInfoCard.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
nearestStructureData: Object,
|
nearestStructureData: Object,
|
||||||
|
|
0
src/iconsets/adwaita.ts
Normal file
|
@ -1,6 +1,7 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import '@/style.css'
|
import '@/style.css'
|
||||||
|
|
||||||
|
|
||||||
// Vuetify
|
// Vuetify
|
||||||
import 'vuetify/styles'
|
import 'vuetify/styles'
|
||||||
import { createVuetify } from 'vuetify'
|
import { createVuetify } from 'vuetify'
|
||||||
|
@ -12,10 +13,11 @@ import OpenLayersMap from "vue3-openlayers";
|
||||||
|
|
||||||
import App from '@/App.vue'
|
import App from '@/App.vue'
|
||||||
import router from '@/plugins/router'
|
import router from '@/plugins/router'
|
||||||
|
import vue3GoogleLogin from 'vue3-google-login'
|
||||||
|
|
||||||
const vuetify = createVuetify({
|
const vuetify = createVuetify({
|
||||||
components,
|
components,
|
||||||
directives,
|
directives,
|
||||||
})
|
})
|
||||||
|
|
||||||
createApp(App).use(router).use(vuetify).use(OpenLayersMap).mount('#app')
|
createApp(App).use(router).use(vuetify).use(OpenLayersMap).use(vue3GoogleLogin,{ clientId: import.meta.env.VITE_CLIENT_ID }).mount('#app')
|
|
@ -1,32 +1,88 @@
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<v-sheet class="d-flex justify-center">
|
<v-sheet class="d-flex justify-center align-center">
|
||||||
|
|
||||||
|
|
||||||
<v-sheet class="ma-2 pa-4 mb-auto">
|
<v-sheet class="ma-2 pa-4 mb-auto">
|
||||||
|
|
||||||
<v-row class="ma-2" justify="centered">
|
<v-sheet style="display: flex; align-items: center;justify-content: center;">
|
||||||
|
|
||||||
<v-img cover src="../../icons/LittleLines.svg"></v-img>
|
<img src="../../icons/LittleLines.svg">
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-list-item
|
|
||||||
center
|
|
||||||
class="text-black"
|
|
||||||
title="Little Lines"
|
|
||||||
subtitle="openKMITL Community"
|
|
||||||
|
|
||||||
>
|
|
||||||
|
|
||||||
</v-list-item>
|
|
||||||
<a class="versionbutton">DATE-VERSION</a>
|
|
||||||
|
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
|
|
||||||
|
<p class="text-center text-h4 font-weight-black">Little Lines</p>
|
||||||
|
|
||||||
|
<p class="text-center">TechTransThai Community</p>
|
||||||
|
|
||||||
|
|
||||||
|
<v-sheet style="display: flex; align-items: center;justify-content: center;">
|
||||||
|
<a class="versionbutton">2024.06.0</a>
|
||||||
|
</v-sheet>
|
||||||
|
|
||||||
|
|
||||||
|
<v-list class = "ma-2" density="compact" max-width="420px" min-width="200px" width="95vw">
|
||||||
|
<v-list-item
|
||||||
|
v-for="(item, i) in sourcecode_items"
|
||||||
|
:key="i"
|
||||||
|
:value="item"
|
||||||
|
:href="'https://forge.techtransthai.org/little-lines'"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<!-- <v-icon :icon="item.icon"></v-icon> -->
|
||||||
|
<img src="../../icons/Adwaita/right.svg">
|
||||||
|
</template>
|
||||||
|
<v-list-item-title v-text="item.text"></v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
|
||||||
|
<v-list class = "ma-2" density="compact">
|
||||||
|
<v-list-subheader>รายงานปัญหาและข้อเสนอแนะ</v-list-subheader>
|
||||||
|
|
||||||
|
<v-list-item
|
||||||
|
v-for="(item, i) in report_items"
|
||||||
|
:key="i"
|
||||||
|
:value="item"
|
||||||
|
:href="item.link"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<!-- <v-icon :icon="item.icon"></v-icon> -->
|
||||||
|
<img src="../../icons/Adwaita/right.svg">
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-list-item-title v-text="item.text"></v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-sheet>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
|
import { getTopRight } from 'ol/extent';
|
||||||
|
import {RouterLink} from 'vue-router';
|
||||||
|
|
||||||
|
const sourcecode_items = [
|
||||||
|
{ text: 'ซอร์สโค้ด',
|
||||||
|
icon: 'mdi-chevron-right',}
|
||||||
|
]
|
||||||
|
|
||||||
|
const report_items = [
|
||||||
|
{ text: 'Discord',
|
||||||
|
icon: 'mdi-chevron-right',
|
||||||
|
link: 'https://discord.gg/3tRdRE3tGv'},
|
||||||
|
{ text: 'Facebook',
|
||||||
|
icon: 'mdi-chevron-right',
|
||||||
|
link: 'https://www.facebook.com'},
|
||||||
|
{ text: 'Google Forms',
|
||||||
|
icon: 'mdi-chevron-right',
|
||||||
|
link: 'https://forms.google.com'},
|
||||||
|
]
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -45,6 +101,11 @@
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-h4 {
|
||||||
|
font-family: Cantarell, sans-serif !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -1,79 +1,83 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- <h1>Favorite</h1> -->
|
<v-card
|
||||||
|
class="mx-auto fav-card">
|
||||||
<!-- <v-card
|
|
||||||
class="mx-auto"
|
|
||||||
max-width="420"
|
|
||||||
height="600"
|
|
||||||
>
|
|
||||||
|
|
||||||
<v-list lines="two">
|
<v-list lines="two">
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="item in items"
|
v-for="(favorite, index) in favorite"
|
||||||
:key="item.title"
|
:key="index"
|
||||||
:title="item.title"
|
@click="handleClick(favorite)"
|
||||||
:subtitle="item.desc"
|
|
||||||
:prepend-avatar="pin_svg"
|
|
||||||
></v-list-item>
|
|
||||||
</v-list>
|
|
||||||
|
|
||||||
</v-card> -->
|
|
||||||
|
|
||||||
<v-card fluid
|
|
||||||
class="mx-auto"
|
|
||||||
max-width="500px"
|
|
||||||
width="90vw"
|
|
||||||
|
|
||||||
max-height="600px"
|
|
||||||
height="90vh"
|
|
||||||
>
|
>
|
||||||
<v-list
|
|
||||||
item-props
|
|
||||||
lines="three"
|
|
||||||
>
|
|
||||||
<v-list-item
|
|
||||||
v-for="(item, i) in items"
|
|
||||||
:to="{name: 'destination-info'}"
|
|
||||||
:key="i"
|
|
||||||
:value="item"
|
|
||||||
|
|
||||||
>
|
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-avatar>
|
<v-icon class="icon-pin"></v-icon>
|
||||||
<v-img
|
|
||||||
:height="30"
|
|
||||||
src= "./icons/Adwaita/pin.svg"
|
|
||||||
></v-img>
|
|
||||||
</v-avatar>
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<v-list-item-title v-text="item.title"></v-list-item-title>
|
<v-list-item-content>
|
||||||
<v-list-item-subtitle v-text="item.desc"></v-list-item-subtitle>
|
<v-list-item-title>{{ favorite.place_name }}</v-list-item-title>
|
||||||
<v-divider></v-divider>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import {RouterLink} from 'vue-router';
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
const pin_svg = "./icons/Adwaita/pin.svg"
|
export default {
|
||||||
const items = [
|
setup() {
|
||||||
{
|
const favorite = ref([]);
|
||||||
title: 'หอพักนักศึกษา',
|
const router = useRouter();
|
||||||
desc: 'ลาดกระบัง, กรุงเทพมหานคร',
|
|
||||||
},
|
const handleClick = (favoriteItem) => {
|
||||||
{
|
console.log('Clicked:', favoriteItem);
|
||||||
title: 'อนุสาวรีย์ชัยสมรภูมิ',
|
router.push({ name: 'home', params: { favoriteLocation: favoriteItem } });
|
||||||
desc: 'ราชเทวี, กรุงเทพมหานคร',
|
|
||||||
},
|
};
|
||||||
]
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const currentUser = JSON.parse(sessionStorage.getItem('current_user'));
|
||||||
|
|
||||||
|
if (currentUser && currentUser.id) {
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/favorites/${currentUser.id}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch favorites');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
favorite.value = data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
favorite,
|
||||||
|
handleClick
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.icon-pin{
|
||||||
|
background-color: black;
|
||||||
|
-webkit-mask: url(icons/Adwaita/pin.svg) no-repeat center;
|
||||||
|
mask: url(icons/Adwaita/pin.svg) no-repeat center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.fav-card
|
||||||
|
{
|
||||||
|
margin-top: 2vh;
|
||||||
|
max-width: 90vw;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
box-shadow: 0px 1px 4px 1px rgba(0, 0, 0, 0.13);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,12 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<router-view />
|
<router-view
|
||||||
<Popup
|
class="router-view"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DestinationInfoCard
|
||||||
|
class="DestinationInfoCard"
|
||||||
v-if="popupData"
|
v-if="popupData"
|
||||||
:nearestStructureData="popupData"
|
:nearestStructureData="popupData"
|
||||||
:onClose="closePopup"
|
:onClose="closePopup"
|
||||||
|
@updateRouting="handleRouting"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <searchbar/> -->
|
<!-- <searchbar/> -->
|
||||||
|
@ -14,30 +20,43 @@
|
||||||
<v-app-bar scroll-threshold="0"
|
<v-app-bar scroll-threshold="0"
|
||||||
class="mx-auto px-auto"
|
class="mx-auto px-auto"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<div class="flex-grow-1">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
@keyup.enter="performSearch"
|
@keyup.enter="performSearch"
|
||||||
@input="performSearch"
|
@input="performSearch"
|
||||||
|
@click="toggleSearchBar()"
|
||||||
density="compact"
|
density="compact"
|
||||||
variant="solo"
|
variant="solo"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
single-line
|
single-line
|
||||||
hide-details
|
hide-details
|
||||||
|
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-btn icon>
|
</div>
|
||||||
|
|
||||||
|
<v-btn icon @click="showSearchBar = !showSearchBar">
|
||||||
<v-icon>mdi-crosshairs-gps</v-icon>
|
<v-icon>mdi-crosshairs-gps</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
</v-layout>
|
</v-layout>
|
||||||
|
|
||||||
<div v-if="searchResults.length > 0" class="search-results">
|
<v-list v-if="searchResults.length > 0 && !showSearchBar" class="search-results">
|
||||||
<ul>
|
<v-list-item
|
||||||
<li v-for="result in searchResults" :key="result.place_id" @click="moveToLocation(result)">
|
v-for="result in searchResults"
|
||||||
|
:key="result.place_id"
|
||||||
|
@click="moveToLocation(result),toggleSearchBar()"
|
||||||
|
>
|
||||||
|
<v-list-item-icon>
|
||||||
|
<v-icon>mdi-map-marker</v-icon>
|
||||||
|
</v-list-item-icon>
|
||||||
{{ result.display_name }}
|
{{ result.display_name }}
|
||||||
</li>
|
</v-list-item>
|
||||||
</ul>
|
</v-list>
|
||||||
</div>
|
|
||||||
<!-- <map/> -->
|
<!-- <map/> -->
|
||||||
<ol-map
|
<ol-map
|
||||||
:loadTilesWhileAnimating="true"
|
:loadTilesWhileAnimating="true"
|
||||||
|
@ -61,6 +80,33 @@
|
||||||
<ol-tile-layer>
|
<ol-tile-layer>
|
||||||
<ol-source-osm />
|
<ol-source-osm />
|
||||||
</ol-tile-layer>
|
</ol-tile-layer>
|
||||||
|
|
||||||
|
<ol-vector-layer v-if="isRouting">
|
||||||
|
<ol-source-vector>
|
||||||
|
<!-- Line String Geometry -->
|
||||||
|
<ol-feature>
|
||||||
|
<ol-geom-line-string :coordinates="route"></ol-geom-line-string>
|
||||||
|
</ol-feature>
|
||||||
|
|
||||||
|
<!-- Multi Point Geometry -->
|
||||||
|
<ol-feature>
|
||||||
|
<ol-geom-multi-point :coordinates="[
|
||||||
|
[route[0][0],route[0][1]],
|
||||||
|
[route[route.length-1][0],route[route.length-1][1]]
|
||||||
|
]"></ol-geom-multi-point>
|
||||||
|
</ol-feature>
|
||||||
|
</ol-source-vector>
|
||||||
|
|
||||||
|
<!-- Style for the Line and Points -->
|
||||||
|
<ol-style>
|
||||||
|
<!-- Style for Line -->
|
||||||
|
<ol-style-stroke :color="strokeColor" :width="strokeWidth"></ol-style-stroke>
|
||||||
|
|
||||||
|
<!-- Style for Points -->
|
||||||
|
<ol-style-icon :src="startMarker" :scale="0.1" :anchor="[0.5, 1]"></ol-style-icon>
|
||||||
|
</ol-style>
|
||||||
|
</ol-vector-layer>
|
||||||
|
|
||||||
</ol-map>
|
</ol-map>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -68,11 +114,12 @@
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
import searchbar from '@/components/searchbar.vue';
|
|
||||||
import Popup from "@/components/Popup.vue"; // Import the Popup componen
|
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import DestinationInfoCard from '@/components/DestinationInfoCard.vue';
|
||||||
|
|
||||||
|
import startMarker from '../../assets/map-marker-symbolic.png';
|
||||||
|
import stopMarker from '../../assets/flag-filled-symbolic.png';
|
||||||
|
|
||||||
const center = ref([100.538611, 13.764722]);
|
const center = ref([100.538611, 13.764722]);
|
||||||
const projection = ref("EPSG:4326");
|
const projection = ref("EPSG:4326");
|
||||||
|
@ -81,17 +128,27 @@ const rotation = ref(0);
|
||||||
|
|
||||||
const popupData = ref(null);
|
const popupData = ref(null);
|
||||||
|
|
||||||
|
const isRouting = ref(false);
|
||||||
|
const route = ref(null);
|
||||||
|
const infoWheelchair = ref(null)
|
||||||
|
const strokeWidth = ref(5);
|
||||||
|
const strokeColor = ref("red");
|
||||||
|
|
||||||
//search
|
//search
|
||||||
const searchQuery = ref("");
|
const searchQuery = ref("");
|
||||||
const searchResults = ref([]);
|
const searchResults = ref([]);
|
||||||
|
const showSearchBar = ref(false);
|
||||||
|
|
||||||
|
const toggleSearchBar = () => {
|
||||||
|
showSearchBar.value = !showSearchBar.value;
|
||||||
|
};
|
||||||
|
|
||||||
const moveToLocation = (result) => {
|
const moveToLocation = (result) => {
|
||||||
// Extract latitude and longitude from the selected result
|
|
||||||
const lat = parseFloat(result.lat);
|
const lat = parseFloat(result.lat);
|
||||||
const lon = parseFloat(result.lon);
|
const lon = parseFloat(result.lon);
|
||||||
|
|
||||||
// Update the center coordinates to move the camera to the selected location
|
|
||||||
center.value = [lon, lat];
|
center.value = [lon, lat];
|
||||||
|
showSearchBar.value = false;
|
||||||
|
popupData.value = result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const performSearch = async () => {
|
const performSearch = async () => {
|
||||||
|
@ -100,26 +157,56 @@ const performSearch = async () => {
|
||||||
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQuery.value)}`
|
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQuery.value)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Process the search results and limit to, let's say, 5 results
|
|
||||||
searchResults.value = response.data.slice(0, 5);
|
searchResults.value = response.data.slice(0, 5);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching search results:", error);
|
console.error("Error fetching search results:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//Show API
|
//Show API
|
||||||
const handleMapClick = async event => {
|
const handleMapClick = async event => {
|
||||||
const clickedCoordinate = event.coordinate;
|
const clickedCoordinate = event.coordinate;
|
||||||
|
const overpassQuery = `[out:json];
|
||||||
|
(
|
||||||
|
node(around:10,${clickedCoordinate[1]},${clickedCoordinate[0]})["wheelchair"];
|
||||||
|
way(around:10,${clickedCoordinate[1]},${clickedCoordinate[0]})["wheelchair"];
|
||||||
|
relation(around:10,${clickedCoordinate[1]},${clickedCoordinate[0]})["wheelchair"];
|
||||||
|
);
|
||||||
|
out;`;
|
||||||
|
const overpassUrl = 'https://overpass-api.de/api/interpreter';
|
||||||
|
|
||||||
|
axios.post(overpassUrl, `data=${encodeURIComponent(overpassQuery)}`, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
}).then(response => {
|
||||||
|
// Process the data returned by the Overpass API
|
||||||
|
response.data.elements.forEach(element => {
|
||||||
|
if (element.tags && element.tags.wheelchair) {
|
||||||
|
console.log(`wheelchair: ${element.tags.wheelchair}`);
|
||||||
|
const wheelchairValues = element.tags.wheelchair;
|
||||||
|
infoWheelchair.value = wheelchairValues;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error fetching data from Overpass API:', error);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${clickedCoordinate[1]}&lon=${clickedCoordinate[0]}`
|
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${clickedCoordinate[1]}&lon=${clickedCoordinate[0]}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const nearestStructureData = response.data;
|
let nearestStructureData = response.data;
|
||||||
|
|
||||||
|
nearestStructureData = {
|
||||||
|
...nearestStructureData,
|
||||||
|
infoWheelchair: infoWheelchair
|
||||||
|
}
|
||||||
|
//infoWheelchair can null,yes,no
|
||||||
popupData.value = nearestStructureData; // Show popup
|
popupData.value = nearestStructureData; // Show popup
|
||||||
|
|
||||||
|
// console.log(nearestStructureData)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching reverse geocoding data:", error);
|
console.error("Error fetching reverse geocoding data:", error);
|
||||||
}
|
}
|
||||||
|
@ -129,6 +216,14 @@ const closePopup = () => {
|
||||||
popupData.value = null; // Hide popup
|
popupData.value = null; // Hide popup
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRouting = (res) => {
|
||||||
|
console.log("Received Route:", res);
|
||||||
|
route.value = res.route;
|
||||||
|
isRouting.value = res.isRouting;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -166,4 +261,12 @@ const closePopup = () => {
|
||||||
.search-results li:hover {
|
.search-results li:hover {
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.router-view{
|
||||||
|
z-index: -15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DestinationInfoCard{
|
||||||
|
z-index: 15;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -3,31 +3,82 @@
|
||||||
<v-app>
|
<v-app>
|
||||||
<top-bar :show-back-icon="true" :page-title="pageTitle" />
|
<top-bar :show-back-icon="true" :page-title="pageTitle" />
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container>
|
<div class="text-center mt-8 mb-16">
|
||||||
<v-form name="login-form">
|
<div class="text-h4 font-weight-bold">
|
||||||
<div class="mb-3">
|
ยินดีต้อนรับกลับสู่
|
||||||
<label for="username">Username: </label>
|
<div>Little Lines</div>
|
||||||
<input type="text" id="username" v-model="input.username" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label for="password">Password: </label>
|
|
||||||
<input type="password" id="password" v-model="input.password" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</v-form>
|
<div class="mx-5">
|
||||||
</v-container>
|
<v-text-field
|
||||||
|
class="email"
|
||||||
|
v-model="input.email"
|
||||||
|
label="อีเมล"
|
||||||
|
variant="solo"
|
||||||
|
>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<img
|
||||||
|
class="iconEdit"
|
||||||
|
:src="edit"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
class="password"
|
||||||
|
v-model="input.password"
|
||||||
|
label="รหัสผ่าน"
|
||||||
|
variant="solo"
|
||||||
|
>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<img
|
||||||
|
class="iconEdit"
|
||||||
|
:src="edit"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="iconEyeNotLooking"
|
||||||
|
:src="eyeNotLooking"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-contaioner>
|
||||||
|
<v-row class="button">
|
||||||
|
<v-btn @click.prevent="login" rounded="xl" variant="flat" class="text-white" width="45vw" height="44px" color="#f16322">
|
||||||
|
ลงชื่อเข้าใช้</v-btn>
|
||||||
|
</v-row>
|
||||||
|
<v-row class="button">
|
||||||
|
<v-btn @click.prevent="loginGoogle" class="text-none" rounded="xl" variant="tonal" width="45vw" height="44px">
|
||||||
|
ลงชื่อเข้าใช้ด้วย Google</v-btn>
|
||||||
|
</v-row>
|
||||||
|
<v-row class="button">
|
||||||
|
<v-btn :to="{name: 'register'}" rounded="xl" variant="tonal" width="45vw" height="44px">
|
||||||
|
ฉันต้องการสมัครสมาชิก</v-btn>
|
||||||
|
</v-row>
|
||||||
|
</v-contaioner>
|
||||||
|
|
||||||
|
|
||||||
<v-btn @click.prevent="login">ลงชื่อเข้าใช้</v-btn>
|
|
||||||
<v-btn :to="{name: 'register'}">ฉันต้องการสมัครสมาชิก</v-btn>
|
|
||||||
|
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import edit from '../../icons/Material/edit.svg';
|
||||||
|
import eyeNotLooking from '../../icons/Material/eye-not-looking.svg';
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {RouterLink} from 'vue-router';
|
import {RouterLink} from 'vue-router';
|
||||||
import TopBar from '@/components/TopBar.vue';
|
import TopBar from '@/components/TopBar.vue';
|
||||||
|
import { VContainer } from 'vuetify/lib/components/index.mjs';
|
||||||
|
|
||||||
|
import { googleSdkLoaded } from "vue3-google-login";
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -38,20 +89,107 @@ export default {
|
||||||
return {
|
return {
|
||||||
pageTitle: 'ลงชื่อเข้าใช้',
|
pageTitle: 'ลงชื่อเข้าใช้',
|
||||||
input: {
|
input: {
|
||||||
username: '',
|
email: '',
|
||||||
password: ''
|
password: ''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
login() {
|
login() {
|
||||||
if (this.input.username !== '' || this.input.password !== '') {
|
if (this.input.email !== '' && this.input.password !== '') {
|
||||||
console.log('Authenticated: Checking with Backend');
|
console.log('Authenticated: Checking with Backend');
|
||||||
|
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/users/login`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: this.input.email,
|
||||||
|
password: this.input.password,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json();
|
||||||
} else {
|
} else {
|
||||||
console.log('Username and Password cannot be empty');
|
throw Error(`Login failed (${res.status})`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
|
||||||
|
console.log(data.success);
|
||||||
|
sessionStorage.setItem('current_user', JSON.stringify({id:data.user._id,username:data.user.username,email:data.user.email}));
|
||||||
|
|
||||||
|
this.$router.push({ name: 'home' });
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
// Handle the error, e.g., display an error message to the user
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('Email and Password cannot be empty');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loginGoogle(){
|
||||||
|
googleSdkLoaded(google => {
|
||||||
|
google.accounts.oauth2
|
||||||
|
.initCodeClient({
|
||||||
|
client_id:
|
||||||
|
import.meta.env.VITE_CLIENT_ID,
|
||||||
|
scope: "email profile openid",
|
||||||
|
redirect_uri: `${import.meta.env.VITE_BACKEND_URL}/api/users/googleAuth/callback`,
|
||||||
|
callback: response => {
|
||||||
|
if (response.code) {
|
||||||
|
this.sendCodeToBackend(response.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.requestCode();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async sendCodeToBackend(code) {
|
||||||
|
try {
|
||||||
|
const headers = {
|
||||||
|
Authorization: code
|
||||||
|
};
|
||||||
|
const response = await axios.post(`${import.meta.env.VITE_BACKEND_URL}/api/users/googleAuth`, null, { headers });
|
||||||
|
const userDetails = response.data;
|
||||||
|
console.log("User Details:", userDetails);
|
||||||
|
this.userDetails = userDetails;
|
||||||
|
|
||||||
|
sessionStorage.setItem('current_user', JSON.stringify({id:userDetails.user._id}));
|
||||||
|
this.$router.push({ name: 'home' });
|
||||||
|
|
||||||
|
// Redirect to the homepage ("/")
|
||||||
|
// this.$router.push({ name: 'home' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to send authorization code:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.username {
|
||||||
|
margin-bottom: -21px;
|
||||||
|
}
|
||||||
|
.iconEdit, .iconEyeNotLooking {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
.iconEdit {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.iconEyeNotLooking {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -3,31 +3,89 @@
|
||||||
<v-app>
|
<v-app>
|
||||||
<top-bar :show-back-icon="true" :page-title="pageTitle" />
|
<top-bar :show-back-icon="true" :page-title="pageTitle" />
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container>
|
|
||||||
<v-form name="register-form">
|
<div class="text-center mt-8 mb-16">
|
||||||
<div class="mb-3">
|
<div class="text-h4">
|
||||||
<label for="username">Username: </label>
|
<div class="font-weight-bold">Little Lines</div>
|
||||||
<input type="text" id="username" v-model="input.username" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div>ระบบนำทางสำหรับ Micromobility</div>
|
||||||
<label for="password">Password: </label>
|
|
||||||
<input type="password" id="password" v-model="input.password" />
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="password">Password Confirmation: </label>
|
|
||||||
<input type="password" id="passwordConfirm" v-model="input.passwordConfirm" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</v-form>
|
<div class="mx-5">
|
||||||
</v-container>
|
<v-text-field
|
||||||
|
class="email"
|
||||||
|
v-model="input.email"
|
||||||
|
label="อีเมล"
|
||||||
|
variant="solo"
|
||||||
|
>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<img
|
||||||
|
class="iconEdit"
|
||||||
|
:src="edit"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
class="password"
|
||||||
|
v-model="input.password"
|
||||||
|
label="รหัสผ่าน"
|
||||||
|
variant="solo"
|
||||||
|
>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<img
|
||||||
|
class="iconEdit"
|
||||||
|
:src="edit"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="iconEyeNotLooking"
|
||||||
|
:src="eyeNotLooking"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
class="passwordC"
|
||||||
|
v-model="input.passwordConfirm"
|
||||||
|
label="ยืนยันรหัสผ่าน"
|
||||||
|
variant="solo"
|
||||||
|
>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<img
|
||||||
|
class="iconEdit"
|
||||||
|
:src="edit"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="iconEyeNotLooking"
|
||||||
|
:src="eyeNotLooking"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-checkbox color="#F16322" class="check">
|
||||||
|
<template v-slot:label>
|
||||||
|
<div>ฉันได้อ่านและยอมรับ</div>
|
||||||
|
<a href="https://www.google.co.th/?hl=th" >นโยบายความเป็นส่วนตัว</a>
|
||||||
|
</template>
|
||||||
|
</v-checkbox>
|
||||||
|
|
||||||
|
<div class="button-register">
|
||||||
|
<v-btn @click.prevent="register" rounded="xl" variant="flat" class="text-white" width="45vw" height="44px" color="#f16322">
|
||||||
|
สมัครสมาชิก</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
<v-btn @click.prevent="login">สมัครสมาชิค</v-btn>
|
|
||||||
|
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import edit from '../../icons/Material/edit.svg';
|
||||||
|
import eyeNotLooking from '../../icons/Material/eye-not-looking.svg';
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TopBar from '@/components/TopBar.vue';
|
import TopBar from '@/components/TopBar.vue';
|
||||||
|
|
||||||
|
@ -38,24 +96,84 @@
|
||||||
name: 'register',
|
name: 'register',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
pageTitle: 'สมัครสมาชิค',
|
pageTitle: 'สมัครสมาชิก',
|
||||||
input: {
|
input: {
|
||||||
username: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
passwordConfirm:''
|
passwordConfirm:''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
login() {
|
register() {
|
||||||
if (this.input.username !== '' || this.input.password !== '') {
|
if (this.input.username !== '' && ((this.input.password !='') && (this.input.password == this.input.passwordConfirm))) {
|
||||||
console.log('Authenticated: Checking with Backend');
|
console.log('Authenticated: Checking with Backend');
|
||||||
|
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/users/create`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: "temp",
|
||||||
|
password: this.input.password,
|
||||||
|
email: this.input.email,
|
||||||
|
isGoogleAccount: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if(res.ok){
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return res.json().then(data => {throw Error(`${data.registerStatus}`) });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
console.log(data.registerStatus)
|
||||||
|
this.$router.push({name : 'login'})
|
||||||
|
})
|
||||||
|
.catch((err) =>{
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
console.log("fisnished fetch");
|
||||||
} else {
|
} else {
|
||||||
console.log('Username and Password cannot be empty');
|
console.log('Email and Password cannot be empty');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.user {
|
||||||
|
background-color: aqua;
|
||||||
|
}
|
||||||
|
.email, .password {
|
||||||
|
margin-bottom: -21px;
|
||||||
|
}
|
||||||
|
.check {
|
||||||
|
display: flex;
|
||||||
|
justify-content:center;
|
||||||
|
padding-top: 5%;
|
||||||
|
padding-bottom: 5%;
|
||||||
|
}
|
||||||
|
.button-register {
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
-ms-transform: translateX(-50%);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
.iconEdit, .iconEyeNotLooking {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
.iconEdit {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.iconEyeNotLooking {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<v-container class="d-flex justify-center align-center">
|
<v-container class="d-flex justify-center align-center">
|
||||||
<div>
|
<div v-if="!currentUser">
|
||||||
<h1>ยังไม่ได้ลงชื่อเข้าใช้</h1>
|
<h1>ยังไม่ได้ลงชื่อเข้าใช้</h1>
|
||||||
<h2>ลงชื่อเข้าใช้เพื่อบันทึกการตั้งค่าอย่างปลอดภัย</h2>
|
<h2>ลงชื่อเข้าใช้เพื่อบันทึกการตั้งค่าอย่างปลอดภัย</h2>
|
||||||
<v-container class="d-flex justify-center align-center">
|
<v-container class="d-flex justify-center align-center">
|
||||||
<v-btn class="ma-2" width="150" :to="{name: 'login'}">ลงชื่อเข้าใช้</v-btn>
|
<v-btn class="ma-2" width="150" :to="{name: 'login'}">ลงชื่อเข้าใช้</v-btn>
|
||||||
<v-btn class="ma-2" width="150" :to="{name: 'register'}">สมัครสมาชิค</v-btn>
|
<v-btn class="ma-2" width="150" :to="{name: 'register'}">สมัครสมาชิก</v-btn>
|
||||||
</v-container>
|
</v-container>
|
||||||
</div>
|
</div>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<v-btn class = "ma-2 mt-3" width="100%"
|
<v-btn @click.prevent="logout" class = "ma-2 mt-3" width="100%"
|
||||||
color="red">
|
color="red">
|
||||||
ลงชื่อออก
|
ลงชื่อออก
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
@ -69,6 +69,10 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
import {RouterLink} from 'vue-router';
|
import {RouterLink} from 'vue-router';
|
||||||
|
import { VContainer } from 'vuetify/lib/components/index.mjs';
|
||||||
|
import router from '@/plugins/router'
|
||||||
|
|
||||||
|
const currentUser = sessionStorage.getItem('current_user');
|
||||||
|
|
||||||
const account_items = [
|
const account_items = [
|
||||||
{ text: 'ตั้งค่าบัญชี',
|
{ text: 'ตั้งค่าบัญชี',
|
||||||
|
@ -90,5 +94,18 @@ const display_items = [
|
||||||
icon: 'mdi-chevron-right',}
|
icon: 'mdi-chevron-right',}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
if (sessionStorage.getItem('current_user')){
|
||||||
|
console.log('loging out...');
|
||||||
|
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/users/logout`, {
|
||||||
|
method: "GET"
|
||||||
|
}).then(()=>{
|
||||||
|
console.log('loged out');
|
||||||
|
sessionStorage.removeItem('current_user');
|
||||||
|
router.push({ name: 'home' });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|