feat: add Vue 3 personal site for valitovgaziz in main_dc/my_site

This commit is contained in:
valitovgaziz
2026-06-10 10:47:59 +05:00
parent 2084acb078
commit 6ba49127aa
13 changed files with 2909 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Valitov Gaziz | Технологический предприниматель</title>
<meta name="description" content="Валитов Газиз — технологический предприниматель и fullstack-разработчик. Создатель Yalarba.ru, EasySite102.ru." />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
+1395
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
{
"name": "my_site",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.22",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"vite": "^7.1.7"
}
}
+52
View File
@@ -0,0 +1,52 @@
<template>
<div id="app" :class="{ 'menu-open': mobileMenuOpen }">
<TheHeader @toggle-theme="toggleTheme" @toggle-menu="toggleMenu" :theme="theme" :menu-open="mobileMenuOpen" />
<main class="main-content">
<router-view />
</main>
<TheFooter />
</div>
</template>
<script>
import TheHeader from './components/TheHeader.vue'
import TheFooter from './components/TheFooter.vue'
export default {
name: 'App',
components: {
TheHeader,
TheFooter,
},
data() {
return {
theme: 'dark',
mobileMenuOpen: false,
}
},
created() {
const saved = localStorage.getItem('theme')
if (saved) {
this.theme = saved
}
document.documentElement.setAttribute('data-theme', this.theme)
},
methods: {
toggleTheme() {
this.theme = this.theme === 'dark' ? 'light' : 'dark'
localStorage.setItem('theme', this.theme)
document.documentElement.setAttribute('data-theme', this.theme)
},
toggleMenu() {
this.mobileMenuOpen = !this.mobileMenuOpen
},
},
}
</script>
<style>
.main-content {
min-height: calc(100vh - 160px);
padding-top: 70px;
}
</style>
+174
View File
@@ -0,0 +1,174 @@
:root {
--bg: #ffffff;
--bg-secondary: #f5f7fa;
--text: #1a1a2e;
--text-secondary: #4a4a6a;
--accent: #2563eb;
--accent-hover: #1d4ed8;
--card-bg: #ffffff;
--border: #e2e8f0;
--header-bg: rgba(255, 255, 255, 0.9);
--skill-bg: #eef2ff;
--tag-bg: #e0e7ff;
--tag-text: #3730a3;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.1);
--gradient-hero: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--max-width: 1100px;
--radius: 12px;
}
[data-theme="dark"] {
--bg: #0f172a;
--bg-secondary: #1e293b;
--text: #e2e8f0;
--text-secondary: #94a3b8;
--accent: #60a5fa;
--accent-hover: #3b82f6;
--card-bg: #1e293b;
--border: #334155;
--header-bg: rgba(15, 23, 42, 0.9);
--skill-bg: #1e293b;
--tag-bg: #1e293b;
--tag-text: #93c5fd;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.4);
--gradient-hero: linear-gradient(135deg, #1e3a5f 0%, #2d1b69 100%);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background-color: var(--bg);
color: var(--text);
line-height: 1.6;
transition: background-color 0.3s, color 0.3s;
}
a {
color: var(--accent);
text-decoration: none;
transition: color 0.2s;
}
a:hover {
color: var(--accent-hover);
}
img {
max-width: 100%;
height: auto;
}
.container {
max-width: var(--max-width);
margin: 0 auto;
padding: 0 20px;
}
.section {
padding: 80px 0;
}
.section:nth-child(even) {
background-color: var(--bg-secondary);
}
.section-title {
font-size: 2rem;
font-weight: 700;
margin-bottom: 48px;
text-align: center;
position: relative;
}
.section-title::after {
content: '';
display: block;
width: 60px;
height: 4px;
background: var(--accent);
margin: 12px auto 0;
border-radius: 2px;
}
.grid {
display: grid;
gap: 24px;
}
.card {
background: var(--card-bg);
border-radius: var(--radius);
padding: 24px;
box-shadow: var(--shadow);
border: 1px solid var(--border);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.btn {
display: inline-block;
padding: 12px 28px;
border-radius: 8px;
font-weight: 600;
font-size: 0.95rem;
cursor: pointer;
transition: all 0.2s;
border: none;
}
.btn-primary {
background: var(--accent);
color: #fff;
}
.btn-primary:hover {
background: var(--accent-hover);
color: #fff;
}
.btn-outline {
background: transparent;
border: 2px solid var(--accent);
color: var(--accent);
}
.btn-outline:hover {
background: var(--accent);
color: #fff;
}
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
@media (max-width: 768px) {
.section {
padding: 48px 0;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 32px;
}
}
@@ -0,0 +1,71 @@
<template>
<footer class="footer">
<div class="container footer-content">
<div class="footer-info">
<p>Уфа · Ufa · Өфө</p>
<p>&copy; 2025 Valitov Gaziz</p>
</div>
<div class="footer-links">
<a href="https://t.me/valitovgaziz" target="_blank" rel="noopener noreferrer" class="footer-link">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>
</a>
<a href="https://vk.com/valitovgaziz" target="_blank" rel="noopener noreferrer" class="footer-link">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M15.684 0H8.316C2.879 0 0 2.879 0 8.316v7.368C0 21.121 2.879 24 8.316 24h7.368C21.121 24 24 21.121 24 15.684V8.316C24 2.879 21.121 0 15.684 0zm3.6 16.535h-1.651c-.849 0-1.08-.595-1.728-1.289-.566-.604-1.05-1.097-1.89-1.097-.97 0-1.4.513-1.4 1.316v1.07c0 .456-.178.72-1.076.72-1.583 0-3.308-1.088-4.33-2.539-1.443-1.875-1.83-3.36-1.83-3.636 0-.177.151-.343.448-.343h1.652c.468 0 .64.227.82.735.633 1.937 1.699 3.633 2.345 3.633.22 0 .306-.11.306-.576v-2.22c-.111-1.048-.672-1.124-.672-1.504 0-.242.168-.44.45-.44h2.578c.347 0 .46.196.46.573v2.176c0 .346.149.44.254.44.224 0 .38-.094.598-.317.604-.7 1.118-1.847 1.118-1.847.088-.215.224-.423.513-.423h1.651c.493 0 .597.252.493.619-.307.953-1.577 2.764-1.577 2.764-.168.257-.224.381 0 .638.168.224.739.672 1.126 1.09.392.423.672.747.784.985.196.44-.056.735-.54.735z"/></svg>
</a>
<a href="mailto:valitovgaziz@yandex.ru" class="footer-link">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 4L12 13L2 4"/></svg>
</a>
</div>
</div>
</footer>
</template>
<script>
export default {
name: 'TheFooter',
}
</script>
<style scoped>
.footer {
background: var(--bg-secondary);
border-top: 1px solid var(--border);
padding: 32px 0;
margin-top: 80px;
}
.footer-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.footer-info p {
color: var(--text-secondary);
font-size: 0.9rem;
}
.footer-links {
display: flex;
gap: 16px;
}
.footer-link {
color: var(--text-secondary);
transition: color 0.2s;
display: flex;
align-items: center;
}
.footer-link:hover {
color: var(--accent);
}
@media (max-width: 768px) {
.footer-content {
flex-direction: column;
gap: 16px;
text-align: center;
}
}
</style>
@@ -0,0 +1,178 @@
<template>
<header class="header">
<div class="container header-container">
<router-link to="/" class="logo">Valitov<span>Gaziz</span></router-link>
<nav class="nav" :class="{ 'nav-open': menuOpen }">
<router-link to="/" class="nav-link" @click="$emit('toggle-menu')">Главная</router-link>
<a href="#about" class="nav-link" @click="closeMenu">Обо мне</a>
<a href="#projects" class="nav-link" @click="closeMenu">Проекты</a>
<a href="#experience" class="nav-link" @click="closeMenu">Опыт</a>
<a href="#skills" class="nav-link" @click="closeMenu">Навыки</a>
<a href="#contact" class="nav-link" @click="closeMenu">Контакты</a>
<router-link to="/blog" class="nav-link" @click="$emit('toggle-menu')">Блог</router-link>
</nav>
<div class="header-actions">
<button class="theme-toggle" @click="$emit('toggle-theme')" :aria-label="theme === 'dark' ? 'Светлая тема' : 'Тёмная тема'">
<svg v-if="theme === 'dark'" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
<button class="burger" @click="$emit('toggle-menu')" :aria-label="menuOpen ? 'Закрыть меню' : 'Открыть меню'">
<span></span><span></span><span></span>
</button>
</div>
</div>
</header>
</template>
<script>
export default {
name: 'TheHeader',
props: {
theme: { type: String, required: true },
menuOpen: { type: Boolean, default: false },
},
emits: ['toggle-theme', 'toggle-menu'],
methods: {
closeMenu() {
if (this.menuOpen) {
this.$emit('toggle-menu')
}
},
},
}
</script>
<style scoped>
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: var(--header-bg);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border);
transition: background 0.3s;
}
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
height: 70px;
}
.logo {
font-size: 1.4rem;
font-weight: 700;
color: var(--text);
letter-spacing: -0.5px;
}
.logo span {
color: var(--accent);
}
.nav {
display: flex;
gap: 8px;
}
.nav-link {
color: var(--text-secondary);
font-size: 0.9rem;
font-weight: 500;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.2s;
}
.nav-link:hover,
.router-link-exact-active {
color: var(--accent);
background: var(--skill-bg);
}
.header-actions {
display: flex;
align-items: center;
gap: 8px;
}
.theme-toggle {
background: none;
border: 1px solid var(--border);
color: var(--text);
cursor: pointer;
padding: 8px;
border-radius: 8px;
display: flex;
align-items: center;
transition: all 0.2s;
}
.theme-toggle:hover {
border-color: var(--accent);
color: var(--accent);
}
.burger {
display: none;
flex-direction: column;
gap: 5px;
background: none;
border: none;
cursor: pointer;
padding: 4px;
}
.burger span {
display: block;
width: 24px;
height: 2px;
background: var(--text);
border-radius: 2px;
transition: all 0.3s;
}
.menu-open .burger span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.menu-open .burger span:nth-child(2) {
opacity: 0;
}
.menu-open .burger span:nth-child(3) {
transform: rotate(-45deg) translate(5px, -5px);
}
@media (max-width: 768px) {
.nav {
position: fixed;
top: 70px;
left: 0;
right: 0;
background: var(--bg);
flex-direction: column;
padding: 20px;
gap: 4px;
transform: translateY(-100%);
opacity: 0;
pointer-events: none;
transition: all 0.3s;
border-bottom: 1px solid var(--border);
z-index: 999;
}
.nav-open {
transform: translateY(0);
opacity: 1;
pointer-events: all;
}
.burger {
display: flex;
}
}
</style>
+11
View File
@@ -0,0 +1,11 @@
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
+20
View File
@@ -0,0 +1,20 @@
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/blog',
name: 'Blog',
component: () => import('../views/Blog.vue'),
},
],
})
export default router
+181
View File
@@ -0,0 +1,181 @@
<template>
<div class="blog-page">
<section class="blog-hero">
<div class="container">
<h1 class="blog-title">Блог</h1>
<p class="blog-subtitle">Мысли, идеи и заметки о разработке, технологиях и предпринимательстве</p>
</div>
</section>
<section class="section">
<div class="container">
<div class="blog-list">
<article v-for="(post, index) in posts" :key="index" class="blog-post card" :class="{ 'fade-in': true, 'visible': true }">
<div class="post-meta">
<span class="post-date">{{ post.date }}</span>
<span class="post-read-time">{{ post.readTime }}</span>
</div>
<h2 class="post-title">{{ post.title }}</h2>
<p class="post-excerpt">{{ post.excerpt }}</p>
<blockquote v-if="post.quote" class="post-quote">
{{ post.quote }}
</blockquote>
<p v-if="post.additional" class="post-excerpt">{{ post.additional }}</p>
<div class="post-tags">
<span v-for="(tag, tIndex) in post.tags" :key="tIndex" class="post-tag">{{ tag }}</span>
</div>
</article>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
name: 'BlogView',
data() {
return {
posts: [
{
date: '20 марта 2024',
readTime: '6 мин чтения',
title: 'EasySite & YalArba: текущее состояние и планы развития',
excerpt: 'EasySite (B2B) — конструктор сайтов для отелей, санаториев, ресторанов. YalArba (B2C) — агрегатор для туристов с поиском, отзывами и системой бронирования.',
additional: 'Уже работают: JWT-авторизация, Docker-инфраструктура, SSL (Let\'s Encrypt), базовая аналитика. В бете: easysite102.ru и yalarba.ru.',
tags: ['EasySite', 'YalArba', 'Туризм', 'Стартап'],
},
{
date: '25 марта 2024',
readTime: '8 мин чтения',
title: 'Почему я создаю YalArba: история и миссия',
excerpt: 'История началась в 2017 году — работа на заводе и учёба не могли затмить желания создать что-то полезное. Не найдя бесплатных маршрутов для путешествий онлайн, решил сделать решение сам.',
additional: 'Большинство сервисов будут бесплатными — хочется предоставить доступные альтернативы для всех. Бизнес-модель строится на ценности от количества пользователей.',
quote: 'Технологии должны решать реальные проблемы людей, а не создавать новые.',
tags: ['История', 'Миссия', 'Социальный проект', 'Туризм'],
},
{
date: '15 марта 2024',
readTime: '5 мин чтения',
title: 'Новый этап развития Yalarba.ru',
excerpt: 'Завершён переход на новую архитектуру. Реализованы: обновлённый интерфейс поиска маршрутов, интеграция картографических сервисов, улучшенная система рекомендаций, подготовка мобильного приложения.',
tags: ['Yalarba', 'TravelTech', 'Разработка'],
},
{
date: '10 марта 2024',
readTime: '7 мин чтения',
title: 'Переход с Vue 2 на Vue 3: опыт и выводы',
excerpt: 'Ключевые преимущества: Composition API, улучшенная производительность, поддержка TypeScript, меньший размер бандла. Миграция прошла гладко, но потребовала внимания к деталям.',
tags: ['Vue3', 'Фронтенд', 'JavaScript'],
},
{
date: '5 марта 2024',
readTime: '4 мин чтения',
title: 'О важности сообщества в разработке',
excerpt: 'Профессиональный рост через сообщество: обратная связь, совместное обучение, поддержка и вдохновение. Нетворкинг и обмен опытом — ключ к развитию.',
tags: ['Сообщество', 'Разработка', 'IT'],
},
],
}
},
}
</script>
<style scoped>
.blog-hero {
padding: 80px 0 40px;
text-align: center;
background: var(--bg-secondary);
}
.blog-title {
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 12px;
}
.blog-subtitle {
font-size: 1.1rem;
color: var(--text-secondary);
max-width: 600px;
margin: 0 auto;
}
.blog-list {
max-width: 700px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 32px;
}
.blog-post {
text-align: left;
}
.post-meta {
display: flex;
gap: 16px;
margin-bottom: 12px;
font-size: 0.85rem;
}
.post-date {
color: var(--accent);
font-weight: 600;
}
.post-read-time {
color: var(--text-secondary);
}
.post-title {
font-size: 1.4rem;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.3;
}
.post-excerpt {
font-size: 0.95rem;
color: var(--text-secondary);
line-height: 1.7;
margin-bottom: 12px;
}
.post-quote {
border-left: 3px solid var(--accent);
padding: 12px 20px;
margin: 16px 0;
font-style: italic;
color: var(--text-secondary);
background: var(--bg-secondary);
border-radius: 0 8px 8px 0;
}
.post-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 16px;
}
.post-tag {
font-size: 0.8rem;
padding: 4px 12px;
border-radius: 6px;
background: var(--tag-bg);
color: var(--tag-text);
font-weight: 500;
}
@media (max-width: 768px) {
.blog-title {
font-size: 1.8rem;
}
.post-title {
font-size: 1.2rem;
}
}
</style>
+767
View File
@@ -0,0 +1,767 @@
<template>
<div class="home">
<section class="hero">
<div class="hero-bg"></div>
<div class="container hero-content">
<div class="hero-text">
<p class="hero-greeting">Привет, я</p>
<h1 class="hero-name">Валитов Газиз</h1>
<p class="hero-title">Технологический предприниматель &amp; Fullstack-разработчик</p>
<p class="hero-desc">
Создаю цифровые продукты, которые меняют жизнь людей к лучшему.
Основатель проектов Yalarba.ru и EasySite102.ru.
</p>
<div class="hero-buttons">
<a href="https://t.me/valitovgaziz" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
Написать в Telegram
</a>
<router-link to="/blog" class="btn btn-outline">
Читать блог
</router-link>
</div>
<div class="hero-social">
<a href="https://t.me/valitovgaziz" target="_blank" rel="noopener noreferrer" class="social-link" title="Telegram">@valitovgaziz</a>
<a href="https://vk.com/valitovgaziz" target="_blank" rel="noopener noreferrer" class="social-link" title="VK">vk.com/valitovgaziz</a>
<a href="mailto:valitovgaziz@yandex.ru" class="social-link" title="Email">valitovgaziz@yandex.ru</a>
</div>
</div>
<div class="hero-image">
<div class="hero-photo">
<div class="photo-placeholder">
<svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
</div>
</div>
</div>
</div>
</section>
<section id="about" class="section">
<div class="container">
<h2 class="section-title">Обо мне</h2>
<div class="about-content">
<div class="about-text">
<p class="about-paragraph">
Родился в городе Кумертау в 1985 году. Окончил УГАТУ (Уфимский государственный авиационный технический университет), прошёл службу в армии, работал на производстве.
</p>
<p class="about-paragraph">
С 2015 года в IT прошёл путь от техника до основателя собственного технологического проекта. За плечами опыт работы с Go, Vue 3, Nuxt.js, PostgreSQL, Docker и другими современными технологиями.
</p>
<p class="about-paragraph">
Моя миссия создавать продукты, которые приносят реальную пользу людям и делают туризм в Башкортостане доступнее и удобнее.
</p>
</div>
<div class="about-highlights">
<div class="highlight-card">
<div class="highlight-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
</div>
<h4>Техническое видение</h4>
<p>Создаю масштабируемую архитектуру, выбираю правильные инструменты под задачи</p>
</div>
<div class="highlight-card">
<div class="highlight-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
</div>
<h4>Бизнес-ориентация</h4>
<p>Фокус на пользовательской ценности и устойчивых бизнес-моделях</p>
</div>
<div class="highlight-card">
<div class="highlight-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
</div>
<h4>Практический подход</h4>
<p>От прототипа до продукта быстрое тестирование гипотез и итеративное развитие</p>
</div>
<div class="highlight-card">
<div class="highlight-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
</div>
<h4>Мотивация</h4>
<p>Создание проекта, который приносит пользу многим людям</p>
</div>
</div>
</div>
</div>
</section>
<section id="projects" class="section">
<div class="container">
<h2 class="section-title">Проекты</h2>
<div class="projects-grid">
<div class="project-card card">
<div class="project-header">
<h3 class="project-name">Yalarba.ru</h3>
<span class="project-status">Активный</span>
</div>
<p class="project-desc">
Туристическая платформа для Республики Башкортостан. Помогает путешественникам открывать новые места, строить маршруты и планировать поездки.
</p>
<div class="project-tech">
<span class="tech-tag">Go</span>
<span class="tech-tag">Nuxt.js 4</span>
<span class="tech-tag">PostgreSQL</span>
<span class="tech-tag">Docker</span>
</div>
</div>
<div class="project-card card">
<div class="project-header">
<h3 class="project-name">EasySite102.ru</h3>
<span class="project-status">Бета</span>
</div>
<p class="project-desc">
Конструктор сайтов для бизнеса в сфере туризма: отелей, санаториев, ресторанов. Часть экосистемы YalArba.
</p>
<div class="project-tech">
<span class="tech-tag">Vue 3</span>
<span class="tech-tag">Nuxt.js</span>
<span class="tech-tag">Go</span>
<span class="tech-tag">PostgreSQL</span>
</div>
</div>
<div class="project-card card">
<div class="project-header">
<h3 class="project-name">BegushiyBashkir.ru</h3>
<span class="project-status">Активный</span>
</div>
<p class="project-desc">
Беговой клуб. Сайт для бегового сообщества, основанного другом и партнёром Аминевым Загиром.
</p>
<div class="project-tech">
<span class="tech-tag">Vue 3</span>
<span class="tech-tag">Go</span>
<span class="tech-tag">PostgreSQL</span>
<span class="tech-tag">Docker</span>
</div>
</div>
</div>
</div>
</section>
<section id="experience" class="section">
<div class="container">
<h2 class="section-title">Опыт работы</h2>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content card">
<div class="timeline-period">2020 настоящее время</div>
<h3 class="timeline-title">Основатель и Tech Lead</h3>
<p class="timeline-company">Yalarba.ru</p>
<ul class="timeline-duties">
<li>Микросервисы на Go + Nuxt.js 4</li>
<li>Проектирование и оптимизация PostgreSQL</li>
<li>Docker-инфраструктура, управление продуктом</li>
</ul>
</div>
</div>
<div class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content card">
<div class="timeline-period">2017 настоящее время</div>
<h3 class="timeline-title">Fullstack-разработчик (Контракты)</h3>
<p class="timeline-company">Фриланс / Проектная работа</p>
<ul class="timeline-duties">
<li>REST API на Go (GORM, Chi)</li>
<li>Фронтенд на Nuxt.js 4 / Vue 3</li>
<li>Посадочные страницы, интеграции</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section id="education" class="section">
<div class="container">
<h2 class="section-title">Образование</h2>
<div class="education-grid">
<div class="edu-card card">
<div class="edu-year">2025 н.в.</div>
<h4>МТИ Московский технологический институт</h4>
<p>Разработка программного обеспечения</p>
</div>
<div class="edu-card card">
<div class="edu-year">2021</div>
<h4>Университет Иннополис</h4>
<p>Java Enterprise Developer</p>
</div>
<div class="edu-card card">
<div class="edu-year">2016 2020</div>
<h4>Уфимский колледж статистики и информатики</h4>
<p>Техник по информационным системам</p>
</div>
<div class="edu-card card">
<div class="edu-year">2002 2005</div>
<h4>УГАТУ</h4>
<p>Технология сварочного производства</p>
</div>
</div>
</div>
</section>
<section id="skills" class="section">
<div class="container">
<h2 class="section-title">Навыки</h2>
<div class="skills-grid">
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">Golang</span>
<span class="skill-level">Продвинутый</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 90%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">JavaScript</span>
<span class="skill-level">Продвинутый</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 85%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">Vue 3</span>
<span class="skill-level">Средний</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 70%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">Nuxt.js</span>
<span class="skill-level">Средний</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 65%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">PostgreSQL</span>
<span class="skill-level">Средний</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 70%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">Docker</span>
<span class="skill-level">Средний</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 70%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">Java</span>
<span class="skill-level">Начальный</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 40%"></div></div>
</div>
<div class="skill-card card">
<div class="skill-header">
<span class="skill-name">Spring Framework</span>
<span class="skill-level">Начальный</span>
</div>
<div class="skill-bar"><div class="skill-fill" style="width: 35%"></div></div>
</div>
</div>
</div>
</section>
<section id="contact" class="section">
<div class="container">
<h2 class="section-title">Контакты</h2>
<div class="contact-content">
<p class="contact-text">Открыт к общению, сотрудничеству и новым проектам. Пишите!</p>
<div class="contact-links">
<a href="https://t.me/valitovgaziz" target="_blank" rel="noopener noreferrer" class="contact-item">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>
<span>@valitovgaziz</span>
</a>
<a href="mailto:valitovgaziz@yandex.ru" class="contact-item">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 4L12 13L2 4"/></svg>
<span>valitovgaziz@yandex.ru</span>
</a>
<a href="tel:+79625439343" class="contact-item">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
<span>+7 (962) 543-93-43</span>
</a>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
name: 'HomeView',
mounted() {
this.setupScrollObserver()
},
beforeUnmount() {
if (this.observer) {
this.observer.disconnect()
}
},
methods: {
setupScrollObserver() {
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('visible')
}
})
},
{ threshold: 0.1 }
)
document.querySelectorAll('.fade-in').forEach((el) => {
this.observer.observe(el)
})
},
},
}
</script>
<style scoped>
.hero {
position: relative;
min-height: 90vh;
display: flex;
align-items: center;
overflow: hidden;
}
.hero-bg {
position: absolute;
inset: 0;
background: var(--gradient-hero);
opacity: 0.05;
z-index: 0;
}
.hero-content {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
align-items: center;
padding: 40px 0;
}
.hero-greeting {
font-size: 1.1rem;
color: var(--accent);
font-weight: 600;
margin-bottom: 8px;
}
.hero-name {
font-size: 3.5rem;
font-weight: 800;
line-height: 1.1;
margin-bottom: 16px;
letter-spacing: -1px;
}
.hero-title {
font-size: 1.2rem;
color: var(--text-secondary);
margin-bottom: 20px;
font-weight: 500;
}
.hero-desc {
font-size: 1rem;
color: var(--text-secondary);
line-height: 1.7;
margin-bottom: 32px;
}
.hero-buttons {
display: flex;
gap: 12px;
margin-bottom: 32px;
}
.hero-social {
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.social-link {
font-size: 0.85rem;
color: var(--text-secondary);
font-weight: 500;
}
.social-link:hover {
color: var(--accent);
}
.hero-image {
display: flex;
justify-content: center;
}
.hero-photo {
width: 320px;
height: 320px;
border-radius: 50%;
overflow: hidden;
border: 4px solid var(--accent);
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-secondary);
}
.photo-placeholder {
color: var(--text-secondary);
opacity: 0.4;
display: flex;
align-items: center;
justify-content: center;
}
.about-content {
display: grid;
gap: 48px;
}
.about-paragraph {
font-size: 1.1rem;
line-height: 1.8;
color: var(--text-secondary);
margin-bottom: 16px;
}
.about-highlights {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.highlight-card {
text-align: center;
padding: 32px 20px;
border-radius: var(--radius);
background: var(--card-bg);
border: 1px solid var(--border);
transition: transform 0.2s;
}
.highlight-card:hover {
transform: translateY(-4px);
}
.highlight-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 56px;
height: 56px;
border-radius: 16px;
background: var(--skill-bg);
color: var(--accent);
margin-bottom: 16px;
}
.highlight-card h4 {
font-size: 1rem;
margin-bottom: 8px;
}
.highlight-card p {
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.5;
}
.projects-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.project-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
}
.project-name {
font-size: 1.2rem;
font-weight: 700;
}
.project-status {
font-size: 0.75rem;
padding: 4px 10px;
border-radius: 20px;
background: var(--tag-bg);
color: var(--tag-text);
font-weight: 600;
white-space: nowrap;
}
.project-desc {
font-size: 0.9rem;
color: var(--text-secondary);
line-height: 1.6;
margin-bottom: 16px;
}
.project-tech {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tech-tag {
font-size: 0.8rem;
padding: 4px 12px;
border-radius: 6px;
background: var(--skill-bg);
color: var(--accent);
font-weight: 500;
}
.timeline {
position: relative;
max-width: 700px;
margin: 0 auto;
}
.timeline::before {
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
width: 2px;
background: var(--border);
}
.timeline-item {
position: relative;
padding-left: 56px;
margin-bottom: 32px;
}
.timeline-dot {
position: absolute;
left: 12px;
top: 24px;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--accent);
border: 4px solid var(--bg);
z-index: 1;
}
.timeline-period {
font-size: 0.85rem;
color: var(--accent);
font-weight: 600;
margin-bottom: 4px;
}
.timeline-title {
font-size: 1.15rem;
font-weight: 700;
margin-bottom: 4px;
}
.timeline-company {
font-size: 0.9rem;
color: var(--text-secondary);
margin-bottom: 12px;
}
.timeline-duties {
list-style: none;
padding: 0;
}
.timeline-duties li {
position: relative;
padding-left: 20px;
margin-bottom: 6px;
font-size: 0.9rem;
color: var(--text-secondary);
}
.timeline-duties li::before {
content: '—';
position: absolute;
left: 0;
color: var(--accent);
}
.education-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
max-width: 700px;
margin: 0 auto;
}
.edu-card {
text-align: center;
}
.edu-year {
font-size: 0.85rem;
color: var(--accent);
font-weight: 600;
margin-bottom: 8px;
}
.edu-card h4 {
font-size: 0.95rem;
margin-bottom: 6px;
}
.edu-card p {
font-size: 0.85rem;
color: var(--text-secondary);
}
.skills-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
max-width: 700px;
margin: 0 auto;
}
.skill-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.skill-name {
font-weight: 600;
font-size: 0.95rem;
}
.skill-level {
font-size: 0.8rem;
color: var(--text-secondary);
}
.skill-bar {
height: 8px;
background: var(--bg-secondary);
border-radius: 4px;
overflow: hidden;
}
.skill-fill {
height: 100%;
background: var(--gradient-hero);
border-radius: 4px;
transition: width 1s ease;
}
.contact-content {
text-align: center;
max-width: 500px;
margin: 0 auto;
}
.contact-text {
font-size: 1.1rem;
color: var(--text-secondary);
margin-bottom: 32px;
}
.contact-links {
display: flex;
flex-direction: column;
gap: 16px;
}
.contact-item {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 24px;
border-radius: var(--radius);
background: var(--card-bg);
border: 1px solid var(--border);
color: var(--text);
font-size: 1rem;
transition: all 0.2s;
}
.contact-item:hover {
border-color: var(--accent);
color: var(--accent);
transform: translateX(4px);
}
.contact-item svg {
flex-shrink: 0;
}
@media (max-width: 768px) {
.hero-content {
grid-template-columns: 1fr;
text-align: center;
gap: 32px;
}
.hero-name {
font-size: 2.2rem;
}
.hero-buttons {
justify-content: center;
}
.hero-social {
justify-content: center;
}
.hero-photo {
width: 200px;
height: 200px;
}
.about-highlights {
grid-template-columns: repeat(2, 1fr);
}
.projects-grid {
grid-template-columns: 1fr;
}
.education-grid {
grid-template-columns: 1fr;
}
.skills-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.about-highlights {
grid-template-columns: 1fr;
}
.hero-name {
font-size: 1.8rem;
}
.hero-buttons {
flex-direction: column;
}
}
</style>
+19
View File
@@ -0,0 +1,19 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
host: true,
port: 3002
}
})