first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:04:35 +03:00
commit 763b147cc3
199 changed files with 29356 additions and 0 deletions

802
app/pages/index.vue Normal file
View File

@@ -0,0 +1,802 @@
<script setup lang="ts">
import BackToTopStart from "~/components/home/BackToTopStart.vue";
import MainMenuArea from "~/components/home/MainMenuArea.vue";
import type {
Home,
Setting,
AboutMe,
Service,
ServiceTitle,
Resume,
Category,
MainMenu,
OpenClose,
Bundle
} from "~/types";
import Preloader from "~/components/Preloader.vue";
const token = ref('')
const config = useRuntimeConfig()
const apiUrl = computed(() => config.public.BASE_API_URL || 'http://127.0.0.1:8000')
const {data: openClose} = await useFetch<OpenClose>(`${apiUrl.value}/api/v1/open-close/`)
const {data: bundle} = await useFetch<Bundle>(`${apiUrl.value}/api/v1/bundle/`)
const {data: categories} = await useFetch<Category[]>(`${apiUrl.value}/api/v1/categories/`)
const home = computed(() => bundle.value?.home)
const aboutme = computed(() => bundle.value?.about_me)
const services_title = computed(() => bundle.value?.service_title)
const resume = computed(() => bundle.value?.resume)
const menu = computed(() => bundle.value?.main_menu)
const services = computed(() => bundle.value?.services)
// Setting and Categories are not present in the bundle JSON provided.
// We initialize them as undefined or empty to prevent errors in template.
const setting = computed(() => undefined)
//const categories = computed(() => categories?.value)
// make a typed, safe array for template iteration
const servicesArray = computed(() => (services.value as Service[] | undefined) ?? [])
const categoriesArray = computed(() => (categories.value as Category[] | undefined) ?? [])
// Helper function to get full image URL
const getImageUrl = (imagePath: string | null | undefined) => {
if (!imagePath) return '/assets/images/work/placeholder.jpg'
if (imagePath.startsWith('http')) return imagePath
return `${apiUrl.value}${imagePath}`
}
const selectedCategoryId = ref<number | 'all'>('all')
const normalizeCategoryIds = (
categories?: Array<number | { id: number | null } | null>
) => {
return (categories || [])
.map((entry) => (typeof entry === 'number' ? entry : entry?.id ?? null))
.filter((id): id is number => typeof id === 'number')
}
// Flatten all portfolio items from all categories, deduplicated by id
const portfolioItems = computed(() => {
if (!categories.value) return []
const byId = new Map<number, any>()
for (const category of categories.value) {
for (const portfolio of category.portfolio_categories || []) {
const categoryIds = normalizeCategoryIds(portfolio.categories)
if (category?.id && !categoryIds.includes(category.id)) {
categoryIds.push(category.id)
}
const existing = byId.get(portfolio.id)
if (existing) {
const merged = Array.from(new Set([...existing.categoryIds, ...categoryIds]))
existing.categoryIds = merged
existing.categoryClasses = merged.map((id: number) => `cat-${id}`).join(' ')
continue
}
byId.set(portfolio.id, {
...portfolio,
categoryIds,
categoryClasses: categoryIds.map((id: number) => `cat-${id}`).join(' ')
})
}
}
return Array.from(byId.values())
})
const filteredPortfolioItems = computed(() => {
const selected = selectedCategoryId.value
const items = selected === 'all'
? portfolioItems.value
: portfolioItems.value.filter((item: any) =>
Array.isArray(item.categoryIds) && item.categoryIds.includes(selected)
)
// Anasayfada maksimum 6 portfolio göster
return items.slice(0, 6)
})
/**
* Strip HTML and truncate to `max` characters, returning plain text.
*/
const truncateHtml = (html?: string, max = 120) => {
if (!html) return ''
// remove HTML tags
const text = html.replace(/<\/?[^>]+(>|$)/g, '')
if (text.length <= max) return text
return text.slice(0, max).trim() + '...'
}
// Contact form state
const contactForm = ref({
name: '',
email: '',
subject: '',
message: ''
})
const isSubmitting = ref(false)
const submitMessage = ref('')
const submitError = ref('')
// Contact form submit handler
const handleContactSubmit = async (event: Event) => {
event.preventDefault()
// Reset messages
submitMessage.value = ''
submitError.value = ''
// Validate turnstile token
if (!token.value) {
submitError.value = 'Lütfen captcha doğrulamasını tamamlayın'
return
}
// Validate form fields
if (!contactForm.value.name || !contactForm.value.email ||
!contactForm.value.subject || !contactForm.value.message) {
submitError.value = 'Lütfen tüm alanları doldurun'
return
}
isSubmitting.value = true
try {
const response = await $fetch('/api/contact/post.create', {
method: 'POST',
body: {
name: contactForm.value.name,
email: contactForm.value.email,
subject: contactForm.value.subject,
message: contactForm.value.message
}
})
// @ts-expect-error - API response type
submitMessage.value = response?.message || 'Mesajınız başarıyla gönderildi!'
// Reset form
contactForm.value = {
name: '',
email: '',
subject: '',
message: ''
}
// Reset turnstile
token.value = ''
} catch (error: any) {
console.error('Contact form error:', error)
submitError.value = error?.data?.statusMessage || 'Mesaj gönderilirken bir hata oluştu'
} finally {
isSubmitting.value = false
}
}
// http://212.64.215.243:1337/api/homes?filters[published&populate=*
// http://127.0.0.1:8000/api/v1/services/
</script>
<template>
<div v-if="openClose?.site_active">
<!-- <preloader/>-->
<!--Main-Menu Area Start-->
<main-menu-area :home="home" :setting="setting" :menu="menu"/>
<!--Main-Menu Area Start-->
<!-- Main Content Area Start -->
<div class="main-content">
<div class="main-content-inner">
<!-- About div Start -->
<div class="home-section" id="home">
<ClientOnly>
<div class="particals"></div>
</ClientOnly>
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="home-content">
<div class="home-image">
<img :src="getImageUrl(home?.image)" :alt="home?.name" class="wow zoomIn" data-wow-delay="0.2s">
</div>
<div class="home-main-content">
<h4 class="heading wow fadeInUp" data-wow-delay="0.3s">
{{ home?.name }}
</h4>
<div class="social-info wow fadeInUp" data-wow-delay="0.5s">
<ul>
<li>
<a :href="setting?.facebook">
<i class="fab fa-facebook-f"></i>
</a>
</li>
<li>
<a :href="setting?.x">
<i class="fab fa-twitter"></i>
</a>
</li>
<li>
<a :href="setting?.linkedin">
<i class="fab fa-linkedin-in"></i>
</a>
</li>
<li>
<a :href="setting?.instagram">
<i class="fab fa-instagram"></i>
</a>
</li>
<li>
<a :href="setting?.pinterest">
<i class="fab fa-pinterest-p"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- About div End -->
<!-- About div Start -->
<div v-if="aboutme?.is_active" class="about-section" id="about">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-heading wow fadeInUp" data-wow-delay="0.2s">
<h2 class="title">
{{ aboutme?.title }} <span class="color"></span>
<span class="bg-text">{{ aboutme?.title }}</span>
</h2>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="about">
<div class="row">
<div class="col-lg-4">
<div class="about-image wow fadeInUp" data-wow-delay="0.3s">
<img :src="getImageUrl(aboutme?.image)" :alt="aboutme?.title">
</div>
</div>
<div class="col-lg-8 align-self-center">
<div class="short-description wow fadeInUp">
<div v-if="aboutme?.image_sub" v-html="aboutme.image_sub"></div>
<div class="about-links">
<a v-if="aboutme?.cv" :href="aboutme.cv" download class="mybtn3 mybtn-bg">
<span>CV Indir</span> </a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="about-content wow fadeInUp">
<div class="personal-info">
<ul>
<li>
<span><label>Yaş Günü:</label> {{ aboutme?.birthday }}</span>
</li>
<li>
<span><label>Yaşım:</label> {{ aboutme?.age }}</span>
</li>
<li>
<span><label>Şehir:</label> {{ aboutme?.city }}</span>
</li>
<li>
<span><label>İlgi Alanları:</label> {{ aboutme?.interests }}</span>
</li>
<li>
<span><label>Üniversite:</label> {{ aboutme?.study }}</span>
</li>
<li>
<span><label>Derecelendirme:</label> {{ aboutme?.degree }}</span>
</li>
<li>
<span><label>Website:</label> <a :href="aboutme?.website"
target="_blank">{{ aboutme?.website }}</a></span>
</li>
<li>
<span><label>E-Posta:</label> <a :href="`mailto:${aboutme?.mail}`">{{ aboutme?.mail }}</a></span>
</li>
<li>
<span><label>Telefon:</label> <a :href="`tel:${aboutme?.phone}`">{{ aboutme?.phone }}</a></span>
</li>
<li>
<span><label>X:</label> <a :href="aboutme?.x" target="_blank">{{ aboutme?.x }}</a></span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- About div End -->
<!-- Counter Area Start -->
<div v-if="aboutme?.counter_active" class="counter-area">
<div class="container">
<div class="row">
<div class="col-lg-3 col-md-6">
<div class="single-counter wow fadeInUp">
<img src="/assets/images/icon/005-target.png" alt="">
<ClientOnly>
<div class="counter-wrapper">
<div class="counter">{{ aboutme?.done || 80 }}</div>
<span>k+</span>
</div>
</ClientOnly>
<p class="text">
{{ aboutme?.project_done || 'Project Done' }}
</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="single-counter wow fadeInUp">
<img src="/assets/images/icon/002-medical-mask.png" alt="">
<ClientOnly>
<div class="counter-wrapper">
<div class="counter">{{ aboutme?.user_h || 19 }}</div>
<span>k+</span>
</div>
</ClientOnly>
<p class="text">
{{ aboutme?.hapy_user || 'Happy User' }}
</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="single-counter wow fadeInUp">
<img src="/assets/images/icon/053-success-1.png" alt="">
<ClientOnly>
<div class="counter-wrapper">
<div class="counter">{{ aboutme?.great || 39 }}</div>
<span>k+</span>
</div>
</ClientOnly>
<p class="text">
{{ aboutme?.great_reviews || 'Great Reviews' }}
</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="single-counter wow fadeInUp">
<img src="/assets/images/icon/045-hacker.png" alt="">
<ClientOnly>
<div class="counter-wrapper">
<div class="counter">{{ aboutme?.team || 25 }}</div>
<span>+</span>
</div>
</ClientOnly>
<p class="text">
{{ aboutme?.support_team || 'Support Team' }}
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Counter Area End -->
<!-- My service Start -->
<div v-if="services_title?.is_active" class="service-wrapper" id="service">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-heading wow fadeInUp" data-wow-delay="0.2s">
<h2 class="title">
{{ services_title?.title }} <span class="color"></span>
<span class="bg-text">{{ services_title?.title_sub }}</span>
</h2>
</div>
</div>
</div>
<div class="row justify-content-center">
<template v-for="servis in servicesArray" :key="servis.id">
<div class="col-lg-4 col-md-6">
<NuxtLink :to="`/services/${servis.slug}`" class="single-feature wow fadeInUp">
<img :src="getImageUrl(servis?.image)" :alt="servis?.title">
<ClientOnly>
<div class="content">
<h4 class="title">
{{ truncateHtml(servis?.title,21) }}
</h4>
<p>{{ truncateHtml(servis?.content) }}</p>
</div>
</ClientOnly>
</NuxtLink>
</div>
</template>
</div>
</div>
</div>
<!-- My service End -->
<!-- Resume Area Start -->
<div v-if="resume?.is_active" class="resume-wrapper" id="resume">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-heading wow fadeInUp" data-wow-delay="0.2s">
<h2 class="title">
{{ resume?.title }} <span class="color"></span>
<span class="bg-text">{{ resume?.title_sub }}</span>
</h2>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="resume-box">
<div class="resume-title">
<h4 class="title">
{{resume?.education}}
</h4>
</div>
<div class="education-list">
<div v-for="edu in resume?.edu_resume" :key="edu.id" class="single-education wow fadeInUp">
<div class="year">
<span>{{ edu.between_years }}</span>
</div>
<h4 class="university-name">
{{ edu.title }}
</h4>
<p class="degree">{{ edu.content }}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="resume-box">
<div class="resume-title">
<h4 class="title">
{{resume?.experience}}
</h4>
</div>
<div class="education-list">
<div v-for="exp in resume?.exp_resume" :key="exp.id" class="single-education wow fadeInUp">
<div class="year">
<span>{{ exp.between_years }}</span>
</div>
<h4 class="university-name">
{{ exp.title }}
</h4>
<p class="degree">{{ exp.content }}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="resume-box">
<div class="resume-title">
<h4 class="title">
{{resume?.coding_skills}}
</h4>
</div>
<div class="skill-list">
<div v-for="skill in resume?.skill_resume" :key="skill.id" class="single-skill wow fadeInUp">
<div class="heading">
<h4 class="name">
{{ skill.title }}
</h4>
<h5 class="value">
{{ skill.degree }}%
</h5>
</div>
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
:aria-valuenow="skill.degree" aria-valuemin="0" aria-valuemax="100"
:style="`width: ${skill.degree}%`"></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="resume-box">
<div class="resume-title">
<h4 class="title">
{{resume?.knowledge}}
</h4>
</div>
<div class="knowledge-list wow fadeInUp">
<div v-for="know in resume?.know_resume" :key="know.id" class="single-knowledge">
<p>
{{ know.title }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Resume Area End -->
<!-- My Client Area Area Strat -->
<!-- My Client Area Area End -->
<!-- Portfolio Area Start -->
<div class="project-gallery" id="project-gallery">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-heading wow fadeInUp" data-wow-delay="0.2s">
<h2 class="title">
<span class="color">Portfolio</span>
<span class="bg-text">Portfolio</span>
</h2>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="project-gallery-filter d-flex justify-content-center">
<ul class="project-gallery-menu d-inline-block wow fadeInUp" data-wow-delay="0.3s">
<li
:class="['filter', { active: selectedCategoryId === 'all' }]"
data-filter="all"
@click="selectedCategoryId = 'all'"
>
Tümü
</li>
<li
v-for="category in categoriesArray"
:key="category?.id"
:class="['filter', { active: selectedCategoryId === category?.id }]"
:data-filter="`.cat-${category?.id}`"
@click="selectedCategoryId = category?.id"
>
{{ category?.title }}
</li>
</ul>
</div>
<div class="row project-gallery-item">
<div
v-for="portfolio in filteredPortfolioItems"
:key="portfolio.id"
:class="['col-md-6', 'col-lg-4', 'gallery-item', portfolio.categoryClasses]"
>
<div class="gallery-item-content wow fadeInUp">
<div class="item-thumbnail">
<img :src="getImageUrl(portfolio.image)" alt="">
<div class="content-overlay">
<div class="content">
<div class="links">
<a :href="portfolio.url" target="_blank" rel="noopener noreferrer" class="link">
<i class="fas fa-link" />
</a>
<a
class="img-popup image-preview"
:href="getImageUrl(portfolio.image)"
>
<i class="fas fa-eye" />
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<NuxtLink to="/portfolio" class="mybtn3 mybtn-bg wow fadeInUp"><span>Hepsini Gör</span></NuxtLink>
</div>
</div>
</div>
</div>
<!-- Portfolio Area End -->
<!-- Testimonial Start -->
<!-- Testimonial End -->
<!-- Pricing Area Start -->
<!-- Pricing Area End -->
<!-- Blog Area Start -->
<!-- Blog Area End -->
<!-- Contact Area Start -->
<div class="contact" id="contact">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-heading wow fadeInUp" data-wow-delay="0.2s">
<h2 class="title">
<span class="color">İletişim</span>
<span class="bg-text">İletişim</span>
</h2>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-4 col-md-6">
<!-- Single Info -->
<div class="single-info wow fadeInUp">
<div class="info-icon">
<i class="flaticon-placeholder"></i>
</div>
<div class="info-content">
<h5>Şehir:</h5>
<p>{{ aboutme?.city || '700 Oak Street, Brockton MA 2301' }}</p>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6">
<!-- Single Info -->
<div class="single-info wow fadeInUp">
<div class="info-icon">
<i class="flaticon-telephone"></i>
</div>
<div class="info-content">
<h5>Telefon:</h5>
<p>{{ setting?.phone || '+0123 123 856' }}</p>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6">
<!-- Single Info -->
<div class="single-info wow fadeInUp">
<div class="info-icon">
<i class="flaticon-email-2"></i>
</div>
<div class="info-contentr">
<h5>E-Posta:</h5>
<p>{{ setting?.email || 'demomail@gmail.com' }}</p>
</div>
</div>
</div>
</div>
<div class="row cAndm">
<div class="col-lg-6">
<div class="home-page-form">
<div class="contact-form">
<form id="contact-form" @submit="handleContactSubmit">
<!-- Success/Error Messages -->
<div v-if="submitMessage" class="alert alert-success" role="alert">
{{ submitMessage }}
</div>
<div v-if="submitError" class="alert alert-danger" role="alert">
{{ submitError }}
</div>
<div class="controls">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<input
id="name"
v-model="contactForm.name"
type="text"
name="name"
class="form-control"
placeholder="İsim Soyisim *"
required
:disabled="isSubmitting">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input
id="email"
v-model="contactForm.email"
type="email"
name="email"
class="form-control"
placeholder="E-Posta *"
required
:disabled="isSubmitting">
<div class="help-block with-errors"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<input
id="subject"
v-model="contactForm.subject"
type="text"
name="subject"
class="form-control"
placeholder="Konu *"
required
:disabled="isSubmitting">
<div class="help-block with-errors"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<textarea
id="message"
v-model="contactForm.message"
name="message"
class="form-control"
placeholder="Mesajınız *"
rows="7"
required
:disabled="isSubmitting"></textarea>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="d-flex justify-content-center my-3">
<NuxtTurnstile v-model="token" class="w-100 d-flex justify-content-center" />
</div>
<div class="col-md-12">
<button
type="submit"
class="mybtn3 mybtn-bg"
:disabled="isSubmitting">
<span>{{ isSubmitting ? 'Gönderiliyor...' : 'Gönder' }}</span>
</button>
</div>
</div>
</div>
</form> <!-- End Contact From -->
</div>
</div>
</div>
<div class="col-lg-6">
<div class="google_map_wrapper">
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d48163.351005043995!2d28.738157687720992!3d41.02067366506668!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x14caa4102814e62d%3A0xf17358ce73944614!2zS8O8w6fDvGvDp2VrbWVjZS_EsHN0YW5idWw!5e0!3m2!1str!2str!4v1768471478488!5m2!1str!2str" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</div>
</div>
<!--/.row-->
</div>
<!--/.container-->
</div>
<!-- Contact Area End -->
</div>
</div>
<!-- Main Content Area End -->
<back-to-top-start/>
</div>
</template>
<style scoped>
</style>

41
app/pages/menu-test.vue Normal file
View File

@@ -0,0 +1,41 @@
<template>
<div style="padding: 20px;">
<h1>Menu Debug Page</h1>
<div style="margin: 20px 0; padding: 20px; background: #f0f0f0;">
<h2>Raw Menu Data:</h2>
<pre>{{ JSON.stringify(menu, null, 2) }}</pre>
</div>
<div style="margin: 20px 0; padding: 20px; background: #e0e0e0;">
<h2>Menu Items:</h2>
<ul>
<li>Home: {{ menu?.home }}</li>
<li>About: {{ menu?.about }}</li>
<li>Services: {{ menu?.services }}</li>
<li>Resume: {{ menu?.resume }}</li>
<li>Portfolio: {{ menu?.portfolio }}</li>
<li>Contact: {{ menu?.contact }}</li>
</ul>
</div>
<div style="margin: 20px 0; padding: 20px; background: #d0d0d0;">
<h2>Test Display:</h2>
<p>{{ menu?.home || 'Anasayfa' }}</p>
<p>{{ menu?.about || 'Hakkımda' }}</p>
<p>{{ menu?.services || 'Hizmetlerim' }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import type { MainMenu } from "~/types";
const config = useRuntimeConfig()
const apiUrl = computed(() => config.public.BASE_API_URL || 'http://127.0.0.1:8000')
const {data: menu} = await useFetch<MainMenu>(`${apiUrl.value}/api/v1/main-menu/`)
console.log('Menu Test Page - menu:', menu.value)
</script>

View File

@@ -0,0 +1,223 @@
<script setup lang="ts">
import MainMenuArea from "~/components/home/MainMenuArea.vue";
import type {Home, Setting, Category, PortfolioCategory, MainMenu, OpenClose} from "~/types";
// jQuery ve Magnific Popup için window tipini genişlet
interface WindowWithJQuery extends Window {
$?: any;
}
declare const window: WindowWithJQuery;
const config = useRuntimeConfig()
const apiUrl = computed(() => config.public.BASE_API_URL || 'http://127.0.0.1:8000')
const {data: openClose} = await useFetch<OpenClose>(`${apiUrl.value}/api/v1/open-close/`)
const {data: setting} = await useFetch<Setting>(`${apiUrl.value}/api/v1/settings/`)
const {data: home} = await useFetch<Home>(`${apiUrl.value}/api/v1/home/`)
const {data: menu} = await useFetch<MainMenu>(`${apiUrl.value}/api/v1/main-menu/`)
const {data: categories} = await useFetch<Category[]>(`${apiUrl.value}/api/v1/categories/`)
// Tüm portfolioları toplama
const allPortfolios = computed(() => {
if (!categories.value) return []
const portfolios: PortfolioCategory[] = []
categories.value.forEach(category => {
category.portfolio_categories.forEach(portfolio => {
if (!portfolios.find(p => p.id === portfolio.id)) {
portfolios.push(portfolio)
}
})
})
return portfolios
})
// Seçili kategori
const selectedCategory = ref<number | null>(null)
// Filtrelenmiş portfoliolar
const filteredPortfolios = computed(() => {
if (!selectedCategory.value || !categories.value) return allPortfolios.value
const category = categories.value.find(c => c.id === selectedCategory.value)
return category ? category.portfolio_categories : []
})
// Pagination
const currentPage = ref(1)
const itemsPerPage = 8
const totalPages = computed(() => Math.ceil(filteredPortfolios.value.length / itemsPerPage))
const paginatedPortfolios = computed(() => {
const start = (currentPage.value - 1) * itemsPerPage
const end = start + itemsPerPage
return filteredPortfolios.value.slice(start, end)
})
// Kategori seçme
const selectCategory = (categoryId: number | null) => {
selectedCategory.value = categoryId
currentPage.value = 1
}
// Sayfa değiştirme
const changePage = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page
}
}
// Magnific Popup'ı başlat
const initMagnificPopup = () => {
if (typeof window !== 'undefined' && window.$) {
window.$('.img-popup').magnificPopup({
type: 'image',
gallery: {
enabled: false
}
})
}
}
// Component mount olduğunda ve sayfa değiştiğinde popup'ı yeniden başlat
onMounted(() => {
// jQuery ve Magnific Popup'ın yüklenmesi için biraz bekliyoruz
setTimeout(() => {
initMagnificPopup()
}, 100)
})
// Sayfa veya kategori değiştiğinde popup'ı yeniden başlat
watch([currentPage, selectedCategory], () => {
nextTick(() => {
setTimeout(() => {
initMagnificPopup()
}, 100)
})
})
</script>
<template >
<div v-if="openClose?.site_active">
<main-menu-area :home="home" :setting="setting" :menu="menu"/>
<!-- Main Content Area Start -->
<div class="main-content">
<div class="main-content-inner">
<!-- Portfolios Area Start -->
<section class="portfolios-page">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="row">
<div v-for="portfolio in paginatedPortfolios" :key="portfolio.id" class="col-lg-6 col-md-6">
<div class="gallery-item-content">
<div class="item-thumbnail">
<img :src="`${apiUrl}${portfolio.image}`" :alt="portfolio.url">
<div class="content-overlay">
<div class="content">
<div class="links">
<a :href="portfolio.url" target="_blank" class="link"><i class="fas fa-link"></i></a>
<a class="img-popup image-preview"
:href="`${apiUrl}${portfolio.image}`">
<i class="fas fa-eye"></i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12 d-flex justify-content-center">
<nav v-if="totalPages > 1" aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item" :class="{ disabled: currentPage === 1 }">
<a class="page-link" href="#" @click.prevent="changePage(currentPage - 1)" aria-label="Previous">
<span aria-hidden="true"><i class="fas fa-angle-double-left"></i></span>
</a>
</li>
<li v-for="page in totalPages" :key="page" class="page-item">
<a class="page-link" :class="{ active: currentPage === page }" href="#" @click.prevent="changePage(page)">{{ page }}</a>
</li>
<li class="page-item" :class="{ disabled: currentPage === totalPages }">
<a class="page-link" href="#" @click.prevent="changePage(currentPage + 1)" aria-label="Next">
<span aria-hidden="true"><i class="fas fa-angle-double-right"></i></span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="categori-widget">
<h4 class="title">
Kategoriler
</h4>
<ul class="cat-list">
<li>
<a href="#" @click.prevent="selectCategory(null)" :class="{ active: selectedCategory === null }">
<p>
All
</p>
<span class="count">
{{ allPortfolios.length }}
</span>
</a>
</li>
<li v-for="category in categories" :key="category.id">
<a href="#" @click.prevent="selectCategory(category.id)" :class="{ active: selectedCategory === category.id }">
<p>
{{ category.title }}
</p>
<span class="count">
{{ category.portfolio_categories.length }}
</span>
</a>
</li>
</ul>
</div>
<!-- <div class="latest-post-widget">
<h4 class="title">
Latest Projects
</h4>
<ul class="post-list">
<li v-for="project in latestProjects" :key="project.id">
<div class="post">
<div class="post-img">
<img :src="`${apiUrl}${project.image}`" alt="">
</div>
<div class="post-details">
<a :href="project.url" target="_blank" class="post-title">
{{ project.url }}
</a>
</div>
</div>
</li>
</ul>
</div>-->
</div>
</div>
</div>
</section>
<!-- Portfolios Area End -->
</div>
</div>
<!-- Main Content Area End -->
<!-- Back to Top Start -->
<div class="bottomtotop">
<i class="fas fa-chevron-right"></i>
</div>
<!-- Back to Top End -->
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
<div>SERVİCES</div>
</template>
<style scoped>
</style>