helpdesk page

This commit is contained in:
Henri
2026-02-25 15:30:13 +02:00
parent 981f47d909
commit 89778446a6
11 changed files with 745 additions and 230 deletions

385
package-lock.json generated
View File

@@ -11,10 +11,13 @@
"@tailwindcss/vite": "^4.1.17",
"js-yaml": "^4.1.1",
"lucide-svelte": "^0.554.0",
"maplibre-gl": "^5.19.0",
"svelte-maplibre": "^1.2.6",
"tailwindcss": "^4.1.17"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/js-yaml": "^4.0.9",
"svelte": "^5.43.8",
"vite": "npm:rolldown-vite@7.2.5"
}
@@ -95,6 +98,115 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@mapbox/geojson-rewind": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
"integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
"license": "ISC",
"dependencies": {
"get-stream": "^6.0.1",
"minimist": "^1.2.6"
},
"bin": {
"geojson-rewind": "geojson-rewind"
}
},
"node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@mapbox/point-geometry": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
"license": "ISC"
},
"node_modules/@mapbox/tiny-sdf": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz",
"integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/unitbezier": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/vector-tile": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.4.tgz",
"integrity": "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/point-geometry": "~1.1.0",
"@types/geojson": "^7946.0.16",
"pbf": "^4.0.1"
}
},
"node_modules/@mapbox/whoots-js": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
"license": "ISC",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@maplibre/geojson-vt": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-5.0.4.tgz",
"integrity": "sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==",
"license": "ISC"
},
"node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "24.5.0",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.5.0.tgz",
"integrity": "sha512-55sPMCtWAZY7r7ftU2at1SsBJfhJyIE5X16Tbl+uFcnTuiCxEuh6839iq/hgjLM8zUEjXdRu2V30bfsYCNB1Qg==",
"license": "ISC",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
"@mapbox/unitbezier": "^0.0.1",
"json-stringify-pretty-compact": "^4.0.0",
"minimist": "^1.2.8",
"quickselect": "^3.0.0",
"rw": "^1.3.3",
"tinyqueue": "^3.0.0"
},
"bin": {
"gl-style-format": "dist/gl-style-format.mjs",
"gl-style-migrate": "dist/gl-style-migrate.mjs",
"gl-style-validate": "dist/gl-style-validate.mjs"
}
},
"node_modules/@maplibre/mlt": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.6.tgz",
"integrity": "sha512-rgtY3x65lrrfXycLf6/T22ZnjTg5WgIOsptOIoCaMZy4O4UAKTyZlYY0h6v8le721pTptF94U65yMDQkug+URw==",
"license": "(MIT OR Apache-2.0)",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0"
}
},
"node_modules/@maplibre/vt-pbf": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.0.tgz",
"integrity": "sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==",
"license": "MIT",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0",
"@mapbox/vector-tile": "^2.0.4",
"@maplibre/geojson-vt": "^5.0.4",
"@types/geojson": "^7946.0.16",
"@types/supercluster": "^7.1.3",
"pbf": "^4.0.1",
"supercluster": "^8.0.1"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
@@ -676,6 +788,37 @@
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT"
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/js-yaml": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/leaflet": {
"version": "1.9.21",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/supercluster": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz",
"integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -721,6 +864,30 @@
"node": ">=6"
}
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-geo": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2.5.0 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -749,6 +916,15 @@
"node": ">=0.10.0"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -758,6 +934,12 @@
"node": ">=8"
}
},
"node_modules/earcut": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
"license": "ISC"
},
"node_modules/enhanced-resolve": {
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@@ -803,6 +985,12 @@
}
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"license": "MIT"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -817,12 +1005,39 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gl-matrix": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz",
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
"license": "MIT"
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/is-reference": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
@@ -853,6 +1068,24 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-stringify-pretty-compact": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
"license": "MIT"
},
"node_modules/just-compare": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/just-compare/-/just-compare-2.3.0.tgz",
"integrity": "sha512-6shoR7HDT+fzfL3gBahx1jZG3hWLrhPAf+l7nCwahDdT9XDtosB9kIF0ZrzUp5QY8dJWfQVr5rnsPqsbvflDzg==",
"license": "MIT"
},
"node_modules/kdbush": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
"integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==",
"license": "ISC"
},
"node_modules/lightningcss": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
@@ -1126,6 +1359,52 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/maplibre-gl": {
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.19.0.tgz",
"integrity": "sha512-REhYUN8gNP3HlcIZS6QU2uy8iovl31cXsrNDkCcqWSQbCkcpdYLczqDz5PVIwNH42UQNyvukjes/RoHPDrOUmQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/point-geometry": "^1.1.0",
"@mapbox/tiny-sdf": "^2.0.7",
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^2.0.4",
"@mapbox/whoots-js": "^3.1.0",
"@maplibre/geojson-vt": "^5.0.4",
"@maplibre/maplibre-gl-style-spec": "^24.4.1",
"@maplibre/mlt": "^1.1.6",
"@maplibre/vt-pbf": "^4.2.1",
"@types/geojson": "^7946.0.16",
"@types/supercluster": "^7.1.3",
"earcut": "^3.0.2",
"gl-matrix": "^3.4.4",
"kdbush": "^4.0.2",
"murmurhash-js": "^1.0.0",
"pbf": "^4.0.1",
"potpack": "^2.1.0",
"quickselect": "^3.0.0",
"supercluster": "^8.0.1",
"tinyqueue": "^3.0.0"
},
"engines": {
"node": ">=16.14.0",
"npm": ">=8.1.0"
},
"funding": {
"url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -1133,6 +1412,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -1151,6 +1436,18 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/pbf": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz",
"integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
"license": "BSD-3-Clause",
"dependencies": {
"resolve-protobuf-schema": "^2.1.0"
},
"bin": {
"pbf": "bin/pbf"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1169,6 +1466,16 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pmtiles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-3.2.1.tgz",
"integrity": "sha512-3R4fBwwoli5mw7a6t1IGwOtfmcSAODq6Okz0zkXhS1zi9sz1ssjjIfslwPvcWw5TNhdjNBUg9fgfPLeqZlH6ng==",
"license": "BSD-3-Clause",
"dependencies": {
"@types/leaflet": "^1.9.8",
"fflate": "^0.8.0"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -1197,6 +1504,33 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/potpack": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz",
"integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==",
"license": "ISC"
},
"node_modules/protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==",
"license": "MIT"
},
"node_modules/quickselect": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC"
},
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"license": "MIT",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
}
},
"node_modules/rolldown": {
"version": "1.0.0-beta.50",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.50.tgz",
@@ -1229,6 +1563,12 @@
"@rolldown/binding-win32-x64-msvc": "1.0.0-beta.50"
}
},
"node_modules/rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
"license": "BSD-3-Clause"
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1238,6 +1578,15 @@
"node": ">=0.10.0"
}
},
"node_modules/supercluster": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
"license": "ISC",
"dependencies": {
"kdbush": "^4.0.2"
}
},
"node_modules/svelte": {
"version": "5.43.13",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.13.tgz",
@@ -1263,6 +1612,36 @@
"node": ">=18"
}
},
"node_modules/svelte-maplibre": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-1.2.6.tgz",
"integrity": "sha512-NntxiZptS07HwblUxIkDllAeBSj6DTyEtECkOqxEi3e/uam7Qunkd/Cp535NN1K7eIx5MLs4cyAa8jgPDgGLFw==",
"license": "MIT",
"dependencies": {
"d3-geo": "^3.1.0",
"dequal": "^2.0.3",
"just-compare": "^2.3.0",
"maplibre-gl": "^4.0.0 || ^5.0.1",
"pmtiles": "^3.0.3"
},
"peerDependencies": {
"@deck.gl/core": "^9",
"@deck.gl/layers": "^9",
"@deck.gl/mapbox": "^9",
"svelte": "^5.0.0"
},
"peerDependenciesMeta": {
"@deck.gl/core": {
"optional": true
},
"@deck.gl/layers": {
"optional": true
},
"@deck.gl/mapbox": {
"optional": true
}
}
},
"node_modules/tailwindcss": {
"version": "4.1.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
@@ -1298,6 +1677,12 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyqueue": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz",
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
"license": "ISC"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",

View File

@@ -10,6 +10,7 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/js-yaml": "^4.0.9",
"svelte": "^5.43.8",
"vite": "npm:rolldown-vite@7.2.5"
},
@@ -20,6 +21,8 @@
"@tailwindcss/vite": "^4.1.17",
"js-yaml": "^4.1.1",
"lucide-svelte": "^0.554.0",
"maplibre-gl": "^5.19.0",
"svelte-maplibre": "^1.2.6",
"tailwindcss": "^4.1.17"
}
}

View File

@@ -0,0 +1,48 @@
services:
- name: "Arvuti diagnostika"
quantity: "15min"
price: "Tasuta"
- name: "Arvuti diagnostika (alates +15min)"
quantity: "tk"
price: "20€"
- name: "Detail paigaldamine arvutisse"
quantity: "tk"
price: "10-15€"
- name: "Sülearvuti tolmupuhastus + termopasta"
quantity: "tk"
price: "10-20€"
- name: "Sülearvuti ekraani paigaldus"
quantity: "tk"
price: "15€"
- name: "Arvuti komplekteerimine"
quantity: "tk"
price: "20€"
- name: "Tarkvara hooldustöö ja paigaldamine"
quantity: "tk"
price: "10-15€"
- name: "Muu elektroonika parandus"
quantity: "tk"
price: "10-50€"
- name: "Andmete taastamine"
quantity: "tk"
price: "10-30€"
- name: "Nutiseadmete tarkvarahooldus"
quantity: "tk"
price: "15€"
- name: "Operatsioonisüsteemi taaspaigaldus"
quantity: "tk"
price: "15€"
- name: "Nutitelefoni klaasi/ekraani/aku vahetus"
quantity: "tk"
price: "15-30€"

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-laptop"
version="1.1"
id="svg1"
sodipodi:docname="helpdesk-icon.svg"
xml:space="preserve"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#999999"
borderopacity="1"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="24.4375"
inkscape:cx="13.769821"
inkscape:cy="9.9641944"
inkscape:window-width="2560"
inkscape:window-height="1369"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g1" /><path
d="M20 16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9m16 0H4m16 0 1.28 2.55a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45L4 16"
id="path1" /><g
style="fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
id="g1"
transform="matrix(0.37409905,0,0,0.37409905,7.5108114,6.0120903)"><path
d="m 4,9 a 2,2 0 0 0 -2,2 v 2 a 2,2 0 0 0 2,2 h 4 a 1,1 0 0 1 1,1 v 4 a 2,2 0 0 0 2,2 h 2 a 2,2 0 0 0 2,-2 v -4 a 1,1 0 0 1 1,-1 h 4 a 2,2 0 0 0 2,-2 V 11 A 2,2 0 0 0 20,9 H 16 A 1,1 0 0 1 15,8 V 4 A 2,2 0 0 0 13,2 H 11 A 2,2 0 0 0 9,4 V 8 A 1,1 0 0 1 8,9 Z"
id="path1-9"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

View File

@@ -1,18 +1,23 @@
<!--
A responsive grid wrapper using CSS Grid. It exposes `min` and
`gap` props to control the responsive column minimum width and
spacing. Content is projected into the default slot.
A responsive grid wrapper using CSS Grid. It exposes `min`, `columns`,
and `gap` props to control the responsive column minimum width, fixed
column count, and spacing. Content is projected into the default slot.
-->
<script>
export let min = "220px"; // minimum column width
export let min = "220px"; // minimum column width (used when columns is not set)
export let columns = null; // fixed number of columns (e.g., "2", "3", "4")
export let gap = "var(--space-3)";
export let className = "";
$: gridTemplateColumns = columns
? `repeat(${columns}, 1fr)`
: '1fr';
</script>
<div
class="grid {className}"
style="--min: {min}; --gap: {gap}; grid-template-columns: 1fr; gap: var(--gap);"
style="--min: {min}; --gap: {gap}; --columns: {gridTemplateColumns}; grid-template-columns: var(--columns); gap: var(--gap);"
>
<slot />
</div>
@@ -20,6 +25,10 @@ spacing. Content is projected into the default slot.
<style>
@media (min-width: 768px) {
.grid {
grid-template-columns: var(--columns) !important;
}
.grid:not([style*="--columns: repeat"]) {
grid-template-columns: repeat(auto-fit, minmax(var(--min), 1fr)) !important;
}
}

View File

@@ -0,0 +1,38 @@
{
"hero": {
"title": "HELPDESK"
},
"whatIs": {
"title": "What is HELPDESK?",
"description": "HELPDESK is a computer support service provided by MTÜ Lapikud. The service is aimed at students, lecturers, and anyone interested both inside and outside Tallinn University of Technology. Our team consists of experienced and smart IT students from Tallinn University of Technology."
},
"services": {
"title": "What services do we offer?",
"list": [
"Computer hardware diagnostics and repair",
"Computer hardware maintenance",
"Software diagnostics and configuration",
"Software installation",
"Other electronics repair"
]
},
"pricing": {
"title": "Price List",
"tableHeaders": {
"service": "Service",
"quantity": "Quantity/Time",
"price": "Price"
},
"note": "Parts are ordered by the client themselves or by agreement with the technician.<br/><br/>If needed, we can come to the student village and Technopol area.<br/><br/>With our student-friendly prices and personal approach, we offer a service you won't be disappointed with!"
},
"hours": {
"title": "Opening Hours:",
"schedule": "<strong>M</strong>onday <strong>F</strong>riday 14-19*",
"note": "* This is the time period when we are most likely to be found. However, we cannot guarantee presence."
},
"location": {
"title": "Location",
"busInfo": "Accessible via <span class='text-[#E69635]'>buses 11, 37, 83, 91 from Keemia stop</span> or <span class='text-[#E69635]'>buses 10, 27, 33, 36, 45 from Tehnikaülikool stop.</span>",
"address": "Akadeemia tee 5 dormitory, room 008A"
}
}

View File

@@ -0,0 +1,38 @@
{
"hero": {
"title": "HELPDESK"
},
"whatIs": {
"title": "Mis on HELPDESK?",
"description": "HELPDESK on MTÜ Lapikute poolt pakutav arvutiabiteenus. Teenus on suunatud tudengitele, õppejõududele ning kõikidele huvilistele ka väljaspool Tallinna Tehnikaülikooli. Meie meeskond koosneb kogenud ja nutikatest Tallinna Tehnikaülikooli IT-tudengitest."
},
"services": {
"title": "Mis teenuseid osutame?",
"list": [
"Arvuti riistvara diagnostika ja remont",
"Arvuti riistvara hooldus",
"Tarkvara diagnostika ja seadistamine",
"Tarkvara paigaldamine",
"Muu elektroonika parandamine"
]
},
"pricing": {
"title": "Hinnakiri",
"tableHeaders": {
"service": "Teenus",
"quantity": "Kogus/Aeg",
"price": "Hind"
},
"note": "Jupid tellitakse kliendi enda poolt või kokkuleppel tehnikuga.<br/><br/>Vajadusel tuleme üliõpilasküla ja Tehnopoli piires kohale.<br/><br/>Oma tudengisõbralike hindade ja personaalse lähenemisega pakume teenust, milles ei pea pettuma!"
},
"hours": {
"title": "Lahtiolekuajad:",
"schedule": "<strong>E</strong>smaspäevast <strong>R</strong>eedeni 14-19*",
"note": "* Tegu on ajaperioodiga, mil meid on kõige tõenäolisem tabada. Siiski ei saa garanteerida kohalolekut."
},
"location": {
"title": "Asukoht",
"busInfo": "Saab <span class='text-[#E69635]'>11, 37, 83, 91 bussiga Keemia peatusest</span> või <span class='text-[#E69635]'>Tehnikaülikool peatusest 10, 27, 33, 36, 45 bussiga.</span>",
"address": "Akadeemia tee 5 ühiselamu, ruum 008A"
}
}

View File

@@ -2,244 +2,192 @@
import {
Section,
Grid,
Card,
Center,
Container,
} from "$components";
import { onMount } from "svelte";
import { text } from "$lib/i18n";
import yaml from 'js-yaml';
import Laptop from "lucide-svelte/icons/laptop";
import Smartphone from "lucide-svelte/icons/smartphone";
import HardDrive from "lucide-svelte/icons/hard-drive";
import Wifi from "lucide-svelte/icons/wifi";
import Settings from "lucide-svelte/icons/settings";
import Wrench from "lucide-svelte/icons/wrench";
import Clock from "lucide-svelte/icons/clock";
import { MapLibre, Marker } from "svelte-maplibre";
import MapPin from "lucide-svelte/icons/map-pin";
import Mail from "lucide-svelte/icons/mail";
import Phone from "lucide-svelte/icons/phone";
import CheckCircle from "lucide-svelte/icons/check-circle";
import BusFront from "lucide-svelte/icons/bus-front";
import { navigate } from "$lib/router/router.js";
const services = [
{
icon: Laptop,
title: "Arvuti diagnostika",
description: "Tuvastame riistvara- ja tarkvaraprobleemid ning pakume lahendusi",
price: "Alates 10€"
},
{
icon: Settings,
title: "Operatsioonisüsteemi paigaldus",
description: "Windows, Linux või macOS paigaldamine ja seadistamine",
price: "20€"
},
{
icon: HardDrive,
title: "Riistvara vahetus",
description: "RAM, SSD, HDD või muu komponendi vahetus ja paigaldus",
price: "Vastavalt tööle"
},
{
icon: Wrench,
title: "Arvuti puhastus",
description: "Füüsiline puhastus tolmust ja termopasta vahetus",
price: "15€"
},
{
icon: Wifi,
title: "Võrgu seadistamine",
description: "Koduvõrgu või WiFi seadistamine ja probleemide lahendamine",
price: "15€"
},
{
icon: Smartphone,
title: "Nutiseadmete abi",
description: "Nutitelefonide ja tahvelarvutite seadistamine ja nõustamine",
price: "10€"
}
];
// Images
const helpdeskbg = "/assets/helpdesk/helpdesk_bg.jpg";
const helpdesk = "/assets/helpdesk/helpdesk-on-black.png";
const whyChooseUs = [
"Tudengisõbralikud hinnad",
"Kiire teenindus",
"Kogenud meeskond",
"Paindlikud lahtiolekuajad",
"Garantii töödele",
"Sõbralik teenindus"
];
// Coordinates for Akadeemia tee 5, 12616 Tallinn [longitude, latitude]
const center = [24.66887400515207, 59.396427975093935];
// Coordinates for Keemia bus stop [longitude, latitude]
const keemiaBusStop = [24.668325396145953, 59.397074517662276];
// Coordinates for Tehnikaülikool bus stop [longitude, latitude]
const tehnikaBusStop = [24.67328945396854, 59.39508874042947];
// Google Maps directions URL
const getDirectionsUrl = (lng, lat) => {
return `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`;
};
// Navigate to directions using router
const openDirections = (lng, lat) => {
navigate(getDirectionsUrl(lng, lat), { external: true });
};
// Load pricing data
let pricingData = { services: [] };
onMount(async () => {
const response = await fetch('/_data/helpdesk/hinnakiri.yml');
const yamlText = await response.text();
pricingData = yaml.load(yamlText);
});
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Helpdesk</h1>
<p class="text-2xl leading-relaxed">
Tudengisõbralik arvutiabiteenus. Aitame lahendada kõik sinu IT-mured!
</p>
</div>
</Center>
</Section>
<!-- Services Section -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Meie teenused</h2>
<p class="text-xl text-gray-600">Pakume laia valikut IT-teenuseid soodsa hinnaga</p>
<div class="w-full bg-black">
<div class="flex absolute justify-center items-center flex-row w-full h-[60vh] px-4">
<img src={helpdesk} alt={$text["hero"]?.title || "Helpdesk logo"} class="z-10" />
</div>
<img
class="h-[60vh] object-cover object-left w-full opacity-50 blur-[1.5px]"
src={helpdeskbg}
alt="Helpdesk"
/>
</div>
<Grid columns={3} gap="var(--space-5)">
{#each services as service}
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<svelte:component this={service.icon} size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">{service.title}</h3>
<p class="text-gray-600 mb-4">{service.description}</p>
<p class="text-lg font-bold" style="color: var(--orange)">{service.price}</p>
</Card>
{/each}
</Grid>
</Section>
<!-- Why Choose Us -->
<Section background="neutral">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Miks valida meid?</h2>
</div>
<Center>
<Card variant="glass" class="max-w-4xl">
<Grid columns={2} gap="var(--space-4)">
{#each whyChooseUs as reason}
<div class="flex items-center gap-3">
<CheckCircle size={24} style="color: var(--orange)" />
<span class="text-lg">{reason}</span>
</div>
{/each}
</Grid>
</Card>
</Center>
</Section>
<!-- Pricing Info -->
<Section>
<Grid gap="var(--space-5)">
<Card variant="glass">
<h2 class="text-4xl font-bold mb-6">Hinnakirjast</h2>
<div class="space-y-4 text-lg">
<p>
Meie hinnad on tudengisõbralikud ja läbipaistvad. Enne töö alustamist teeme alati hinnapakkumise,
et sa teaksid täpselt, mida oodata.
</p>
<p>
<strong>Diagnostika:</strong> Esimene diagnostika on tasuta! Maksad ainult siis, kui otsustad lasta meil
probleem lahendada.
</p>
<p>
<strong>Töötunnid:</strong> Tavaline tunnitasu on 15€. Keerulisemad tööd võivad olla kallimad,
kuid lepime alati kokku enne töö alustamist.
</p>
<p>
<strong>Varuosad:</strong> Varuosade hind lisandub töötasule. Aitame leida parimad hinnad ja kvaliteedi.
<!-- Main Content -->
<Container>
<Section>
<Grid columns="2" gap="var(--space-5)">
<!-- What is HELPDESK? -->
<div class="flex flex-col">
<h2 class="text-4xl font-light pb-4">{$text["whatIs"]?.title || ""}</h2>
<p class="text-lg">
{$text["whatIs"]?.description || ""}
</p>
</div>
</Card>
<Card variant="glass">
<h2 class="text-4xl font-bold mb-6">Kuidas toimub teenindus?</h2>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">1.</div>
<div>
<strong class="text-lg">Võta ühendust</strong><br/>
Kirjuta meile või tule otse meie kontorisse. Kirjelda oma probleemi.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">2.</div>
<div>
<strong class="text-lg">Diagnostika</strong><br/>
Vaatame probleemi üle ja selgitame välja põhjuse. Teeme hinnapakkumise.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">3.</div>
<div>
<strong class="text-lg">Remont</strong><br/>
Kui oled nõus, alustame tööd. Hoiame sind kursis protsessiga.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">4.</div>
<div>
<strong class="text-lg">Üleandmine</strong><br/>
Anname arvuti üle, selgitame tehtud tööd ja anname nõu tulevikuks.
</div>
</div>
<!-- What services do we offer? -->
<div class="flex flex-col">
<h2 class="text-4xl font-light pb-4">{$text["services"]?.title || ""}</h2>
<ul class="text-lg list-disc pl-5">
{#each $text["services"]?.list || [] as service}
<li>{service}</li>
{/each}
</ul>
</div>
</Card>
</Grid>
</Section>
<!-- Contact Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl text-white">
<h2 class="text-4xl font-bold mb-8">Võta ühendust</h2>
<!-- Pricing Table -->
<div class="flex flex-col">
<h2 class="text-4xl font-light pb-4">{$text["pricing"]?.title || ""}</h2>
<div class="overflow-hidden rounded-lg border border-gray-200 shadow-sm">
<table class="w-full text-lg">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left font-semibold text-gray-900 border-b border-gray-200">
{$text["pricing"]?.tableHeaders?.service || ""}
</th>
<th class="px-6 py-4 text-left font-semibold text-gray-900 border-b border-gray-200">
{$text["pricing"]?.tableHeaders?.quantity || ""}
</th>
<th class="px-6 py-4 text-left font-semibold text-gray-900 border-b border-gray-200">
{$text["pricing"]?.tableHeaders?.price || ""}
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{#each pricingData.services as service}
<tr class="hover:bg-gray-50 transition-colors duration-200">
<td class="px-6 py-4 text-gray-900">{service.name}</td>
<td class="px-6 py-4 text-gray-600">{service.quantity}</td>
<td class="px-6 py-4 font-medium" class:text-green-600={service.price === "Tasuta"} class:text-gray-900={service.price !== "Tasuta"}>
{service.price}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<p class="pt-4 text-lg font-bold">
{@html $text["pricing"]?.note || ""}
</p>
</div>
<Grid columns={2} gap="var(--space-6)">
<Card variant="glass">
<div class="mb-4">
<MapPin size={32} class="mx-auto" />
</div>
<h3 class="text-xl font-bold mb-2">Asukoht</h3>
<p>Akadeemia tee 5</p>
<p>12616 Tallinn</p>
<p class="mt-2">ICT-teaduskond, Raja 15 ruum 111</p>
</Card>
<!-- Opening Hours & Location -->
<div class="flex flex-col">
<h2 class="text-4xl font-light pb-4">
{@html $text["hours"]?.title || ""}
<br />
{@html $text["hours"]?.schedule || ""}
</h2>
<p class="text-lg font-light opacity-70 pb-16">
{$text["hours"]?.note || ""}
</p>
<Card variant="glass">
<div class="mb-4">
<Clock size={32} class="mx-auto" />
</div>
<h3 class="text-xl font-bold mb-2">Lahtiolekuajad</h3>
<p>Esmaspäev - Reede</p>
<p>10:00 - 16:00</p>
<p class="mt-2 text-sm">Võta ette ühendust, et vältida ootamist</p>
</Card>
<h2 class="text-4xl font-light pb-4">{$text["location"]?.title || ""}</h2>
<div class="w-full h-96 rounded-lg overflow-hidden">
<MapLibre
style="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
class="relative w-full h-full"
{center}
zoom={15}
interactive={false}
attributionControl={false}
>
<!-- Main location -->
<Marker lngLat={center}>
<div
role="button"
tabindex="0"
onclick={() => openDirections(center[0], center[1])}
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(center[0], center[1])}
class="flex flex-col items-center hover:scale-110 transition-transform cursor-pointer"
title="Get directions to Akadeemia tee 5"
>
<span class="text-xs font-semibold text-white bg-[#E69635] px-2 py-0.5 rounded shadow-md mb-1 whitespace-nowrap">Akadeemia tee 5</span>
<MapPin class="w-7 h-7 text-[#E69635] drop-shadow-lg" />
</div>
</Marker>
<Card variant="glass">
<div class="mb-4">
<Mail size={32} class="mx-auto" />
</div>
<h3 class="text-xl font-bold mb-2">E-post</h3>
<a href="mailto:erki@lapikud.ee" class="hover:underline">
erki@lapikud.ee
</a>
</Card>
<!-- Keemia bus stop -->
<Marker lngLat={keemiaBusStop}>
<div
role="button"
tabindex="0"
onclick={() => openDirections(keemiaBusStop[0], keemiaBusStop[1])}
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(keemiaBusStop[0], keemiaBusStop[1])}
class="flex flex-col items-center hover:scale-110 transition-transform cursor-pointer"
title="Get directions to Keemia bus stop"
>
<span class="text-xs font-semibold text-gray-900 bg-white px-2 py-0.5 rounded shadow-md mb-1">Keemia</span>
<BusFront class="w-7 h-7 text-[#E69635] drop-shadow-lg" />
</div>
</Marker>
<Card variant="glass">
<div class="mb-4">
<Phone size={32} class="mx-auto" />
</div>
<h3 class="text-xl font-bold mb-2">Telefon</h3>
<a href="tel:+37259079190" class="hover:underline">
+372 5907 9190
</a>
</Card>
</Grid>
</div>
</Center>
</Section>
<style>
.feature-icon {
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
background: rgba(255, 127, 0, 0.1);
border-radius: 50%;
margin: 0 auto;
}
</style>
<!-- Tehnikaülikool bus stop -->
<Marker lngLat={tehnikaBusStop}>
<div
role="button"
tabindex="0"
onclick={() => openDirections(tehnikaBusStop[0], tehnikaBusStop[1])}
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(tehnikaBusStop[0], tehnikaBusStop[1])}
class="flex flex-col items-center hover:scale-110 transition-transform cursor-pointer"
title="Get directions to Tehnikaülikool bus stop"
>
<span class="text-xs font-semibold text-gray-900 bg-white px-2 py-0.5 rounded shadow-md mb-1">Tehnikaülikool</span>
<BusFront class="w-7 h-7 text-[#E69635] drop-shadow-lg" />
</div>
</Marker>
</MapLibre>
</div>
<p class="pb-4 text-lg">
{@html $text["location"]?.busInfo || ""}
</p>
<p class="text-lg font-bold">
{$text["location"]?.address || ""}
</p>
</div>
</Grid>
</Section>
</Container>