forked from lapikud/lapikud.github.io
feat: Migrate more to VueV3 logic, navbar improvements
This commit is contained in:
@@ -1,10 +0,0 @@
|
|||||||
/** @type { import('@storybook/vue3-vite').StorybookConfig } */
|
|
||||||
const config = {
|
|
||||||
stories: ['../src/**/*.stories.js'],
|
|
||||||
framework: {
|
|
||||||
name: '@storybook/vue3-vite',
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/** @type { import('@storybook/vue3-vite').Preview } */
|
|
||||||
const preview = {
|
|
||||||
parameters: {
|
|
||||||
controls: {
|
|
||||||
matchers: {
|
|
||||||
color: /(background|color)$/i,
|
|
||||||
date: /Date$/i,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default preview;
|
|
||||||
@@ -15,15 +15,6 @@
|
|||||||
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
|
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
|
||||||
|
|
||||||
<title>Lapikud</title>
|
<title>Lapikud</title>
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
var redirect = sessionStorage.redirect;
|
|
||||||
delete sessionStorage.redirect;
|
|
||||||
if (redirect && redirect !== location.href) {
|
|
||||||
history.replaceState(null, null, redirect);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
2542
package-lock.json
generated
2542
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,15 +7,11 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"storybook": "storybook dev -p 6006",
|
|
||||||
"build-storybook": "storybook build",
|
|
||||||
"optimise": "node scripts/optimise-images.js"
|
"optimise": "node scripts/optimise-images.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/vue3-vite": "^10.2.15",
|
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@vitejs/plugin-vue": "^6.0.2",
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
"storybook": "^10.2.15",
|
|
||||||
"vite": "npm:rolldown-vite@7.2.5",
|
"vite": "npm:rolldown-vite@7.2.5",
|
||||||
"vue": "^3.5.25"
|
"vue": "^3.5.25"
|
||||||
},
|
},
|
||||||
@@ -26,6 +22,7 @@
|
|||||||
"@lucide/vue": "^1.23.0",
|
"@lucide/vue": "^1.23.0",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
|
"lucide": "^1.23.0",
|
||||||
"maplibre-gl": "^5.19.0",
|
"maplibre-gl": "^5.19.0",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"tailwindcss": "^4.1.17"
|
"tailwindcss": "^4.1.17"
|
||||||
|
|||||||
141
public/assets/asikarikas_logo_white.svg
Normal file
141
public/assets/asikarikas_logo_white.svg
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="798px" height="301px" viewBox="0 0 798 301" enable-background="new 0 0 798 301" xml:space="preserve">
|
||||||
|
<polyline fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="279.318,81.557 246.113,81.557
|
||||||
|
208.437,43.879 90.656,43.879 "/>
|
||||||
|
<polyline fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="246.114,81.557 199.732,81.557
|
||||||
|
168.725,112.564 63.387,112.564 24.189,112.564 "/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<rect id="SVGID_1_" x="-131.841" y="-130" width="1000" height="1000"/>
|
||||||
|
</defs>
|
||||||
|
<clipPath id="SVGID_2_">
|
||||||
|
<use xlink:href="#SVGID_1_" overflow="visible"/>
|
||||||
|
</clipPath>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M100.456,43.879c0,5.413-4.388,9.8-9.8,9.8c-5.412,0-9.8-4.387-9.8-9.8
|
||||||
|
c0-5.412,4.388-9.8,9.8-9.8C96.068,34.08,100.456,38.467,100.456,43.879"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="90.656" cy="43.879" r="9.8"/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M33.988,112.564c0,5.413-4.388,9.8-9.8,9.8c-5.412,0-9.8-4.387-9.8-9.8
|
||||||
|
c0-5.412,4.388-9.799,9.8-9.799C29.6,102.765,33.988,107.152,33.988,112.564"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="24.188" cy="112.564" r="9.8"/>
|
||||||
|
<polyline clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="
|
||||||
|
515.697,81.557 548.902,81.557 586.579,43.879 704.359,43.879 "/>
|
||||||
|
<polyline clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="
|
||||||
|
548.902,81.557 595.284,81.557 626.291,112.564 731.628,112.564 770.827,112.564 "/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M714.159,43.879c0,5.413-4.388,9.8-9.8,9.8s-9.8-4.387-9.8-9.8
|
||||||
|
c0-5.412,4.388-9.8,9.8-9.8S714.159,38.467,714.159,43.879"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="704.359" cy="43.879" r="9.8"/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M780.627,112.564c0,5.413-4.388,9.8-9.8,9.8s-9.8-4.387-9.8-9.8
|
||||||
|
c0-5.412,4.388-9.799,9.8-9.799S780.627,107.152,780.627,112.564"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="770.827" cy="112.564" r="9.8"/>
|
||||||
|
<polyline clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="
|
||||||
|
529.525,230.107 582.326,177.307 697.132,177.307 "/>
|
||||||
|
<polyline clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="
|
||||||
|
158.577,230.107 251.946,230.107 300.039,278.2 481.432,278.2 529.525,230.107 622.894,230.107 "/>
|
||||||
|
<polyline clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" points="
|
||||||
|
251.945,230.107 199.144,177.307 84.338,177.307 "/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M73.209,177.306c0,6.146,4.983,11.129,11.129,11.129
|
||||||
|
s11.129-4.983,11.129-11.129s-4.983-11.129-11.129-11.129S73.209,171.16,73.209,177.306"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="84.338" cy="177.306" r="11.129"/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M708.261,177.306c0,6.146-4.982,11.129-11.129,11.129
|
||||||
|
c-6.146,0-11.129-4.983-11.129-11.129s4.983-11.129,11.129-11.129C703.278,166.177,708.261,171.16,708.261,177.306"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="697.132" cy="177.306" r="11.129"/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M634.022,230.107c0,6.146-4.982,11.128-11.129,11.128
|
||||||
|
s-11.129-4.982-11.129-11.128s4.982-11.129,11.129-11.129S634.022,223.961,634.022,230.107"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="622.894" cy="230.107" r="11.129"/>
|
||||||
|
<path clip-path="url(#SVGID_2_)" fill="#AEDAFF" d="M147.448,230.107c0,6.146,4.982,11.128,11.129,11.128
|
||||||
|
c6.146,0,11.129-4.982,11.129-11.128s-4.983-11.129-11.129-11.129C152.43,218.979,147.448,223.961,147.448,230.107"/>
|
||||||
|
|
||||||
|
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#007EFF" stroke-width="9" stroke-miterlimit="10" cx="158.577" cy="230.107" r="11.129"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" d="M272.659,240.478v-42.085h8.998v19.059l14.029-19.059h10.406l-15.063,19.174l15.609,22.911H295.8
|
||||||
|
l-14.144-22.02v22.02H272.659z"/>
|
||||||
|
<path fill="#FFFFFF" d="M304.803,240.478l16.012-42.085h10.291l16.242,42.085h-10.233l-3.421-8.94h-16.299l-2.932,8.94H304.803z
|
||||||
|
M331.595,224.466l-4.714-13.597c-0.134-0.403-0.307-1.092-0.518-2.07s-0.431-2.175-0.661-3.593
|
||||||
|
c-0.25,1.361-0.489,2.583-0.719,3.665c-0.23,1.083-0.402,1.807-0.518,2.17l-4.542,13.425H331.595z"/>
|
||||||
|
<path fill="#FFFFFF" d="M382.218,240.478H371.84l-11.067-19.663v19.663h-8.998v-42.085h12.878c5.117,0,8.95,0.992,11.499,2.975
|
||||||
|
s3.823,4.969,3.823,8.955c0,2.894-0.872,5.366-2.616,7.417c-1.744,2.051-3.996,3.249-6.755,3.594L382.218,240.478z
|
||||||
|
M360.772,216.331h1.352c3.641,0,6.046-0.398,7.215-1.193c1.169-0.795,1.754-2.18,1.754-4.154c0-2.07-0.628-3.54-1.883-4.413
|
||||||
|
c-1.255-0.872-3.618-1.308-7.086-1.308h-1.352V216.331z"/>
|
||||||
|
<path fill="#FFFFFF" d="M387.622,240.478v-42.085h8.998v42.085H387.622z"/>
|
||||||
|
<path fill="#FFFFFF" d="M406.222,240.478v-42.085h8.997v19.059l14.028-19.059h10.406l-15.063,19.174l15.609,22.911h-10.838
|
||||||
|
l-14.144-22.02v22.02H406.222z"/>
|
||||||
|
<path fill="#FFFFFF" d="M438.365,240.478l16.012-42.085h10.291l16.242,42.085h-10.234l-3.421-8.94h-16.299l-2.933,8.94H438.365z
|
||||||
|
M465.156,224.466l-4.714-13.597c-0.135-0.403-0.307-1.092-0.518-2.07s-0.432-2.175-0.661-3.593
|
||||||
|
c-0.249,1.361-0.488,2.583-0.719,3.665c-0.229,1.083-0.402,1.807-0.518,2.17l-4.542,13.425H465.156z"/>
|
||||||
|
<path fill="#FFFFFF" d="M482.635,235.131l5.002-5.663c0.632,1.552,1.647,2.774,3.047,3.665c1.398,0.891,2.97,1.337,4.714,1.337
|
||||||
|
c2.108,0,3.775-0.499,5.002-1.495c1.227-0.996,1.84-2.338,1.84-4.024c0-1.993-2.041-3.977-6.123-5.951
|
||||||
|
c-1.322-0.632-2.367-1.15-3.133-1.552c-3.68-1.916-6.229-3.79-7.646-5.62c-1.419-1.83-2.128-4.01-2.128-6.54
|
||||||
|
c0-3.469,1.375-6.319,4.125-8.552c2.75-2.232,6.319-3.349,10.709-3.349c2.548,0,4.747,0.446,6.597,1.337s3.483,2.285,4.901,4.183
|
||||||
|
l-5.318,5.088c-0.632-1.226-1.471-2.142-2.515-2.745c-1.045-0.604-2.305-0.905-3.78-0.905c-1.782,0-3.22,0.445-4.313,1.336
|
||||||
|
c-1.092,0.891-1.639,2.046-1.639,3.464c0,2.224,2.166,4.255,6.497,6.094c0.862,0.364,1.523,0.652,1.983,0.863
|
||||||
|
c3.564,1.629,6.204,3.469,7.92,5.519c1.715,2.051,2.573,4.389,2.573,7.014c0,3.948-1.347,7.096-4.039,9.443
|
||||||
|
c-2.693,2.348-6.311,3.521-10.853,3.521c-3.219,0-5.979-0.556-8.278-1.667C485.48,238.82,483.765,237.22,482.635,235.131z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" d="M241.341,169.329l50.897-133.778h32.713l51.629,133.778H344.05l-10.874-28.418h-51.812l-9.32,28.418
|
||||||
|
H241.341z M326.506,118.431L311.52,75.209c-0.427-1.279-0.975-3.473-1.645-6.579c-0.671-3.107-1.371-6.914-2.102-11.422
|
||||||
|
c-0.792,4.326-1.553,8.209-2.284,11.65c-0.731,3.442-1.279,5.743-1.645,6.899l-14.438,42.673H326.506z"/>
|
||||||
|
<path fill="#FFFFFF" d="M382.063,152.333l15.899-18.001c2.011,4.935,5.239,8.818,9.687,11.651
|
||||||
|
c4.446,2.833,9.442,4.249,14.986,4.249c6.7,0,12-1.583,15.899-4.751c3.898-3.167,5.849-7.432,5.849-12.793
|
||||||
|
c0-6.335-6.488-12.64-19.464-18.916c-4.203-2.01-7.524-3.655-9.96-4.935c-11.696-6.091-19.8-12.046-24.307-17.864
|
||||||
|
c-4.509-5.817-6.763-12.747-6.763-20.789c0-11.025,4.371-20.087,13.113-27.185c8.741-7.096,20.088-10.646,34.038-10.646
|
||||||
|
c8.102,0,15.092,1.417,20.972,4.249c5.878,2.833,11.071,7.265,15.58,13.295l-16.905,16.174c-2.01-3.898-4.676-6.808-7.995-8.727
|
||||||
|
c-3.321-1.919-7.326-2.878-12.017-2.878c-5.665,0-10.234,1.417-13.707,4.25c-3.472,2.833-5.208,6.503-5.208,11.011
|
||||||
|
c0,7.068,6.883,13.524,20.651,19.373c2.741,1.158,4.843,2.071,6.305,2.741c11.331,5.179,19.723,11.027,25.175,17.544
|
||||||
|
c5.451,6.52,8.179,13.951,8.179,22.296c0,12.55-4.28,22.556-12.839,30.018c-8.56,7.463-20.058,11.194-34.495,11.194
|
||||||
|
c-10.234,0-19.007-1.768-26.317-5.3C391.11,164.061,385.657,158.973,382.063,152.333z"/>
|
||||||
|
<path fill="#FFFFFF" d="M496.013,169.329V35.551h28.602v133.778H496.013z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" d="M275.115,237.214v-41.209h8.811v18.663l13.737-18.663h10.189l-14.75,18.775l15.284,22.435h-10.612
|
||||||
|
l-13.849-21.562v21.562H275.115z"/>
|
||||||
|
<path fill="#FFFFFF" d="M306.591,237.214l15.679-41.209h10.077l15.904,41.209h-10.021l-3.35-8.754h-15.96l-2.871,8.754H306.591z
|
||||||
|
M332.825,221.535l-4.617-13.314c-0.131-0.394-0.3-1.069-0.506-2.026c-0.207-0.957-0.422-2.13-0.647-3.519
|
||||||
|
c-0.244,1.333-0.479,2.529-0.704,3.589c-0.226,1.061-0.395,1.769-0.507,2.125l-4.448,13.146H332.825z"/>
|
||||||
|
<path fill="#FFFFFF" d="M382.395,237.214h-10.162l-10.837-19.253v19.253h-8.811v-41.209h12.611c5.01,0,8.763,0.971,11.259,2.914
|
||||||
|
c2.495,1.942,3.744,4.865,3.744,8.768c0,2.834-0.854,5.254-2.562,7.262c-1.708,2.008-3.913,3.181-6.615,3.519L382.395,237.214z
|
||||||
|
M361.396,213.569h1.323c3.565,0,5.921-0.39,7.065-1.168c1.145-0.778,1.717-2.134,1.717-4.067c0-2.027-0.615-3.467-1.844-4.321
|
||||||
|
c-1.229-0.854-3.542-1.281-6.938-1.281h-1.323V213.569z"/>
|
||||||
|
<path fill="#FFFFFF" d="M387.687,237.214v-41.209h8.811v41.209H387.687z"/>
|
||||||
|
<path fill="#FFFFFF" d="M405.898,237.214v-41.209h8.811v18.663l13.736-18.663h10.19l-14.75,18.775l15.284,22.435h-10.611
|
||||||
|
l-13.85-21.562v21.562H405.898z"/>
|
||||||
|
<path fill="#FFFFFF" d="M437.374,237.214l15.679-41.209h10.077l15.903,41.209h-10.021l-3.35-8.754h-15.96l-2.871,8.754H437.374z
|
||||||
|
M463.608,221.535l-4.616-13.314c-0.132-0.394-0.301-1.069-0.507-2.026c-0.207-0.957-0.423-2.13-0.647-3.519
|
||||||
|
c-0.244,1.333-0.479,2.529-0.704,3.589c-0.225,1.061-0.394,1.769-0.507,2.125l-4.447,13.146H463.608z"/>
|
||||||
|
<path fill="#FFFFFF" d="M480.723,231.978l4.897-5.545c0.619,1.52,1.614,2.716,2.983,3.589c1.37,0.873,2.909,1.309,4.617,1.309
|
||||||
|
c2.063,0,3.696-0.488,4.897-1.464c1.201-0.976,1.802-2.29,1.802-3.941c0-1.952-1.999-3.894-5.996-5.827
|
||||||
|
c-1.295-0.619-2.317-1.126-3.068-1.52c-3.603-1.876-6.099-3.711-7.487-5.503s-2.083-3.927-2.083-6.404
|
||||||
|
c0-3.396,1.347-6.188,4.039-8.375c2.693-2.186,6.188-3.279,10.485-3.279c2.496,0,4.649,0.437,6.461,1.309
|
||||||
|
c1.811,0.873,3.41,2.238,4.799,4.096l-5.207,4.982c-0.62-1.201-1.44-2.097-2.464-2.688c-1.022-0.591-2.256-0.887-3.701-0.887
|
||||||
|
c-1.745,0-3.152,0.437-4.223,1.309c-1.069,0.873-1.604,2.003-1.604,3.392c0,2.177,2.12,4.166,6.361,5.968
|
||||||
|
c0.844,0.356,1.491,0.638,1.942,0.844c3.49,1.595,6.075,3.397,7.755,5.405c1.679,2.008,2.519,4.297,2.519,6.868
|
||||||
|
c0,3.866-1.318,6.948-3.954,9.247c-2.637,2.299-6.179,3.448-10.626,3.448c-3.153,0-5.855-0.544-8.107-1.632
|
||||||
|
C483.509,235.591,481.829,234.024,480.723,231.978z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" d="M244.45,168.546l49.838-130.995h32.033l50.554,130.995h-31.854l-10.648-27.828h-50.733l-9.127,27.828
|
||||||
|
H244.45z M327.843,118.707l-14.674-42.323c-0.418-1.252-0.955-3.4-1.61-6.442c-0.657-3.042-1.342-6.77-2.058-11.185
|
||||||
|
c-0.776,4.236-1.521,8.039-2.237,11.408c-0.716,3.371-1.253,5.623-1.611,6.756l-14.137,41.786H327.843z"/>
|
||||||
|
<path fill="#FFFFFF" d="M382.245,151.903l15.568-17.627c1.969,4.832,5.13,8.635,9.485,11.409c4.354,2.774,9.245,4.161,14.674,4.161
|
||||||
|
c6.562,0,11.751-1.55,15.569-4.653c3.816-3.101,5.727-7.277,5.727-12.527c0-6.203-6.354-12.377-19.059-18.522
|
||||||
|
c-4.116-1.968-7.368-3.579-9.753-4.832c-11.453-5.964-19.388-11.796-23.802-17.493c-4.415-5.696-6.621-12.482-6.621-20.356
|
||||||
|
c0-10.796,4.279-19.669,12.84-26.619c8.56-6.949,19.67-10.424,33.331-10.424c7.933,0,14.777,1.387,20.534,4.161
|
||||||
|
c5.756,2.774,10.841,7.114,15.256,13.019l-16.553,15.837c-1.969-3.817-4.579-6.666-7.829-8.545
|
||||||
|
c-3.252-1.879-7.174-2.818-11.767-2.818c-5.548,0-10.021,1.387-13.422,4.161c-3.399,2.773-5.1,6.368-5.1,10.782
|
||||||
|
c0,6.92,6.74,13.243,20.222,18.969c2.685,1.134,4.742,2.029,6.174,2.684c11.096,5.071,19.312,10.798,24.651,17.18
|
||||||
|
c5.338,6.384,8.008,13.661,8.008,21.833c0,12.289-4.191,22.087-12.571,29.394c-8.382,7.308-19.641,10.961-33.778,10.961
|
||||||
|
c-10.021,0-18.61-1.731-25.769-5.19C391.104,163.387,385.764,158.406,382.245,151.903z"/>
|
||||||
|
<path fill="#FFFFFF" d="M493.823,168.546V37.551h28.007v130.995H493.823z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
8
public/assets/tipilan-icon-black.svg
Normal file
8
public/assets/tipilan-icon-black.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg width="175" height="150" viewBox="0 0 175 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.5 110V122.5V137.5C67.5 137.933 67.5219 138.36 67.5644 138.781C67.607 139.202 67.6699 139.618 67.7529 140.025C67.8358 140.433 67.939 140.833 68.0605 141.225C68.1818 141.617 68.3217 142.001 68.4794 142.375C68.6372 142.749 68.8118 143.114 69.0038 143.468C69.1958 143.822 69.4046 144.166 69.6288 144.498C69.853 144.83 70.0932 145.152 70.3476 145.46C70.6019 145.768 70.8708 146.064 71.1532 146.347C71.4357 146.629 71.7315 146.898 72.0399 147.152C72.3484 147.407 72.6695 147.647 73.0019 147.871C73.3343 148.095 73.6779 148.304 74.0321 148.496C74.3864 148.688 74.7508 148.863 75.1249 149.021C75.499 149.178 75.8834 149.318 76.2753 149.439C76.6672 149.561 77.0669 149.664 77.4745 149.747C77.8821 149.83 78.2974 149.893 78.7187 149.936C79.1399 149.978 79.5671 150 79.9999 150H94.9999C95.4327 150 95.8599 149.978 96.2812 149.936C96.7024 149.893 97.1177 149.83 97.5253 149.747C97.9329 149.664 98.3336 149.561 98.7255 149.439C99.1174 149.318 99.5008 149.178 99.8749 149.021C100.249 148.863 100.613 148.688 100.968 148.496C101.322 148.304 101.666 148.095 101.998 147.871C102.33 147.647 102.651 147.407 102.96 147.152C103.268 146.898 103.564 146.629 103.847 146.347C104.129 146.064 104.398 145.768 104.652 145.46C104.907 145.152 105.147 144.83 105.371 144.498C105.595 144.166 105.804 143.822 105.996 143.468C106.188 143.114 106.363 142.749 106.52 142.375C106.678 142.001 106.818 141.617 106.939 141.225C107.061 140.833 107.164 140.433 107.247 140.025C107.33 139.618 107.393 139.202 107.435 138.781C107.478 138.36 107.5 137.933 107.5 137.5V122.5V110H94.9998H79.9998H67.5Z" fill="#0A121F"/>
|
||||||
|
<path d="M25 57.5L87.5 102.5L150 57.5L93.75 80L87.5 57.5L81.25 80L25 57.5Z" fill="#0A121F"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M125 0V12.5V37.5V50H137.5H162.5C162.933 50 163.36 49.9781 163.781 49.9356C164.202 49.893 164.618 49.8301 165.025 49.7472C165.433 49.6642 165.834 49.561 166.226 49.4395C166.617 49.3182 167.001 49.1783 167.375 49.0206C167.749 48.863 168.113 48.6882 168.468 48.4962C168.822 48.3042 169.166 48.0954 169.498 47.8712C169.83 47.647 170.152 47.4068 170.46 47.1524C170.768 46.8981 171.064 46.6292 171.347 46.3468C171.629 46.0643 171.898 45.7685 172.152 45.4601C172.407 45.1516 172.647 44.8306 172.871 44.4982C173.095 44.1658 173.304 43.8222 173.496 43.4679C173.688 43.1136 173.863 42.7492 174.02 42.3751C174.178 42.0009 174.318 41.6166 174.439 41.2247C174.561 40.8328 174.664 40.4331 174.747 40.0255C174.83 39.6179 174.893 39.2026 174.936 38.7814C174.978 38.3601 175 37.9329 175 37.5001V12.5001V5.7671e-05H162.5H137.5L125 0Z" fill="#0A121F"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.5 0V50H112.5V0H62.5Z" fill="#0A121F"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0.000115342V12.5001V37.5001C0 37.9329 0.0219205 38.3601 0.0644401 38.7814C0.10696 39.2026 0.169891 39.6179 0.252851 40.0255C0.335812 40.4331 0.438994 40.8328 0.560468 41.2247C0.681791 41.6166 0.821689 42.001 0.979409 42.3751C1.1372 42.7492 1.31282 43.1136 1.5048 43.4679C1.69678 43.8222 1.90464 44.1658 2.12882 44.4982C2.35302 44.8306 2.59323 45.1516 2.84757 45.4601C3.10191 45.7685 3.37083 46.0644 3.65324 46.3468C3.93566 46.6292 4.23152 46.8982 4.53995 47.1525C4.8484 47.4068 5.16947 47.647 5.50188 47.8712C5.83427 48.0954 6.17787 48.3042 6.53214 48.4962C6.88643 48.6882 7.2508 48.8629 7.62492 49.0206C7.99903 49.1784 8.38343 49.3182 8.77531 49.4395C9.16719 49.5609 9.56693 49.6642 9.97453 49.7472C10.3821 49.8301 10.7974 49.893 11.2187 49.9356C11.6399 49.9781 12.0671 50 12.4999 50H37.4999H49.9999V37.5V12.5V0H37.4999H12.4999L0 0.000115342Z" fill="#0A121F"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
8
public/assets/tipilan-icon-white.svg
Normal file
8
public/assets/tipilan-icon-white.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg width="175" height="150" viewBox="0 0 175 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.5 110V122.5V137.5C67.5 137.933 67.5219 138.36 67.5644 138.781C67.607 139.202 67.6699 139.618 67.7529 140.025C67.8358 140.433 67.939 140.833 68.0605 141.225C68.1818 141.617 68.3217 142.001 68.4794 142.375C68.6372 142.749 68.8118 143.114 69.0038 143.468C69.1958 143.822 69.4046 144.166 69.6288 144.498C69.853 144.83 70.0932 145.152 70.3476 145.46C70.6019 145.768 70.8708 146.064 71.1532 146.347C71.4357 146.629 71.7315 146.898 72.0399 147.152C72.3484 147.407 72.6695 147.647 73.0019 147.871C73.3343 148.095 73.6779 148.304 74.0321 148.496C74.3864 148.688 74.7508 148.863 75.1249 149.021C75.499 149.178 75.8834 149.318 76.2753 149.439C76.6672 149.561 77.0669 149.664 77.4745 149.747C77.8821 149.83 78.2974 149.893 78.7187 149.936C79.1399 149.978 79.5671 150 79.9999 150H94.9999C95.4327 150 95.8599 149.978 96.2812 149.936C96.7024 149.893 97.1177 149.83 97.5253 149.747C97.9329 149.664 98.3336 149.561 98.7255 149.439C99.1174 149.318 99.5008 149.178 99.8749 149.021C100.249 148.863 100.613 148.688 100.968 148.496C101.322 148.304 101.666 148.095 101.998 147.871C102.33 147.647 102.651 147.407 102.96 147.152C103.268 146.898 103.564 146.629 103.847 146.347C104.129 146.064 104.398 145.768 104.652 145.46C104.907 145.152 105.147 144.83 105.371 144.498C105.595 144.166 105.804 143.822 105.996 143.468C106.188 143.114 106.363 142.749 106.52 142.375C106.678 142.001 106.818 141.617 106.939 141.225C107.061 140.833 107.164 140.433 107.247 140.025C107.33 139.618 107.393 139.202 107.435 138.781C107.478 138.36 107.5 137.933 107.5 137.5V122.5V110H94.9998H79.9998H67.5Z" fill="white"/>
|
||||||
|
<path d="M25 57.5L87.5 102.5L150 57.5L93.75 80L87.5 57.5L81.25 80L25 57.5Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M125 0V12.5V37.5V50H137.5H162.5C162.933 50 163.36 49.9781 163.781 49.9356C164.202 49.893 164.618 49.8301 165.025 49.7472C165.433 49.6642 165.834 49.561 166.226 49.4395C166.617 49.3182 167.001 49.1783 167.375 49.0206C167.749 48.863 168.113 48.6882 168.468 48.4962C168.822 48.3042 169.166 48.0954 169.498 47.8712C169.83 47.647 170.152 47.4068 170.46 47.1524C170.768 46.8981 171.064 46.6292 171.347 46.3468C171.629 46.0643 171.898 45.7685 172.152 45.4601C172.407 45.1516 172.647 44.8306 172.871 44.4982C173.095 44.1658 173.304 43.8222 173.496 43.4679C173.688 43.1136 173.863 42.7492 174.02 42.3751C174.178 42.0009 174.318 41.6166 174.439 41.2247C174.561 40.8328 174.664 40.4331 174.747 40.0255C174.83 39.6179 174.893 39.2026 174.936 38.7814C174.978 38.3601 175 37.9329 175 37.5001V12.5001V5.7671e-05H162.5H137.5L125 0Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.5 0V50H112.5V0H62.5Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0.000115342V12.5001V37.5001C0 37.9329 0.0219205 38.3601 0.0644401 38.7814C0.10696 39.2026 0.169891 39.6179 0.252851 40.0255C0.335812 40.4331 0.438994 40.8328 0.560468 41.2247C0.681791 41.6166 0.821689 42.001 0.979409 42.3751C1.1372 42.7492 1.31282 43.1136 1.5048 43.4679C1.69678 43.8222 1.90464 44.1658 2.12882 44.4982C2.35302 44.8306 2.59323 45.1516 2.84757 45.4601C3.10191 45.7685 3.37083 46.0644 3.65324 46.3468C3.93566 46.6292 4.23152 46.8982 4.53995 47.1525C4.8484 47.4068 5.16947 47.647 5.50188 47.8712C5.83427 48.0954 6.17787 48.3042 6.53214 48.4962C6.88643 48.6882 7.2508 48.8629 7.62492 49.0206C7.99903 49.1784 8.38343 49.3182 8.77531 49.4395C9.16719 49.5609 9.56693 49.6642 9.97453 49.7472C10.3821 49.8301 10.7974 49.893 11.2187 49.9356C11.6399 49.9781 12.0671 50 12.4999 50H37.4999H49.9999V37.5V12.5V0H37.4999H12.4999L0 0.000115342Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
12
src/App.vue
12
src/App.vue
@@ -1,8 +1,20 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue';
|
||||||
import { Router, text } from "./lib/index.js";
|
import { Router, text } from "./lib/index.js";
|
||||||
import { routes } from "./routes/index.js";
|
import { routes } from "./routes/index.js";
|
||||||
import Navbar from "./layout/Navbar.vue";
|
import Navbar from "./layout/Navbar.vue";
|
||||||
import Footer from "./layout/Footer.vue";
|
import Footer from "./layout/Footer.vue";
|
||||||
|
|
||||||
|
// Handle redirect from 404 page (replaces inline script in index.html)
|
||||||
|
onMounted(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const redirect = sessionStorage.redirect;
|
||||||
|
if (redirect && redirect !== window.location.href) {
|
||||||
|
window.history.replaceState(null, null, redirect);
|
||||||
|
}
|
||||||
|
delete sessionStorage.redirect;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,22 @@
|
|||||||
export { navigate, goBack, reload, getPath, getQuery, switchLanguageRoute } from './router/router.js';
|
export { useRouter } from './router/useRouter.js';
|
||||||
export { default as Router } from './router/Router.vue';
|
export { default as Router } from './router/Router.vue';
|
||||||
|
|
||||||
export { currentLang, currentLocale, languageLocales, text, switchLang, usePageText } from './i18n.js';
|
export { currentLang, currentLocale, languageLocales, text, switchLang, usePageText } from './i18n.js';
|
||||||
|
export { useLang } from './useLang.js';
|
||||||
|
export { useImage } from './useImage.js';
|
||||||
|
export { useYaml } from './useYaml.js';
|
||||||
|
|
||||||
|
// Backwards compatibility exports from old utilities
|
||||||
|
export { navigate, goBack, reload, getPath, getQuery, switchLanguageRoute } from './router/router.js';
|
||||||
export { getLangText } from './langHelpers.js';
|
export { getLangText } from './langHelpers.js';
|
||||||
|
export { loadYaml } from './yaml.js';
|
||||||
|
export {
|
||||||
|
getRootAssetPath,
|
||||||
|
getOptimisedImagePath,
|
||||||
|
getHeroImagePath,
|
||||||
|
getHeroImageSrcSet,
|
||||||
|
getHeroImageFallback,
|
||||||
|
getResponsiveImageSrcSet,
|
||||||
|
getOptimisedImageSrcSet,
|
||||||
|
getOptimisedImageFallback
|
||||||
|
} from './imageHelpers.js';
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeUnmount, onMounted, shallowRef, watch } from 'vue';
|
import { onBeforeUnmount, onMounted, shallowRef, watch } from 'vue';
|
||||||
|
import { useRouter } from './useRouter.js';
|
||||||
|
import { getLanguageFromRoute, getTranslatedRoute } from '../../routes';
|
||||||
|
import { switchLang } from '../i18n.js';
|
||||||
|
|
||||||
const props = defineProps({ routes: { type: Object, required: true } });
|
const props = defineProps({ routes: { type: Object, required: true } });
|
||||||
const currentPath = shallowRef(window.location.pathname);
|
const router = useRouter();
|
||||||
|
const currentPath = shallowRef(typeof window !== 'undefined' ? window.location.pathname : '/');
|
||||||
const CurrentComponent = shallowRef(null);
|
const CurrentComponent = shallowRef(null);
|
||||||
let routeRequest = 0;
|
let routeRequest = 0;
|
||||||
|
|
||||||
|
// Sync with router composable's currentPath
|
||||||
|
watch(() => router.currentPath.value, (path) => {
|
||||||
|
currentPath.value = path;
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
async function resolveCurrentComponent(path) {
|
async function resolveCurrentComponent(path) {
|
||||||
const request = ++routeRequest;
|
const request = ++routeRequest;
|
||||||
const routeEntry = props.routes[path] || props.routes['/'];
|
const routeEntry = props.routes[path] || props.routes['/'];
|
||||||
@@ -26,11 +35,21 @@ function navigate(url) {
|
|||||||
window.history.pushState({}, '', `${url.pathname}${url.search}${url.hash}`);
|
window.history.pushState({}, '', `${url.pathname}${url.search}${url.hash}`);
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
currentPath.value = url.pathname;
|
currentPath.value = url.pathname;
|
||||||
|
router.currentPath.value = url.pathname;
|
||||||
window.dispatchEvent(new PopStateEvent('popstate'));
|
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||||
|
|
||||||
|
// Handle language switching based on route
|
||||||
|
const lang = getLanguageFromRoute(url.pathname);
|
||||||
|
if (lang) switchLang(lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePopState = () => {
|
const handlePopState = () => {
|
||||||
currentPath.value = window.location.pathname;
|
const newPath = window.location.pathname;
|
||||||
|
currentPath.value = newPath;
|
||||||
|
router.currentPath.value = newPath;
|
||||||
|
|
||||||
|
const lang = getLanguageFromRoute(newPath);
|
||||||
|
if (lang) switchLang(lang);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (event) => {
|
const handleClick = (event) => {
|
||||||
@@ -54,6 +73,7 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('popstate', handlePopState);
|
window.removeEventListener('popstate', handlePopState);
|
||||||
window.removeEventListener('click', handleClick);
|
window.removeEventListener('click', handleClick);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(currentPath, resolveCurrentComponent, { immediate: true });
|
watch(currentPath, resolveCurrentComponent, { immediate: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
125
src/lib/router/useRouter.js
Normal file
125
src/lib/router/useRouter.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import { computed, onScopeDispose, ref, watch } from 'vue';
|
||||||
|
import { getLanguageFromRoute, getTranslatedRoute } from '../../routes';
|
||||||
|
import { switchLang } from '../i18n.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue composable for router functionality
|
||||||
|
* Provides reactive navigation and route management
|
||||||
|
*/
|
||||||
|
export function useRouter() {
|
||||||
|
const currentPath = ref(typeof window !== 'undefined' ? window.location.pathname : '/');
|
||||||
|
const currentRoute = computed(() => currentPath.value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to a path (internal or external)
|
||||||
|
* @param {string} path - The path or URL to navigate to
|
||||||
|
* @param {Object} options - Navigation options
|
||||||
|
* @param {boolean} options.external - Whether this is an external link
|
||||||
|
*/
|
||||||
|
function navigate(path, options = {}) {
|
||||||
|
const { external = false } = options;
|
||||||
|
|
||||||
|
if (external) {
|
||||||
|
window.open(path, '_blank', 'noopener,noreferrer');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(path, window.location.origin);
|
||||||
|
if (url.origin !== window.location.origin) {
|
||||||
|
window.location.assign(url.href);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedLang =
|
||||||
|
getLanguageFromRoute(window.location.pathname) ||
|
||||||
|
localStorage.getItem('language') ||
|
||||||
|
'est';
|
||||||
|
url.pathname = getTranslatedRoute(url.pathname, selectedLang);
|
||||||
|
commitNavigation(`${url.pathname}${url.search}${url.hash}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit navigation by updating history and path
|
||||||
|
*/
|
||||||
|
function commitNavigation(path) {
|
||||||
|
window.history.pushState({}, '', path);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
currentPath.value = path;
|
||||||
|
window.dispatchEvent(new PopStateEvent('popstate'));
|
||||||
|
|
||||||
|
const lang = getLanguageFromRoute(new URL(path, window.location.origin).pathname);
|
||||||
|
if (lang) switchLang(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch language and navigate to translated route
|
||||||
|
* @param {string} targetLang - Target language ('est' or 'en')
|
||||||
|
*/
|
||||||
|
function switchLanguageRoute(targetLang) {
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
const translatedPath = getTranslatedRoute(currentPath, targetLang);
|
||||||
|
|
||||||
|
switchLang(targetLang);
|
||||||
|
|
||||||
|
if (translatedPath !== currentPath) {
|
||||||
|
commitNavigation(translatedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go back in browser history
|
||||||
|
*/
|
||||||
|
function goBack() {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload the current page
|
||||||
|
*/
|
||||||
|
function reload() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current pathname
|
||||||
|
* @returns {string} Current path
|
||||||
|
*/
|
||||||
|
function getPath() {
|
||||||
|
return currentPath.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get URL query parameters as an object
|
||||||
|
* @returns {Object} Query parameters
|
||||||
|
*/
|
||||||
|
function getQuery() {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
return Object.fromEntries(params.entries());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up popstate listener for browser navigation
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const handlePopState = () => {
|
||||||
|
currentPath.value = window.location.pathname;
|
||||||
|
const lang = getLanguageFromRoute(window.location.pathname);
|
||||||
|
if (lang) switchLang(lang);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('popstate', handlePopState);
|
||||||
|
|
||||||
|
onScopeDispose(() => {
|
||||||
|
window.removeEventListener('popstate', handlePopState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentPath,
|
||||||
|
currentRoute,
|
||||||
|
navigate,
|
||||||
|
switchLanguageRoute,
|
||||||
|
goBack,
|
||||||
|
reload,
|
||||||
|
getPath,
|
||||||
|
getQuery
|
||||||
|
};
|
||||||
|
}
|
||||||
124
src/lib/useImage.js
Normal file
124
src/lib/useImage.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
import {
|
||||||
|
getRootAssetPath,
|
||||||
|
getOptimisedImagePath,
|
||||||
|
getHeroImagePath,
|
||||||
|
getHeroImageSrcSet,
|
||||||
|
getHeroImageFallback,
|
||||||
|
getResponsiveImageSrcSet,
|
||||||
|
getOptimisedImageSrcSet,
|
||||||
|
getOptimisedImageFallback
|
||||||
|
} from './imageHelpers.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue composable for image handling
|
||||||
|
* Provides reactive image path generation
|
||||||
|
*/
|
||||||
|
export function useImage() {
|
||||||
|
/**
|
||||||
|
* Get an asset path from category root
|
||||||
|
* @param {string} category - e.g., "helpdesk-page-images"
|
||||||
|
* @param {string} filename - e.g., "helpdesk-icon.svg" or "helpdesk-icon"
|
||||||
|
* @param {string} defaultExt - e.g., "svg"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getRootAssetPathComputed = (category, filename, defaultExt = "svg") => {
|
||||||
|
return computed(() => getRootAssetPath(category, filename, defaultExt));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the optimised image path
|
||||||
|
* @param {string} category - e.g., "mentors-images", "past-management-images"
|
||||||
|
* @param {string} filename - e.g., "mari.jpg"
|
||||||
|
* @param {string} format - "webp" or "jpg" (default: "webp")
|
||||||
|
* @param {string} variant - e.g., "full", "400", "800", "1200" (default: "full")
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getOptimisedImagePathComputed = (category, filename, format = "webp", variant = "full") => {
|
||||||
|
return computed(() => getOptimisedImagePath(category, filename, format, variant));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hero image path
|
||||||
|
* @param {string} category - e.g., "home-page-images"
|
||||||
|
* @param {string} filename - e.g., "hero.png"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getHeroImagePathComputed = (category, filename) => {
|
||||||
|
return computed(() => getHeroImagePath(category, filename));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get srcset for hero/background images with responsive variants
|
||||||
|
* @param {string} category - e.g., "home-page-images"
|
||||||
|
* @param {string} filename - e.g., "hero.png"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getHeroImageSrcSetComputed = (category, filename) => {
|
||||||
|
return computed(() => getHeroImageSrcSet(category, filename));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the fallback (jpg) path for hero images
|
||||||
|
* @param {string} category - e.g., "home-page-images"
|
||||||
|
* @param {string} filename - e.g., "hero.png"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getHeroImageFallbackComputed = (category, filename) => {
|
||||||
|
return computed(() => getHeroImageFallback(category, filename));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get srcset for responsive variants
|
||||||
|
* @param {string} category - e.g., "home-page-images"
|
||||||
|
* @param {string} filename - e.g., "hero.png"
|
||||||
|
* @param {number[]} widths - e.g., [400, 800, 1200]
|
||||||
|
* @param {string} format - "webp" or "jpg"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getResponsiveImageSrcSetComputed = (category, filename, widths = [400, 800, 1200], format = "webp") => {
|
||||||
|
return computed(() => getResponsiveImageSrcSet(category, filename, widths, format));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get srcset for regular optimised images (full variant only)
|
||||||
|
* @param {string} category - e.g., "mentors-images"
|
||||||
|
* @param {string} filename - e.g., "mari.jpg"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getOptimisedImageSrcSetComputed = (category, filename) => {
|
||||||
|
return computed(() => getOptimisedImageSrcSet(category, filename));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the fallback jpg for regular optimised images
|
||||||
|
* @param {string} category - e.g., "mentors-images"
|
||||||
|
* @param {string} filename - e.g., "mari.jpg"
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getOptimisedImageFallbackComputed = (category, filename) => {
|
||||||
|
return computed(() => getOptimisedImageFallback(category, filename));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Computed versions
|
||||||
|
getRootAssetPathComputed,
|
||||||
|
getOptimisedImagePathComputed,
|
||||||
|
getHeroImagePathComputed,
|
||||||
|
getHeroImageSrcSetComputed,
|
||||||
|
getHeroImageFallbackComputed,
|
||||||
|
getResponsiveImageSrcSetComputed,
|
||||||
|
getOptimisedImageSrcSetComputed,
|
||||||
|
getOptimisedImageFallbackComputed,
|
||||||
|
|
||||||
|
// Original functions for backwards compatibility
|
||||||
|
getRootAssetPath,
|
||||||
|
getOptimisedImagePath,
|
||||||
|
getHeroImagePath,
|
||||||
|
getHeroImageSrcSet,
|
||||||
|
getHeroImageFallback,
|
||||||
|
getResponsiveImageSrcSet,
|
||||||
|
getOptimisedImageSrcSet,
|
||||||
|
getOptimisedImageFallback
|
||||||
|
};
|
||||||
|
}
|
||||||
37
src/lib/useLang.js
Normal file
37
src/lib/useLang.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
import { getLangText } from './langHelpers.js';
|
||||||
|
import { currentLang } from './i18n.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue composable for language helpers
|
||||||
|
* Provides reactive language text handling
|
||||||
|
*/
|
||||||
|
export function useLang() {
|
||||||
|
/**
|
||||||
|
* Get text with language fallback (reactive version)
|
||||||
|
* @param {Object} item - The item containing text
|
||||||
|
* @param {string} key - The key to get text for
|
||||||
|
* @returns {import('vue').ComputedRef<string>}
|
||||||
|
*/
|
||||||
|
const getLangTextComputed = (item, key) => {
|
||||||
|
return computed(() => getLangText(item, key, currentLang.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get text with specific language fallback
|
||||||
|
* @param {Object} item - The item containing text
|
||||||
|
* @param {string} key - The key to get text for
|
||||||
|
* @param {string} lang - Specific language to use (default: current language)
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
const getLangTextStatic = (item, key, lang = currentLang.value) => {
|
||||||
|
return getLangText(item, key, lang);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getLangTextComputed,
|
||||||
|
getLangTextStatic,
|
||||||
|
// Original function for backwards compatibility
|
||||||
|
getLangText
|
||||||
|
};
|
||||||
|
}
|
||||||
43
src/lib/useYaml.js
Normal file
43
src/lib/useYaml.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import { loadYaml } from './yaml.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue composable for YAML data loading
|
||||||
|
* Provides reactive YAML data loading
|
||||||
|
*/
|
||||||
|
export function useYaml() {
|
||||||
|
/**
|
||||||
|
* Load YAML data reactively
|
||||||
|
* @param {string} path - Path to the YAML file
|
||||||
|
* @param {any} fallback - Fallback value if loading fails
|
||||||
|
* @returns {import('vue').Ref<any>} Reactive reference to the loaded data
|
||||||
|
*/
|
||||||
|
const loadYamlComposable = (path, fallback) => {
|
||||||
|
const data = ref(fallback);
|
||||||
|
const loading = ref(true);
|
||||||
|
const error = ref(null);
|
||||||
|
|
||||||
|
loadYaml(path, fallback)
|
||||||
|
.then((result) => {
|
||||||
|
data.value = result;
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
error.value = err;
|
||||||
|
data.value = fallback;
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadYamlComposable,
|
||||||
|
// Original function for backwards compatibility
|
||||||
|
loadYaml
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,18 +4,21 @@
|
|||||||
Grid,
|
Grid,
|
||||||
} from "../components/index.js";
|
} from "../components/index.js";
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
import { usePageText } from "../lib/index.js";
|
import { useRouter, usePageText, useImage, useYaml } from "../lib/index.js";
|
||||||
import { getHeroImageFallback, getHeroImageSrcSet, getRootAssetPath } from "../lib/imageHelpers.js";
|
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
|
||||||
|
|
||||||
import maplibregl from 'maplibre-gl';
|
import maplibregl from 'maplibre-gl';
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||||
import { navigate } from "../lib/router/router.js";
|
import { createElement, BusFront, MapPin } from 'lucide';
|
||||||
|
|
||||||
|
// Initialize composables
|
||||||
|
const router = useRouter();
|
||||||
|
const image = useImage();
|
||||||
|
const yaml = useYaml();
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
const helpdeskbg = getHeroImageFallback("helpdesk-page-images", "helpdesk_bg");
|
const helpdeskbg = image.getHeroImageFallback("helpdesk-page-images", "helpdesk_bg");
|
||||||
const helpdeskbgWebpSet = getHeroImageSrcSet("helpdesk-page-images", "helpdesk_bg");
|
const helpdeskbgWebpSet = image.getHeroImageSrcSet("helpdesk-page-images", "helpdesk_bg");
|
||||||
const helpdesk = getRootAssetPath("helpdesk-page-images", "helpdesk-on-black.png");
|
const helpdesk = image.getRootAssetPath("helpdesk-page-images", "helpdesk-on-black.png");
|
||||||
|
|
||||||
// Coordinates for Akadeemia tee 5, 12616 Tallinn [longitude, latitude]
|
// Coordinates for Akadeemia tee 5, 12616 Tallinn [longitude, latitude]
|
||||||
const center = [24.66887400515207, 59.396427975093935];
|
const center = [24.66887400515207, 59.396427975093935];
|
||||||
@@ -35,7 +38,7 @@
|
|||||||
|
|
||||||
// Navigate to directions using router
|
// Navigate to directions using router
|
||||||
const openDirections = (lng, lat, loc) => {
|
const openDirections = (lng, lat, loc) => {
|
||||||
navigate(getDirectionsUrl(lng, lat, loc), { external: true });
|
router.navigate(getDirectionsUrl(lng, lat, loc), { external: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load pricing data
|
// Load pricing data
|
||||||
@@ -46,7 +49,7 @@
|
|||||||
const text = usePageText("Helpdesk");
|
const text = usePageText("Helpdesk");
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
pricingData.value = await loadYaml('/_data/hinnakiri.yml', { services: [] });
|
pricingData.value = await yaml.loadYaml('/_data/hinnakiri.yml', { services: [] });
|
||||||
if (disposed || !mapElement.value) return;
|
if (disposed || !mapElement.value) return;
|
||||||
|
|
||||||
map = new maplibregl.Map({
|
map = new maplibregl.Map({
|
||||||
@@ -70,9 +73,8 @@
|
|||||||
marker.setAttribute('aria-label', `Get directions to ${label}`);
|
marker.setAttribute('aria-label', `Get directions to ${label}`);
|
||||||
const markerLabel = document.createElement('span');
|
const markerLabel = document.createElement('span');
|
||||||
markerLabel.textContent = label;
|
markerLabel.textContent = label;
|
||||||
const markerIcon = document.createElement('b');
|
const markerIcon = type === 'bus' ? createElement(BusFront) : createElement(MapPin);
|
||||||
markerIcon.setAttribute('aria-hidden', 'true');
|
markerIcon.setAttribute('aria-hidden', 'true');
|
||||||
markerIcon.textContent = type === 'bus' ? '●' : '◆';
|
|
||||||
marker.replaceChildren(markerLabel, markerIcon);
|
marker.replaceChildren(markerLabel, markerIcon);
|
||||||
marker.addEventListener('click', () => openDirections(coordinates[0], coordinates[1], type));
|
marker.addEventListener('click', () => openDirections(coordinates[0], coordinates[1], type));
|
||||||
new maplibregl.Marker({ element: marker }).setLngLat(coordinates).addTo(map);
|
new maplibregl.Marker({ element: marker }).setLngLat(coordinates).addTo(map);
|
||||||
@@ -211,6 +213,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
box-shadow: 0 2px 5px rgb(0 0 0 / 20%);
|
box-shadow: 0 2px 5px rgb(0 0 0 / 20%);
|
||||||
}
|
}
|
||||||
.helpdesk-marker--location span { background: var(--orange); color: white; }
|
.helpdesk-marker--location span { background: var(--color-orange-500); color: white; }
|
||||||
.helpdesk-marker b { color: var(--orange); font-size: 1.5rem; line-height: 1; }
|
.helpdesk-marker svg { color: var(--color-orange-500); width: 1.5rem; height: 1.5rem; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {Section, Stack, Grid, Center, Image, Svg, Button} from "../components/index.js";
|
import {Section, Stack, Grid, Center, Image, Svg, Button} from "../components/index.js";
|
||||||
import { navigate } from "../lib/router/router.js";
|
import { useRouter, usePageText, currentLang, useImage, useYaml } from "../lib/index.js";
|
||||||
import { usePageText, currentLang } from "../lib/index.js";
|
|
||||||
import { getHeroImagePath, getHeroImageSrcSet, getHeroImageFallback, getOptimisedImagePath, getOptimisedImageFallback } from "../lib/imageHelpers.js";
|
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
|
||||||
|
|
||||||
import { ArrowRight } from "@lucide/vue";
|
import { ArrowRight } from "@lucide/vue";
|
||||||
import { ArrowLeft } from "@lucide/vue";
|
import { ArrowLeft } from "@lucide/vue";
|
||||||
|
|
||||||
|
// Initialize composables
|
||||||
|
const router = useRouter();
|
||||||
|
const image = useImage();
|
||||||
|
const yaml = useYaml();
|
||||||
|
|
||||||
// Partners data
|
// Partners data
|
||||||
const partners = ref([]);
|
const partners = ref([]);
|
||||||
const text = usePageText("Home");
|
const text = usePageText("Home");
|
||||||
|
|
||||||
|
// Load partners data using yaml composable
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
partners.value = await loadYaml('/_data/partners.yml', []);
|
partners.value = await yaml.loadYaml('/_data/partners.yml', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
const partnerLogoExtByName = {
|
const partnerLogoExtByName = {
|
||||||
@@ -37,9 +40,9 @@
|
|||||||
<div class="relative min-h-screen flex items-center">
|
<div class="relative min-h-screen flex items-center">
|
||||||
<div class="absolute inset-0 left-1/2 w-screen -translate-x-1/2" aria-hidden="true">
|
<div class="absolute inset-0 left-1/2 w-screen -translate-x-1/2" aria-hidden="true">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getHeroImagePath('home-page-images', 'hero')"
|
:webpSrc="image.getHeroImagePath('home-page-images', 'hero')"
|
||||||
:src="getHeroImageFallback('home-page-images', 'hero')"
|
:src="image.getHeroImageFallback('home-page-images', 'hero')"
|
||||||
:webpSrcSet="getHeroImageSrcSet('home-page-images', 'hero')"
|
:webpSrcSet="image.getHeroImageSrcSet('home-page-images', 'hero')"
|
||||||
:alt="text['hero']?.imageAlt || 'illustrative hero image'"
|
:alt="text['hero']?.imageAlt || 'illustrative hero image'"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
@@ -72,7 +75,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
class="rounded-lg border-orange-500 bg-orange-500 px-3 py-2 text-gray-900 transition-colors hover:border-orange-300 hover:bg-orange-300"
|
class="rounded-lg border-orange-500 bg-orange-500 px-3 py-2 text-gray-900 transition-colors hover:border-orange-300 hover:bg-orange-300"
|
||||||
:onClick="() => navigate('/tudengile')"
|
:onClick="() => router.navigate('/tudengile')"
|
||||||
>
|
>
|
||||||
{{ text.hero?.joinButton || "Liitu LAPikutega!" }}
|
{{ text.hero?.joinButton || "Liitu LAPikutega!" }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -99,8 +102,8 @@
|
|||||||
<Center dir="col" class="w-full max-w-sm justify-start mx-auto">
|
<Center dir="col" class="w-full max-w-sm justify-start mx-auto">
|
||||||
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getOptimisedImagePath('home-page-images', 'student_temp', 'webp')"
|
:webpSrc="image.getOptimisedImagePath('home-page-images', 'student_temp', 'webp')"
|
||||||
:src="getOptimisedImageFallback('home-page-images', 'student_temp')"
|
:src="image.getOptimisedImageFallback('home-page-images', 'student_temp')"
|
||||||
:alt="text['services']?.student?.imageAlt || 'Tudengile Pilt'"
|
:alt="text['services']?.student?.imageAlt || 'Tudengile Pilt'"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="w-full h-full rounded-full object-[center_35%]"
|
class="w-full h-full rounded-full object-[center_35%]"
|
||||||
@@ -114,8 +117,8 @@
|
|||||||
<Center dir="col" class="w-full max-w-sm justify-start mx-auto">
|
<Center dir="col" class="w-full max-w-sm justify-start mx-auto">
|
||||||
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getOptimisedImagePath('home-page-images', 'helpdesk', 'webp')"
|
:webpSrc="image.getOptimisedImagePath('home-page-images', 'helpdesk', 'webp')"
|
||||||
:src="getOptimisedImageFallback('home-page-images', 'helpdesk')"
|
:src="image.getOptimisedImageFallback('home-page-images', 'helpdesk')"
|
||||||
:alt="text['services']?.helpdesk?.imageAlt || 'Helpdesk Pilt'"
|
:alt="text['services']?.helpdesk?.imageAlt || 'Helpdesk Pilt'"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="w-full h-full rounded-full object-[center_38%]"
|
class="w-full h-full rounded-full object-[center_38%]"
|
||||||
@@ -129,8 +132,8 @@
|
|||||||
<Center dir="col" class="w-full max-w-sm justify-start mx-auto md:col-span-2 lg:col-span-1">
|
<Center dir="col" class="w-full max-w-sm justify-start mx-auto md:col-span-2 lg:col-span-1">
|
||||||
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getOptimisedImagePath('home-page-images', 'company_temp', 'webp')"
|
:webpSrc="image.getOptimisedImagePath('home-page-images', 'company_temp', 'webp')"
|
||||||
:src="getOptimisedImageFallback('home-page-images', 'company_temp')"
|
:src="image.getOptimisedImageFallback('home-page-images', 'company_temp')"
|
||||||
:alt="text['services']?.company?.imageAlt || 'Ettevõttele Pilt'"
|
:alt="text['services']?.company?.imageAlt || 'Ettevõttele Pilt'"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="w-full h-full rounded-full object-[center_25%]"
|
class="w-full h-full rounded-full object-[center_25%]"
|
||||||
@@ -175,7 +178,7 @@
|
|||||||
<div class="flex flex-col items-start gap-6">
|
<div class="flex flex-col items-start gap-6">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-md border-3 border-orange-500 bg-transparent px-10 py-2 transition-colors hover:bg-orange-500/10"
|
class="rounded-md border-3 border-orange-500 bg-transparent px-10 py-2 transition-colors hover:bg-orange-500/10"
|
||||||
:onClick="() => navigate(currentLang === 'en' ? '/aboutus' : '/lapikutest')"
|
:onClick="() => router.navigate(currentLang === 'en' ? '/aboutus' : '/lapikutest')"
|
||||||
>
|
>
|
||||||
{{ text.aboutSection?.moreInfo || "Rohkem infot" }}
|
{{ text.aboutSection?.moreInfo || "Rohkem infot" }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -183,7 +186,7 @@
|
|||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<Button
|
<Button
|
||||||
class="border-orange-500 bg-orange-500 px-20 py-2 transition-colors hover:border-orange-300 hover:bg-orange-300"
|
class="border-orange-500 bg-orange-500 px-20 py-2 transition-colors hover:border-orange-300 hover:bg-orange-300"
|
||||||
:onClick="() => navigate(currentLang === 'en' ? '/contact' : '/kontakt')"
|
:onClick="() => router.navigate(currentLang === 'en' ? '/contact' : '/kontakt')"
|
||||||
>
|
>
|
||||||
{{ text.aboutSection?.contact || "Kontakt" }}
|
{{ text.aboutSection?.contact || "Kontakt" }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -196,8 +199,8 @@
|
|||||||
<div class="relative overflow-hidden min-h-90 md:h-full md:self-stretch">
|
<div class="relative overflow-hidden min-h-90 md:h-full md:self-stretch">
|
||||||
<div class="absolute inset-0">
|
<div class="absolute inset-0">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getOptimisedImagePath('home-page-images', 'about_us', 'webp')"
|
:webpSrc="image.getOptimisedImagePath('home-page-images', 'about_us', 'webp')"
|
||||||
:src="getOptimisedImageFallback('home-page-images', 'about_us')"
|
:src="image.getOptimisedImageFallback('home-page-images', 'about_us')"
|
||||||
:alt="text['aboutSection']?.imageAlt || 'Lapikud team'"
|
:alt="text['aboutSection']?.imageAlt || 'Lapikud team'"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="h-full w-full max-w-none!"
|
class="h-full w-full max-w-none!"
|
||||||
@@ -296,8 +299,8 @@
|
|||||||
<div class="relative overflow-hidden min-h-90 md:min-h-0 md:h-full md:self-stretch">
|
<div class="relative overflow-hidden min-h-90 md:min-h-0 md:h-full md:self-stretch">
|
||||||
<div class="absolute inset-0">
|
<div class="absolute inset-0">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getOptimisedImagePath('home-page-images', 'temp', 'webp')"
|
:webpSrc="image.getOptimisedImagePath('home-page-images', 'temp', 'webp')"
|
||||||
:src="getOptimisedImageFallback('home-page-images', 'temp')"
|
:src="image.getOptimisedImageFallback('home-page-images', 'temp')"
|
||||||
:alt="text['whatWeDo']?.imageAlt || 'Lapikud parandamas riistvara'"
|
:alt="text['whatWeDo']?.imageAlt || 'Lapikud parandamas riistvara'"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="h-full w-full max-w-none!"
|
class="h-full w-full max-w-none!"
|
||||||
@@ -323,7 +326,7 @@
|
|||||||
<div class="flex flex-col items-end gap-6">
|
<div class="flex flex-col items-end gap-6">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-md border-3 border-orange-500 bg-transparent px-10 py-2 transition-colors hover:bg-orange-500/10"
|
class="rounded-md border-3 border-orange-500 bg-transparent px-10 py-2 transition-colors hover:bg-orange-500/10"
|
||||||
:onClick="() => navigate(currentLang === 'en' ? '/ourwork' : '/ettevottele')"
|
:onClick="() => router.navigate(currentLang === 'en' ? '/ourwork' : '/ettevottele')"
|
||||||
>
|
>
|
||||||
{{ text.whatWeDo?.moreInfo || "Rohkem infot" }}
|
{{ text.whatWeDo?.moreInfo || "Rohkem infot" }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -332,7 +335,7 @@
|
|||||||
<ArrowLeft class="h-12 w-12 text-orange-500 md:h-14 md:w-14" :strokeWidth="2.4" />
|
<ArrowLeft class="h-12 w-12 text-orange-500 md:h-14 md:w-14" :strokeWidth="2.4" />
|
||||||
<Button
|
<Button
|
||||||
class="border-orange-500 bg-orange-500 px-20 py-2 transition-colors hover:border-orange-300 hover:bg-orange-300"
|
class="border-orange-500 bg-orange-500 px-20 py-2 transition-colors hover:border-orange-300 hover:bg-orange-300"
|
||||||
:onClick="() => navigate(currentLang === 'en' ? '/student' : '/tudengile')"
|
:onClick="() => router.navigate(currentLang === 'en' ? '/student' : '/tudengile')"
|
||||||
>
|
>
|
||||||
{{ text.whatWeDo?.contact || "Kontakt" }}
|
{{ text.whatWeDo?.contact || "Kontakt" }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -389,7 +392,7 @@
|
|||||||
<Center class="m-10">
|
<Center class="m-10">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-md border-orange-500 bg-transparent px-20 py-3 transition-colors hover:bg-orange-500/10"
|
class="rounded-md border-orange-500 bg-transparent px-20 py-3 transition-colors hover:bg-orange-500/10"
|
||||||
:onClick="() => navigate('/partners')"
|
:onClick="() => router.navigate('/partners')"
|
||||||
>
|
>
|
||||||
{{ text.partners?.moreInfo || "Rohkem infot partnerluste kohta" }}
|
{{ text.partners?.moreInfo || "Rohkem infot partnerluste kohta" }}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
Image,
|
Image,
|
||||||
} from "../components/index.js";
|
} from "../components/index.js";
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
import { currentLang, getLangText, usePageText, useYaml, useImage } from "../lib/index.js";
|
||||||
import { currentLang, getLangText, usePageText } from "../lib/index.js";
|
|
||||||
import { getOptimisedImagePath, getOptimisedImageFallback } from "../lib/imageHelpers.js";
|
|
||||||
import { Mail } from "@lucide/vue";
|
import { Mail } from "@lucide/vue";
|
||||||
import { Phone } from "@lucide/vue";
|
import { Phone } from "@lucide/vue";
|
||||||
|
|
||||||
|
// Initialize composables
|
||||||
|
const yaml = useYaml();
|
||||||
|
const image = useImage();
|
||||||
|
|
||||||
const currentManagement = ref([]);
|
const currentManagement = ref([]);
|
||||||
const pastManagement = ref([]);
|
const pastManagement = ref([]);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
@@ -46,8 +48,8 @@
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
[currentManagement.value, pastManagement.value] = await Promise.all([
|
[currentManagement.value, pastManagement.value] = await Promise.all([
|
||||||
loadYaml('/_data/management.yml', []),
|
yaml.loadYaml('/_data/management.yml', []),
|
||||||
loadYaml('/_data/past_management.yml', []),
|
yaml.loadYaml('/_data/past_management.yml', []),
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading management data:', error);
|
console.error('Error loading management data:', error);
|
||||||
@@ -88,9 +90,9 @@
|
|||||||
<div class="flex flex-col items-center gap-6 sm:flex-row sm:items-start">
|
<div class="flex flex-col items-center gap-6 sm:flex-row sm:items-start">
|
||||||
<div class="h-[150px] w-[150px] shrink-0">
|
<div class="h-[150px] w-[150px] shrink-0">
|
||||||
<picture>
|
<picture>
|
||||||
<source :srcset="getOptimisedImagePath('management-images', member.photo, 'webp')" type="image/webp" />
|
<source :srcset="image.getOptimisedImagePath('management-images', member.photo, 'webp')" type="image/webp" />
|
||||||
<img
|
<img
|
||||||
:src="getOptimisedImageFallback('management-images', member.photo)"
|
:src="image.getOptimisedImageFallback('management-images', member.photo)"
|
||||||
:alt="member.name"
|
:alt="member.name"
|
||||||
class="w-full h-full object-cover rounded-md"
|
class="w-full h-full object-cover rounded-md"
|
||||||
/>
|
/>
|
||||||
@@ -151,8 +153,8 @@
|
|||||||
<div class="h-24 w-24 overflow-hidden rounded-[10px] bg-linear-to-br outline-2 outline-transparent outline-offset-2 transition [@media(min-width:769px)]:h-28 [@media(min-width:769px)]:w-28">
|
<div class="h-24 w-24 overflow-hidden rounded-[10px] bg-linear-to-br outline-2 outline-transparent outline-offset-2 transition [@media(min-width:769px)]:h-28 [@media(min-width:769px)]:w-28">
|
||||||
<template v-if="member.image">
|
<template v-if="member.image">
|
||||||
<Image
|
<Image
|
||||||
:webpSrc="getOptimisedImagePath('past-management-images', member.image, 'webp')"
|
:webpSrc="image.getOptimisedImagePath('past-management-images', member.image, 'webp')"
|
||||||
:src="getOptimisedImageFallback('past-management-images', member.image)"
|
:src="image.getOptimisedImageFallback('past-management-images', member.image)"
|
||||||
:alt="member.name"
|
:alt="member.name"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="block h-full w-full"
|
class="block h-full w-full"
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Section, Container } from "../components/index.js";
|
import { Section, Container } from "../components/index.js";
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
import { currentLang, getLangText, usePageText, useYaml, useImage } from "../lib/index.js";
|
||||||
import { currentLang, getLangText, usePageText } from "../lib/index.js";
|
|
||||||
import { getOptimisedImagePath, getOptimisedImageFallback } from "../lib/imageHelpers.js";
|
|
||||||
|
|
||||||
import { Mail } from "@lucide/vue";
|
import { Mail } from "@lucide/vue";
|
||||||
import { Phone } from "@lucide/vue";
|
import { Phone } from "@lucide/vue";
|
||||||
import { MessageCircle } from "@lucide/vue";
|
import { MessageCircle } from "@lucide/vue";
|
||||||
import { User } from "@lucide/vue";
|
import { User } from "@lucide/vue";
|
||||||
|
|
||||||
|
// Initialize composables
|
||||||
|
const yaml = useYaml();
|
||||||
|
const image = useImage();
|
||||||
|
|
||||||
const mentors = ref([]);
|
const mentors = ref([]);
|
||||||
const activeMentors = computed(() => mentors.value.filter((mentor) => mentor.active !== false));
|
const activeMentors = computed(() => mentors.value.filter((mentor) => mentor.active !== false));
|
||||||
const text = usePageText("Mentors");
|
const text = usePageText("Mentors");
|
||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
mentors.value = await loadYaml('/_data/mentors.yml', []);
|
mentors.value = await yaml.loadYaml('/_data/mentors.yml', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getContactIcon(contactType) {
|
function getContactIcon(contactType) {
|
||||||
@@ -80,9 +81,9 @@
|
|||||||
<template v-if="mentor.photo">
|
<template v-if="mentor.photo">
|
||||||
<div class="shrink-0">
|
<div class="shrink-0">
|
||||||
<picture>
|
<picture>
|
||||||
<source :srcset="getOptimisedImagePath('mentors-images', mentor.photo, 'webp')" type="image/webp" />
|
<source :srcset="image.getOptimisedImagePath('mentors-images', mentor.photo, 'webp')" type="image/webp" />
|
||||||
<img
|
<img
|
||||||
:src="getOptimisedImageFallback('mentors-images', mentor.photo)"
|
:src="image.getOptimisedImageFallback('mentors-images', mentor.photo)"
|
||||||
:alt="mentor.name"
|
:alt="mentor.name"
|
||||||
class="w-full md:w-48 h-48 object-cover"
|
class="w-full md:w-48 h-48 object-cover"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Section, Grid, Card, Button } from "../components/index.js";
|
import { Section, Grid, Card, Button } from "../components/index.js";
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
import { currentLang, getLangText, usePageText, useRouter, useYaml, useImage } from "../lib/index.js";
|
||||||
import { currentLang, getLangText, navigate, usePageText } from "../lib/index.js";
|
|
||||||
import { getOptimisedImagePath, getOptimisedImageFallback } from "../lib/imageHelpers.js";
|
|
||||||
|
|
||||||
import { ExternalLink } from "@lucide/vue";
|
import { ExternalLink } from "@lucide/vue";
|
||||||
|
|
||||||
|
// Initialize composables
|
||||||
|
const router = useRouter();
|
||||||
|
const yaml = useYaml();
|
||||||
|
const image = useImage();
|
||||||
|
|
||||||
const projects = ref([]);
|
const projects = ref([]);
|
||||||
const text = usePageText("OurWork");
|
const text = usePageText("OurWork");
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const parsed = await loadYaml('/_data/ourwork.yml', []);
|
const parsed = await yaml.loadYaml('/_data/ourwork.yml', []);
|
||||||
projects.value = parsed.filter((p) => p?.title);
|
projects.value = parsed.filter((p) => p?.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -28,12 +31,12 @@
|
|||||||
|
|
||||||
function projectImage(project) {
|
function projectImage(project) {
|
||||||
return project?.photo
|
return project?.photo
|
||||||
? getOptimisedImagePath("ourwork-images", project.photo, "webp")
|
? image.getOptimisedImagePath("ourwork-images", project.photo, "webp")
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleImageError(event, photo) {
|
function handleImageError(event, photo) {
|
||||||
event.currentTarget.src = getOptimisedImageFallback("ourwork-images", photo);
|
event.currentTarget.src = image.getOptimisedImageFallback("ourwork-images", photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCtaEmail() {
|
function openCtaEmail() {
|
||||||
@@ -41,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openCtaContact() {
|
function openCtaContact() {
|
||||||
navigate('/contact');
|
router.navigate('/contact');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,16 @@
|
|||||||
Button,
|
Button,
|
||||||
} from "../components/index.js";
|
} from "../components/index.js";
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
import { useRouter, usePageText, useYaml } from "../lib/index.js";
|
||||||
import { usePageText } from "../lib/index.js";
|
|
||||||
import { navigate } from "../lib/router/router.js";
|
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const yaml = useYaml();
|
||||||
const members = ref({ junior: [], senior: [] });
|
const members = ref({ junior: [], senior: [] });
|
||||||
const text = usePageText("Student");
|
const text = usePageText("Student");
|
||||||
const joinFormUrl = "https://pilves.lapikud.ee/apps/forms/s/WXed8sbG2s45GMKGAiXCemgE";
|
const joinFormUrl = "https://pilves.lapikud.ee/apps/forms/s/WXed8sbG2s45GMKGAiXCemgE";
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
members.value = await loadYaml('/_data/members.yml', { junior: [], senior: [] });
|
members.value = await yaml.loadYaml('/_data/members.yml', { junior: [], senior: [] });
|
||||||
});
|
});
|
||||||
|
|
||||||
const scrollToJoin = () => {
|
const scrollToJoin = () => {
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
<p class="max-w-3xl text-base leading-8 text-white/70">{{ text.mentor?.description || '' }}</p>
|
<p class="max-w-3xl text-base leading-8 text-white/70">{{ text.mentor?.description || '' }}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
:onClick="() => navigate('/mentorid')"
|
:onClick="() => router.navigate('/mentorid')"
|
||||||
class="border-orange-500 bg-orange-500 px-8 py-3 text-sm font-semibold text-black hover:opacity-90"
|
class="border-orange-500 bg-orange-500 px-8 py-3 text-sm font-semibold text-black hover:opacity-90"
|
||||||
>
|
>
|
||||||
{{ text.mentor?.cta || '' }}
|
{{ text.mentor?.cta || '' }}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Section, Stack, Image } from "../components/index.js";
|
import { Section, Stack, Image } from "../components/index.js";
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { loadYaml } from '../lib/yaml.js';
|
import { currentLang, currentLocale, getLangText, usePageText, useYaml, useImage } from "../lib/index.js";
|
||||||
import { currentLang, currentLocale, getLangText, usePageText } from "../lib/index.js";
|
|
||||||
import { getRootAssetPath } from "../lib/imageHelpers.js";
|
|
||||||
|
|
||||||
|
// Initialize composables
|
||||||
|
const yaml = useYaml();
|
||||||
|
const image = useImage();
|
||||||
|
|
||||||
const workshops = ref([]);
|
const workshops = ref([]);
|
||||||
const text = usePageText("Workshops");
|
const text = usePageText("Workshops");
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const parsed = await loadYaml('/_data/workshops.yml', []);
|
const parsed = await yaml.loadYaml('/_data/workshops.yml', []);
|
||||||
workshops.value = parsed.filter((w) => w?.title);
|
workshops.value = parsed.filter((w) => w?.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,7 +90,7 @@
|
|||||||
:class="`overflow-hidden ${getImageHeightClass(getGallery(workshop).length, i)}`"
|
:class="`overflow-hidden ${getImageHeightClass(getGallery(workshop).length, i)}`"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
:src="getRootAssetPath('workshop-images', filename, 'jpg')"
|
:src="image.getRootAssetPath('workshop-images', filename, 'jpg')"
|
||||||
:alt="`${getField(workshop, 'title')} ${i + 1}`"
|
:alt="`${getField(workshop, 'title')} ${i + 1}`"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
class="h-full w-full"
|
class="h-full w-full"
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import Svg, { VARIANTS } from '../components/Svg.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Components/Svg',
|
|
||||||
component: Svg,
|
|
||||||
argTypes: {
|
|
||||||
type: { control: 'select', options: VARIANTS },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Preview = {
|
|
||||||
args: { type: VARIANTS[0] },
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user