Compare commits

..

221 commits

Author SHA1 Message Date
0600f793f2 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2024-09-24 00:08:29 +02:00
7c6e7e0bb4 Disable prerender
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-03-06 00:19:25 +01:00
c80eb89db7 Lint and format
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-03-06 00:14:26 +01:00
9d0dbc520b Update error def
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-03-06 00:10:33 +01:00
fe9a3f3f25 API Not found error message
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-03-06 00:08:04 +01:00
2c6a1e570a bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-03-06 00:02:10 +01:00
85c607dc07 Add Infinity to .env.example
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-01-11 17:35:26 +01:00
4e0f3f0f39 Use Infinity 2024-01-11 17:34:01 +01:00
34f5697521 Bump deps 2024-01-11 17:33:43 +01:00
c3edf756cc Add prerender
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-29 11:29:43 +01:00
edc883d4a8 Use load fetch instead of window one
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-29 11:14:26 +01:00
57736e1d86 Use helper script
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-29 10:55:45 +01:00
f394456e7b Wops fix env example
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-29 10:49:47 +01:00
2a969dedff Remove static env var from prod compose
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-27 12:16:39 +01:00
3a3527e282 Boh
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-27 12:15:08 +01:00
c9fb8fa385 Do not check
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-27 12:13:56 +01:00
e0b0385709 fix args
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-27 12:11:40 +01:00
0d19c5e75a Fix build compose
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-27 12:05:16 +01:00
90b2576555 Add check before build
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-27 12:03:19 +01:00
06db990965 Add public backend url to example
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-27 12:02:21 +01:00
8e9c58a610 Bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-27 11:51:39 +01:00
378c14675f Update compose env var
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 16:14:16 +01:00
f1c9473fd7 Fix build
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 16:12:45 +01:00
6df86a1925 ?. where needed
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 16:06:21 +01:00
0c4de8c4b4 Update error typdef 2023-12-22 15:52:09 +01:00
5cc5d09430 Handler now sane 2023-12-22 15:51:36 +01:00
988e07f869 Add image to route
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 15:34:25 +01:00
d44f97ec94 Add notation on typdef 2023-12-22 15:34:02 +01:00
b7a5609e91 Add route.cover
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 15:32:39 +01:00
a3868ac971 Remove useless log 2023-12-22 15:32:33 +01:00
ae484436df Update Route typedef 2023-12-22 15:32:27 +01:00
91e2116e13 less border-radius, that was too bad
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 15:20:36 +01:00
6556188778 Fresh no fetch
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 15:19:42 +01:00
f99c20f827 Update home page view 2023-12-22 15:19:32 +01:00
aff0c03ad2 Add status to hooks
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 13:34:40 +01:00
92e2ca02f8 Wops
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 13:30:43 +01:00
f1aa779111 Enable ssr
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 13:30:05 +01:00
6394e16a45 Use sveltekit fetch instead of window.fetch
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 13:20:49 +01:00
02a6276240 Add error definition 2023-12-22 13:20:37 +01:00
59a6d8ea38 Update error page 2023-12-22 13:20:31 +01:00
baed80cac6 Do error out
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 12:54:09 +01:00
5d56b382ce Add fetch to missing prototypes
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 12:53:39 +01:00
dba6eced88 Remove high definition of window.fetch 2023-12-22 12:53:29 +01:00
cc11368926 Add fetch as parameter for repo 2023-12-22 12:53:19 +01:00
536b84af2d Add logging of errors 2023-12-22 12:53:11 +01:00
3c7bca466a Remove useless default paddings on body
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 12:45:28 +01:00
ac97696c6a Do not show error instead show undefined value
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 12:44:37 +01:00
a37f8a3f29 Add handler for client and server errors 2023-12-22 12:44:08 +01:00
fbad879acb Better error message page 2023-12-22 12:43:48 +01:00
eac85ec934 Same as before 2023-12-22 12:38:49 +01:00
eb69f00c26 Use sveltekit fetch 2023-12-22 12:38:44 +01:00
9d940a29c3 Add sentry config 2023-12-22 12:34:18 +01:00
5e23f48937 Add error mage 2023-12-22 12:30:07 +01:00
03ff5947fc Do not error out but show undefined value 2023-12-22 12:29:55 +01:00
c9faabc945 Add support for gpx rendering
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 12:19:10 +01:00
5c9bba2af5 npm i leaflet-gpx 2023-12-22 12:19:00 +01:00
76d9f46fa5 Dummy hook
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 12:08:58 +01:00
b1d2481e66 visualize return value 2023-12-22 12:02:00 +01:00
4fb60c5892 Mutable header title 2023-12-22 12:01:23 +01:00
85b178ca07 Remove mock code from repo
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-22 11:55:24 +01:00
a69112d901 Add two mock gpx file 2023-12-22 11:55:20 +01:00
4cd5994798 Set italian language as default
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 11:50:48 +01:00
d60ad6f1ff Do not process dist css by leaflet 2023-12-22 11:50:40 +01:00
e8c67147b1 Bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-22 11:39:19 +01:00
5daa6d7b8b Bump deps and build config 2023-12-22 11:38:57 +01:00
ccd03bac6a Add types
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:44:21 +01:00
b9d4b1ff80 Fix size of map pin
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:36:46 +01:00
5883ca1639 Fix sizes
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:33:38 +01:00
bd8b2553ea Use other images
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:29:45 +01:00
b1f83886f9 Add icons on tabs
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:25:17 +01:00
bd31580e32 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:02:53 +01:00
bc1a2623e7 Add more types
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 16:01:42 +01:00
2df6ffa5f4 Update types
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 15:57:22 +01:00
094dd5b070 Add typedef
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 15:56:52 +01:00
bd12df0b8a Update mock things
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 15:55:05 +01:00
1dfbc02c2a Update readme
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-18 15:31:42 +01:00
6c2b77c592 Use right icon size
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 19:05:22 +01:00
fe386e93a9 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 19:04:04 +01:00
4d53aabef4 Update user marker position
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 19:02:00 +01:00
207af1abf1 Add user marker icon 2023-12-13 19:01:47 +01:00
99b1421ffc Remove min zoom
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:56:22 +01:00
10840f1a20 Remove max bound
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:53:35 +01:00
9b60c4437c On each iteration add marker to map
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:47:39 +01:00
9e197a2b13 Clear position watching on map destroy
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:41:20 +01:00
616a62d4b5 Add retrieved color to path
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:31:38 +01:00
8f30dd9f54 Update typedef 2023-12-13 18:31:30 +01:00
330d19aa94 [mock] route-by-category
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:27:39 +01:00
1641297edb [mock] route-categories 2023-12-13 18:26:54 +01:00
0dfeccf40f Adapt map layout
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-13 18:24:37 +01:00
8f17da9da2 Add re size when clicking on map
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 19:29:22 +01:00
5bcd76d099 Wops
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 19:14:08 +01:00
86543619e8 Update map render
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 19:08:56 +01:00
e396ca0dc9 Add tab aria role 2023-12-12 19:08:47 +01:00
d317405194 Update lockfile
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 19:01:14 +01:00
e08ff8c980 Use vite 4 2023-12-12 19:01:10 +01:00
34ea4f33c9 Using add-sigint without node
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 19:00:30 +01:00
52c7fa0bc1 Use node not yarn
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 19:00:15 +01:00
56303a9682 Using npm not yarn
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:59:49 +01:00
0c631c7a36 Remove yarn related code
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:59:19 +01:00
40277e3380 Remove some unused code
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:53:49 +01:00
a7f9940ab1 Add leaflet typedef 2023-12-12 18:53:42 +01:00
9fb42d5d19 Remove useless config caused by yarn
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:40:02 +01:00
2c290f2a85 Cast as App.Category
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:35:20 +01:00
a1833a0955 Remove js typdef
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:25:34 +01:00
dc30622ed5 Remove splash
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:25:05 +01:00
ca4c7e0b10 Update Dockerfiel
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 18:21:19 +01:00
12e3990bc2 Restore lock with npm instead of yarn 2023-12-12 18:16:49 +01:00
6496a47566 Update manifest
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:39:23 +01:00
01ed1306f1 Absolute positioning
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:35:11 +01:00
e8e7190b6b no prerender
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:33:06 +01:00
110cdc2309 Import dev
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 17:31:55 +01:00
1945b53080 Add splash on production build
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 17:31:40 +01:00
3d613067a3 Updates on splash screen
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 17:27:12 +01:00
e77e710983 Move splash
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:18:27 +01:00
7456e9825d Do not serve ancestor folder 2023-12-12 17:18:22 +01:00
aeaec1c3d2 Using dev from environment
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:12:27 +01:00
9012bb96d6 Remove useless calc
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:10:25 +01:00
ff2a4d2f58 Remove unused code
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 17:03:04 +01:00
4dffc731b6 Add transition on layout instead of on each single page
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-12-12 16:57:28 +01:00
9e7bfc5468 Back with sw.js 2023-12-12 16:56:43 +01:00
bd57812964 Remove logs
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-12 16:27:22 +01:00
85e6287ede Remove old styles 2023-12-12 16:25:11 +01:00
d6fb920b18 Update home page css layout 2023-12-12 16:19:16 +01:00
2a22519d88 Bump deps 2023-12-12 16:13:55 +01:00
1cd138fbd8 Add the first row for the header 2023-12-11 18:25:12 +01:00
abb1b6d9af No need for fixed positioning 2023-12-11 18:25:01 +01:00
622ada546d Show every route
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-12-11 18:17:55 +01:00
a2cb117724 Temporary removal of service worker 2023-12-11 18:17:50 +01:00
49028eb9fd Remove old fix 2023-12-10 16:18:35 +01:00
48d51f8ffc Update vite config 2023-12-10 16:18:01 +01:00
4f8e727bad Update path styling 2023-12-10 16:17:53 +01:00
e61c66169a Add .pnp to gitignore 2023-12-10 15:44:35 +01:00
cc80b9a3e9 bump deps 2023-12-10 15:40:50 +01:00
bf16190776 Bump versioj
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 12:21:37 +01:00
c54bd1f78e Update vh into dvh
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 12:20:13 +01:00
732f9210e9 Test view transition api
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 12:15:38 +01:00
989a7be6e7 Add name
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 11:51:17 +01:00
610b4d6552 Fix missing sport details values
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 06:39:00 +01:00
74963c446f Add dump condition
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 06:27:04 +01:00
c038bcc2a1 Update
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 06:24:49 +01:00
ba817bf72c Remove pre render
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-27 06:19:16 +01:00
22c3fa6a0d Add void yarnrc
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 06:13:13 +01:00
1aa582f753 Add lock file for cicd
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 06:11:52 +01:00
c65a0c1c54 Add paths implementation
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 06:10:06 +01:00
b3fb6e46aa re apply map click
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 05:40:22 +01:00
0dfca0de17 Add route data to tabs
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 05:40:03 +01:00
af238ff7e2 Fix wrong typedef 2023-11-27 05:39:48 +01:00
a572d710b3 Insert sport details data
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 05:37:21 +01:00
9b6d7b9d53 Update tabs mock
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 05:30:41 +01:00
0143a16042 Fix route slug 2023-11-27 05:30:25 +01:00
33bbf50a2d Remove server load 2023-11-27 05:30:09 +01:00
6c29411356 remove request animation frame 2023-11-27 04:12:18 +01:00
f1858c6fef Update routes 2023-11-27 03:58:33 +01:00
2644aa37f0 Update route and paths 2023-11-27 03:58:30 +01:00
40dd8d0fb2 Get routes by category and display them 2023-11-27 03:58:08 +01:00
12e1351cca Add typedef 2023-11-27 03:57:53 +01:00
2a556eef7d Add mock api calls 2023-11-27 03:40:47 +01:00
e217bb072a Bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 03:03:01 +01:00
140fbd8d76 Update package.json 2023-11-27 03:00:30 +01:00
2c922d120a Do not use yrn
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-11-27 02:57:36 +01:00
fdc301108a Update gitignore 2023-11-27 02:57:33 +01:00
fcb0c37fc0 restore yarnrc
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-10-25 11:51:15 +02:00
be22846192 Bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-10-25 11:32:36 +02:00
67f6aab867 Remove the stop section
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-09-25 17:47:29 +02:00
70af2ef43c Fix build yaml
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-09-25 17:44:09 +02:00
725c7f0f07 Remove useless intsall 2023-09-25 17:34:56 +02:00
d01eae92d5 Update Dockerfile using our image and for yarn latest usage 2023-09-25 17:34:41 +02:00
97c1cbe74d Ignore yarn folder 2023-09-25 17:34:13 +02:00
dc83fe5646 Use our own image
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-09-25 17:31:36 +02:00
545fc85991 Update pipeline syntax!
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-09-25 11:48:21 +02:00
97e967b5b7 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-09-22 13:34:44 +02:00
14cbd611d6 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-09-18 14:37:54 +02:00
ad78d0d225 Remove registry usage
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-09-11 10:02:17 +02:00
aa5a3a147d bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-09-11 10:00:30 +02:00
ca0c1d7a62 Bmp deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-08-30 10:00:39 +02:00
8d6b698813 Bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-08-27 19:32:09 +02:00
e2af677ab6 move options sequence
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-08-23 15:36:32 +02:00
85c482dfaa Update Dockerfile
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-08-23 14:59:28 +02:00
6e9b2dcaa4 Use install directly
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-08-23 14:54:59 +02:00
15bd322d23 add tmp proxy registry
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-08-23 14:45:25 +02:00
6c403910f6 Bump deps 2023-08-23 14:42:13 +02:00
c2fd79fe1b Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-08-05 00:51:35 +02:00
11c1a316a5 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-08-04 09:58:34 +02:00
39250cb4de Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-08-02 16:15:17 +02:00
8770fbd589 Bump deps 2023-08-01 14:40:00 +02:00
bd3f25813c Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-31 14:15:16 +02:00
9bb1ebe18e Bump deps
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-07-28 17:13:10 +02:00
d228582f6f Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-27 21:48:51 +02:00
d149d011a2 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-27 00:50:29 +02:00
55137504a6 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-25 14:29:33 +02:00
f72eab4241 Remove lines
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 17:44:35 +02:00
214657a085 Fix size of home page path component
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 13:47:02 +02:00
a69a4d54c5 Remove animation
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 13:41:38 +02:00
dfd1c2a237 Remove animation in favor of bottom-alignment
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 13:41:14 +02:00
365f9a0cc6 grid-template-rows to manage alignment on home
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 12:48:33 +02:00
b09aca2c20 do not register service worker on development
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 12:37:10 +02:00
0135d749f0 Fix height of main
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 12:09:14 +02:00
d9e726850e Remove useless node, adapt general page style with grids
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 11:55:34 +02:00
34cb1a754a Remove useless footer node
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 02:16:58 +02:00
7ada0b0e2a Share header css between components
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 02:05:20 +02:00
519c91c628 Toast div as absolute position
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 01:58:49 +02:00
b2347b0466 Add support for postMessage
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-24 01:47:46 +02:00
e4829976e4 Broadcast channel 2023-07-24 01:46:50 +02:00
bec8fcdce1 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-23 15:30:42 +02:00
10fa4522ba Add toast dinamically
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-22 19:58:19 +02:00
7d8f59dec7 update sw.ts and toast component 2023-07-22 19:57:47 +02:00
0a2f580f30 Add toast 2023-07-22 19:50:42 +02:00
9437316feb Add checkmark 2023-07-22 19:50:12 +02:00
1828a4b978 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-22 17:31:05 +02:00
305f07f790 add predered routes to cache
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-22 17:23:20 +02:00
5540593879 Bump deps
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-22 04:10:26 +02:00
db76100b8a fix path of sw.js
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 20:15:43 +02:00
3cd8752fe1 only onMount
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 20:11:59 +02:00
551fe07d0e Updates on sw.js registering
Some checks failed
ci/woodpecker/push/build Pipeline failed
2023-07-19 20:08:53 +02:00
c5cca78b1c fix manifest
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 18:26:53 +02:00
47d31d5f48 Update 512
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 18:23:35 +02:00
b92e591b13 Add pwa manifest and icons
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 18:05:54 +02:00
44da1123e2 Add prerendering
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 17:47:24 +02:00
c981aa649f Disable server side rendering for offline usage
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 17:40:42 +02:00
84d38c2ef0 Add custom sw.ts
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 17:21:13 +02:00
bb53698682 Remove any configuration relative to sw.js
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-07-19 17:17:32 +02:00
73 changed files with 11318 additions and 4695 deletions

View file

@ -1,2 +1,4 @@
TAG=0.5.0 TAG=0.5.0
ORIGIN=https://pianello.webdeploy.it ORIGIN=https://pianello.webdeploy.it
PUBLIC_BACKEND_URL=https://ale-dev.teck-developer.com
BODY_SIZE_LIMIT=Infinity

View file

@ -1,26 +1,23 @@
pipeline: steps:
build_and_deploy: build_and_deploy:
image: alpine:3.14 image: git.webdeploy.it/webdeploy/alpine
secrets: [docker_password, docker_username, runner_private_key] secrets: [docker_password, docker_username, runner_private_key]
commands: commands:
- apk add --no-cache openssh docker docker-compose jq
################### Provisioning ################### Provisioning
- echo "$${DOCKER_PASSWORD}" | docker login --password-stdin --username "$${DOCKER_USERNAME}" git.webdeploy.it - echo "$${DOCKER_PASSWORD}" | docker login --password-stdin --username "$${DOCKER_USERNAME}" git.webdeploy.it
- echo "TAG=$(jq -r .version ./frontend/package.json)" >> .env - echo "TAG=$(jq -r .version ./frontend/package.json)" >> .env
- echo "ORIGIN=https://pianello.webdeploy.it" >> .env - echo "ORIGIN=https://pianello.webdeploy.it" >> .env
- echo "PUBLIC_BACKEND_URL=https://ale-dev.teck-developer.com" >> ./frontend/.env
- docker-compose -f docker-compose.build.yml build - docker-compose -f docker-compose.build.yml build
- docker push git.webdeploy.it/pianello/frontend:latest - docker push git.webdeploy.it/pianello/frontend:latest
- docker push git.webdeploy.it/pianello/frontend:$(jq -r .version ./frontend/package.json) - docker push git.webdeploy.it/pianello/frontend:$(jq -r .version ./frontend/package.json)
# DEPLOY STUFF #### Deploy
- eval $(ssh-agent -s) - config_ssh.sh "$${RUNNER_PRIVATE_KEY}"
- mkdir -p ~/.ssh
- printf "%s" "$${RUNNER_PRIVATE_KEY}" > ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
- ssh-add ~/.ssh/id_ed25519
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- cat .env | ssh pianello@pianello.webdeploy.it "cat - > .env" # copy env - cat .env | ssh pianello@pianello.webdeploy.it "cat - > .env" # copy env
- cat docker-compose.prod.yml | ssh pianello@pianello.webdeploy.it "cat - > docker-compose.yml && docker compose pull && docker compose stop && docker compose up -d --remove-orphans" - cat docker-compose.prod.yml | ssh pianello@pianello.webdeploy.it "cat - > docker-compose.yml && docker compose pull && docker compose up -d --remove-orphans"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
branches: maybeitworked when:
branch: main

View file

@ -3,6 +3,8 @@
### CI/CD ### CI/CD
[![status-badge](https://cicd.webdeploy.it/api/badges/pianello/pianello-web-app/status.svg)](https://cicd.webdeploy.it/pianello/pianello-web-app) [![status-badge](https://cicd.webdeploy.it/api/badges/pianello/pianello-web-app/status.svg)](https://cicd.webdeploy.it/pianello/pianello-web-app)
[![status-badge](https://kuma.dashboard.webdeploy.it/api/badge/28/uptime/400)](https://kuma.dashboard.webdeploy.it)
### Legal ### Legal
This project is licensed under the [GNU General Public License v3](./LICENSE.md). This project is licensed under the [GNU General Public License v3](./LICENSE.md).

View file

@ -7,6 +7,8 @@ services:
build: build:
context: frontend context: frontend
dockerfile: Dockerfile dockerfile: Dockerfile
args:
PUBLIC_BACKEND_URL: ${PUBLIC_BACKEND_URL}
version_tag: version_tag:
extends: frontend extends: frontend

View file

@ -7,6 +7,6 @@ services:
environment: environment:
ORIGIN: "${ORIGIN}" ORIGIN: "${ORIGIN}"
PORT: 8000 PORT: 8000
BODY_SIZE_LIMIT: 0 BODY_SIZE_LIMIT: Infinity
ports: ports:
- 127.0.0.1:8000:8000 - 127.0.0.1:8000:8000

1
frontend/.gitignore vendored
View file

@ -8,3 +8,4 @@ node_modules
!.env.example !.env.example
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
.pnp*

View file

@ -1,2 +0,0 @@
engine-strict=true
resolution-mode=highest

View file

@ -1,18 +1,19 @@
FROM node:18 AS build FROM git.webdeploy.it/webdeploy/yarn AS build
WORKDIR /usr WORKDIR /usr
COPY package.json ./ COPY package.json ./
COPY yarn.lock ./ COPY package-lock.json ./
RUN yarn install RUN npm ci
COPY . ./ COPY . ./
RUN yarn build # RUN npm run check
RUN npm run build
FROM node:18-alpine3.16 FROM git.webdeploy.it/webdeploy/yarn
WORKDIR /app WORKDIR /app
COPY package.json ./ COPY package.json ./
COPY yarn.lock ./ COPY package-lock.json ./
RUN yarn install --frozen-lockfile RUN npm ci
COPY --from=build /usr/build /app COPY --from=build /usr/build /app
EXPOSE 8000 EXPOSE 8000

5798
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,10 @@
{ {
"name": "frontend", "name": "frontend",
"version": "0.5.0", "version": "0.6.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build && yarn add-sigint", "build": "vite build && ./add-sigint.sh",
"add-sigint": "./add-sigint.sh", "add-sigint": "./add-sigint.sh",
"preview": "vite preview", "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
@ -13,27 +13,28 @@
"format": "prettier --plugin-search-dir . --write ." "format": "prettier --plugin-search-dir . --write ."
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-node": "^5.2.4",
"@sveltejs/kit": "^1.22.3", "@sveltejs/kit": "^2.5.28",
"@typescript-eslint/eslint-plugin": "^6.1.0", "@sveltejs/vite-plugin-svelte": "^3.1.2",
"@typescript-eslint/parser": "^6.1.0", "@types/leaflet": "^1.9.12",
"@vite-pwa/sveltekit": "^0.2.5", "@typescript-eslint/eslint-plugin": "^8.7.0",
"eslint": "^8.45.0", "@typescript-eslint/parser": "^8.7.0",
"eslint-config-prettier": "^8.5.0", "eslint": "^9.11.1",
"eslint-plugin-svelte": "^2.30.0", "eslint-config-prettier": "^9.1.0",
"prettier": "^3.0.0", "eslint-plugin-svelte": "^2.44.0",
"prettier-plugin-svelte": "^3.0.0", "prettier": "^3.3.3",
"svelte": "^4.1.0", "prettier-plugin-svelte": "^3.2.6",
"svelte-check": "^3.4.6", "svelte": "^4.2.19",
"tslib": "^2.4.1", "svelte-check": "^4.0.2",
"typescript": "^5.0.0", "tslib": "^2.7.0",
"vite": "^4.4.4", "typescript": "^5.6.2",
"vite-plugin-pwa": "^0.16.4" "vite": "^5.4.7"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@sveltejs/adapter-node": "^1.3.1", "@sentry/sveltekit": "^8.31.0",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"svelte-preprocess": "^5.0.4" "leaflet-gpx": "^2.1.2",
"svelte-preprocess": "^6.0.2"
} }
} }

73
frontend/src/app.d.ts vendored
View file

@ -2,20 +2,75 @@
// for information about these interfaces // for information about these interfaces
declare global { declare global {
namespace App { namespace App {
// interface Error {} export interface Error {
message: string;
}
// interface Locals {} // interface Locals {}
// interface PageData {} // interface PageData {}
// interface Platform {} // interface Platform {}
interface Category {
id: 1;
name_it: 'Natura';
name_en: 'Nature';
description_it: 'Giri nella natura';
description_en: 'Countryside routes';
icon: '';
created_at: null;
updated_at: null;
deleted_at: null;
color: string;
cover: string;
}
interface Sport {
id: 1;
name_it: 'Trekking';
name_en: 'Trekking';
description_it: 'Percorso escursionistico';
description_en: 'Trekking route';
icon: '';
created_at: '2023-10-31T18:37:30.000000Z';
updated_at: null;
deleted_at: null;
}
interface SportDetails {
id: 1;
route_id: 1;
sport_id: 1;
short_description_it: 'Percorso escursionistico intermedio. Buon allenamento richiesto. Sentieri facilmente percorribili. Adatto a ogni livello di abilit\u00e0. ';
short_description_en: 'Intermediate hiking route. Good training required. Easily accessible paths. Suitable for all skill levels.';
gpx_path: '';
distance: 16800;
duration: 288;
elevation_gain: 439;
elevation_loss: null;
altitude_max: 620;
altitude_min: 180;
difficulty_it: 'Facile';
difficulty_en: 'Easy';
route_type_it: 'Percorso ad anello';
route_type_en: 'Ring route';
created_at: '2023-11-02T10:57:41.000000Z';
updated_at: null;
deleted_at: null;
sport: Sport;
}
interface Route { interface Route {
id: number; id: number;
name: string; cover: string;
duration: number; name_it: string;
image: string; name_en: string;
/* Sometimes from API comes 'title' instead of 'name' :c */
title_it: string;
title_en: string;
description_it: string;
description_en: string;
route_category_id: number;
created_at: '2023-11-02T10:50:07.000000Z';
updated_at: null;
deleted_at: null;
route_sport_details: Array<SportDetails>;
elevation_gain?: number;
} }
} }
} }
export { Route, Category, Sport, SportDetails };
export {
Route
};

View file

@ -1,15 +1,16 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="it">
<head>
<head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="manifest" href="manifest.json" />
%sveltekit.head% %sveltekit.head%
</head> </head>
<style> <style>
:root { :root {
--footer-height: calc(68px + 20px + 20px); --footer-height: 108px;
--accent-color: #213c8b; --accent-color: #213c8b;
--pianello-red: #de0e1b; --pianello-red: #de0e1b;
--pianello-yellow: #f6ae04; --pianello-yellow: #f6ae04;
@ -39,10 +40,9 @@
font-family: 'Roboto-Regular'; font-family: 'Roboto-Regular';
src: url('/fonts/roboto/Roboto-Regular.ttf') format('TrueType'); src: url('/fonts/roboto/Roboto-Regular.ttf') format('TrueType');
} }
</style> </style>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div> <div style="display: contents">%sveltekit.body%</div>
</body> </body>
</html> </html>

View file

@ -1,4 +1,3 @@
:root { :root {
--footer-height: calc(68px + 20px + 20px); --footer-height: calc(68px + 20px + 20px);
--accent-color: #213c8b; --accent-color: #213c8b;

View file

@ -0,0 +1,13 @@
.header {
padding: 15px;
background-color: transparent;
color: black;
margin: 0 auto;
padding-left: 25px;
padding-right: 25px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
}

41
frontend/src/error.html Normal file
View file

@ -0,0 +1,41 @@
<html>
<head>
<style>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
background-image: url(/splash.webp);
background-size: cover;
display: grid;
place-content: center;
height: 100%;
font-size: 24px;
font-family: monospace;
}
.wrapper {
display: flex;
backdrop-filter: blur(5px);
border-radius: 10px;
border: 1px solid black;
}
#status {
border-right: 1px solid black;
}
p {
padding: 20px;
}
</style>
</head>
<body>
<div class="wrapper">
<p id="status">Code %sveltekit.status%</p>
<p id="message">%sveltekit.error.message%</p>
</div>
</body>
</html>

View file

@ -0,0 +1,7 @@
export async function handleError({ error, message }) {
console.error(error);
return {
message
};
}

View file

@ -0,0 +1,16 @@
export async function handle({ event, resolve }) {
const response = await resolve(event);
return response;
}
export async function handleFetch({ request, fetch }) {
return fetch(request);
}
export async function handleError({ error, message }) {
console.error(error);
return {
message
};
}

View file

@ -1,75 +0,0 @@
<script lang="ts">
import { useRegisterSW } from 'virtual:pwa-register/svelte'
const {
needRefresh,
updateServiceWorker,
offlineReady
} = useRegisterSW({
onRegistered(r) {
// uncomment following code if you want check for updates
// r && setInterval(() => {
// console.log('Checking for sw update')
// r.update()
// }, 20000 /* 20s for testing purposes */)
console.log(`SW Registered: ${r}`)
},
onRegisterError(error) {
console.log('SW registration error', error)
},
})
const close = () => {
offlineReady.set(false)
needRefresh.set(false)
}
$: toast = $offlineReady || $needRefresh
</script>
{#if toast}
<div class="pwa-toast" role="alert">
<div class="message">
{#if $offlineReady}
<span>
App ready to work offline
</span>
{:else}
<span>
New content available, click on reload button to update.
</span>
{/if}
</div>
{#if $needRefresh}
<button on:click={() => updateServiceWorker(true)}>
Reload
</button>
{/if}
<button on:click={close}>
Close
</button>
</div>
{/if}
<style>
.pwa-toast {
position: fixed;
right: 0;
bottom: 0;
margin: 16px;
padding: 12px;
border: 1px solid #8885;
border-radius: 4px;
z-index: 2;
text-align: left;
box-shadow: 3px 4px 5px 0 #8885;
background-color: white;
}
.pwa-toast .message {
margin-bottom: 8px;
}
.pwa-toast button {
border: 1px solid #8885;
outline: none;
margin-right: 5px;
border-radius: 2px;
padding: 3px 10px;
}
</style>

View file

@ -16,8 +16,10 @@
#container-of-container { #container-of-container {
width: 100%; width: 100%;
position: fixed; position: fixed;
bottom: 0; bottom: 10px;
left: 0; left: 0;
display: grid;
height: 10dvh;
} }
#container { #container {
display: flex; display: flex;
@ -28,7 +30,6 @@
box-shadow: 0 0 50px #ccc; box-shadow: 0 0 50px #ccc;
background-color: var(--card-background-color); background-color: var(--card-background-color);
margin: 0 auto; margin: 0 auto;
margin-bottom: 10px;
} }
#container > a { #container > a {

View file

@ -1,4 +1,6 @@
<script lang="ts"> <script lang="ts">
import '../../css/header.css';
export const title: string = 'Naturalistici'; export const title: string = 'Naturalistici';
const goBack = () => { const goBack = () => {
@ -6,7 +8,7 @@
}; };
</script> </script>
<div> <div class="header">
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
@ -19,17 +21,8 @@
<style> <style>
div { div {
position: fixed;
padding: 15px;
background-color: transparent; background-color: transparent;
color: black; color: black;
margin: 0 auto;
padding-left: 15px;
padding-right: 15px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
} }
div a { div a {
cursor: pointer; cursor: pointer;

View file

@ -1,41 +1,33 @@
<script lang="ts"> <script lang="ts">
export const title: string = 'Naturalistici'; import '../../css/header.css';
export let title: string = 'Naturalistici';
const goBack = () => { const goBack = () => {
history.back(); history.back();
}; };
</script> </script>
<div> <div class="header">
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
<a on:click={goBack}> <a on:click={goBack}>
<img class="back" src="/images/white-back-arrow.png" alt="" /> <img class="back" src="/images/white-back-arrow.png" alt="" />
</a> </a>
<p>Percorsi <b>{title}</b></p> <p>{title}</p>
<img src="/images/app-bar-logo.png" alt="" /> <img src="/images/app-bar-logo.png" alt="" />
</div> </div>
<style> <style>
div { div {
color: white; color: white;
margin: 0 auto;
padding: 15px;
padding-left: 25px;
padding-right: 25px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #de0e1b; background-color: #de0e1b;
position: fixed;
font-size: 22px;
} }
div p { div p {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-weight: bold;
} }
div a { div a {
cursor: pointer; cursor: pointer;

View file

@ -1,85 +1,77 @@
<script lang="ts"> <script lang="ts">
export let type: string; import { PUBLIC_BACKEND_URL } from '$env/static/public';
export let name: string;
export let id: number;
export let color: string; export let color: string;
export const path = `/paths/${type}`; export const path = `/paths/${id}`;
export let src: string;
let image = '/archi.png';
switch (id) {
case 1:
image = '/archi.png';
break;
case 2:
image = '/montagne.png';
break;
case 3:
image = '/bibbito.png';
break;
}
</script> </script>
<a data-sveltekit-reload href={path} class="route-card"> <a href={path} class="route-card">
<div class="route-card-left" style="background-color: {color}"> <div class="route-card-left" style="background-color: {color}">
<img src="/images/app-bar-logo.png" alt="logo" /> <img src={image} alt="logo" />
</div> </div>
<div class="route-card-center"> <div class="route-card-center">
<div class="name"> <div class="name">
<div>Percorsi</div> <div class="bold">{name}</div>
<div class="bold">{type}</div>
</div> </div>
</div> </div>
<div class="route-card-right" /> <div class="route-card-right" style:background-image="url({PUBLIC_BACKEND_URL}{src})" />
</a> </a>
<style> <style>
a { a {
text-decoration: inherit; text-decoration: inherit;
color: inherit; color: inherit;
cursor: auto; cursor: pointer;
}
.bold {
font-weight: bold;
}
.route-card {
text-decoration: none;
display: block;
flex: 1;
--route-card-radius: 45px;
border-radius: var(--route-card-radius);
box-shadow: 0 0 27px #ccc;
margin-top: 10px;
margin-bottom: 10px;
width: 90%;
display: flex;
background-color: var(--card-background-color);
}
.route-card-center {
font-size: 18px;
}
.route-card > * {
flex: 1;
}
.route-card-left {
flex: 0 1 20%;
border-top-left-radius: var(--route-card-radius);
border-bottom-left-radius: var(--route-card-radius);
display: grid; display: grid;
place-items: center; grid-template-columns: 20% minmax(1px, 1fr) 20%;
max-width: 100%;
width: calc(100% - 20px);
margin: 0 auto;
height: 100%;
font-size: 24px;
} }
.route-card-left img {
display: block;
width: 80%;
height: auto;
}
.route-card-right { .route-card-right {
flex: 0 1 20%; border-top-right-radius: 45px;
border-top-right-radius: var(--route-card-radius); border-bottom-right-radius: 45px;
border-bottom-right-radius: var(--route-card-radius);
background-image: url('/images/test-1.jpg');
background-position: center; background-position: center;
background-size: cover; background-size: cover;
} }
.route-card .name { .route-card-left {
border-top-left-radius: 45px;
border-bottom-left-radius: 45px;
display: grid; display: grid;
height: 100%; place-content: center;
align-content: center;
padding: 20px;
font-size: 28px;
} }
.route-card .name > * { .route-card-left img {
flex: 1; width: 100%;
max-width: 120px;
}
.route-card-center {
display: grid;
place-content: center;
font-weight: bold;
padding: 4px;
max-width: 100%;
text-overflow: ellipsis;
text-align: center;
} }
</style> </style>

View file

@ -1,16 +1,17 @@
<script lang="ts"> <script lang="ts">
import { PUBLIC_BACKEND_URL } from '$env/static/public';
export let route: App.Route; export let route: App.Route;
</script> </script>
<a href="/routes/{route.id}"> <a href="/routes/{route.id}">
<div id="image" style="background-image: url(/images/test-1.jpg" /> <div id="image" style:background-image="url({PUBLIC_BACKEND_URL}{route.cover})" />
<div id='path-holder'> <div id="path-holder">
<div style='font-size: 20px;'>Percorso</div> <p class="path-name">{route.title_it}</p>
<p>{route.name}</p>
</div> </div>
<div id='duration-holder'> <div id="duration-holder">
<div style='font-size: 15px;'>Durata</div> <div style="font-size: 15px;">Dislivello</div>
<p id="duration">{Math.floor(Number(route.duration) / 60)}'</p> <p id="duration">{route.elevation_gain}</p>
</div> </div>
</a> </a>
@ -20,18 +21,18 @@
color: black; color: black;
display: block; display: block;
box-shadow: 0 0 50px #ccc; box-shadow: 0 0 50px #ccc;
border-radius: 30px; border-radius: 5px;
width: calc((100% - 50px) / 2); width: calc((100% - 50px));
font-size: 18px; font-size: 18px;
} }
#image { #image {
width: 100%; width: 100%;
height: 150px; height: 140px;
object-position: center; object-position: center;
background-position: center; background-position: center;
object-fit: cover; object-fit: cover;
border-radius: 30px; border-radius: 5px;
border-bottom-right-radius: 0px; border-bottom-right-radius: 0px;
border-bottom-left-radius: 0px; border-bottom-left-radius: 0px;
} }
@ -59,6 +60,14 @@
padding-top: 2px; padding-top: 2px;
padding-bottom: 2px; padding-bottom: 2px;
font-weight: bold; font-weight: bold;
font-size: 22px; font-size: 1em;
line-height: 1em;
text-overflow: ellipsis;
overflow: hidden;
}
.path-name {
view-transition-name: title;
max-height: 2em;
} }
</style> </style>

View file

@ -1,12 +1,14 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { dev } from '$app/environment';
let visible = false; let visible = dev ? false : true;
let time = 0; let time = 2000;
onMount(() => { onMount(() => {
time = 0; if (dev) return;
setTimeout(() => { setTimeout(() => {
visible = false; visible = false;
}, time); }, time);
@ -14,15 +16,32 @@
</script> </script>
{#if visible} {#if visible}
<img transition:fade src="/splash.webp" alt="splash" /> <img class="background" transition:fade src="/splash.webp" alt="splash" />
<div class="wrapper">
<img transition:fade class="logo" src="/images/splash-logo.png" alt="splash-logo" />
</div>
{/if} {/if}
<style> <style>
img { .background {
position: absolute; position: absolute;
z-index: 99; z-index: 3;
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0 auto; margin: 0 auto;
} }
.logo {
max-width: 100%;
height: auto;
z-index: 4;
}
.wrapper {
position: absolute;
display: grid;
place-content: center;
height: 100dvh;
width: 100dvw;
}
</style> </style>

View file

@ -1,53 +1,57 @@
<script lang="ts"> <script lang="ts">
import InfoTab from './tabs/InfoTab.svelte'; import InfoTabTrekking from './tabs/InfoTabTrekking.svelte';
import InfoTabBike from './tabs/InfoTabBike.svelte';
import DescTab from './tabs/DescTab.svelte'; import DescTab from './tabs/DescTab.svelte';
import MapTab from './tabs/MapTab.svelte'; import MapTab from './tabs/MapTab.svelte';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
export let route: App.Route;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let component = InfoTab; let ref: HTMLDivElement;
const activeClicked = (evt) => { let component = InfoTabTrekking;
const clickedTab = evt.target.closest('button');
for (const active of clickedTab.parentElement.querySelectorAll('.active')) { onMount(() => {
active.classList.remove('active'); console.log(route);
} });
const dispatchClick = (c: InfoTabTrekking | InfoTabBike | DescTab | MapTab) => {
clickedTab.classList.add('active'); component = c;
dispatch('tab-click', { component });
switch (clickedTab.getAttribute('id')) {
case 'info':
component = InfoTab;
break;
case 'desc':
component = DescTab;
break;
case 'map':
component = MapTab;
break;
}
dispatch('map-click', {component});
}; };
</script> </script>
<!-- svelte-ignore a11y-click-events-have-key-events --> <div id="tabs">
<!-- svelte-ignore a11y-no-static-element-interactions --> <button
class:active={component === InfoTabTrekking}
on:click={() => dispatchClick(InfoTabTrekking)}
id="info-trekking"
>
<img src="/trekking.svg" alt="trekking" />
</button>
<button
class:active={component === InfoTabBike}
on:click={() => dispatchClick(InfoTabBike)}
id="info-bike"
>
<img src="/bike.svg" alt="bike" />
</button>
<button class:active={component === DescTab} on:click={() => dispatchClick(DescTab)} id="desc">
Descrizione</button
>
<button class:active={component === MapTab} on:click={() => dispatchClick(MapTab)} id="map">
Mappa</button
>
</div>
<div id="container"> <div id="container">
<div id="tabs" on:click={activeClicked}> <div class="tab" bind:this={ref} role="tab">
<button id="info" class="active">Info</button> <svelte:component this={component} {route} />
<button id="desc">Descrizione</button>
<button id="map">Mappa</button>
</div>
<div class="tab">
<svelte:component this={component} />
</div> </div>
</div> </div>
<style> <style>
#container { #container {
background-color: white; background-color: white;
height: calc(20vh); height: 100%;
} }
#tabs { #tabs {
display: flex; display: flex;
@ -69,12 +73,13 @@
padding: 15px; padding: 15px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
cursor: pointer; cursor: pointer;
} }
.tab { .tab {
padding: 10px; padding: 10px;
background-color: white; background-color: white;
overflow-y: scroll;
height: 100%;
} }
.active { .active {
@ -82,4 +87,11 @@
font-weight: bold !important; font-weight: bold !important;
border-bottom: 1px solid black !important; border-bottom: 1px solid black !important;
} }
#info-bike,
#info-trekking {
display: flex;
justify-content: center;
align-items: center;
}
</style> </style>

View file

@ -0,0 +1,45 @@
<script lang="ts">
import { onMount } from 'svelte';
export let message = "L'applicazione è pronta per funzionare offline!";
let show = false;
let nodeRef: HTMLDivElement;
onMount(() => {
setTimeout(() => {
show = true;
setTimeout(() => {
show = false;
setTimeout(() => {
nodeRef?.parentNode?.removeChild(nodeRef);
}, 5000);
}, 5000);
}, 0);
});
</script>
<div bind:this={nodeRef} id="toast" class:show>
<img src="/icons/checkmark.svg" alt="check" />
<div>{message}</div>
</div>
<style>
.show {
transform: translateY(0vh) !important;
}
#toast {
transform: translateY(10vh);
transition: transform ease 0.4s;
position: fixed;
height: 10vh;
display: flex;
bottom: 0;
z-index: 4;
left: 0;
width: 100%;
justify-content: center;
background-color: #f6ae04;
color: black;
place-items: center;
}
</style>

View file

@ -1,10 +1,11 @@
<script lang="ts">
import { fade } from 'svelte/transition';
export let route: App.Route;
</script>
<div> <div>
The standard Lorem Ipsum passage, used since the 1500s "Lorem ipsum dolor sit amet, consectetur {route.description_it}
adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum."
</div> </div>
<style> <style>

View file

@ -1,76 +0,0 @@
<script>
</script>
<div id="grid">
<div>
<p>Distanza</p>
<p>Value</p>
</div>
<div>
<p>Dislivello Positivo</p>
<p>Value</p>
</div>
<div>
<p>Velocità Media</p>
<p>Value</p>
</div>
<div>
<p>Difficolta</p>
<p>Value</p>
</div>
<div>
<p>Dislivello Negativo</p>
<p>Value</p>
</div>
<div>
<p>Durata</p>
<p>Value</p>
</div>
<div>
<p>Altitudine max</p>
<p>Value</p>
</div>
<div>
<p>Altitudine min</p>
<p>Value</p>
</div>
<div>
<p>Tipo Percorso</p>
<p>Value</p>
</div>
</div>
<style>
#grid {
display: grid;
grid-template-rows: repeat(3, 33%);
grid-template-columns: repeat(3, 1fr);
width: 100%;
background: white;
place-items: center;
font-size: 14px;
}
#grid div {
margin: 0 auto;
display: flex;
text-align: center;
flex-direction: column;
}
#grid div > p {
padding: 10px;
margin: 0px;
}
#grid div p:nth-child(2) {
font-weight: bold;
font-size: 16px;
}
</style>

View file

@ -0,0 +1,77 @@
<script lang="ts">
export let route: App.Route;
</script>
<div id="grid">
<div>
<p>Distanza</p>
<p>{route.route_sport_details[1].distance} m</p>
</div>
<div>
<p>Dislivello Positivo</p>
<p>{route.route_sport_details[1].elevation_gain}</p>
</div>
<div>
<p>Velocità Media</p>
<p>N/A</p>
</div>
<div>
<p>Difficolta</p>
<p>{route.route_sport_details[1].difficulty_it}</p>
</div>
<div>
<p>Dislivello Negativo</p>
<p>{route.route_sport_details[1].elevation_loss}</p>
</div>
<div>
<p>Durata</p>
<p>{route.route_sport_details[1].duration}'</p>
</div>
<div>
<p>Altitudine max</p>
<p>{route.route_sport_details[1].altitude_max}</p>
</div>
<div>
<p>Altitudine min</p>
<p>{route.route_sport_details[1].altitude_min}</p>
</div>
<div>
<p>Tipo Percorso</p>
<p>{route.route_sport_details[1].route_type_it}</p>
</div>
</div>
<style>
#grid {
display: grid;
grid-template-rows: repeat(3, 33%);
grid-template-columns: repeat(3, 1fr);
width: 100%;
background: white;
place-items: center;
font-size: 14px;
}
#grid div {
margin: 0 auto;
display: flex;
text-align: center;
flex-direction: column;
}
#grid div > p {
padding: 10px;
margin: 0px;
}
#grid div p:nth-child(2) {
font-weight: bold;
font-size: 16px;
}
</style>

View file

@ -0,0 +1,77 @@
<script lang="ts">
export let route: App.Route;
</script>
<div id="grid">
<div>
<p>Distanza</p>
<p>{route.route_sport_details[0].distance} m</p>
</div>
<div>
<p>Dislivello Positivo</p>
<p>{route.route_sport_details[0].elevation_gain}</p>
</div>
<div>
<p>Velocità Media</p>
<p>N/A</p>
</div>
<div>
<p>Difficolta</p>
<p>{route.route_sport_details[0].difficulty_it}</p>
</div>
<div>
<p>Dislivello Negativo</p>
<p>{route.route_sport_details[0].elevation_loss}</p>
</div>
<div>
<p>Durata</p>
<p>{route.route_sport_details[0].duration}'</p>
</div>
<div>
<p>Altitudine max</p>
<p>{route.route_sport_details[0].altitude_max}</p>
</div>
<div>
<p>Altitudine min</p>
<p>{route.route_sport_details[0].altitude_min}</p>
</div>
<div>
<p>Tipo Percorso</p>
<p>{route.route_sport_details[0].route_type_it}</p>
</div>
</div>
<style>
#grid {
display: grid;
grid-template-rows: repeat(3, 33%);
grid-template-columns: repeat(3, 1fr);
width: 100%;
background: white;
place-items: center;
font-size: 14px;
}
#grid div {
margin: 0 auto;
display: flex;
text-align: center;
flex-direction: column;
}
#grid div > p {
padding: 10px;
margin: 0px;
}
#grid div p:nth-child(2) {
font-weight: bold;
font-size: 16px;
}
</style>

View file

@ -1,40 +1,57 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { fade, fly } from 'svelte/transition'; import { fade } from 'svelte/transition';
const mapMarkerIcon = new URL('/icons/map.png', import.meta.url).href;
export let route;
const pianelloCoordinates = [43.14, 12.53]; const pianelloCoordinates = [43.14, 12.53];
let mapElement: string | HTMLElement; let mapElement: string | HTMLElement;
let leaflet; let leaflet;
let leafletGPX;
let map; let map;
let layerGroup; let layerGroup;
let latitude; let latitude;
let longitude; let longitude;
let accuracy; let accuracy;
const errorMessage = "Geolocation not available"; let watchPositionId: number;
const errorMessage = 'Geolocation not available';
const attribution =
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
const openStreetMapTile = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
let errored = false; let errored = false;
const watchPosition = async () => { const watchPosition = async () => {
if (!("geolocation" in navigator)) { if (!('geolocation' in navigator)) {
errored = true; errored = true;
throw new Error(errorMessage) throw new Error(errorMessage);
} }
const options = { const options = {
enableHighAccuracy: true, enableHighAccuracy: true,
timeout: 5000, timeout: 5000,
maximumAge: 0, maximumAge: 0
}; };
function success(pos) { function success(pos: GeolocationPosition) {
const crd = pos.coords; const crd = pos.coords;
let string = ""; let string = '';
latitude = crd.latitude; latitude = crd.latitude;
longitude = crd.longitude; longitude = crd.longitude;
accuracy = crd.accuracy; accuracy = crd.accuracy;
string += "Your current position is:"; const icon = leaflet.icon({
iconUrl: mapMarkerIcon,
iconSize: [75, 75] // size of the icon
});
userMarker = leaflet.marker([latitude, longitude], { icon }).addTo(map);
// move the map to have the location in its center
map.panTo(userMarker.getLatLng());
string += 'Your current position is:';
string += `Latitude : ${crd.latitude}`; string += `Latitude : ${crd.latitude}`;
string += `Longitude: ${crd.longitude}`; string += `Longitude: ${crd.longitude}`;
string += `More or less ${crd.accuracy} meters.`; string += `More or less ${crd.accuracy} meters.`;
@ -44,50 +61,72 @@
console.warn(`ERROR(${err.code}): ${err.message}`); console.warn(`ERROR(${err.code}): ${err.message}`);
} }
navigator.geolocation.watchPosition(success, error, options); watchPositionId = navigator.geolocation.watchPosition(success, error, options);
};
const renderMap = () => {
layerGroup = leaflet.layerGroup();
// Startup Map
map = leaflet.map(mapElement, {
//dragging: leaflet.Browser.mobile,
//tap: leaflet.Browser.mobile,
});
map.setView(pianelloCoordinates, 13);
leaflet.tileLayer(openStreetMapTile, { attribution }).addTo(map);
};
const renderGPX = () => {
const gpx = '/gpx/tidone.gpx'; // URL to your GPX file or the GPX itself
new leafletGPX(gpx, { async: true })
.on('loaded', function (e) {
map.fitBounds(e.target.getBounds());
})
.addTo(map);
}; };
onMount(async () => { onMount(async () => {
await watchPosition(); await watchPosition();
leaflet = await import('leaflet'); leaflet = await import('leaflet');
layerGroup = leaflet.layerGroup(); const { GPX } = await import('leaflet-gpx');
leafletGPX = GPX;
// Startup Map renderMap();
map = leaflet.map(mapElement, { renderGPX();
dragging: leaflet.Browser.mobile,
tap: leaflet.Browser.mobile,
minZoom: 12
});
map.setView(pianelloCoordinates, 13);
map.setMaxBounds(map.getBounds());
leaflet
.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
})
.addTo(map);
}); });
onDestroy(async () => { onDestroy(async () => {
if (watchPositionId) {
navigator.geolocation.clearWatch(watchPositionId);
}
if (map) { if (map) {
console.log('Unloading Leaflet map.'); console.log('Unloading Leaflet map.');
map.remove(); map.remove();
} }
}); });
</script> </script>
{#if !errored} {#if !errored}
<div id="map" in:fade bind:this={mapElement} /> <div id="map" in:fade bind:this={mapElement} />
{:else} {:else}
{errorMessage} {errorMessage}
{/if} {/if}
<svelte:head>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""
/>
</svelte:head>
<style> <style>
@import 'leaflet/dist/leaflet.css';
#map { #map {
height: calc(45vh + 60px); height: 100%;
z-index: 0; z-index: 0;
} }
</style> </style>

View file

@ -1,12 +1,6 @@
let latitude; let latitude;
let longitude; let longitude;
onmessage = () => {};
onmessage = () => { export {};
};
export { };

View file

@ -0,0 +1,93 @@
[
{
"id": 1,
"sequence_number": 2,
"name_it": "Cimitero di Pianello Val Tidone",
"name_en": "Pianello Val Tidone Cemetery",
"description_it": "Il terrazzo fluviale alla confluenza tra Tidone e Chiarone \u00e8 stato fin dall\u2019antichit\u00e0 un luogo privilegiato per l\u2019insediamento umano. \r\n\r\nI materiali conservati presso il Museo Archeologico della Val Tidone evidenziano una continuit\u00e0 di vita dal II sec. a.C. al IV sec. d.C. \r\n\r\nIn particolare, in et\u00e0 romana \u00e8 testimoniata la presenza di un abitato di medie dimensioni, sulle cui strutture, cadute ormai in disuso, si impianta una necropoli altomedievale.",
"description_en": "The river terrace at the confluence between Tidone and Chiarone has been a privileged place for human settlement since ancient times.\r\n\r\nThe materials preserved at the Archaeological Museum of Val Tidone highlight a continuity of life from the 2nd century. B.C. to the 4th century. A.D.\r\n\r\nIn particular, in the Roman age there is evidence of the presence of a medium-sized settlement, on whose structures, which had now fallen into disuse, an early medieval necropolis was established.",
"latitude": 44.948232,
"longitude": 9.446436,
"created_at": "2023-11-02T11:10:48.000000Z",
"updated_at": null,
"deleted_at": null
},
{
"id": 2,
"sequence_number": 3,
"name_it": "Museo Archeologico della Val Tidone ",
"name_en": "Archaeological Museum of Val Tidone",
"description_it": "Il Museo Archeologico della Val Tidone accoglie e valorizza reperti provenienti dall\u2019intera vallata, raccontando la Storia della presenza umana e le trasformazioni del territorio dalla Preistoria al Medioevo. Il Museo \u00e8 ospitato nei sotterranei della Rocca Dal Verme, ricostruita dopo il passaggio del Barbarossa nel 1164. Prende il nome dal condottiero veneto Jacopo dal Verme, che qui fu infeudato dai Visconti di Milano, creando il cosiddetto Stato Vermesco. Nei sotterranei \u00e8 esposta anche un\u2019opera dell\u2019artista locale Paolo Vincenzo Novara, originario di Borgonovo V.T., ma che visse e lavor\u00f2 sempre a Pianello V.T. Si tratta di una veduta del borgo pianellese e delle colline circostanti.",
"description_en": "The Archaeological Museum of Val Tidone welcomes and valorises finds from the entire valley, telling the history of human presence and the transformations of the territory from Prehistory to the Middle Ages. The Museum is housed in the basement of the Rocca Dal Verme, rebuilt after the passage of Barbarossa in 1164. It takes its name from the Venetian leader Jacopo dal Verme, who was enfeoffed here by the Visconti of Milan, creating the so-called Vermesco State. In the basement there is also a work by the local artist Paolo Vincenzo Novara, originally from Borgonovo V.T., but who always lived and worked in Pianello V.T. This is a view of the Pianella village and the surrounding hills.",
"latitude": 44.948232,
"longitude": 9.40559,
"created_at": "2023-11-02T11:10:48.000000Z",
"updated_at": null,
"deleted_at": null
},
{
"id": 3,
"sequence_number": 1,
"name_it": "Arcello",
"name_en": "Arcello",
"description_it": "Qui sorgeva una villa romana con ambienti decorati. A fine Ottocento fu rinvenuta una stele funeraria intitolata a Caio Birrivs Mascvlvs, ora a Piacenza. Citato in un documento dell\u2019844, nel 1089 c\u2019era sicuramente un castello, poi distrutto dal Barbarossa e dal Pallavicino: rimangono il basamento della torre e le cantine dell\u2019attuale canonica. Del convento dei Carmelitani, dismesso nel 1652, rimane una torre. Il pittoresco borgo si sviluppa su una terrazza naturale prospiciente la Media Val Tidone e deve la sua importanza per essere stato feudo della nobile famiglia degli Arcelli dal 11 al 14 sec.",
"description_en": "Here stood a Roman villa with decorated rooms. At the end of the 19th century, a funerary stele dedicated to Caio Birrivs Mascvlvs was found, now in Piacenza. Mentioned in a document from 844, in 1089 there was certainly a castle, later destroyed by Barbarossa and Pallavicino: the base of the tower and the cellars of the current rectory remain. Of the Carmelite convent, decommissioned in 1652, only one tower remains. The picturesque village develops on a natural terrace overlooking the Middle Val Tidone and owes its importance to having been a fiefdom of the noble Arcelli family from the 11th to the 14th century.",
"latitude": 44.952267,
"longitude": 9.446436,
"created_at": "2023-11-02T11:20:30.000000Z",
"updated_at": null,
"deleted_at": null
},
{
"id": 4,
"sequence_number": 4,
"name_it": "Case Rebuffi",
"name_en": "Case Rebuffi",
"description_it": "Numerosi rinvenimenti archeologici del secolo scorso confermano che almeno in et\u00e0 romana qui doveva passare una strada: ormai perduti sono i reperti della sepoltura rinvenuta nel 1928. \r\n\r\nUn tale Rebuffum da Peccoraria compare nel Registrum Magnum di Piacenza quando nel 1187 riceve l\u2019investitura feudale di alcuni terreni: il nome del luogo potrebbe riferirsi a questo antenato. \r\n\r\nIl piccolo oratorio dedicato a S. Andrea di Avellino, ora privato, \u00e8 del XVIII secolo.",
"description_en": "Numerous archaeological discoveries from the last century confirm that at least in Roman times a road must have passed here: the remains of the burial found in 1928 are now lost.\r\n\r\nSuch a Rebuffum from Peccoraria appears in the Registrum Magnum of Piacenza when in 1187 he received the feudal investiture of some land: the name of the place could refer to this ancestor.\r\n\r\nThe small oratory dedicated to S. Andrea di Avellino, now private, dates back to the 18th century.",
"latitude": 44.937276,
"longitude": 9.384442,
"created_at": "2023-11-02T11:22:46.000000Z",
"updated_at": null,
"deleted_at": null
},
{
"id": 7,
"sequence_number": 5,
"name_it": "Casanova",
"name_en": "Casanova",
"description_it": "Borgo rurale sul versante sinistro della vallata e antico possedimento dei Dal Verme. Rinvenimenti archeologici testimoniano la presenza di un insediamento di et\u00e0 romana. Il borgo \u00e8 citato in documenti del X secolo come parte di un beneficio concesso dal monastero di S. Colombano di Bobbio. Particolare \u00e8 la presenza di due chiese: la prima, ormai in rovina, \u00e8 di origine medievale, e mostra ancora il campanile dotato di megafono d\u2019allarme. La seconda, dedicata a Santa\u202fMaria Assunta, fu edificata tra il 1733 e il 1749 per volont\u00e0 del conte Federico Dal Verme. L'edificio, a navata unica, \u00e8 caratterizzato da una facciata a capanna e da un ampio sagrato. L\u2019interno custodisce, sull'altare maggiore, un quadro raffigurante l'Assunzione\u202fdi autore ignoto.",
"description_en": "Rural village on the left side of the valley and ancient possession of the Dal Verme family. Archaeological finds testify to the presence of a Roman settlement. The village is mentioned in documents from the 10th century as part of a benefit granted by the monastery of S. Colombano di Bobbio. The presence of two churches is particular: the first, now in ruins, is of medieval origin, and still shows the bell tower equipped with an alarm megaphone. The second, dedicated to Santa Maria Assunta, was built between 1733 and 1749 by order of Count Federico Dal Verme. The building, with a single nave, is characterized by a gabled fa\u00e7ade and a large churchyard. The interior houses, on the main altar, a painting depicting the Assumption by an unknown artist.",
"latitude": 44.927274,
"longitude": 9.377744,
"created_at": "2023-11-02T11:23:28.000000Z",
"updated_at": null,
"deleted_at": null
},
{
"id": 8,
"sequence_number": 6,
"name_it": "Montemartino",
"name_en": "Montemartino",
"description_it": "Il nome del luogo, che ricorda un Santo legato ai longobardi, viene citato in un documento scritto nel X secolo nel Monastero di Bobbio. Nel XIV secolo qui esisteva un castello di propriet\u00e0 dei Da Fontana, le cui strutture sono visibili nel borgo, e la prima chiesa dedicata a S. Bartolomeo, poi rimaneggiata all\u2019inizio del XX secolo.",
"description_en": "The name of the place, which recalls a saint linked to the Lombards, is mentioned in a document written in the 10th century in the Monastery of Bobbio. In the 14th century there was a castle here owned by the Da Fontana family, whose structures are visible in the village, and the first church dedicated to S. Bartolomeo, then remodeled at the beginning of the 20th century.",
"latitude": 44.904475,
"longitude": 9.353009,
"created_at": "2023-11-02T11:23:28.000000Z",
"updated_at": null,
"deleted_at": null
},
{
"id": 9,
"sequence_number": 7,
"name_it": "Vallerenzo",
"name_en": "Vallerenzo",
"description_it": "Il nome del luogo sembra collegarsi al romano Valerius, nome di famiglia romana presente anche nella stele di Valeria Nardis, conservata al Museo Archeologico della Val Tidone, e sulla Tabula Alimentaria di Veleia. In un documento del 1033 compare un luogo detto Valarinci o Valerinci, mentre nel Registrum Magnum di Piacenza un atto del 1219 \u00e8 redatto proprio in Vallarencio. \r\n\r\nL\u2019oratorio del XVIII secolo, recentemente restaurato, \u00e8 dedicato alla B.V. della Misericordia e S. Lodovico.",
"description_en": "The name of the place seems to be connected to the Roman Valerius, a Roman family name also present in the stele of Valeria Nardis, preserved in the Archaeological Museum of Val Tidone, and on the Tabula Alimentaria of Veleia. In a document from 1033 a place called Valarinci or Valerinci appears, while in the Registrum Magnum of Piacenza an act from 1219 is drawn up precisely in Vallarencio.\r\n\r\nThe recently restored 18th century oratory is dedicated to the B.V. della Misericordia and S. Lodovico.",
"latitude": 44.891136,
"longitude": 9.372671,
"created_at": "2023-11-02T11:27:17.000000Z",
"updated_at": null,
"deleted_at": null
}
]

View file

@ -0,0 +1,78 @@
{
"id": 1,
"name_it": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"name_en": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"description_it": "Il percorso pone l\u2019attenzione su diversi borghi situati lungo il corso del Tidone. L\u2019insediamento umano in Val Tidone \u00e8 testimoniato fin dalle epoche pi\u00f9 remote, quando l\u2019uomo, in base alle necessit\u00e0 delle varie epoche, sceglie terrazzi fluviali o luoghi pi\u00f9 riparati per impiantare i propri siti abitati. \r\n\r\nL\u2019itinerario partendo da Arcello, passando per Pianello V.T., e risalendo il corso del torrente fino a Vallerenzo, incontra numerosi borghi, dove rinvenimenti archeologici testimoniano la presenza romana e lo sfruttamento agricolo dell\u2019area. In epoca successiva, la costruzione di chiese, castelli e monasteri fa comprendere l\u2019importanza strategica del territorio durante tutto il Medioevo, nelle lotte per il potere e come snodo degli itinerari di commercio e di pellegrinaggio.",
"description_en": "The route focuses on various villages located along the Tidone. Human settlement in Val Tidone has been witnessed since the most remote times, when man, based on the needs of the various eras, chose river terraces or more sheltered places to establish his own inhabited sites.\r\n\r\nThe itinerary starting from Arcello, passing through Pianello V.T., and going up the course of the stream to Vallerenzo, encounters numerous villages, where archaeological finds testify to the Roman presence and the agricultural exploitation of the area. In the subsequent era, the construction of churches, castles and monasteries makes us understand the strategic importance of the territory throughout the Middle Ages, in the struggles for power and as a hub for trade and pilgrimage itineraries.",
"route_category_id": 2,
"created_at": "2023-11-02T10:50:07.000000Z",
"updated_at": null,
"deleted_at": null,
"route_sport_details": [
{
"id": 1,
"route_id": 1,
"sport_id": 1,
"short_description_it": "Percorso escursionistico intermedio. Buon allenamento richiesto. Sentieri facilmente percorribili. Adatto a ogni livello di abilit\u00e0. ",
"short_description_en": "Intermediate hiking route. Good training required. Easily accessible paths. Suitable for all skill levels.",
"gpx_path": "",
"distance": 16800,
"duration": 288,
"elevation_gain": 439,
"elevation_loss": null,
"altitude_max": 620,
"altitude_min": 180,
"difficulty_it": "Facile",
"difficulty_en": "Easy",
"route_type_it": "Percorso ad anello",
"route_type_en": "Ring route",
"created_at": "2023-11-02T10:57:41.000000Z",
"updated_at": null,
"deleted_at": null,
"sport": {
"id": 1,
"name_it": "Trekking",
"name_en": "Trekking",
"description_it": "Percorso escursionistico",
"description_en": "Trekking route",
"icon": "",
"created_at": "2023-10-31T18:37:30.000000Z",
"updated_at": null,
"deleted_at": null
}
},
{
"id": 2,
"route_id": 1,
"sport_id": 2,
"short_description_it": "Giro in bici per esperti. Ottimo allenamento richiesto. Superfici perlopi\u00f9 asfaltate. Adatto a ogni livello di abilit\u00e0. ",
"short_description_en": "Bike ride for experts. Excellent training required. Mostly asphalted surfaces. Suitable for all skill levels.",
"gpx_path": "-",
"distance": 16800,
"duration": 84,
"elevation_gain": 333,
"elevation_loss": null,
"altitude_max": 51,
"altitude_min": 181,
"difficulty_it": "Facile",
"difficulty_en": "Easy",
"route_type_it": "Percorso andata/ritorno",
"route_type_en": "Round trip route",
"created_at": "2023-11-02T11:01:55.000000Z",
"updated_at": null,
"deleted_at": null,
"sport": {
"id": 2,
"name_it": "Cicloturismo",
"name_en": "Cycle tourism",
"description_it": "Percorso da fare in bicicletta",
"description_en": "Cycling route",
"icon": "",
"created_at": "2023-10-31T18:37:30.000000Z",
"updated_at": null,
"deleted_at": null
}
}
],
"pictures": []
}

View file

@ -0,0 +1,49 @@
[
{
"id": 1,
"title_it": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"title_en": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"description_it": "Il percorso pone l\u2019attenzione su diversi borghi situati lungo il corso del Tidone. L\u2019insediamento umano in Val Tidone \u00e8 testimoniato fin dalle epoche pi\u00f9 remote, quando l\u2019uomo, in base alle necessit\u00e0 delle varie epoche, sceglie terrazzi fluviali o luoghi pi\u00f9 riparati per impiantare i propri siti abitati. \r\n\r\nL\u2019itinerario partendo da Arcello, passando per Pianello V.T., e risalendo il corso del torrente fino a Vallerenzo, incontra numerosi borghi, dove rinvenimenti archeologici testimoniano la presenza romana e lo sfruttamento agricolo dell\u2019area. In epoca successiva, la costruzione di chiese, castelli e monasteri fa comprendere l\u2019importanza strategica del territorio durante tutto il Medioevo, nelle lotte per il potere e come snodo degli itinerari di commercio e di pellegrinaggio.",
"description_en": "The route focuses on various villages located along the Tidone. Human settlement in Val Tidone has been witnessed since the most remote times, when man, based on the needs of the various eras, chose river terraces or more sheltered places to establish his own inhabited sites.\r\n\r\nThe itinerary starting from Arcello, passing through Pianello V.T., and going up the course of the stream to Vallerenzo, encounters numerous villages, where archaeological finds testify to the Roman presence and the agricultural exploitation of the area. In the subsequent era, the construction of churches, castles and monasteries makes us understand the strategic importance of the territory throughout the Middle Ages, in the struggles for power and as a hub for trade and pilgrimage itineraries.",
"length": 33600,
"elevation_gain": 772,
"sports": [
{ "id": 1, "name_it": "Trekking", "name_en": "Trekking", "duration": 288 },
{ "id": 2, "name_it": "Cicloturismo", "name_en": "Cycle tourism", "duration": 84 }
],
"cover": null
},
{
"id": 2,
"title_it": "GLI ARTISTI DEL 900 A PIANELLO VAL TIDONE",
"title_en": "GLI ARTISTI DEL 900 A PIANELLO",
"description_it": "L\u2019itinerario vuole far conoscere l\u2019arte e gli artisti che nel \u2018900 hanno operato in Val Tidone e si sono lasciati ispirare da questo territorio. Si tratta di artisti piacentini, talvolta provenienti proprio dalla Val Tidone, come Franco Corradini originario di Borgonovo, e addirittura dal paese di Pianello V.T., come \u00e8 il caso di Paolo Vincenzo Novara, che abit\u00f2 e lavor\u00f2 nel borgo. \r\n\r\nLa maggior parte del percorso si svolge a Pianello V.T., dove si visitano alcuni spazi della Rocca Dal Verme, il monumento ai Caduti, una cappella del Cimitero, la Chiesa parrocchiale, la Cappella di Lourdes e un mistadello ovvero una piccola cappella votiva. \r\n\r\nAl di fuori del paese, due tappe del percorso portano alla Rocca d\u2019Olgisio e all\u2019oratorio di Roccapulzana. ",
"description_en": "The itinerary aims to raise awareness of the art and artists who worked in Val Tidone in the 1900s and were inspired by this area. These are artists from Piacenza, sometimes coming from Val Tidone, such as Franco Corradini originally from Borgonovo, and even from the town of Pianello V.T., as is the case of Paolo Vincenzo Novara, who lived and worked in the village.\r\n\r\nMost of the route takes place in Pianello V.T., where you visit some spaces of the Rocca Dal Verme, the war memorial, a chapel in the cemetery, the parish church, the Lourdes chapel and a mistadello or a small votive chapel.\r\n\r\nOutside the town, two stages of the route lead to the Rocca d'Olgisio and the oratory of Roccapulzana.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"cover": null
},
{
"id": 3,
"title_it": "NELLE TERRE DI CUNIZA",
"title_en": "IN CUNIZA'S LAND",
"description_it": "Ricca di rinvenimenti archeologici, la Val Tidone fu frequentata sin dal Paleolitico e fu densamente abitata per tutta la sua storia, in particolare nell\u2019Alto Medioevo, per il quale abbiamo le testimonianze archeologiche ma anche parti di edifici ancora conservati. Notizie si ricavano dai nomi dei luoghi e dai documenti antichi. Tra questi, fondamentale \u00e8 il documento di vendita del 1033 in cui Cuniza, donna di legge longobarda, vende alcuni terreni dell\u2019area collinare di Pianello: questo ci permette di ricostruire la fisionomia di alcuni luoghi e di tracciare una possibile strada che congiungeva Pianello con Travo e Bobbio. Il percorso percorre in parte questa strada e passa nei luoghi di propriet\u00e0 della famiglia di Cuniza, non dimenticando il Museo Archeologico, che conserva reperti del periodo.",
"description_en": "Rich in archaeological finds, Val Tidone was frequented since the Paleolithic and was densely inhabited throughout its history, particularly in the Early Middle Ages, for which we have archaeological evidence but also parts of buildings still preserved. Information is obtained from place names and ancient documents. Among these, fundamental is the sales document from 1033 in which Cuniza, a woman of Lombard law, sells some land in the hilly area of Pianello: this allows us to reconstruct the physiognomy of some places and to trace a possible road that connected Pianello with Travo and Bobbio. The route partly follows this road and passes through places owned by the Cuniza family, not forgetting the Archaeological Museum, which preserves finds from the period.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"cover": null
},
{
"id": 4,
"title_it": "NEI LUOGHI DEI PARTIGIANI",
"title_en": "IN THE PLACES OF THE PARTISANS",
"description_it": "A partire dall\u2019autunno del \u201843 si costitu\u00ec a Piacenza il Comitato di Liberazione antifascista che organizz\u00f2 gruppi armati di resistenza. L\u2019Alta e Media Val Tidone rappresent\u00f2 uno dei territori cardine della lotta partigiana piacentina e fu teatro di alcuni episodi che hanno segnato, purtroppo anche in modo negativo, come nel caso dell\u2019eccidio di Str\u00e0, questa parte di storia piacentina. Il nostro territorio vide la presenza di alcuni gruppi armati di grande importanza, con alcuni dei personaggi pi\u00f9 famosi nella storia della Liberazione piacentina, come il \u2018Fausto\u2019, il \u2018Valoroso\u2019, il \u2018Ballonaio\u2019. Pianello e la Rocca d\u2019Olgisio, insieme all\u2019alta Valle del Tidoncello e l\u2019alta Val Luretta, ebbero un ruolo attivo nei fatti svoltisi qui tra l\u2019inizio del \u201844 e la Liberazione.",
"description_en": "Starting from the autumn of 1943, the anti-fascist Liberation Committee was formed in Piacenza and organized armed resistance groups. The Upper and Middle Val Tidone represented one of the key territories of the Piacenza partisan struggle and was the scene of some episodes that marked, unfortunately also in a negative way, as in the case of the Str\u00e0 massacre, this part of Piacenza history. Our territory saw the presence of some armed groups of great importance, with some of the most famous characters in the history of the Piacenza Liberation, such as the 'Fausto', the 'Valoroso', the 'Ballonaio'. Pianello and the Rocca d'Olgisio, together with the upper Tidoncello Valley and the upper Luretta Valley, played an active role in the events that took place here between the beginning of '44 and the Liberation.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"cover": null
}
]

View file

@ -0,0 +1,41 @@
[
{
"id": 1,
"name_it": "Natura",
"name_en": "Nature",
"description_it": "Giri nella natura",
"description_en": "Countryside routes",
"icon": "",
"color": "#de0e1b",
"cover": "/montagne.png",
"created_at": null,
"updated_at": null,
"deleted_at": null
},
{
"id": 2,
"name_it": "Storia",
"name_en": "History",
"description_it": "Giri nella storia",
"description_en": "History routes",
"icon": "",
"color": "#f6ae04",
"cover": "/archi.png",
"created_at": null,
"updated_at": null,
"deleted_at": null
},
{
"id": 3,
"name_it": "Enogastronomia e tradizione",
"name_en": "Tradition, food and wine",
"description_it": "Giri nella tradizione locale",
"description_en": "Culinarian and traditional routes",
"icon": "",
"color": "#213c8b",
"cover": "/bibbito.png",
"created_at": null,
"updated_at": null,
"deleted_at": null
}
]

View file

@ -0,0 +1,161 @@
[
{
"id": 1,
"title_it": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"title_en": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"description_it": "Il percorso pone l\u2019attenzione su diversi borghi situati lungo il corso del Tidone. L\u2019insediamento umano in Val Tidone \u00e8 testimoniato fin dalle epoche pi\u00f9 remote, quando l\u2019uomo, in base alle necessit\u00e0 delle varie epoche, sceglie terrazzi fluviali o luoghi pi\u00f9 riparati per impiantare i propri siti abitati. \r\n\r\nL\u2019itinerario partendo da Arcello, passando per Pianello V.T., e risalendo il corso del torrente fino a Vallerenzo, incontra numerosi borghi, dove rinvenimenti archeologici testimoniano la presenza romana e lo sfruttamento agricolo dell\u2019area. In epoca successiva, la costruzione di chiese, castelli e monasteri fa comprendere l\u2019importanza strategica del territorio durante tutto il Medioevo, nelle lotte per il potere e come snodo degli itinerari di commercio e di pellegrinaggio.",
"description_en": "The route focuses on various villages located along the Tidone. Human settlement in Val Tidone has been witnessed since the most remote times, when man, based on the needs of the various eras, chose river terraces or more sheltered places to establish his own inhabited sites.\r\n\r\nThe itinerary starting from Arcello, passing through Pianello V.T., and going up the course of the stream to Vallerenzo, encounters numerous villages, where archaeological finds testify to the Roman presence and the agricultural exploitation of the area. In the subsequent era, the construction of churches, castles and monasteries makes us understand the strategic importance of the territory throughout the Middle Ages, in the struggles for power and as a hub for trade and pilgrimage itineraries.",
"length": 33600,
"elevation_gain": 772,
"sports": [
{ "id": 1, "name_it": "Trekking", "name_en": "Trekking" },
{ "id": 2, "name_it": "Cicloturismo", "name_en": "Cycle tourism" }
],
"category_id": 2,
"category_name_it": "Storia",
"category_name_en": "History"
},
{
"id": 2,
"title_it": "GLI ARTISTI DEL 900 A PIANELLO VAL TIDONE",
"title_en": "GLI ARTISTI DEL 900 A PIANELLO",
"description_it": "L\u2019itinerario vuole far conoscere l\u2019arte e gli artisti che nel \u2018900 hanno operato in Val Tidone e si sono lasciati ispirare da questo territorio. Si tratta di artisti piacentini, talvolta provenienti proprio dalla Val Tidone, come Franco Corradini originario di Borgonovo, e addirittura dal paese di Pianello V.T., come \u00e8 il caso di Paolo Vincenzo Novara, che abit\u00f2 e lavor\u00f2 nel borgo. \r\n\r\nLa maggior parte del percorso si svolge a Pianello V.T., dove si visitano alcuni spazi della Rocca Dal Verme, il monumento ai Caduti, una cappella del Cimitero, la Chiesa parrocchiale, la Cappella di Lourdes e un mistadello ovvero una piccola cappella votiva. \r\n\r\nAl di fuori del paese, due tappe del percorso portano alla Rocca d\u2019Olgisio e all\u2019oratorio di Roccapulzana. ",
"description_en": "The itinerary aims to raise awareness of the art and artists who worked in Val Tidone in the 1900s and were inspired by this area. These are artists from Piacenza, sometimes coming from Val Tidone, such as Franco Corradini originally from Borgonovo, and even from the town of Pianello V.T., as is the case of Paolo Vincenzo Novara, who lived and worked in the village.\r\n\r\nMost of the route takes place in Pianello V.T., where you visit some spaces of the Rocca Dal Verme, the war memorial, a chapel in the cemetery, the parish church, the Lourdes chapel and a mistadello or a small votive chapel.\r\n\r\nOutside the town, two stages of the route lead to the Rocca d'Olgisio and the oratory of Roccapulzana.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 2,
"category_name_it": "Storia",
"category_name_en": "History"
},
{
"id": 3,
"title_it": "NELLE TERRE DI CUNIZA",
"title_en": "IN CUNIZA'S LAND",
"description_it": "Ricca di rinvenimenti archeologici, la Val Tidone fu frequentata sin dal Paleolitico e fu densamente abitata per tutta la sua storia, in particolare nell\u2019Alto Medioevo, per il quale abbiamo le testimonianze archeologiche ma anche parti di edifici ancora conservati. Notizie si ricavano dai nomi dei luoghi e dai documenti antichi. Tra questi, fondamentale \u00e8 il documento di vendita del 1033 in cui Cuniza, donna di legge longobarda, vende alcuni terreni dell\u2019area collinare di Pianello: questo ci permette di ricostruire la fisionomia di alcuni luoghi e di tracciare una possibile strada che congiungeva Pianello con Travo e Bobbio. Il percorso percorre in parte questa strada e passa nei luoghi di propriet\u00e0 della famiglia di Cuniza, non dimenticando il Museo Archeologico, che conserva reperti del periodo.",
"description_en": "Rich in archaeological finds, Val Tidone was frequented since the Paleolithic and was densely inhabited throughout its history, particularly in the Early Middle Ages, for which we have archaeological evidence but also parts of buildings still preserved. Information is obtained from place names and ancient documents. Among these, fundamental is the sales document from 1033 in which Cuniza, a woman of Lombard law, sells some land in the hilly area of Pianello: this allows us to reconstruct the physiognomy of some places and to trace a possible road that connected Pianello with Travo and Bobbio. The route partly follows this road and passes through places owned by the Cuniza family, not forgetting the Archaeological Museum, which preserves finds from the period.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 2,
"category_name_it": "Storia",
"category_name_en": "History"
},
{
"id": 4,
"title_it": "NEI LUOGHI DEI PARTIGIANI",
"title_en": "IN THE PLACES OF THE PARTISANS",
"description_it": "A partire dall\u2019autunno del \u201843 si costitu\u00ec a Piacenza il Comitato di Liberazione antifascista che organizz\u00f2 gruppi armati di resistenza. L\u2019Alta e Media Val Tidone rappresent\u00f2 uno dei territori cardine della lotta partigiana piacentina e fu teatro di alcuni episodi che hanno segnato, purtroppo anche in modo negativo, come nel caso dell\u2019eccidio di Str\u00e0, questa parte di storia piacentina. Il nostro territorio vide la presenza di alcuni gruppi armati di grande importanza, con alcuni dei personaggi pi\u00f9 famosi nella storia della Liberazione piacentina, come il \u2018Fausto\u2019, il \u2018Valoroso\u2019, il \u2018Ballonaio\u2019. Pianello e la Rocca d\u2019Olgisio, insieme all\u2019alta Valle del Tidoncello e l\u2019alta Val Luretta, ebbero un ruolo attivo nei fatti svoltisi qui tra l\u2019inizio del \u201844 e la Liberazione.",
"description_en": "Starting from the autumn of 1943, the anti-fascist Liberation Committee was formed in Piacenza and organized armed resistance groups. The Upper and Middle Val Tidone represented one of the key territories of the Piacenza partisan struggle and was the scene of some episodes that marked, unfortunately also in a negative way, as in the case of the Str\u00e0 massacre, this part of Piacenza history. Our territory saw the presence of some armed groups of great importance, with some of the most famous characters in the history of the Piacenza Liberation, such as the 'Fausto', the 'Valoroso', the 'Ballonaio'. Pianello and the Rocca d'Olgisio, together with the upper Tidoncello Valley and the upper Luretta Valley, played an active role in the events that took place here between the beginning of '44 and the Liberation.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 2,
"category_name_it": "Storia",
"category_name_en": "History"
},
{
"id": 5,
"title_it": "SULLE TRACCE DELLA GALEINA GRISA",
"title_en": "ON THE FOOTSTEPS OF GALEINA GRISA",
"description_it": "Nella notte della Galeina Grisa, tra il 30 aprile e il primo di maggio \u00e8 tradizione che giovani e vecchi si riuniscano per raggiungere le principali localit\u00e0 della valle per una lunga nottata di festeggiamenti, durante la quale le compagnie di cantori itineranti visitano osterie e cascine proponendo i loro canti goliardici inneggianti alla primavera. In cambio, essi ottengono spuntini a base di salame, uova e cipollotti (i cosiddetti \u2018bavaroni\u2019 in espressione dialettale) annaffiati da abbondante vino rosso, spesso servito in un\u2019unica coppa condivisa da tutti. \r\n\r\nIl percorso ripercorre i luoghi della tradizione che sono soliti accogliere i cantori e profumare di ospitalit\u00e0 e di convivialit\u00e0 agreste, ma consente anche di godere numerosi punti panoramici.",
"description_en": "On the night of Galeina Grisa, between April 30th and May 1st, it is traditional for young and old to come together to reach the main towns of the valley for a long night of celebrations, during which companies of itinerant singers visit taverns and farmhouses proposing their playful songs praising spring. In exchange, they receive snacks based on salami, eggs and spring onions (the so-called 'bavaroni' in dialect) washed down with abundant red wine, often served in a single cup shared by all.\r\n\r\nThe route retraces the traditional places that usually welcome the singers and smell of hospitality and rural conviviality, but also allows you to enjoy numerous panoramic points.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 3,
"category_name_it": "Enogastronomia e tradizione",
"category_name_en": "Tradition, food and wine"
},
{
"id": 6,
"title_it": "PERCORSO BOTTEGHE STORICHE",
"title_en": "HISTORICAL WORKSHOPS ROUTE",
"description_it": "Un rilassante percorso urbano da effettuare a piedi o in bicicletta, tra le prelibate offerte enogastronomiche del paese; in ognuna delle tappe si possono trovare i prodotti tipici della tradizione piacentina e valtidonese ed essere coccolati dalle atmosfere antiche e dai profumi delle varie botteghe paesane. Un tour per golosi ma anche per chi vuole portare con s\u00e9 uno squisito ricordo della giornata in Val Tidone. \r\n\r\nLa bellissima piazza porticata di Pianello e le sue strette viuzze colorate accolgono inoltre numerosi bar che propongono aperitivi, cocktails, gelati e frapp\u00e8, che non potranno che rendere ancora pi\u00f9 piacevole la visita al paese.",
"description_en": "A relaxing urban route to be taken on foot or by bicycle, among the delicious food and wine offerings of the town; in each of the stages you can find the typical products of the Piacenza and Valtidone tradition and be pampered by the ancient atmospheres and scents of the various village shops. A tour for gourmands but also for those who want to take with them an exquisite memory of the day in Val Tidone.\r\n\r\nThe beautiful porticoed square of Pianello and its narrow colorful streets also host numerous bars offering aperitifs, cocktails, ice creams and milkshakes, which will only make a visit to the town even more pleasant.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 3,
"category_name_it": "Enogastronomia e tradizione",
"category_name_en": "Tradition, food and wine"
},
{
"id": 7,
"title_it": "UN ANELLO DI CRINALI E COLORI",
"title_en": "A RING OF RIDGES AND COLOURS",
"description_it": "Dal cimitero di Arcello si parte in direzione sud, mantenendo alle spalle il paese. Dopo circa 300 m lungo la strada asfaltata, si arriva in prossimit\u00e0 della Tenuta Santa Giustina. Qui si imbocca il bivio a destra e da qui si continua a camminare lungo un percorso ad anello che si sviluppa lungo il crinale, salendo gradualmente fino al punto pi\u00f9 alto di questo percorso panoramico. Da qui comincia la discesa, attraverso il bosco di Santa Giustina, fino a raggiungere il lato posteriore della Tenuta. Il percorso si ricongiunge poi chiudendo l\u2019anello in prossimit\u00e0 del primo bivio incontrato. Da qui si riprende la strada asfaltata per tornare al cimitero di Arcello.",
"description_en": "From the Arcello cemetery you set off in a southerly direction, keeping the town behind you. After about 300 m along the asphalt road, you arrive near the Tenuta Santa Giustina. Here you take the fork on the right and from here you continue walking along a circular route that develops along the ridge, gradually climbing up to the highest point of this panoramic route. From here the descent begins, through the Santa Giustina forest, until reaching the back side of the estate. The route then rejoins, closing the ring near the first crossroads encountered. From here we take the asphalt road back to the Arcello cemetery.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 1,
"category_name_it": "Natura",
"category_name_en": "Nature"
},
{
"id": 8,
"title_it": "LUNGO LE SPONDE DEL TIDONE",
"title_en": "ALONG THE BANKS OF THE TIDONE",
"description_it": "Partendo da Casa Nova si percorre la strada asfaltata per un breve tratto, in direzione Pradaglia. Dopo circa 400 m si svolta a destra, scendendo verso il Tidone costeggiando un vigneto. Arrivati in prossimit\u00e0 del torrente, si attraversa il letto del torrente in prossimit\u00e0 del guado, raggiungendo la sponda sinistra del torrente stesso (Comune Alta Val Tidone). Si prosegue in direzione sinistra lungo il Sentiero del Tidone (direzione Sorgente) per circa 2 Km. Superato il Mulino del Ceppetto si guada di nuovo il torrente, per tornate sulla sponda destra. Da qui si risale verso Casa Barbieri, si imbocca una stradina di ghiaia in direzione Pradaglia. Attraversata questa localit\u00e0, si prosegue in direzione Casa Nova per richiudere l\u2019anello, lungo quest\u2019ultimo tratto di strada asfaltata a bassa percorrenza.",
"description_en": "Starting from Casa Nova, follow the asphalt road for a short stretch, towards Pradaglia. After about 400 m, turn right, descending towards the Tidone along a vineyard. Once you arrive near the stream, cross the bed of the stream near the ford, reaching the left bank of the stream itself (Alta Val Tidone Municipality). Continue in a left direction along the Sentiero del Tidone (direction Sorgente) for about 2 km. After passing the Mulino del Ceppetto you cross the stream again, to return to the right bank. From here you go up towards Casa Barbieri, take a gravel road towards Pradaglia. Once you have crossed this location, continue in the direction of Casa Nova to close the ring, along this last stretch of low-traffic asphalt road.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 1,
"category_name_it": "Natura",
"category_name_en": "Nature"
},
{
"id": 9,
"title_it": "DOVE LO SPIRITO AGRESTE INCONTRA LA GENUINIT\u00c0 DI UN CONTESTO RURALE",
"title_en": "WHERE THE RURAL SPIRIT MEETS THE GENUINITY OF A RURAL CONTEXT",
"description_it": "Partendo da Casa Nova si raggiunge Localit\u00e0 La Scabbia lungo un tragitto di 700 m. Da qui il percorso si sviluppa lungo un anello che sale lungo il versante est della vallata, raggiungendo la chiesetta di Madonna del Sasso e continua poi fino a raggiungere il crinale in Localit\u00e0 Gabbiano. Avanzando in costa, mantenendo la Rocca d\u2019Olgisio davanti a sinistra, si inizia la discesa lungo lo stesso versante, passando questa volta per localit\u00e0 Carbonara, e poi via via si scende tra vigneti, campi di grano e sentieri ombreggiati fino ad arrivare di nuovo in Localit\u00e0 La Scabbia, dove l\u2019anello si chiude. Da qui si ritorna a Casa Nova, percorrendo di nuovo il tragitto iniziale di 700 m.",
"description_en": "Starting from Casa Nova you reach Localit\u00e0 La Scabbia along a 700 m journey. From here the route develops along a ring that climbs along the eastern side of the valley, reaching the small church of Madonna del Sasso and then continues until reaching the ridge in Localit\u00e0 Gabbiano. Advancing along the coast, keeping the Rocca d'Olgisio in front on the left, you begin the descent along the same side, this time passing through Carbonara, and then gradually descend through vineyards, wheat fields and shaded paths until you arrive again in Localit\u00e0 La Scabbia, where the ring closes. From here you return to Casa Nova, following the initial 700 m journey again.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 1,
"category_name_it": "Natura",
"category_name_en": "Nature"
},
{
"id": 10,
"title_it": "LE ARENARIE TRA I BOSCHI DELLA VAL CHIARONE",
"title_en": "THE SANDSTONES AMONG THE WOODS OF THE CHIARONE VALLEY",
"description_it": "Il percorso comincia in prossimit\u00e0 dell\u2019inizio del sentiero CAI 209, lungo la Val Chiarone. Imboccando tale sentiero si inizia a salire, fino a raggiungere un boschetto all\u2019interno del quale si sviluppa gran parte del percorso. Dopo circa 1 Km si interseca la bretella che raggiunge la piana di S. Martino. Questa bretella \u00e8 un ampio sentiero pianeggiante che si imbocca sulla sinistra. Attraversata la piana di S. Martino si riprende il sentiero CAI 211che porta al caratteristico Becco del Merlo, un corridoio tra due importanti rocce, lungo circa 20 m. Il sentiero prosegue ad anello ricongiungendosi al bivio dove si \u00e8 imboccata la bretella per la Piana di S. Martino. Da qui si imbocca la discesa, ripercorrendo il percorso gi\u00e0 fatto in precedenza lungo il sentiero CAI 209.",
"description_en": "The route begins near the start of the CAI 209 path, along the Val Chiarone. Taking this path you begin to climb until you reach a grove within which much of the route develops. After about 1 km the road that reaches the S. Martino plain intersects. This link road is a wide flat path that you take on the left. After crossing the S. Martino plain, take the CAI 211 path again which leads to the characteristic Becco del Merlo, a corridor between two important rocks, approximately 20 m long. The path continues in a ring, rejoining the crossroads where you took the slip road to the Piana di S. Martino. From here take the descent, retracing the route already taken previously along the CAI 209 path.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 1,
"category_name_it": "Natura",
"category_name_en": "Nature"
},
{
"id": 11,
"title_it": "NATURA AUTENTICA SUL MONTE SERENO",
"title_en": "AUTHENTIC NATURE ON MONTE SERENO",
"description_it": "Il percorso si sviluppa lungo l\u2019anello contrassegnato dal sentiero CAI 225. Partendo da Ca\u2019 del Fabbro, si imbocca il sentiero, dopo circa 200 m si svola a sinistra e si inizia a salire lungo il versante, lungo un percorso sdrucciolevole caratterizzato da ghiaione. Arrivati sul crinale, dove ci sono diversi punti in cui fioriscono le orchidee tipiche dell\u2019Appennino nella prima met\u00e0 di maggio, si prosegue mantenendo il sentiero CAI225, fino a entrare nel bosco. Qui ci sono un paio di bivi a cui prestare attenzione. Ad un certo punto il sentiero si apre su una radura, il monte Sereno, da cui si pu\u00f2 vedere la Val Tebbia. Da qui si comincia a scendere, attraversando ancora tratti boschivi, alternati a brevi mulattiere. L\u2019ultima parte del percorso \u00e8 fitta di vegetazione, bisogna prestare attenzione ai segnali e presenta alcune criticit\u00e0 nella discesa: il sentiero si restringe e la ripidit\u00e0 aumenta. Arrivati nel fondovalle, si imbocca la strada asfaltata per richiudere l\u2019anello in localit\u00e0 C\u00e0 del Fabbro.",
"description_en": "The route develops along the ring marked by the CAI path 225. Starting from Ca' del Fabbro, take the path, after about 200 m you turn left and start climbing along the slope, along a slippery path characterized by scree . Once you reach the ridge, where there are several points where the typical orchids of the Apennines bloom in the first half of May, continue keeping the CAI225 path until you enter the woods. There are a couple of crossroads to watch out for here. At a certain point the path opens onto a clearing, Mount Sereno, from which you can see Val Tebbia. From here you begin to descend, still crossing wooded sections, alternating with short mule tracks. The last part of the route is thick with vegetation, you must pay attention to the signs and presents some critical issues in the descent: the path narrows and the steepness increases. Once you reach the valley floor, take the asphalt road to close the ring in the C\u00e0 del Fabbro area.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 1,
"category_name_it": "Natura",
"category_name_en": "Nature"
},
{
"id": 12,
"title_it": "L\u2019AGRICOLTURA IN VAL TIDONE, VIGNETI, ULIVI E CAMPI COLTIVATI",
"title_en": "AGRICULTURE IN VAL TIDONE, VINEYARDS, OLIVE TREES AND CULTIVATED FIELDS",
"description_it": "Dal cimitero di Arcello si parte in direzione sud, mantenendo alle spalle il paese. Dopo circa 300 m lungo la strada asfaltata, si arriva in prossimit\u00e0 della Tenuta Santa Giustina. Qui si imbocca il bivio a destra e da qui si continua a camminare lungo il crinale. Dopo circa 2 Km si incontra un bivio: proseguendo a sinistra continua il percorso N2, mantenendo la destra si continua sul percorso T1. La carraia inizia a scendere dolcemente, fino alla localit\u00e0 rurale di Poggio Cavalli. Si imbocca qui la piccola strada asfaltata che, sempre in discesa, porta fino al Podere Ca\u2019 Nova, dove termina il percorso. Quest\u2019ultimo tratto \u00e8 circondato da vigneti e incontra uno degli uliveti della Val Tidone.",
"description_en": "From the Arcello cemetery you set off in a southerly direction, keeping the town behind you. After about 300 m along the asphalt road, you arrive near the Tenuta Santa Giustina. Here you take the fork on the right and from here you continue walking along the ridge. After about 2 km you come to a crossroads: continuing on the left, continue on route N2, keeping to the right, continue on route T1. The carriage road begins to descend gently, up to the rural town of Poggio Cavalli. Here you take the small asphalt road which, always downhill, leads to Podere Ca' Nova, where the route ends. This last stretch is surrounded by vineyards and meets one of the olive groves of Val Tidone.",
"length": 0,
"elevation_gain": 0,
"sports": [],
"category_id": 1,
"category_name_it": "Natura",
"category_name_en": "Nature"
}
]

View file

@ -0,0 +1,35 @@
{
"id": 1,
"name_it": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"name_en": "GLI INSEDIAMENTI ANTICHI LUNGO IL TIDONE",
"description_it": "Il percorso pone l\u2019attenzione su diversi borghi situati lungo il corso del Tidone. L\u2019insediamento umano in Val Tidone \u00e8 testimoniato fin dalle epoche pi\u00f9 remote, quando l\u2019uomo, in base alle necessit\u00e0 delle varie epoche, sceglie terrazzi fluviali o luoghi pi\u00f9 riparati per impiantare i propri siti abitati. \r\n\r\nL\u2019itinerario partendo da Arcello, passando per Pianello V.T., e risalendo il corso del torrente fino a Vallerenzo, incontra numerosi borghi, dove rinvenimenti archeologici testimoniano la presenza romana e lo sfruttamento agricolo dell\u2019area. In epoca successiva, la costruzione di chiese, castelli e monasteri fa comprendere l\u2019importanza strategica del territorio durante tutto il Medioevo, nelle lotte per il potere e come snodo degli itinerari di commercio e di pellegrinaggio.",
"description_en": "The route focuses on various villages located along the Tidone. Human settlement in Val Tidone has been witnessed since the most remote times, when man, based on the needs of the various eras, chose river terraces or more sheltered places to establish his own inhabited sites.\r\n\r\nThe itinerary starting from Arcello, passing through Pianello V.T., and going up the course of the stream to Vallerenzo, encounters numerous villages, where archaeological finds testify to the Roman presence and the agricultural exploitation of the area. In the subsequent era, the construction of churches, castles and monasteries makes us understand the strategic importance of the territory throughout the Middle Ages, in the struggles for power and as a hub for trade and pilgrimage itineraries.",
"route_category_id": 2,
"created_at": "2023-11-02T10:50:07.000000Z",
"updated_at": null,
"deleted_at": null,
"route_sport_details": [
{
"id": 1,
"route_id": 1,
"sport_id": 1,
"short_description_it": "Percorso escursionistico intermedio. Buon allenamento richiesto. Sentieri facilmente percorribili. Adatto a ogni livello di abilit\u00e0. ",
"short_description_en": "Intermediate hiking route. Good training required. Easily accessible paths. Suitable for all skill levels.",
"gpx_path": "",
"distance": 16800,
"duration": 288,
"elevation_gain": 439,
"elevation_loss": null,
"altitude_max": 620,
"altitude_min": 180,
"difficulty_it": "Facile",
"difficulty_en": "Easy",
"route_type_it": "Percorso ad anello",
"route_type_en": "Ring route",
"created_at": "2023-11-02T10:57:41.000000Z",
"updated_at": null,
"deleted_at": null
}
],
"pictures": []
}

60
frontend/src/lib/repo.ts Normal file
View file

@ -0,0 +1,60 @@
import { PUBLIC_BACKEND_URL } from '$env/static/public';
const API_URL = `${PUBLIC_BACKEND_URL}/api`;
const getAllRoutes = async () => {
let data = [];
const response = await fetch(`${API_URL}/all-routes`);
const json = await response.json();
data = json;
return data;
};
const getRouteCategories = async () => {
let data = [];
const response = await fetch(`${API_URL}/route-categories`);
const json = await response.json();
data = json;
return data;
};
const getRouteByCategory = async (categoryId: number) => {
let data = [];
const response = await fetch(`${API_URL}/route-by-category/${categoryId}`);
const json = await response.json();
data = json;
return data;
};
const getRoute = async (routeId: number) => {
let data = {};
const response = await fetch(`${API_URL}/route/${routeId}`);
const json = await response.json();
data = json;
return data;
};
const getSport = async (routeId: number, sportId: number) => {
let data = {};
const response = await fetch(`${API_URL}/route/${routeId}/${sportId}`);
const json = await response.json();
data = json;
return data;
};
const getPlacemarks = async (routeId: number, sportId: number) => {
let data = {};
const response = await fetch(`${API_URL}/getPlacemarks/${routeId}/${sportId}`);
const json = await response.json();
data = json;
return data;
};
export { getAllRoutes, getPlacemarks, getSport, getRoute, getRouteByCategory, getRouteCategories };

View file

@ -0,0 +1,45 @@
<script>
import { page } from '$app/stores';
</script>
<div id="container">
<div class="wrapper">
<p id="status">{$page.status}</p>
<p id="message">{$page.error?.message}</p>
</div>
</div>
<style>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
#container {
background-image: url(/splash.webp);
background-size: cover;
display: grid;
place-content: center;
height: 100dvh;
width: 100dvw;
z-index: 99;
position: absolute;
font-size: 24px;
font-family: monospace;
}
.wrapper {
background: transparent;
display: flex;
backdrop-filter: blur(5px);
border-radius: 10px;
border: 1px solid black;
}
#status {
border-right: 1px solid black;
}
p {
padding: 20px;
}
</style>

View file

@ -0,0 +1,3 @@
export const prerender = false;
export const ssr = true;
export const csr = true;

View file

@ -1,42 +1,66 @@
<script> <script lang="ts">
import { pwaInfo } from 'virtual:pwa-info';
import { onMount } from 'svelte'
import BottomAppBar from '$lib/components/BottomAppBar.svelte'; import BottomAppBar from '$lib/components/BottomAppBar.svelte';
$: webManifestLink = pwaInfo ? pwaInfo.webManifest.linkTag : '' import Toast from '$lib/components/Toast.svelte';
export const prerender = true; import { onMount } from 'svelte';
import { onNavigate } from '$app/navigation';
import { dev } from '$app/environment';
onMount(async () => { let node: HTMLElement;
if (pwaInfo) {
const { registerSW } = await import('virtual:pwa-register') onMount(() => {
registerSW({ if (dev) return;
immediate: true,
onRegistered(r) { if ('serviceWorker' in navigator) {
console.log(`SW Registered: ${r}`) addEventListener('load', function () {
console.log(JSON.stringify(r)); navigator.serviceWorker.register('/service-worker.js');
}, });
onRegisterError(error) {
console.log('SW registration error', error) if (BroadcastChannel) {
const channel = new BroadcastChannel('sw-messages');
channel.addEventListener('message', (event) => {
new Toast({ target: node });
});
} else {
navigator.serviceWorker.addEventListener('message', (evt) => {
new Toast({ target: node });
});
} }
})
navigator.serviceWorker.ready.then((registration) => {
registration?.active?.postMessage('Save client');
});
} }
}) });
onNavigate((navigation: { complete: any }) => {
if (!document.startViewTransition) return;
return new Promise<void>((resolve) => {
document?.startViewTransition(async () => {
resolve();
await navigation.complete;
});
});
});
</script> </script>
<slot></slot> <div bind:this={node}></div>
<footer>
<BottomAppBar />
</footer>
{#await import('$lib/ReloadPrompt.svelte') then { default: ReloadPrompt}} <main><slot /></main>
<ReloadPrompt /> <BottomAppBar />
{/await}
<svelte:head>
{@html webManifestLink}
</svelte:head>
<style> <style>
footer { main {
background: white; height: calc(100dvh - 10dvh - 10px);
overflow-y: scroll;
width: 100%;
display: grid;
grid-template-rows: 70px auto;
}
div {
position: absolute;
width: 0;
height: 0;
} }
</style> </style>

View file

@ -1,49 +1,47 @@
<script> <script lang="ts">
import BottomAppBar from '$lib/components/BottomAppBar.svelte';
import Path from '$lib/components/Path.svelte'; import Path from '$lib/components/Path.svelte';
import Splash from '$lib/components/Splash.svelte';
export let data;
let categories: App.Category[] = data.categories;
</script> </script>
<Splash />
<header>
<div id="welcome-message">Benvenuti a <span class="bold">Pianello Val Tidone</span></div>
</header>
<main> <main>
<div id="welcome-message">Benvenuti a <span class="bold">Pianello Val Tidone</span></div>
<div id="route-cards"> <div id="route-cards">
<Path color="#de0e1b" type="naturalistici" /> {#each categories as category}
<Path color="#f6ae04" type="storici" /> <Path src={category.cover} color={category.color} name={category.name_it} id={category.id} />
<Path color="#213c8b" type="tradizionalistici" /> {/each}
</div> </div>
</main> </main>
<style> <style>
header {
text-align: center;
display: grid;
place-items: center;
}
main { main {
width: 100%;
height: 72vh;
margin: 0px;
font-family: 'Roboto-Regular';
display: grid; display: grid;
place-items: center; margin: 0 auto;
height: calc(90dvh - 10px);
width: 100%;
font-family: 'Roboto-Regular';
grid-template-rows: 70px auto;
} }
.bold { .bold {
font-weight: bold; font-weight: bold;
font-size: 22px;
} }
#welcome-message { #welcome-message {
padding-top: 20px;
font-size: 18px; font-size: 18px;
display: grid;
width: 100%;
place-content: center;
text-align: center;
} }
#route-cards { #route-cards {
display: flex; padding: 20px;
flex-direction: column; display: grid;
height: 100%;
width: 100%; width: 100%;
align-items: center; height: 100%;
gap: 10px;
} }
</style> </style>

View file

@ -0,0 +1,16 @@
import { error } from '@sveltejs/kit';
import { PUBLIC_BACKEND_URL } from '$env/static/public';
const API_URL = `${PUBLIC_BACKEND_URL}/api`;
export async function load({ fetch }) {
let categories = [];
try {
const response = await fetch(`${API_URL}/route-categories`);
categories = await response.json();
} catch (ex) {
error(404, { message: 'API Not Found' });
}
return { categories };
}

View file

@ -1,15 +0,0 @@
import { error } from "@sveltejs/kit";
export async function load({ params }) {
return {
title: params.slug,
routes: [
{id: 1, name: 'Pianello 1', image: '/images/test-1.jpg', duration: 1233},
{id: 2, name: 'Pianello 2', image: '/images/test-1.jpg', duration: 1233},
{id: 3, name: 'Pianello 3', image: '/images/test-1.jpg', duration: 2134},
{id: 4, name: 'Pianello 4', image: '/images/test-1.jpg', duration: 2134 * 4},
{id: 5, name: 'Pianello 5', image: '/images/test-1.jpg', duration: 2134 * 4},
]
}
}

View file

@ -1,15 +1,16 @@
<script lang="ts"> <script lang="ts">
import Route from '$lib/components/Route.svelte'; import Route from '$lib/components/Route.svelte';
import HomeHeader from '$lib/components/HomeHeader.svelte'; import HomeHeader from '$lib/components/HomeHeader.svelte';
export let data; export let data;
</script> </script>
<HomeHeader title={data.title}></HomeHeader> <HomeHeader title={data.category}></HomeHeader>
<div> <div>
{#each data.routes as route} {#each data.routes as route}
<Route {route}></Route> <Route {route}></Route>
{/each} {/each}
</div> </div>
<style> <style>
@ -18,9 +19,7 @@
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
gap: 15px; gap: 15px;
margin-top: 70px;
padding-top: 10px; padding-top: 10px;
max-height: calc(100% - 20vh);
overflow-y: scroll; overflow-y: scroll;
} }
</style> </style>

View file

@ -0,0 +1,26 @@
import { error } from '@sveltejs/kit';
import { PUBLIC_BACKEND_URL } from '$env/static/public';
const API_URL = `${PUBLIC_BACKEND_URL}/api`;
export async function load({ params, fetch }) {
const response = await fetch(`${API_URL}/route-categories`);
const categories: App.Category[] = await response.json();
const categoryId = Number(params.slug);
const category: App.Category = categories.find((c) => c.id === categoryId) as App.Category;
if (!category) {
error(404, { message: 'Path not found' });
}
const response2 = await fetch(`${API_URL}/route-by-category/${categoryId}`);
const routes = await response2.json();
const toReturn = {
category: category.name_it,
routes
};
return toReturn;
}

View file

@ -1,10 +0,0 @@
import { error } from "@sveltejs/kit";
export async function load({ params }) {
return {
id: 1,
name: 'Pianello 1',
image: '/images/test-1.jpg',
duration: 1233
};
}

View file

@ -2,50 +2,46 @@
import Header from '$lib/components/Header.svelte'; import Header from '$lib/components/Header.svelte';
import Tabs from '$lib/components/Tabs.svelte'; import Tabs from '$lib/components/Tabs.svelte';
import MapTab from '$lib/components/tabs/MapTab.svelte'; import MapTab from '$lib/components/tabs/MapTab.svelte';
import { onMount } from 'svelte';
let syncWorker: Worker | undefined = undefined; export let data: App.Route;
let latitude; let divider: HTMLDivElement;
let longitude; let isMap: boolean = false;
let accuracy;
export let data; const tabClick = (event) => {
let divider; isMap = event.detail.component === MapTab;
};
const mapClick = (evt) => {
if(evt.detail.component === MapTab)
divider.style.transform = "translateY(calc(0vh + 70px))";
else
divider.style.transform = "translateY(30vh)";
}
</script> </script>
<Header></Header> <Header></Header>
<img src='/images/splash-background.webp' alt='splash'> <img src="/images/splash-background.webp" alt="splash" />
<div bind:this={divider} id='divider'> <div bind:this={divider} id="divider" class:move-to-top={isMap}>
<div id='banner'> <div id="banner">
<p>Percorso <b>{data.name}</b></p> <p class="path-name">Percorso <b>{data.name_it}</b></p>
<p id='duration'>Durata {Math.floor(Number(data.duration) / 60)}'</p> <p id="duration">Dislivello {data?.route_sport_details[0]?.elevation_gain} m</p>
</div> </div>
<Tabs on:map-click={mapClick}></Tabs> <Tabs on:tab-click={tabClick} route={data}></Tabs>
</div> </div>
<style> <style>
#divider { #divider {
background: white; background: white;
margin-top: 0px; margin-top: 0px;
margin-top: 0; margin-top: 0;
transform: translateY(30vh); align-self: end;
transition: transform 400ms; display: grid;
}
.move-to-top {
align-self: start !important;
height: calc(90dvh - 80px);
grid-template-rows: auto auto 1fr;
} }
img { img {
width: 100%; width: 100%;
height: 100%; height: calc(100% - 10vh - 10px);
position: absolute; position: absolute;
z-index: -1; z-index: -1;
} }
@ -69,4 +65,8 @@
font-size: 14px; font-size: 14px;
text-align: rightìì; text-align: rightìì;
} }
.path-name {
view-transition-name: title;
}
</style> </style>

View file

@ -0,0 +1,16 @@
import { error } from '@sveltejs/kit';
import { PUBLIC_BACKEND_URL } from '$env/static/public';
const API_URL = `${PUBLIC_BACKEND_URL}/api`;
export async function load({ params, fetch }) {
const routeId = Number(params.slug);
const response = await fetch(`${API_URL}/route/${routeId}`);
const route = await response.json();
if (!route) {
error(404, { message: 'Route non found' });
}
return route;
}

80
frontend/src/sw.ts Normal file
View file

@ -0,0 +1,80 @@
/// <reference types="@sveltejs/kit" />
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />
import { build, files, version, prerendered } from '$service-worker';
// Create a unique cache name for this deployment
const CACHE = `sw-cache-${version}`;
const ASSETS = [
...build, // the app itself
...files, // everything in `static`,
...prerendered // dynamic routes
];
let client;
addEventListener('message', (event) => {
client = event.source;
});
self.addEventListener('install', (event) => {
// Create a new cache and add all files to it
async function addFilesToCache() {
const cache = await caches.open(CACHE);
await cache.addAll(ASSETS);
}
event.waitUntil(addFilesToCache());
});
self.addEventListener('activate', (event) => {
// Remove previous cached data from disk
async function deleteOldCaches() {
for (const key of await caches.keys()) {
if (key !== CACHE) await caches.delete(key);
}
}
event.waitUntil(deleteOldCaches());
let channel;
if (BroadcastChannel) {
channel = new BroadcastChannel('sw-messages');
channel.postMessage({ title: 'Cache Downloaded' });
} else {
client.postMessage('Cache Downloaded');
}
});
self.addEventListener('fetch', (event) => {
// ignore POST requests etc
if (event.request.method !== 'GET') return;
async function respond() {
const url = new URL(event.request.url);
const cache = await caches.open(CACHE);
// `build`/`files` can always be served from the cache
if (ASSETS.includes(url.pathname)) {
return cache.match(url.pathname);
}
// for everything else, try the network first, but
// fall back to the cache if we're offline
try {
const response = await fetch(event.request);
if (response.status === 200) {
cache.put(event.request, response.clone());
}
return response;
} catch {
return cache.match(event.request);
}
}
event.respondWith(respond());
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
frontend/static/archi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
frontend/static/bibbito.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

1
frontend/static/bike.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M620-740q-33 0-56.5-23.5T540-820q0-33 23.5-56.5T620-900q33 0 56.5 23.5T700-820q0 33-23.5 56.5T620-740ZM432-540l88 92v248h-80v-200L312-512q-14-12-19-25t-5-31q0-18 5.5-30.5T312-624l112-112q13-13 27.5-18.5T484-760q18 0 32.5 5.5T544-736l76 76q27 27 63 43.5t81 16.5v80q-63 0-114-22.5T560-604l-32-32-96 96Zm-232 60q85 0 142.5 57.5T400-280q0 85-57.5 142.5T200-80q-85 0-142.5-57.5T0-280q0-85 57.5-142.5T200-480Zm0 340q57 0 98.5-41.5T340-280q0-57-41.5-98.5T200-420q-57 0-98.5 41.5T60-280q0 57 41.5 98.5T200-140Zm560-340q85 0 142.5 57.5T960-280q0 85-57.5 142.5T760-80q-85 0-142.5-57.5T560-280q0-85 57.5-142.5T760-480Zm0 340q57 0 98.5-41.5T900-280q0-57-41.5-98.5T760-420q-57 0-98.5 41.5T620-280q0 57 41.5 98.5T760-140Z"/></svg>

After

Width:  |  Height:  |  Size: 813 B

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 42.875 8.625 C 42.84375 8.632813 42.8125 8.644531 42.78125 8.65625 C 42.519531 8.722656 42.292969 8.890625 42.15625 9.125 L 21.71875 40.8125 L 7.65625 28.125 C 7.410156 27.8125 7 27.675781 6.613281 27.777344 C 6.226563 27.878906 5.941406 28.203125 5.882813 28.597656 C 5.824219 28.992188 6.003906 29.382813 6.34375 29.59375 L 21.25 43.09375 C 21.46875 43.285156 21.761719 43.371094 22.050781 43.328125 C 22.339844 43.285156 22.59375 43.121094 22.75 42.875 L 43.84375 10.1875 C 44.074219 9.859375 44.085938 9.425781 43.875 9.085938 C 43.664063 8.746094 43.269531 8.566406 42.875 8.625 Z"/></svg>

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,46 @@
{
"id": "/",
"name": "PianelloExperience",
"short_name": "PianelloExperience",
"start_url": "/",
"display": "standalone",
"background_color": "#fff",
"description": "PianelloExperience",
"icons": [
{
"src": "images/app-icon-48x48.jpeg",
"sizes": "48x48",
"type": "image/jpeg"
},
{
"src": "images/app-icon-72x72.jpeg",
"sizes": "72x72",
"type": "image/jpeg"
},
{
"src": "images/app-icon-96x96.jpeg",
"sizes": "96x96",
"type": "image/jpeg"
},
{
"src": "images/app-icon-144x144.jpeg",
"sizes": "144x144",
"type": "image/jpeg"
},
{
"src": "images/app-icon-168x168.jpeg",
"sizes": "168x168",
"type": "image/jpeg"
},
{
"src": "images/app-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "images/app-icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m280-40 123-622q6-29 27-43.5t44-14.5q23 0 42.5 10t31.5 30l40 64q18 29 46.5 52.5T700-529v-71h60v560h-60v-406q-48-11-89-35t-71-59l-24 120 84 80v300h-80v-240l-84-80-72 320h-84Zm17-395-85-16q-16-3-25-16.5t-6-30.5l30-157q6-32 34-50.5t60-12.5l46 9-54 274Zm243-305q-33 0-56.5-23.5T460-820q0-33 23.5-56.5T540-900q33 0 56.5 23.5T620-820q0 33-23.5 56.5T540-740Z"/></svg>

After

Width:  |  Height:  |  Size: 457 B

View file

@ -1,19 +1,22 @@
import adapter from "@sveltejs/adapter-node"; import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
kit: { kit: {
files: {
serviceWorker: 'src/sw.ts'
},
serviceWorker: { serviceWorker: {
register: false, register: false
}, },
adapter: adapter({ adapter: adapter({
fallback: null, fallback: null,
precompress: true, precompress: true,
strict: true strict: true
}), })
}, }
}; };
export default config; export default config;

View file

@ -1,50 +1,20 @@
import { sveltekit } from "@sveltejs/kit/vite"; import { sveltekit } from '@sveltejs/kit/vite';
import adapter from "@sveltejs/adapter-node"; import adapter from '@sveltejs/adapter-node';
import { SvelteKitPWA } from '@vite-pwa/sveltekit'
const config = { const config = {
plugins: [ plugins: [sveltekit()],
sveltekit(),
SvelteKitPWA({
workbox: {
globPatterns: ['**/*.{js,ts,css,html}'],
},
manifest: {
start_url: '/',
background_color: "#213c8b",
theme_color: "#de0e1b",
lang: 'it',
name: "PianelloExperience",
display: "fullscreen",
short_name: "PianelloExperience",
description: "Pianello Experience",
icons: [
{
src: "/images/app-icon-512x512.png",
sizes: "512x512",
type: "image/png",
},
{
src: "/images/app-icon-192x192.png",
sizes: "192x192",
type: "image/png",
},
],
},
})
],
test: { test: {
include: ["src/**/*.{test,spec}.{js,ts}"], include: ['src/**/*.{test,spec}.{js,ts}']
}, },
build: { build: {
minify: "esbuild", minify: 'esbuild',
target: "esnext", target: 'esnext'
}, },
kit: { kit: {
adapter: adapter({ adapter: adapter({
precompress: false, precompress: false
}) })
}, }
}; };
export default config; export default config;

File diff suppressed because it is too large Load diff