lang changes, new pages, data changes

This commit is contained in:
Henri
2026-02-12 05:27:53 +02:00
parent 1504844f0b
commit 9d13b57d2e
268 changed files with 3310 additions and 1541 deletions

View File

@@ -1,6 +1,7 @@
<script>
export let onClick = () => {};
export let buttonHoverStyle = "";
export let style = "";
let isHovered = false;
</script>
@@ -10,7 +11,7 @@
on:mouseenter={() => (isHovered = true)}
on:mouseleave={() => (isHovered = false)}
class={`btn border-[1.5px] px-5 py-2.5 font-medium cursor-pointer flex items-center gap-2 whitespace-nowrap ${$$restProps.class}`}
style={isHovered && buttonHoverStyle ? buttonHoverStyle : null}
style={isHovered && buttonHoverStyle ? buttonHoverStyle : style}
>
<slot />
</button>

View File

@@ -5,7 +5,7 @@
Section,
Stack,
} from "$components";
import { navigate, getPath, currentLang, text, switchLang } from "$lib";
import { navigate, getPath, currentLang, text, switchLanguageRoute } from "$lib";
import { onDestroy } from "svelte";
// Icon imports (Lucide)
@@ -58,6 +58,11 @@
navigate(path, options);
closeMobileMenu();
}
function handleLanguageSwitch(lang) {
switchLanguageRoute(lang);
closeMobileMenu();
}
</script>
<style>
@@ -93,58 +98,69 @@
<!-- Desktop Navigation -->
<nav class="z-10 hidden md:flex gap-4 justify-end text-xl">
<Dropdown
name={$text.nav.about}
name={$text.about}
buttonClass="rounded-[5px]"
buttonHoverStyle="background-color:var(--orange); color:var(--black)"
panelClassName="bg-[var(--off-black)]"
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/mis-teeme")}
>{$text.nav.aboutPages.info}</Button
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/lapikutest")}
>{$text.aboutPages.info}</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/liitu-meiega")}
><Bot />{$text.nav.aboutPages.join}</Button
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/tudengile")}
><Bot />{$text.aboutPages.join}</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/mentorid")}
><HandHeart />{$text.nav.aboutPages.mentors}</Button
><HandHeart />{$text.aboutPages.mentors}</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/juhatus")}
><Lectern />{$text.nav.aboutPages.board}</Button
><Lectern />{$text.aboutPages.board}</Button
>
</Dropdown>
<Dropdown
name={$text.nav.events}
name={$text.events}
buttonClass="rounded-[5px]"
buttonHoverStyle="background-color:var(--orange); color:var(--black)"
panelClassName="bg-[var(--off-black)]"
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/kalender")}
><CalendarClock />{$text.nav.eventsPages.calendar}</Button
><CalendarClock />{$text.eventsPages.calendar}</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/koolitused")}
><Presentation />{$text.nav.eventsPages.workshops}</Button
><Presentation />{$text.eventsPages.workshops}</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("/rebased")}
><Swords />{$text.nav.eventsPages.fresh}</Button
><Swords />{$text.eventsPages.fresh}</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("https://asikarikas.ee/", { external: true })}
><Trophy />ASI Karikas</Button
>
<Button class="rounded-[5px] bg-transparent" buttonHoverStyle="color:var(--orange)" onClick={() => navigate("https://remondikohvik.lapikud.ee/", { external: true })}
><Coffee />{$text.nav.eventsPages.repair}</Button
><Coffee />{$text.eventsPages.repair}</Button
>
</Dropdown>
<Button buttonHoverStyle="background-color:var(--orange); color:var(--black)" class="rounded-[5px]" onClick={() => navigate("/kontakt")}
>{$text.nav.contact}
>{$text.contact}
</Button>
<Button buttonHoverStyle="background-color:var(--orange); color:var(--black)" class="rounded-[5px]" onClick={() => navigate("/helpdesk")}
>{$text.nav.helpdesk}
>{$text.helpdesk}
</Button>
<!-- Language Switcher -->
<Button
class="rounded-[5px] px-3 py-1 text-sm ml-2"
style="color: var(--orange)"
buttonHoverStyle="background-color:var(--orange); color:var(--black)"
onClick={() => switchLanguageRoute($currentLang === 'est' ? 'en' : 'est')}
>
{$currentLang === 'est' ? 'EN' : 'EST'}
</Button>
</nav>
<!-- Mobile Hamburger Button -->
<Button
onClick={toggleMobileMenu}
class="md:hidden ml-auto p-2 text-white hover:text-orange-500 transition-colors bg-transparent"
class="md:hidden ml-auto p-2 text-white transition-colors bg-transparent"
buttonHoverStyle="color: var(--orange)"
style="opacity: {mobileMenuOpen ? '0' : '1'}; pointer-events: {mobileMenuOpen ? 'none' : 'auto'};"
aria-label="Toggle menu"
>
@@ -164,87 +180,97 @@
<div class="absolute top-8 right-0 px-(--site-padding)">
<Button
onClick={closeMobileMenu}
class="ml-auto p-2 hover:text-orange-500 transition-colors flex bg-transparent"
class="ml-auto p-2 transition-colors flex bg-transparent"
buttonHoverStyle="color: var(--orange)"
aria-label="Close menu"
>
<X size={32} color="white" class="hover:stroke-orange-500" />
<X size={32} color="white" />
</Button>
</div>
<!-- Menu Content -->
<Stack gap="var(--space-5)" className="flex-1 px-8 pb-8 text-white text-xl justify-center">
<!-- About Section -->
<Stack gap="var(--space-3)" className="border-b border-gray-700 pb-4">
<h3 class="text-2xl font-bold text-orange-500">{$text.nav.about}</h3>
<Stack gap="var(--space-3)">
<h3 class="text-2xl font-bold" style="color: var(--orange)">{$text.about}</h3>
<Stack gap="var(--space-2)" className="pl-4">
<Button
onClick={() => handleNavigation("/mis-teeme")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
onClick={() => handleNavigation("/lapikutest")}
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
{$text.nav.aboutPages.info}
{$text.aboutPages.info}
</Button>
<Button
onClick={() => handleNavigation("/liitu-meiega")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
onClick={() => handleNavigation("/tudengile")}
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<Bot size={20} />
{$text.nav.aboutPages.join}
{$text.aboutPages.join}
</Button>
<Button
onClick={() => handleNavigation("/mentorid")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<HandHeart size={20} />
{$text.nav.aboutPages.mentors}
{$text.aboutPages.mentors}
</Button>
<Button
onClick={() => handleNavigation("/juhatus")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
onClick={() => handleNavigation("/ettevottele")}
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<Lectern size={20} />
{$text.nav.aboutPages.board}
Ettevõttele
</Button>
</Stack>
</Stack>
<!-- Events Section -->
<Stack gap="var(--space-3)" className="border-b border-gray-700 pb-4">
<h3 class="text-2xl font-bold text-orange-500">{$text.nav.events}</h3>
<Stack gap="var(--space-3)" >
<h3 class="text-2xl font-bold" style="color: var(--orange)">{$text.events}</h3>
<Stack gap="var(--space-2)" className="pl-4">
<Button
onClick={() => handleNavigation("/kalender")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<CalendarClock size={20} />
{$text.nav.eventsPages.calendar}
{$text.eventsPages.calendar}
</Button>
<Button
onClick={() => handleNavigation("/mentorid")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<Presentation size={20} />
{$text.nav.eventsPages.workshops}
{$text.eventsPages.workshops}
</Button>
<Button
onClick={() => handleNavigation("/rebased")}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<Swords size={20} />
{$text.nav.eventsPages.fresh}
{$text.eventsPages.fresh}
</Button>
<Button
onClick={() => handleNavigation("https://asikarikas.ee/", { external: true })}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<Trophy size={20} />
ASI Karikas
</Button>
<Button
onClick={() => handleNavigation("https://remondikohvik.lapikud.ee/", { external: true })}
class="text-left hover:text-orange-500 transition-colors flex items-center gap-2 bg-transparent"
class="text-left transition-colors flex items-center gap-2 bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
<Coffee size={20} />
{$text.nav.eventsPages.repair}
{$text.eventsPages.repair}
</Button>
</Stack>
</Stack>
@@ -253,18 +279,31 @@
<Stack gap="var(--space-3)">
<Button
onClick={() => handleNavigation("/kontakt")}
class="text-left text-2xl font-semibold hover:text-orange-500 transition-colors bg-transparent"
class="text-left text-2xl font-semibold transition-colors bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
{$text.nav.contact}
{$text.contact}
</Button>
<Button
onClick={() => handleNavigation("/helpdesk")}
class="text-left text-2xl font-semibold hover:text-orange-500 transition-colors bg-transparent"
class="text-left text-2xl font-semibold transition-colors bg-transparent"
buttonHoverStyle="color: var(--orange)"
>
{$text.nav.helpdesk}
{$text.helpdesk}
</Button>
</Stack>
<!-- Language Switcher -->
<Stack gap="var(--space-3)">
<Button
class="w-full rounded-lg py-3 text-lg"
style="background-color: var(--orange); color: var(--black)"
onClick={() => handleLanguageSwitch($currentLang === 'est' ? 'en' : 'est')}
>
{$currentLang === 'est' ? 'English' : 'Eesti'}
</Button>
</Stack>
</Stack>
</Stack>
</div>
{/if}

View File

@@ -1,26 +1,52 @@
import { writable } from 'svelte/store';
import { getLanguageFromRoute } from '../routes';
// Load saved language or default to 'est'
const savedLang = typeof window !== 'undefined'
? localStorage.getItem('language') || 'est'
: 'est';
// Get initial language from URL or localStorage
function getInitialLanguage() {
if (typeof window === 'undefined') return 'est';
const path = window.location.pathname;
const langFromRoute = getLanguageFromRoute(path);
// If we detected a language from route, use it and save it
if (langFromRoute) {
localStorage.setItem('language', langFromRoute);
return langFromRoute;
}
// Otherwise use saved language or default to 'est'
return localStorage.getItem('language') || 'est';
}
export const currentLang = writable(savedLang);
const initialLang = getInitialLanguage();
export const currentLang = writable(initialLang);
// Store for translations
export const text = writable({});
async function loadTranslations(lang) {
try {
const response = await fetch(`/locales/${lang}.json`);
const data = await response.json();
text.set(data);
const translations = {};
// Dynamically import all translation files for the specific language
// Vite will code-split these and only bundle the ones actually used
const modules = import.meta.glob('/src/locales/*/*.json');
for (const path in modules) {
if (path.includes(`/locales/${lang}/`)) {
const module = await modules[path]();
Object.assign(translations, module.default || module);
}
}
text.set(translations);
} catch (error) {
console.error(`Failed to load translations for ${lang}:`, error);
}
}
loadTranslations(savedLang);
loadTranslations(initialLang);
export function switchLang(lang) {
currentLang.set(lang);
@@ -28,4 +54,15 @@ export function switchLang(lang) {
if (typeof window !== 'undefined') {
localStorage.setItem('language', lang);
}
}
// Update language when route changes
if (typeof window !== 'undefined') {
window.addEventListener('popstate', () => {
const path = window.location.pathname;
const lang = getLanguageFromRoute(path);
if (lang) {
switchLang(lang);
}
});
}

View File

@@ -1,4 +1,5 @@
export { navigate, goBack, reload, getPath, getQuery } from './router/router.js';
export { navigate, goBack, reload, getPath, getQuery, switchLanguageRoute } from './router/router.js';
export { default as Router } from './router/Router.svelte';
export { currentLang, text, switchLang } from './i18n.js';
export { currentLang, text, switchLang } from './i18n.js';
export { getLangText } from './langHelpers.js';

17
src/lib/langHelpers.js Normal file
View File

@@ -0,0 +1,17 @@
// Helper function to get text with fallback to Estonian
export function getLangText(item, key, lang = 'est') {
const value = item[key];
// If it's a simple string, return it
if (typeof value === 'string') {
return value;
}
// If it's an object with language keys
if (value && typeof value === 'object') {
// Try to get the requested language, fallback to Estonian
return value[lang] || value.est || '';
}
return '';
}

View File

@@ -1,14 +1,43 @@
import { getTranslatedRoute, getLanguageFromRoute } from '../../routes';
import { switchLang } from '../i18n.js';
/**
* Navigate to a path (internal or external)
* @param {string} path - The path or URL to navigate to
* @param {boolean} external - Whether this is an external link (default: false)
* @param {Object} options - Navigation options
* @param {boolean} options.external - Whether this is an external link
*/
export function navigate(path, external = false) {
export function navigate(path, options = {}) {
const { external = false } = options;
if (external) {
window.location.href = path;
} else {
window.history.pushState({}, "", path);
window.dispatchEvent(new PopStateEvent('popstate'));
// Update language based on new route
const lang = getLanguageFromRoute(path);
if (lang) {
switchLang(lang);
}
}
}
/**
* Switch language and navigate to translated route
* @param {string} targetLang - Target language ('est' or 'en')
*/
export function switchLanguageRoute(targetLang) {
const currentPath = window.location.pathname;
const translatedPath = getTranslatedRoute(currentPath, targetLang);
// Always update the language first
switchLang(targetLang);
// Only navigate if the path is different
if (translatedPath !== currentPath) {
navigate(translatedPath);
}
}

View File

@@ -0,0 +1,7 @@
{
"address": "Address: ",
"mail": "E-mail: ",
"messenger": "Messenger: ",
"reg": "Reg. code: ",
"socials": "Social Media"
}

1
src/locales/en/Home.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,18 @@
{
"about": "About Us",
"aboutPages": {
"info": "What we're doing?",
"join": "Join Us",
"mentors": "Mentors",
"board": "Board"
},
"events": "Events",
"eventsPages": {
"calendar": "Events Calendar",
"workshops": "Workshops",
"fresh": "Freshmen Initiation",
"repair": "Repair Café"
},
"contact": "Contact",
"helpdesk": "Helpdesk"
}

View File

@@ -0,0 +1,7 @@
{
"address": "Aadress: ",
"mail": "E-post: ",
"messenger": "Messenger: ",
"reg": "Reg. kood: ",
"socials": "Sotsiaalmeedia"
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,18 @@
{
"about": "Meist",
"aboutPages": {
"info": "Mis Teeme?",
"join": "Liitu Meiega",
"mentors": "Mentorid",
"board": "Juhatus"
},
"events": "Üritused",
"eventsPages": {
"calendar": "Ürituste Kalender",
"workshops": "Koolitused",
"fresh": "Rebaste Ristimine",
"repair": "Remondikohvik"
},
"contact": "Kontakt",
"helpdesk": "Helpdesk"
}

274
src/routes/AboutUs.svelte Normal file
View File

@@ -0,0 +1,274 @@
<script>
import {
Section,
Grid,
Card,
Center,
} from "$components";
import { onMount } from "svelte";
import yaml from 'js-yaml';
import { currentLang, getLangText } from "$lib";
import Users from "lucide-svelte/icons/users";
import History from "lucide-svelte/icons/history";
import Target from "lucide-svelte/icons/target";
import Heart from "lucide-svelte/icons/heart";
import Award from "lucide-svelte/icons/award";
import Sparkles from "lucide-svelte/icons/sparkles";
import Mail from "lucide-svelte/icons/mail";
import Phone from "lucide-svelte/icons/phone";
let management = [];
let pastManagement = [];
onMount(async () => {
const [mgmtResponse, pastResponse] = await Promise.all([
fetch('/_data/management.yml'),
fetch('/_data/past_management.yml')
]);
const [mgmtText, pastText] = await Promise.all([
mgmtResponse.text(),
pastResponse.text()
]);
management = yaml.load(mgmtText);
pastManagement = yaml.load(pastText);
});
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Kes me oleme?</h1>
<p class="text-2xl leading-relaxed">
MTÜ Lapikud on Tallinna Tehnikaülikooli Tarkvaraarendusklubi,
mis ühendab endisi ja praegusi IT huvilisi tudengeid
</p>
</div>
</Center>
</Section>
<!-- History Section -->
<Section>
<Grid gap="var(--space-5)">
<Card variant="glass">
<div class="flex items-center gap-4 mb-6">
<div class="feature-icon" style="color: var(--orange)">
<History size={32} strokeWidth={1.5} />
</div>
<h2 class="text-4xl font-bold">Meie lugu</h2>
</div>
<div class="space-y-4 text-lg">
<p>
Organisatsioon loodi <strong>2002. aastal</strong> informaatika tudengite poolt,
kelle erialatunnuseks oli "LAP". Targad mehed otsustasid, et järelikult on nad "Lapikud".
</p>
<p>
Üle 20 aasta oleme kasvanud väikesest tudengite grupist suureks kogukonnaks,
mis on aidanud sadadel tudengitel saada väärtuslikku kogemust IT-valdkonnas.
</p>
<p>
Täna oleme uhked oma traditsiooni üle ja jätkame sellega,
et pakkuda tudengitele võimalusi areneda ja kasvada.
</p>
</div>
</Card>
<Card variant="glass">
<div class="flex items-center gap-4 mb-6">
<div class="feature-icon" style="color: var(--orange)">
<Target size={32} strokeWidth={1.5} />
</div>
<h2 class="text-4xl font-bold">Meie eesmärk</h2>
</div>
<div class="space-y-4 text-lg">
<p>
Meie põhieesmärgiks on Tallinna Tehnikaülikooli IT tudengitele
<strong>erialase lisandväärtuse loomine</strong>.
</p>
<p>
Kaasame tudengeid huvitavatesse IT projektidesse ning anname tudengitele võimaluse
midagi reaalset oma käega ära teha. Aktiivsetel tudengitel on võimalus saada meie juures esimene töökogemus.
</p>
<p>
Meie projektidest kooruvad välja huvitavad ja tarvilikud lõputööd ning
meie liikmeskonnast on tulnud ka lõputöö juhendajaid.
</p>
</div>
</Card>
</Grid>
</Section>
<!-- Values Section -->
<Section background="neutral">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Meie väärtused</h2>
<p class="text-xl text-gray-600">Mis meile oluline on</p>
</div>
<Grid columns={3} gap="var(--space-5)">
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Users size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Kogukond</h3>
<p class="text-gray-600">
Oleme tugev kogukond, kus iga liige on oluline. Toetame ja aitame üksteist.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Award size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Kvaliteet</h3>
<p class="text-gray-600">
Teeme tööd südamega ja püüdleme alati parima tulemuse poole.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Sparkles size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Innovatsioon</h3>
<p class="text-gray-600">
Kasutame uusimaid tehnoloogiaid ja otsime alati paremaid lahendusi.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Heart size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Kirg</h3>
<p class="text-gray-600">
Teeme seda, mida armastame. Meie kirglikud liikmed on meie tugevus.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Users size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Kaasamine</h3>
<p class="text-gray-600">
Kaasame kõiki teaduskondi - IT, majandus, inseneeria ja muud.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Target size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Praktiline</h3>
<p class="text-gray-600">
Teeme reaalset tööd, mis annab tudengitele praktilist kogemust.
</p>
</Card>
</Grid>
</Section>
<!-- Current Management -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Meie juhatus</h2>
<p class="text-xl text-gray-600">Inimesed, kes viivad organisatsiooni edasi</p>
</div>
<Grid columns={2} gap="var(--space-6)">
{#each management as person}
<Card variant="glass">
<div class="flex flex-col md:flex-row gap-6">
{#if person.photo}
<div class="shrink-0">
<img
src={`/assets/management-images/${person.photo}`}
alt={person.name}
class="w-full md:w-48 h-48 object-cover rounded-lg"
/>
</div>
{/if}
<div class="flex-1">
<h3 class="text-2xl font-bold mb-2">{person.name}</h3>
<p class="text-lg font-semibold mb-2" style="color: var(--orange)">
{getLangText(person, 'role', $currentLang)}
</p>
{#if person.subrole}
<p class="text-gray-600 mb-4">{getLangText(person, 'subrole', $currentLang)}</p>
{/if}
<div class="space-y-2">
{#if person.email}
<div class="flex items-center gap-2">
<Mail size={18} style="color: var(--orange)" />
<a href={`mailto:${person.email}`} class="hover:underline">
{person.email}
</a>
</div>
{/if}
{#if person.phone}
<div class="flex items-center gap-2">
<Phone size={18} style="color: var(--orange)" />
<a href={`tel:${person.phone}`} class="hover:underline">
{person.phone}
</a>
</div>
{/if}
</div>
</div>
</div>
</Card>
{/each}
</Grid>
</Section>
<!-- Past Management -->
{#if pastManagement && pastManagement.length > 0}
<Section background="neutral">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Endised juhatused</h2>
<p class="text-xl text-gray-600">Täname kõiki, kes on organisatsiooni juhtinud</p>
</div>
<div class="space-y-12">
{#each pastManagement as yearGroup}
<div>
<h3 class="text-3xl font-bold mb-6 text-center">{yearGroup.year}</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-6">
{#each yearGroup.members as member}
<Card variant="glass" class="text-center">
{#if member.image}
<img
src={`/assets/past-management-images/optimised/${member.image}`}
alt={member.name}
class="w-full h-32 object-cover rounded-lg mb-3"
on:error={(e) => {
e.target.src = '/assets/past-management-images/original/' + member.image;
}}
/>
{/if}
<p class="font-semibold">{member.name}</p>
</Card>
{/each}
</div>
</div>
{/each}
</div>
</Section>
{/if}
<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%;
}
</style>

313
src/routes/Contact.svelte Normal file
View File

@@ -0,0 +1,313 @@
<script>
import {
Section,
Grid,
Card,
Center,
} from "$components";
import MapPin from "lucide-svelte/icons/map-pin";
import Mail from "lucide-svelte/icons/mail";
import Phone from "lucide-svelte/icons/phone";
import MessageCircle from "lucide-svelte/icons/message-circle";
import Facebook from "lucide-svelte/icons/facebook";
import Instagram from "lucide-svelte/icons/instagram";
import Github from "lucide-svelte/icons/github";
import Building from "lucide-svelte/icons/building";
import Clock from "lucide-svelte/icons/clock";
const contactMethods = [
{
icon: MapPin,
title: "Aadress",
content: "Akadeemia tee 5, 12616 Tallinn",
subtitle: "ICT-teaduskond, Raja 15 ruum 111",
link: "https://goo.gl/maps/..."
},
{
icon: Mail,
title: "E-post",
content: "lapikud@lapikud.ee",
link: "mailto:lapikud@lapikud.ee"
},
{
icon: Phone,
title: "Telefon",
content: "+372 58 160 799",
link: "tel:+37258160799"
},
{
icon: MessageCircle,
title: "Messenger",
content: "m.me/Lapikud",
link: "https://m.me/Lapikud"
}
];
const socialMedia = [
{
icon: Facebook,
name: "Facebook",
link: "https://www.facebook.com/Lapikud/"
},
{
icon: Instagram,
name: "Instagram",
link: "https://www.instagram.com/lapikud/"
},
{
icon: Github,
name: "GitHub",
link: "https://github.com/Lapikud/"
}
];
const organizationInfo = [
{
icon: Building,
title: "Registrikood",
content: "80167145"
},
{
icon: Building,
title: "Pangakonto",
content: "EE812200221019551756",
subtitle: "Swedbank"
}
];
let formData = {
name: '',
email: '',
subject: '',
message: ''
};
function handleSubmit(e) {
e.preventDefault();
// Create mailto link with form data
const mailtoLink = `mailto:lapikud@lapikud.ee?subject=${encodeURIComponent(formData.subject)}&body=${encodeURIComponent(`Nimi: ${formData.name}\nE-post: ${formData.email}\n\nSõnum:\n${formData.message}`)}`;
window.location.href = mailtoLink;
}
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Võta ühendust</h1>
<p class="text-2xl leading-relaxed">
Kas sul on küsimusi? Tahad liituda? Või soovid alustada koostööd? Võta julgelt ühendust!
</p>
</div>
</Center>
</Section>
<!-- Contact Methods -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Kuidas meiega ühendust võtta?</h2>
<p class="text-xl text-gray-600">Vali endale sobiv viis</p>
</div>
<Grid columns={2} gap="var(--space-5)">
{#each contactMethods as method}
<Card variant="glass">
<div class="flex items-start gap-4">
<div class="feature-icon shrink-0" style="color: var(--orange)">
<svelte:component this={method.icon} size={32} strokeWidth={1.5} />
</div>
<div class="flex-1">
<h3 class="text-xl font-bold mb-2">{method.title}</h3>
{#if method.link}
<a
href={method.link}
class="text-lg hover:underline"
style="color: var(--orange)"
target={method.link.startsWith('http') ? '_blank' : '_self'}
rel={method.link.startsWith('http') ? 'noopener noreferrer' : ''}
>
{method.content}
</a>
{:else}
<p class="text-lg">{method.content}</p>
{/if}
{#if method.subtitle}
<p class="text-gray-600 text-sm mt-1">{method.subtitle}</p>
{/if}
</div>
</div>
</Card>
{/each}
</Grid>
</Section>
<!-- Contact Form -->
<Section background="neutral">
<div class="max-w-3xl mx-auto">
<div class="text-center mb-8">
<h2 class="text-4xl font-bold mb-4">Saada meile sõnum</h2>
<p class="text-xl text-gray-600">Täida vorm ja me võtame sinuga ühendust</p>
</div>
<Card variant="glass">
<form on:submit={handleSubmit} class="space-y-6">
<div>
<label for="name" class="block text-sm font-semibold mb-2">Nimi *</label>
<input
type="text"
id="name"
bind:value={formData.name}
required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-orange-500 focus:ring-2 focus:ring-orange-200 outline-none transition"
placeholder="Sinu nimi"
/>
</div>
<div>
<label for="email" class="block text-sm font-semibold mb-2">E-post *</label>
<input
type="email"
id="email"
bind:value={formData.email}
required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-orange-500 focus:ring-2 focus:ring-orange-200 outline-none transition"
placeholder="sinu@email.ee"
/>
</div>
<div>
<label for="subject" class="block text-sm font-semibold mb-2">Teema *</label>
<input
type="text"
id="subject"
bind:value={formData.subject}
required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-orange-500 focus:ring-2 focus:ring-orange-200 outline-none transition"
placeholder="Sõnumi teema"
/>
</div>
<div>
<label for="message" class="block text-sm font-semibold mb-2">Sõnum *</label>
<textarea
id="message"
bind:value={formData.message}
required
rows="6"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-orange-500 focus:ring-2 focus:ring-orange-200 outline-none transition resize-none"
placeholder="Kirjuta oma sõnum siia..."
></textarea>
</div>
<button
type="submit"
class="w-full py-3 px-6 bg-orange-500 hover:bg-orange-600 text-white font-semibold rounded-lg transition-colors"
>
Saada sõnum
</button>
</form>
</Card>
</div>
</Section>
<!-- Office Hours & Location -->
<Section>
<Grid gap="var(--space-5)">
<Card variant="glass">
<div class="flex items-start gap-4 mb-4">
<div class="feature-icon" style="color: var(--orange)">
<Clock size={32} strokeWidth={1.5} />
</div>
<div>
<h2 class="text-3xl font-bold mb-4">Lahtiolekuajad</h2>
</div>
</div>
<div class="space-y-3 text-lg">
<div class="flex justify-between">
<span class="font-semibold">Esmaspäev - Reede:</span>
<span>10:00 - 16:00</span>
</div>
<div class="flex justify-between">
<span class="font-semibold">Laupäev - Pühapäev:</span>
<span>Suletud</span>
</div>
<p class="text-sm text-gray-600 mt-4">
* Soovitame enne külastamist võtta ühendust, et vältida ootamist
</p>
</div>
</Card>
<Card variant="glass">
<div class="flex items-start gap-4 mb-4">
<div class="feature-icon" style="color: var(--orange)">
<MapPin size={32} strokeWidth={1.5} />
</div>
<div>
<h2 class="text-3xl font-bold mb-4">Kuidas meid leida?</h2>
</div>
</div>
<div class="space-y-3 text-lg">
<p><strong>Aadress:</strong> Akadeemia tee 5, 12616 Tallinn</p>
<p><strong>Hoone:</strong> ICT-teaduskond (Raja 15)</p>
<p><strong>Ruum:</strong> 111</p>
<p class="text-sm text-gray-600 mt-4">
Asume Tallinna Tehnikaülikooli ICT-teaduskonna hoones.
Kui tuled ühistranspordiga, on lähimad peatused "Mustakivi" ja "Tehnikaülikool".
</p>
</div>
</Card>
</Grid>
</Section>
<!-- Social Media & Legal Info -->
<Section background="neutral">
<Grid gap="var(--space-5)">
<Card variant="glass">
<h2 class="text-3xl font-bold mb-6 text-center">Jälgi meid sotsiaalmeedias</h2>
<div class="flex flex-wrap justify-center gap-4">
{#each socialMedia as social}
<a
href={social.link}
target="_blank"
rel="noopener noreferrer"
class="flex items-center gap-3 px-6 py-3 bg-orange-100 hover:bg-orange-200 rounded-lg transition-colors"
>
<svelte:component this={social.icon} size={24} style="color: var(--orange)" />
<span class="font-semibold">{social.name}</span>
</a>
{/each}
</div>
</Card>
<Card variant="glass">
<h2 class="text-3xl font-bold mb-6 text-center">MTÜ Lapikud</h2>
<div class="space-y-3">
{#each organizationInfo as info}
<div class="flex items-start gap-4">
<svelte:component this={info.icon} size={24} style="color: var(--orange)" />
<div>
<p class="font-semibold">{info.title}</p>
<p class="text-gray-700">{info.content}</p>
{#if info.subtitle}
<p class="text-sm text-gray-600">{info.subtitle}</p>
{/if}
</div>
</div>
{/each}
</div>
</Card>
</Grid>
</Section>
<style>
.feature-icon {
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
background: rgba(255, 127, 0, 0.1);
border-radius: 50%;
}
</style>

245
src/routes/Helpdesk.svelte Normal file
View File

@@ -0,0 +1,245 @@
<script>
import {
Section,
Grid,
Card,
Center,
} from "$components";
import Laptop from "lucide-svelte/icons/laptop";
import Smartphone from "lucide-svelte/icons/smartphone";
import HardDrive from "lucide-svelte/icons/hard-drive";
import Wifi from "lucide-svelte/icons/wifi";
import Settings from "lucide-svelte/icons/settings";
import Wrench from "lucide-svelte/icons/wrench";
import Clock from "lucide-svelte/icons/clock";
import MapPin from "lucide-svelte/icons/map-pin";
import Mail from "lucide-svelte/icons/mail";
import Phone from "lucide-svelte/icons/phone";
import CheckCircle from "lucide-svelte/icons/check-circle";
const services = [
{
icon: Laptop,
title: "Arvuti diagnostika",
description: "Tuvastame riistvara- ja tarkvaraprobleemid ning pakume lahendusi",
price: "Alates 10€"
},
{
icon: Settings,
title: "Operatsioonisüsteemi paigaldus",
description: "Windows, Linux või macOS paigaldamine ja seadistamine",
price: "20€"
},
{
icon: HardDrive,
title: "Riistvara vahetus",
description: "RAM, SSD, HDD või muu komponendi vahetus ja paigaldus",
price: "Vastavalt tööle"
},
{
icon: Wrench,
title: "Arvuti puhastus",
description: "Füüsiline puhastus tolmust ja termopasta vahetus",
price: "15€"
},
{
icon: Wifi,
title: "Võrgu seadistamine",
description: "Koduvõrgu või WiFi seadistamine ja probleemide lahendamine",
price: "15€"
},
{
icon: Smartphone,
title: "Nutiseadmete abi",
description: "Nutitelefonide ja tahvelarvutite seadistamine ja nõustamine",
price: "10€"
}
];
const whyChooseUs = [
"Tudengisõbralikud hinnad",
"Kiire teenindus",
"Kogenud meeskond",
"Paindlikud lahtiolekuajad",
"Garantii töödele",
"Sõbralik teenindus"
];
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Helpdesk</h1>
<p class="text-2xl leading-relaxed">
Tudengisõbralik arvutiabiteenus. Aitame lahendada kõik sinu IT-mured!
</p>
</div>
</Center>
</Section>
<!-- Services Section -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Meie teenused</h2>
<p class="text-xl text-gray-600">Pakume laia valikut IT-teenuseid soodsa hinnaga</p>
</div>
<Grid columns={3} gap="var(--space-5)">
{#each services as service}
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<svelte:component this={service.icon} size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">{service.title}</h3>
<p class="text-gray-600 mb-4">{service.description}</p>
<p class="text-lg font-bold" style="color: var(--orange)">{service.price}</p>
</Card>
{/each}
</Grid>
</Section>
<!-- Why Choose Us -->
<Section background="neutral">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Miks valida meid?</h2>
</div>
<Center>
<Card variant="glass" class="max-w-4xl">
<Grid columns={2} gap="var(--space-4)">
{#each whyChooseUs as reason}
<div class="flex items-center gap-3">
<CheckCircle size={24} style="color: var(--orange)" />
<span class="text-lg">{reason}</span>
</div>
{/each}
</Grid>
</Card>
</Center>
</Section>
<!-- Pricing Info -->
<Section>
<Grid gap="var(--space-5)">
<Card variant="glass">
<h2 class="text-4xl font-bold mb-6">Hinnakirjast</h2>
<div class="space-y-4 text-lg">
<p>
Meie hinnad on tudengisõbralikud ja läbipaistvad. Enne töö alustamist teeme alati hinnapakkumise,
et sa teaksid täpselt, mida oodata.
</p>
<p>
<strong>Diagnostika:</strong> Esimene diagnostika on tasuta! Maksad ainult siis, kui otsustad lasta meil
probleem lahendada.
</p>
<p>
<strong>Töötunnid:</strong> Tavaline tunnitasu on 15€. Keerulisemad tööd võivad olla kallimad,
kuid lepime alati kokku enne töö alustamist.
</p>
<p>
<strong>Varuosad:</strong> Varuosade hind lisandub töötasule. Aitame leida parimad hinnad ja kvaliteedi.
</p>
</div>
</Card>
<Card variant="glass">
<h2 class="text-4xl font-bold mb-6">Kuidas toimub teenindus?</h2>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">1.</div>
<div>
<strong class="text-lg">Võta ühendust</strong><br/>
Kirjuta meile või tule otse meie kontorisse. Kirjelda oma probleemi.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">2.</div>
<div>
<strong class="text-lg">Diagnostika</strong><br/>
Vaatame probleemi üle ja selgitame välja põhjuse. Teeme hinnapakkumise.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">3.</div>
<div>
<strong class="text-lg">Remont</strong><br/>
Kui oled nõus, alustame tööd. Hoiame sind kursis protsessiga.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">4.</div>
<div>
<strong class="text-lg">Üleandmine</strong><br/>
Anname arvuti üle, selgitame tehtud tööd ja anname nõu tulevikuks.
</div>
</div>
</div>
</Card>
</Grid>
</Section>
<!-- Contact Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl text-white">
<h2 class="text-4xl font-bold mb-8">Võta ühendust</h2>
<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>

View File

@@ -7,6 +7,7 @@
Container,
} from "$components";
import { onMount } from "svelte";
import yaml from 'js-yaml';
// Import Lucide icons
import Wrench from "lucide-svelte/icons/wrench";
@@ -18,8 +19,9 @@
let partners = [];
onMount(async () => {
const response = await fetch('/assets/partners/partners.json');
partners = await response.json();
const response = await fetch('/_data/partners.yml');
const yamlText = await response.text();
partners = yaml.load(yamlText);
});
</script>
@@ -38,7 +40,7 @@
<Section>
<Grid gap="var(--space-5)">
<Card variant="animated-neutral" href="/liitu-meiega">
<Card variant="animated-neutral" href="/tudengile">
<div class="feature-icon" style="color: var(--orange)">
<Users size={32} strokeWidth={1.5} />
</div>
@@ -106,7 +108,7 @@
{#if partner.url}
<a href={partner.url} target="_blank" rel="noopener noreferrer" class="partner-item" title={partner.name}>
{#if partner.image}
<img src="/assets/partners/{partner.image}" alt={partner.name} />
<img src={`/assets/partners_logos/${partner.image}`} alt={partner.name} />
{:else}
<span class="partner-name">{partner.name}</span>
{/if}
@@ -114,7 +116,7 @@
{:else}
<div class="partner-item">
{#if partner.image}
<img src="/assets/partners/{partner.image}" alt={partner.name} />
<img src={`/assets/partners_logos/${partner.image}`} alt={partner.name} />
{:else}
<span class="partner-name">{partner.name}</span>
{/if}

176
src/routes/Mentors.svelte Normal file
View File

@@ -0,0 +1,176 @@
<script>
import {
Section,
Grid,
Card,
Center,
} from "$components";
import { onMount } from "svelte";
import yaml from 'js-yaml';
import { currentLang, getLangText } from "$lib";
import Mail from "lucide-svelte/icons/mail";
import Phone from "lucide-svelte/icons/phone";
import MessageCircle from "lucide-svelte/icons/message-circle";
import User from "lucide-svelte/icons/user";
let mentors = [];
onMount(async () => {
const response = await fetch('/_data/mentors.yml');
const yamlText = await response.text();
mentors = yaml.load(yamlText);
});
function getContactIcon(contactType) {
const type = contactType.toLowerCase();
if (type.includes('email') || type.includes('e-post')) return Mail;
if (type.includes('phone') || type.includes('telefon')) return Phone;
if (type.includes('messenger') || type.includes('discord') || type.includes('facebook')) return MessageCircle;
return User;
}
function getContactLink(method) {
const type = method.name.toLowerCase();
const value = method.value;
if (type.includes('email')) return `mailto:${value}`;
if (type.includes('phone') || type.includes('telefon')) return `tel:${value}`;
if (type.includes('messenger') || type.includes('facebook')) {
return value.startsWith('http') ? value : `https://m.me/${value}`;
}
if (type.includes('discord')) return '#';
return value.startsWith('http') ? value : '#';
}
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Meie Mentorid</h1>
<p class="text-2xl leading-relaxed">
Kogenud tudengid, kes aitavad sind areneda ja jagavad oma teadmisi
</p>
</div>
</Center>
</Section>
<!-- Mentors Grid -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Tutvuge meie mentorite meeskonnaga</h2>
<p class="text-xl text-gray-600">Igaüks neist on spetsialist omas valdkonnas ja valmis sind aitama</p>
</div>
<Grid columns={2} gap="var(--space-6)">
{#each mentors as mentor}
<Card variant="glass" class="overflow-hidden">
<div class="flex flex-col md:flex-row gap-6">
<!-- Mentor Photo -->
{#if mentor.photo}
<div class="shrink-0">
<img
src={`/assets/mentors-images/optimised/${mentor.photo}`}
alt={mentor.name}
class="w-full md:w-48 h-48 object-cover rounded-lg"
on:error={(e) => {
e.target.src = '/assets/mentors-images/original/' + mentor.photo;
}}
/>
</div>
{/if}
<!-- Mentor Info -->
<div class="flex-1">
<h3 class="text-2xl font-bold mb-2">{mentor.name}</h3>
{#if mentor.age}
<p class="text-gray-600 mb-2">Vanus: {mentor.age}</p>
{/if}
{#if mentor.speciality}
<p class="text-lg font-semibold mb-2" style="color: var(--orange)">
{getLangText(mentor, 'speciality', $currentLang)}
</p>
{/if}
{#if mentor.teams && mentor.teams.length > 0}
<div class="mb-3">
<span class="font-semibold">Meeskonnad: </span>
<span class="text-gray-700">{mentor.teams.join(', ')}</span>
</div>
{/if}
{#if mentor.term}
<p class="text-gray-600 mb-3">Ametiperiood: {mentor.term} {mentor.term === 1 ? 'aasta' : 'aastat'}</p>
{/if}
{#if mentor.description}
<p class="text-sm font-semibold mb-2" style="color: var(--orange)">
Otsin: {getLangText(mentor, 'description', $currentLang)}
</p>
{/if}
{#if mentor.activities}
<p class="text-gray-700 text-sm mb-4 line-clamp-3">
{getLangText(mentor, 'activities', $currentLang)}
</p>
{/if}
<!-- Contact Methods -->
{#if mentor.contactMethods && mentor.contactMethods.length > 0}
<div class="flex flex-wrap gap-2 mt-4">
{#each mentor.contactMethods as method}
{@const IconComponent = getContactIcon(method.name)}
<a
href={getContactLink(method)}
class="inline-flex items-center gap-2 px-3 py-2 bg-orange-100 hover:bg-orange-200 rounded-lg transition-colors text-sm"
title={method.name}
target={method.value.startsWith('http') ? '_blank' : '_self'}
rel={method.value.startsWith('http') ? 'noopener noreferrer' : ''}
>
<svelte:component this={IconComponent} size={16} />
<span class="capitalize">{method.name}</span>
</a>
{/each}
</div>
{/if}
</div>
</div>
</Card>
{/each}
</Grid>
</Section>
<!-- Become a Mentor Section -->
<Section background="neutral">
<Center>
<Card variant="glass" class="max-w-3xl">
<h2 class="text-4xl font-bold mb-6 text-center">Taha saada mentoriks?</h2>
<p class="text-lg text-center mb-6">
Kui oled kogenud tudeng ja soovid jagada oma teadmisi ning aidata uusi liikmeid,
siis võta meiega ühendust!
</p>
<div class="text-center">
<a
href="mailto:lapikud@lapikud.ee"
class="inline-block px-8 py-3 bg-orange-500 hover:bg-orange-600 text-white font-semibold rounded-lg transition-colors"
>
Võta ühendust
</a>
</div>
</Card>
</Center>
</Section>
<style>
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>

272
src/routes/OurWork.svelte Normal file
View File

@@ -0,0 +1,272 @@
<script>
import {
Section,
Grid,
Card,
Center,
} from "$components";
import { onMount } from "svelte";
import yaml from 'js-yaml';
import { currentLang, getLangText } from "$lib";
import Rocket from "lucide-svelte/icons/rocket";
import Users from "lucide-svelte/icons/users";
import Target from "lucide-svelte/icons/target";
import Briefcase from "lucide-svelte/icons/briefcase";
import Code from "lucide-svelte/icons/code";
import Smartphone from "lucide-svelte/icons/smartphone";
import Globe from "lucide-svelte/icons/globe";
import ArrowRight from "lucide-svelte/icons/arrow-right";
import ExternalLink from "lucide-svelte/icons/external-link";
let projects = [];
onMount(async () => {
const response = await fetch('/_data/ourwork.yml');
const yamlText = await response.text();
projects = yaml.load(yamlText).filter(p => p.title);
});
const services = [
{
icon: Globe,
title: "Veebilahendused",
description: "Kaasaegsed ja responsiivsed veebilehed, mis toimivad igal seadmel"
},
{
icon: Smartphone,
title: "Mobiilirakendused",
description: "iOS ja Android rakendused kasutajasõbralike liidestega"
},
{
icon: Code,
title: "Tarkvara arendus",
description: "Kohandatud tarkvaralahendused teie ärivajadusteks"
},
{
icon: Briefcase,
title: "Konsultatsioon",
description: "IT-nõustamine ja projekti planeerimine"
}
];
const whyUs = [
{
icon: Users,
title: "Motiveeritud meeskond",
description: "Meie tudengid on andekad, õhevil ja tahavad tõestada oma oskuseid"
},
{
icon: Target,
title: "Kvaliteetne tulemus",
description: "Võtame iga projekti tõsiselt ja pingutame parima tulemuse nimel"
},
{
icon: Rocket,
title: "Kaasaegsed tehnoloogiad",
description: "Kasutame uusimaid tööriistu ja tehnoloogiaid"
}
];
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Ettevõttele</h1>
<p class="text-2xl leading-relaxed">
Aitame sinu ideed ellu viia! Meie kogenud tudengid on valmis teie projekte realiseerima.
</p>
</div>
</Center>
</Section>
<!-- Services Section -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Mida me pakume?</h2>
<p class="text-xl text-gray-600">Laia valik IT-teenuseid teie ärivajaduste täitmiseks</p>
</div>
<Grid columns={2} gap="var(--space-5)">
{#each services as service}
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<svelte:component this={service.icon} size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">{service.title}</h3>
<p class="text-gray-600">{service.description}</p>
</Card>
{/each}
</Grid>
</Section>
<!-- Why Choose Us -->
<Section background="neutral">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Miks meiega koostööd teha?</h2>
</div>
<Grid columns={3} gap="var(--space-5)">
{#each whyUs as item}
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<svelte:component this={item.icon} size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">{item.title}</h3>
<p class="text-gray-600">{item.description}</p>
</Card>
{/each}
</Grid>
</Section>
<!-- Process Section -->
<Section>
<Grid gap="var(--space-5)">
<Card variant="glass">
<h2 class="text-4xl font-bold mb-6">Kuidas töötame?</h2>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">1.</div>
<div>
<strong class="text-lg">Esimene kohtumine</strong><br/>
Arutame teie ideed, vajadusi ja eesmärke. Mõistame, mida soovite saavutada.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">2.</div>
<div>
<strong class="text-lg">Planeerimine</strong><br/>
Koostame projekti plaani, hinnapakkumise ja ajakava. Lepime kokku detailides.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">3.</div>
<div>
<strong class="text-lg">Arendus</strong><br/>
Meie meeskond töötab teie projekti kallal. Hoiame teid regulaarselt kursis.
</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 valmis toote üle koos dokumentatsiooni ja juhendamisega.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">5.</div>
<div>
<strong class="text-lg">Tugi</strong><br/>
Pakume vajadusel jätkuvat tuge ja hooldust.
</div>
</div>
</div>
</Card>
<Card variant="glass">
<h2 class="text-4xl font-bold mb-6">Millal meie sobime?</h2>
<div class="space-y-4 text-lg">
<p>
<strong>Startup ja väikeettevõtted:</strong> Meie teenused on hinnaefektiivsed ja paindlikud,
sobides hästi piiratud eelarvega projektidele.
</p>
<p>
<strong>Prototüüpide arendus:</strong> Vajate kiiresti töökorras prototüüpi?
Meie agile meeskond suudab kiiresti teie idee ellu viia.
</p>
<p>
<strong>Lõputööd ja projektid:</strong> Teie projekt võib saada meie tudengite
lõputöö teemaks, mis tagab motiveeritud ja pühendunud meeskonna.
</p>
<p>
<strong>Pikaajalised projektid:</strong> Pakume ka pikemaajalisi koostöövõimalusi
keerulisemate süsteemide arendamiseks.
</p>
</div>
</Card>
</Grid>
</Section>
<!-- Portfolio Section -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Meie tehtud tööd</h2>
<p class="text-xl text-gray-600">Vaata, mida oleme varem loonud</p>
</div>
<Grid columns={3} gap="var(--space-5)">
{#each projects as project}
<Card variant="animated-neutral">
{#if project.photo}
<div class="mb-4 overflow-hidden rounded-lg">
<img
src={`/assets/ourwork-images/optimised/${project.photo}`}
alt={getLangText(project, 'title', $currentLang)}
class="w-full h-48 object-cover hover:scale-105 transition-transform duration-300"
on:error={(e) => {
e.target.src = '/assets/ourwork-images/original/' + project.photo;
}}
/>
</div>
{/if}
<h3 class="text-xl font-bold mb-2">{getLangText(project, 'title', $currentLang)}</h3>
{#if project.description}
<p class="text-gray-600 text-sm mb-4">{getLangText(project, 'description', $currentLang)}</p>
{/if}
{#if project.url}
<a
href={project.url}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-2 text-orange-500 hover:text-orange-600 font-semibold"
>
Vaata projekti <ExternalLink size={16} />
</a>
{/if}
</Card>
{/each}
</Grid>
</Section>
<!-- CTA Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl text-white">
<h2 class="text-4xl font-bold mb-6">Alustame koostööd?</h2>
<p class="text-xl mb-8">
Võta ühendust ja arutame, kuidas saame sinu ideed ellu viia!
</p>
<div class="flex flex-wrap gap-4 justify-center">
<a
href="mailto:lapikud@lapikud.ee"
class="inline-flex items-center gap-2 px-8 py-3 bg-white text-orange-500 hover:bg-gray-100 font-semibold rounded-lg transition-colors"
>
Saada e-kiri <ArrowRight size={20} />
</a>
<a
href="/contact"
class="inline-flex items-center gap-2 px-8 py-3 bg-transparent border-2 border-white hover:bg-white hover:text-orange-500 font-semibold rounded-lg transition-colors"
>
Kontaktandmed
</a>
</div>
</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>

210
src/routes/Student.svelte Normal file
View File

@@ -0,0 +1,210 @@
<script>
import {
Section,
Grid,
Card,
Center,
Container,
} from "$components";
import { onMount } from "svelte";
import yaml from 'js-yaml';
import Users from "lucide-svelte/icons/users";
import GraduationCap from "lucide-svelte/icons/graduation-cap";
import Code from "lucide-svelte/icons/code";
import Calendar from "lucide-svelte/icons/calendar";
import Lightbulb from "lucide-svelte/icons/lightbulb";
import Trophy from "lucide-svelte/icons/trophy";
let members = { junior: [], senior: [] };
onMount(async () => {
const response = await fetch('/_data/members.yml');
const yamlText = await response.text();
members = yaml.load(yamlText);
});
</script>
<!-- Hero Section -->
<Section background="orange">
<Center>
<div class="text-center max-w-4xl">
<h1 class="text-5xl font-bold mb-6">Liitu Meiega!</h1>
<p class="text-2xl leading-relaxed">
Tule arenda oma oskuseid ja saa ägedaid sõpru! Omanda praktilist kogemust reaalsetest projektidest.
</p>
</div>
</Center>
</Section>
<!-- Benefits Section -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Miks Lapikutega liituda?</h2>
<p class="text-xl text-gray-600">Siin on mõned põhjused, miks meie kogukond on suurepärane koht arenguks</p>
</div>
<Grid columns={3} gap="var(--space-5)">
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Code size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Praktilised projektid</h3>
<p class="text-gray-600">
Osale reaalsetel tarkvaraarenduse projektidel. Omanda kogemust, mida ei õpeta ühelgi kursusel.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Users size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Tugev kogukond</h3>
<p class="text-gray-600">
Liitu sõbralike ja ambitsioonikate tudengitega. Leia oma meeskond ja loo püsivaid sõprussuhteid.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<GraduationCap size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Mentorlus</h3>
<p class="text-gray-600">
Saa juhendamist kogenud tudengitelt ja arendajatelt. Õpi parimate käest ja arene kiiremini.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Calendar size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Üritused</h3>
<p class="text-gray-600">
Osale huvitavatel üritustel, hackathonidel ja koolitustel. Laienda oma silmaringi ja võrgustikku.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Lightbulb size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Lõputööd</h3>
<p class="text-gray-600">
Meie projektidest kasvavad välja huvitavad lõputööd. Leia oma teema ja juhendaja meie seast.
</p>
</Card>
<Card variant="glass">
<div class="feature-icon mb-4" style="color: var(--orange)">
<Trophy size={48} strokeWidth={1.5} />
</div>
<h3 class="text-2xl font-bold mb-3">Töökogemus</h3>
<p class="text-gray-600">
Aktiivsed liikmed saavad võimaluse teenida oma esimest töökogemust ja palka meie projektides.
</p>
</Card>
</Grid>
</Section>
<!-- What You'll Do Section -->
<Section background="neutral">
<Grid gap="var(--space-5)">
<Card variant="glass">
<h2 class="text-4xl font-bold mb-4">Mida saad teha?</h2>
<div class="space-y-4 text-lg">
<p>
<strong>Tarkvara arendus:</strong> Töötad reaalsetel projektidel kasutades kaasaegseid tehnoloogiaid.
Õpid koodi kirjutamist meeskonnas, versioonihaldustööriistade kasutamist ja tarkvara kvaliteedi tagamist.
</p>
<p>
<strong>Helpdesk:</strong> Aita tudengeid ja õppejõude IT-probleemide lahendamisel.
Arenda oma tehnilisi oskuseid ja õpi suhtlemist klientidega.
</p>
<p>
<strong>Projektijuhtimine:</strong> Võta vastutus projekti koordineerimise eest.
Õpi planeerimist, meeskonna juhtimist ja suhtlemist klientidega.
</p>
<p>
<strong>Turundus ja disain:</strong> Aita meie projektide ja ürituste turundamisel.
Loo sisu sotsiaalmeedias, disaini materjale ja jälgi tulemusi.
</p>
</div>
</Card>
<Card variant="glass">
<h2 class="text-4xl font-bold mb-4">Kuidas liituda?</h2>
<div class="space-y-4 text-lg">
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">1.</div>
<div>
<strong>Võta ühendust</strong><br/>
Kirjuta meile e-posti aadressil <a href="mailto:lapikud@lapikud.ee" class="text-orange-500 hover:underline">lapikud@lapikud.ee</a>
või tule meie kontorisse Akadeemia tee 5.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">2.</div>
<div>
<strong>Tutvumine</strong><br/>
Kohtu meie meeskonnaga, tutvusta ennast ja räägi oma huvidest. Näitame sulle meie projekte ja võimalusi.
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-3xl font-bold" style="color: var(--orange)">3.</div>
<div>
<strong>Alusta tööd</strong><br/>
Leia endale sobiv projekt või meeskond ja hakka tegutsema! Meie mentorid aitavad sind iga sammu juures.
</div>
</div>
</div>
</Card>
</Grid>
</Section>
<!-- Members Section -->
<Section>
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Meie liikmed</h2>
<p class="text-xl text-gray-600">Suurepärased inimesed, kes moodustavad meie kogukonna</p>
</div>
{#if members.senior && members.senior.length > 0}
<div class="mb-12">
<h3 class="text-3xl font-bold mb-6 text-center">Senior liikmed</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{#each members.senior as member}
<div class="bg-white rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
<p class="text-center font-medium">{member}</p>
</div>
{/each}
</div>
</div>
{/if}
{#if members.junior && members.junior.length > 0}
<div>
<h3 class="text-3xl font-bold mb-6 text-center">Junior liikmed</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{#each members.junior as member}
<div class="bg-white rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
<p class="text-center font-medium">{member}</p>
</div>
{/each}
</div>
</div>
{/if}
</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>

View File

@@ -2,7 +2,72 @@
// examples: '/page', '/page/:id'
import Home from './Home.svelte'
import Student from './Student.svelte'
import Mentors from './Mentors.svelte'
import Helpdesk from './Helpdesk.svelte'
import OurWork from './OurWork.svelte'
import AboutUs from './AboutUs.svelte'
import Contact from './Contact.svelte'
// Route mapping for language switching
export const routeMap = {
// Home
'/': { est: '/', en: '/' },
// Student/Tudengile
'/tudengile': { est: '/tudengile', en: '/student' },
'/student': { est: '/tudengile', en: '/student' },
// Mentors/Mentorid
'/mentorid': { est: '/mentorid', en: '/mentors' },
'/mentors': { est: '/mentorid', en: '/mentors' },
// Helpdesk
'/helpdesk': { est: '/helpdesk', en: '/helpdesk' },
// OurWork/Ettevõttele
'/ettevottele': { est: '/ettevottele', en: '/ourwork' },
'/ourwork': { est: '/ettevottele', en: '/ourwork' },
// AboutUs/Lapikutest
'/lapikutest': { est: '/lapikutest', en: '/aboutus' },
'/aboutus': { est: '/lapikutest', en: '/aboutus' },
// Contact/Kontakt
'/kontakt': { est: '/kontakt', en: '/contact' },
'/contact': { est: '/kontakt', en: '/contact' },
};
// Detect language from route
export function getLanguageFromRoute(path) {
const estRoutes = ['/tudengile', '/mentorid', '/ettevottele', '/lapikutest', '/kontakt'];
const enRoutes = ['/student', '/mentors', '/ourwork', '/aboutus', '/contact'];
if (estRoutes.includes(path)) return 'est';
if (enRoutes.includes(path)) return 'en';
// Default to Estonian
return 'est';
}
// Get translated route
export function getTranslatedRoute(currentPath, targetLang) {
const mapping = routeMap[currentPath];
if (!mapping) return currentPath;
return mapping[targetLang] || currentPath;
}
export const routes = {
'/': Home,
'/tudengile': Student,
'/student': Student,
'/mentorid': Mentors,
'/mentors': Mentors,
'/helpdesk': Helpdesk,
'/ettevottele': OurWork,
'/ourwork': OurWork,
'/lapikutest': AboutUs,
'/aboutus': AboutUs,
'/kontakt': Contact,
'/contact': Contact,
}