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", "@tailwindcss/vite": "^4.1.17",
"js-yaml": "^4.1.1", "js-yaml": "^4.1.1",
"lucide-svelte": "^0.554.0", "lucide-svelte": "^0.554.0",
"maplibre-gl": "^5.19.0",
"svelte-maplibre": "^1.2.6",
"tailwindcss": "^4.1.17" "tailwindcss": "^4.1.17"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.2.1", "@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/js-yaml": "^4.0.9",
"svelte": "^5.43.8", "svelte": "^5.43.8",
"vite": "npm:rolldown-vite@7.2.5" "vite": "npm:rolldown-vite@7.2.5"
} }
@@ -95,6 +98,115 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@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": { "node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", "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==", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT" "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": { "node_modules/acorn": {
"version": "8.15.0", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -721,6 +864,30 @@
"node": ">=6" "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": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -749,6 +916,15 @@
"node": ">=0.10.0" "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": { "node_modules/detect-libc": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -758,6 +934,12 @@
"node": ">=8" "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": { "node_modules/enhanced-resolve": {
"version": "5.18.3", "version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "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": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "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": "^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": { "node_modules/graceful-fs": {
"version": "4.2.11", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC" "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": { "node_modules/is-reference": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
@@ -853,6 +1068,24 @@
"js-yaml": "bin/js-yaml.js" "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": { "node_modules/lightningcss": {
"version": "1.30.2", "version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
@@ -1126,6 +1359,52 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@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": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -1133,6 +1412,12 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/nanoid": {
"version": "3.3.11", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "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": "^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": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1169,6 +1466,16 @@
"url": "https://github.com/sponsors/jonschlinkert" "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": { "node_modules/postcss": {
"version": "8.5.6", "version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -1197,6 +1504,33 @@
"node": "^10 || ^12 || >=14" "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": { "node_modules/rolldown": {
"version": "1.0.0-beta.50", "version": "1.0.0-beta.50",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.50.tgz", "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" "@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": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1238,6 +1578,15 @@
"node": ">=0.10.0" "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": { "node_modules/svelte": {
"version": "5.43.13", "version": "5.43.13",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.13.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.13.tgz",
@@ -1263,6 +1612,36 @@
"node": ">=18" "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": { "node_modules/tailwindcss": {
"version": "4.1.17", "version": "4.1.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
@@ -1298,6 +1677,12 @@
"url": "https://github.com/sponsors/SuperchupuDev" "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": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",

View File

@@ -10,6 +10,7 @@
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.2.1", "@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/js-yaml": "^4.0.9",
"svelte": "^5.43.8", "svelte": "^5.43.8",
"vite": "npm:rolldown-vite@7.2.5" "vite": "npm:rolldown-vite@7.2.5"
}, },
@@ -20,6 +21,8 @@
"@tailwindcss/vite": "^4.1.17", "@tailwindcss/vite": "^4.1.17",
"js-yaml": "^4.1.1", "js-yaml": "^4.1.1",
"lucide-svelte": "^0.554.0", "lucide-svelte": "^0.554.0",
"maplibre-gl": "^5.19.0",
"svelte-maplibre": "^1.2.6",
"tailwindcss": "^4.1.17" "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 A responsive grid wrapper using CSS Grid. It exposes `min`, `columns`,
`gap` props to control the responsive column minimum width and and `gap` props to control the responsive column minimum width, fixed
spacing. Content is projected into the default slot. column count, and spacing. Content is projected into the default slot.
--> -->
<script> <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 gap = "var(--space-3)";
export let className = ""; export let className = "";
$: gridTemplateColumns = columns
? `repeat(${columns}, 1fr)`
: '1fr';
</script> </script>
<div <div
class="grid {className}" 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 /> <slot />
</div> </div>
@@ -20,6 +25,10 @@ spacing. Content is projected into the default slot.
<style> <style>
@media (min-width: 768px) { @media (min-width: 768px) {
.grid { .grid {
grid-template-columns: var(--columns) !important;
}
.grid:not([style*="--columns: repeat"]) {
grid-template-columns: repeat(auto-fit, minmax(var(--min), 1fr)) !important; 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 { import {
Section, Section,
Grid, Grid,
Card, Container,
Center,
} from "$components"; } from "$components";
import { onMount } from "svelte";
import { text } from "$lib/i18n";
import yaml from 'js-yaml';
import Laptop from "lucide-svelte/icons/laptop"; import { MapLibre, Marker } from "svelte-maplibre";
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 MapPin from "lucide-svelte/icons/map-pin"; import MapPin from "lucide-svelte/icons/map-pin";
import Mail from "lucide-svelte/icons/mail"; import BusFront from "lucide-svelte/icons/bus-front";
import Phone from "lucide-svelte/icons/phone"; import { navigate } from "$lib/router/router.js";
import CheckCircle from "lucide-svelte/icons/check-circle";
const services = [ // Images
{ const helpdeskbg = "/assets/helpdesk/helpdesk_bg.jpg";
icon: Laptop, const helpdesk = "/assets/helpdesk/helpdesk-on-black.png";
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€"
}
];
const whyChooseUs = [ // Coordinates for Akadeemia tee 5, 12616 Tallinn [longitude, latitude]
"Tudengisõbralikud hinnad", const center = [24.66887400515207, 59.396427975093935];
"Kiire teenindus", // Coordinates for Keemia bus stop [longitude, latitude]
"Kogenud meeskond", const keemiaBusStop = [24.668325396145953, 59.397074517662276];
"Paindlikud lahtiolekuajad", // Coordinates for Tehnikaülikool bus stop [longitude, latitude]
"Garantii töödele", const tehnikaBusStop = [24.67328945396854, 59.39508874042947];
"Sõbralik teenindus"
]; // 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> </script>
<!-- Hero Section --> <!-- Hero Section -->
<Section background="orange"> <div class="w-full bg-black">
<Center> <div class="flex absolute justify-center items-center flex-row w-full h-[60vh] px-4">
<div class="text-center max-w-4xl"> <img src={helpdesk} alt={$text["hero"]?.title || "Helpdesk logo"} class="z-10" />
<h1 class="text-5xl font-bold mb-6">Helpdesk</h1> </div>
<p class="text-2xl leading-relaxed"> <img
Tudengisõbralik arvutiabiteenus. Aitame lahendada kõik sinu IT-mured! class="h-[60vh] object-cover object-left w-full opacity-50 blur-[1.5px]"
</p> src={helpdeskbg}
alt="Helpdesk"
/>
</div> </div>
</Center>
</Section>
<!-- Services Section --> <!-- Main Content -->
<Container>
<Section> <Section>
<div class="text-center mb-12"> <Grid columns="2" gap="var(--space-5)">
<h2 class="text-4xl font-bold mb-4">Meie teenused</h2> <!-- What is HELPDESK? -->
<p class="text-xl text-gray-600">Pakume laia valikut IT-teenuseid soodsa hinnaga</p> <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> </div>
<Grid columns={3} gap="var(--space-5)"> <!-- What services do we offer? -->
{#each services as service} <div class="flex flex-col">
<Card variant="glass"> <h2 class="text-4xl font-light pb-4">{$text["services"]?.title || ""}</h2>
<div class="feature-icon mb-4" style="color: var(--orange)"> <ul class="text-lg list-disc pl-5">
<svelte:component this={service.icon} size={48} strokeWidth={1.5} /> {#each $text["services"]?.list || [] as service}
</div> <li>{service}</li>
<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} {/each}
</Grid> </ul>
</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> </div>
<Center> <!-- Pricing Table -->
<Card variant="glass" class="max-w-4xl"> <div class="flex flex-col">
<Grid columns={2} gap="var(--space-4)"> <h2 class="text-4xl font-light pb-4">{$text["pricing"]?.title || ""}</h2>
{#each whyChooseUs as reason} <div class="overflow-hidden rounded-lg border border-gray-200 shadow-sm">
<div class="flex items-center gap-3"> <table class="w-full text-lg">
<CheckCircle size={24} style="color: var(--orange)" /> <thead class="bg-gray-50">
<span class="text-lg">{reason}</span> <tr>
</div> <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} {/each}
</Grid> </tbody>
</Card> </table>
</Center> </div>
</Section> <p class="pt-4 text-lg font-bold">
{@html $text["pricing"]?.note || ""}
</p>
</div>
<!-- Pricing Info --> <!-- Opening Hours & Location -->
<Section> <div class="flex flex-col">
<Grid gap="var(--space-5)"> <h2 class="text-4xl font-light pb-4">
<Card variant="glass"> {@html $text["hours"]?.title || ""}
<h2 class="text-4xl font-bold mb-6">Hinnakirjast</h2> <br />
<div class="space-y-4 text-lg"> {@html $text["hours"]?.schedule || ""}
<p> </h2>
Meie hinnad on tudengisõbralikud ja läbipaistvad. Enne töö alustamist teeme alati hinnapakkumise, <p class="text-lg font-light opacity-70 pb-16">
et sa teaksid täpselt, mida oodata. {$text["hours"]?.note || ""}
</p> </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.
</p>
</div>
</Card>
<Card variant="glass"> <h2 class="text-4xl font-light pb-4">{$text["location"]?.title || ""}</h2>
<h2 class="text-4xl font-bold mb-6">Kuidas toimub teenindus?</h2> <div class="w-full h-96 rounded-lg overflow-hidden">
<div class="space-y-6"> <MapLibre
<div class="flex items-start gap-4"> style="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
<div class="text-3xl font-bold" style="color: var(--orange)">1.</div> class="relative w-full h-full"
<div> {center}
<strong class="text-lg">Võta ühendust</strong><br/> zoom={15}
Kirjuta meile või tule otse meie kontorisse. Kirjelda oma probleemi. 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> </div>
</Marker>
<!-- 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> </div>
<div class="flex items-start gap-4"> </Marker>
<div class="text-3xl font-bold" style="color: var(--orange)">2.</div>
<div> <!-- Tehnikaülikool bus stop -->
<strong class="text-lg">Diagnostika</strong><br/> <Marker lngLat={tehnikaBusStop}>
Vaatame probleemi üle ja selgitame välja põhjuse. Teeme hinnapakkumise. <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> </div>
</Marker>
</MapLibre>
</div> </div>
<div class="flex items-start gap-4"> <p class="pb-4 text-lg">
<div class="text-3xl font-bold" style="color: var(--orange)">3.</div> {@html $text["location"]?.busInfo || ""}
<div> </p>
<strong class="text-lg">Remont</strong><br/> <p class="text-lg font-bold">
Kui oled nõus, alustame tööd. Hoiame sind kursis protsessiga. {$text["location"]?.address || ""}
</p>
</div> </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>
</div>
</Card>
</Grid> </Grid>
</Section> </Section>
</Container>
<!-- 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>
<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>
<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>
<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>
<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>