first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:07:47 +03:00
commit 5285a0dd86
522 changed files with 41738 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

75
README.md Normal file
View File

@@ -0,0 +1,75 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

1143
Temp/blog-details.html Normal file

File diff suppressed because it is too large Load Diff

1245
Temp/blog-sidebar-right.html Normal file

File diff suppressed because it is too large Load Diff

8
app/app.vue Normal file
View File

@@ -0,0 +1,8 @@
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<script lang="ts" setup>
</script>

View File

@@ -0,0 +1,121 @@
<script setup lang="ts">
</script>
<template>
<footer class="footer-area padding-top font-f-2">
<div class="container">
<div class="row">
<div class="col-lg-4 col-sm-6">
<div class="single-footer mr50 hadding2">
<div class="site-logo home1-site-logo">
<a href="#"
><img src="/assets/img/logo/header-logo1.svg" alt="" />
</a>
<span class="font-f-2">Were hiring</span>
</div>
<div class="space30"></div>
<p class="font-f-2 font-16 line-height-26">
As a graphic designer, add motion to your skillset. Animate
whatever you create and tell your story in a magical way with
Artboard Studio.
</p>
</div>
</div>
<div class="col-lg col-sm-6 hadding2">
<div class="single-footer">
<h3 class="font-f-1 font-20 weight-700 line-height-20">
Discover
</h3>
<div>
<ul class="font-f-2 font-16 line-height-26 pera-c-1">
<li><a href="#">Learn</a></li>
<li><a href="blog.html">Blog</a></li>
<li><a href="#">Pricing</a></li>
<li><a href="#">Template Maker</a></li>
<li><a href="#">Mockup Generator</a></li>
</ul>
</div>
</div>
</div>
<div class="col-lg col-sm-6 hadding2">
<div class="single-footer">
<h3 class="font-f-2 font-20 weight-700 line-height-20">
Company
</h3>
<div>
<ul class="font-f-2 font-16 line-height-26 pera-c-1">
<li><a href="about.html">About us</a></li>
<li><a href="#">Licensing</a></li>
<li><a href="#">privacy Policy</a></li>
<li><a href="#">Terms of Use</a></li>
<li><a href="#">Refund Policy</a></li>
</ul>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 hadding2">
<div class="single-footer-contact">
<h3 class="font-f-2 font-20 weight-700 line-height-20">
Get in touch
</h3>
<div class="foonter-contact-1">
<div class="foonter-contact-icon-1">
<div class="">
<img src="/assets/img/icons/footer-icon-1.svg" alt="" />
</div>
</div>
<div class="foonter-contact-p">
<a href="tel:+910225850556">USA: +91 02 2585 0556</a>
<a href="tel:+610225850556">UK: +61 02 2585 0556</a>
</div>
</div>
<div class="foonter-contact-1">
<div class="foonter-contact-icon-1">
<div class="">
<img src="/assets/img/icons/footer-icon-2.svg" alt="" />
</div>
</div>
<div class="foonter-contact-p">
<a href="mailto:Contacthelp@Demoui.co"
>Contacthelp@Demoui.co</a
>
<a href="mailto:Info@Demoui.co">Info@Demoui.co</a>
</div>
</div>
</div>
</div>
</div>
<div class="row align-items-center copyright2">
<div class="col-lg-6">
<p class="font-16 weight-400 font-f-2 line-height-16 pera-c-1">
© 2023 Avigo by fleexstudio. All Rights Reserved.
</p>
</div>
<div class="col-lg-6 text-right">
<div class="social social1 comon-footer-icons">
<ul>
<li>
<a href="#"><i class="fa-brands fa-twitter"></i></a>
</li>
<li>
<a href="#"><i class="fa-brands fa-facebook-f"></i></a>
</li>
<li>
<a href="#"><i class="fa-brands fa-instagram"></i></a>
</li>
<li>
<a href="#"><i class="fa-brands fa-github"></i></a>
</li>
</ul>
</div>
</div>
</div>
</div>
</footer>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,113 @@
<script setup lang="ts">
</script>
<template>
<header>
<div class="header-area d-none d-lg-block" id="header">
<div class="container">
<div class="row">
<div class="col-12">
<div class="header-elements">
<div class="site-logo home1-site-logo">
<a href="#"
><img src="/assets/img/logo/header-logo1.svg" alt=""/>
</a>
<span>Were hiring</span>
</div>
<div class="main-menu-ex main-menu-ex1">
<ul>
<li class="has-dropdown"><a href="#">Home <i class="fa-solid fa-angle-down"></i></a>
<ul class="sub-menu">
<li class="has-dropdown has-dropdown1"><a href="index4.html">Multipage<span><i
class="fa-solid fa-angle-right"></i></span></a>
<ul class="sub-menu">
<li><a href="index.html">Real Estate</a></li>
<li><a href="index2.html">Management</a></li>
<li><a href="index3.html">App landing</a></li>
<li><a href="index4.html">Web Hosting </a></li>
<li><a href="index5.html">Consulting </a></li>
</ul>
</li>
<li class="has-dropdown has-dropdown1"><a href="index4.html">Landing Page<span><i
class="fa-solid fa-angle-right"></i></span></a>
<ul class="sub-menu">
<li><a href="single-index1.html">Real Estate</a></li>
<li><a href="single-index2.html">Management</a></li>
<li><a href="single-index3.html">App landing</a></li>
<li><a href="single-index4.html">Web Hosting </a></li>
<li><a href="single-index5.html">Consulting </a></li>
</ul>
</li>
</ul>
</li>
<li><a href="about.html">About Us</a>
</li>
<li class="has-dropdown"><a href="#">Service <i class="fa-solid fa-angle-down"></i></a>
<ul class="sub-menu">
<li class="has-dropdown has-dropdown1"><a href="index4.html">Our Service<span><i
class="fa-solid fa-angle-right"></i></span></a>
<ul class="sub-menu">
<li><a href="service.html">Our Service 1</a></li>
<li><a href="service2.html">Our Service 2</a></li>
</ul>
</li>
<li><a href="service-details.html">Service Details</a></li>
</ul>
</li>
<li class="dropdown-menu-parrent"><a href="#">Pages <i class="fa-solid fa-angle-down"></i></a>
<ul>
<li><a href="property.html">Property</a></li>
<li><a href="property-details.html">Property Details</a></li>
<li><a href="pricing-plan.html">Pricing Plan</a></li>
<li><a href="faq.html">FAQ</a></li>
<li><a href="team.html">Our Team</a></li>
</ul>
</li>
<li class="has-dropdown"><a href="#">Blog <i class="fa-solid fa-angle-down"></i></a>
<ul class="sub-menu">
<li class="has-dropdown has-dropdown1"><a href="blog.html">Blog<span><i
class="fa-solid fa-angle-right"></i></span></a>
<ul class="sub-menu">
<li><a href="blog.html">Our Blog</a></li>
<li><a href="blog-details.html">Blog Formets</a></li>
<li><a href="blog-sidebar-left.html">Blog Sidebar Left</a></li>
<li><a href="blog-sidebar-right.html">Blog Sidebar Right</a></li>
</ul>
</li>
<li><a href="blog-details.html">Single Blog</a></li>
</ul>
</li>
<li><a href="contact.html">Contact Us</a></li>
<li class="d-lg-none"><a href="#">Sign In / Sign up</a></li>
</ul>
</div>
<div class="header-site-btn">
<a href="#" class="home2-site-btn-1 font-f-2 weight-400 font-16 line-height-16">Sign In / Sign Up</a>
<a class="cta-btn font-f-2 weight-700" href="property.html">List Your Property</a>
</div>
<div class="mobile-menu-bar d-lg-none">
<i class="fas fa-bars"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
</script>
<template>
<div class="mobile-header mobile-header-4 d-block d-lg-none ">
<div class="container-fluid">
<div class="col-12">
<div class="mobile-header-elements">
<div class="mobile-logo">
<a href="index.html"><img src="/assets/img/logo/header-logo1.svg" alt=""></a>
</div>
<div class="mobile-nav-icon">
<i class="fa-solid fa-bars"></i>
</div>
</div>
</div>
</div>
</div>
<div class="mobile-sidebar d-block d-lg-none">
<div class="menu-close">
<i class="fa-solid fa-xmark"></i>
</div>
<div class="mobile-nav">
<ul class="mobile-nav-list">
<li><a href="#">Home</a>
<ul class="sub-menu">
<li><a href="single-index1.html">Real Estate</a></li>
<li><a href="single-index2.html">Project Management</a></li>
<li><a href="single-index3.html">Appland</a></li>
<li><a href="single-index4.html">Hostika </a></li>
<li><a href="single-index5.html">Insurance </a></li>
</ul>
</li>
<li><a href="about.html">About Us</a></li>
<li><a href="#">Service</a>
<ul class="sub-menu">
<li><a href="service.html">Service</a></li>
<li><a href="service2.html">Service</a></li>
<li><a href="service-details.html">Service Details</a></li>
</ul>
</li>
<li><a href="#">Blog</a>
<ul class="sub-menu">
<li><a href="blog.html">Blog</a></li>
<li><a href="blog-left-bar.html">Blog Left</a></li>
<li><a href="blog-right-bar.html">Blog Right</a></li>
<li><a href="blog-details.html">Blog Details</a></li>
<li><a href="blog-single.html">Single Blog</a></li>
</ul>
</li>
<li><a href="#">Pages</a>
<ul class="sub-menu">
<li><a href="property.html">Property</a></li>
<li><a href="property-details.html">Property Details</a></li>
<li><a href="pricing-plan.html">Pricing Plan</a></li>
<li><a href="team.html">Our Team</a></li>
<li><a href="faq.html">Our Faq</a></li>
</ul>
</li>
<li><a href="contact.html">Contact Us</a></li>
<li><a href="#">Sign In / Sign up </a></li>
</ul>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
</script>
<template>
<div class="paginacontainer">
<div class="progress-wrap">
<svg class="progress-circle svg-content" width="100%" height="100%" viewBox="-1 -1 102 102">
<path d="M50,1 a49,49 0 0,1 0,98 a49,49 0 0,1 0,-98"/>
</svg>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
</script>
<template>
<div class="row text-center">
<div class="col-12 m-auto text-center">
<div class="theme-pagination text-center">
<ul>
<li><a href="#"><i class="fa-solid fa-angle-left"></i></a></li>
<li><a class="active" href="#">01</a></li>
<li><a href="#">02</a></li>
<li>...</li>
<li><a href="#">12</a></li>
<li><a href="#"><i class="fa-solid fa-angle-right"></i></a></li>
</ul>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,40 @@
<script setup lang="ts">
</script>
<template>
<div class="subsribe-all sp2">
<div class="container">
<div class="row align-items-center">
<div class="col-md-7">
<div class="all-subsribe-hadding">
<h1
class="font-f-2 weight-700 font-30 font-lg-45 line-height-30 line-height-lg-45 font-w"
>
Get the properties you're interested in delivered straight to
your inbox.
</h1>
</div>
</div>
<div class="col-md-5">
<div class="comon-subsribe-all-input">
<input type="email" placeholder="Enter your email here" />
<div class="subsribe-btn">
<div class="home2-btn">
<button type="submit"
class="font-18 line-height-30 weight-700 font-f-2 font-w"
>
Get Notified
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,23 @@
<script setup lang="ts">
</script>
<template>
<div class="single-widget widget_tag_cloud">
<h3 class="font-f-2">Tags</h3>
<div class="space10"></div>
<div class="tagcloud">
<a href="#" class="font-f-2">Healthcare</a>
<a href="#" class="font-f-2">Consult</a>
<a href="#" class="font-f-2">Cahless</a>
<a href="#" class="font-f-2">Cyberattacks</a>
<a href="#" class="font-f-2">Meeting</a>
<a href="#" class="font-f-2">Leadership</a>
<a href="#" class="font-f-2">Health Insurace</a>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import Paginate from "~/components/Paginate.vue";
</script>
<template>
<div class="col-lg-8">
<div class="blogs padding-right">
<article class="post-blog post-format-image">
<div class="single-blog">
<div class="execution-images">
<div class="execution-img1">
<img src="/assets/img/shapes/service-details-shaop-bg.svg" alt="">
</div>
<div class="execution-img2">
<img src="/assets/img/image/service-details-img.png" alt="">
</div>
<div class="execution-img3">
<img src="/assets/img/shapes/service-details-shape.svg" alt="">
</div>
</div>
<div class="author-bio mt-4 mb-3">
<div class="author-name">
<a href="#" class="font-f-2"><i class="fa-solid fa-user"></i> Miro koyetin</a>
</div>
<div class="blog-category">
<a href="#" class="font-f-2"><i class="fa-solid fa-tag"></i>Lifestyle</a>
</div>
<div class="blog-date">
<a href="#" class="font-f-2"><i class="fa-solid fa-calendar-days"></i>18 March 2023</a>
</div>
</div>
<div class="hadding2">
<h4 class="font-f-2 font-24 line-height-28 weight-500"><a href="blog-single.html">We leverage
technology to solve challenges for your business.</a></h4>
<div class="space32"></div>
<p class="details-p-hadding font-18 line-height-18 weight-500 font-f-2">Client completes business
questionnaire</p>
<div class="space16"></div>
<p class="font-f-2 weight-400 line-height-28 font-16">After an initial complimentary phone call
consultation, for new and potential clients we then conduct an initial assessment, which reviews
and covers the current or potential business opportunities to made marketing strategies,
marketplace competitive analysis, social media presence and other specific an assessments as
needed to determine the best recommendations to improve client productivity, ensure efficiency,
and generate revenue immediately. This aids in the discovery process for both client and
consultant hen have a two hour meeting to discuss your business goals and provide.</p>
<div class="space40"></div>
<p class="details-p-hadding font-18 line-height-18 weight-500 font-f-2">Client completes business
questionnaire</p>
<div class="space24"></div>
<p class="font-f-2 weight-400 line-height-28 font-16">After an initial complimentary phone call
consultation, for new and potential clients we then conduct an initial assessment, which reviews
and covers the current or potential business opportunities to made marketing strategies,
marketplace competitive analysis, social media presence and other specific an assessments as
needed to determine the best recommendations to improve client productivity, ensure efficiency,
and generate revenue immediately. This aids in the discovery process for both client and
consultant hen have a two hour meeting to discuss your business goals and provide.</p>
</div>
</div>
</article>
</div>
<div class="space60"></div>
<Paginate/>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
</script>
<template>
<div class="pages-hero">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="page-hadding hadding2">
<h1
class="font-f-2 weight-700 font-40 font-lg-60 line-height-48 line-height-lg-65">
Blog <span class="after">sidebar right</span>
</h1>
<p class="font-f-2 font-16 weight-500 line-height-16"><a href="index.html">Home</a> <span><i
class="fa-solid fa-angle-right"></i></span> Blog <span><i class="fa-solid fa-angle-right"></i></span>
Blog Details </p>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
</script>
<template>
<div id="preloader">
<div id="loader"></div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
</script>
<template>
<div class="single-widget widget_categories">
<h3 class="font-f-2">Blog Category</h3>
<div class="space10"></div>
<div class="blog-category-list">
<ul>
<li><a class="active font-f-2" href="#">Excutive consultant (10) <span><i
class="fa-solid fa-angle-right"></i></span></a></li>
<li><a href="#" class="font-f-2">Marketing & advertising (12) <span><i
class="fa-solid fa-angle-right"></i></span></a></li>
<li><a href="#" class="font-f-2">Accounbility & execution (08)<span><i
class="fa-solid fa-angle-right"></i></span></a></li>
<li><a href="#" class="font-f-2">Business planning (21)<span><i class="fa-solid fa-angle-right"></i></span></a>
</li>
<li><a href="#" class="font-f-2">Privet planning (13)<span><i
class="fa-solid fa-angle-right"></i></span></a></li>
<li><a href="#" class="font-f-2">Mutual invest planning (06)<span><i
class="fa-solid fa-angle-right"></i></span></a></li>
<li><a href="#" class="font-f-2">Project managment (1)<span><i class="fa-solid fa-angle-right"></i></span></a>
</li>
</ul>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
</script>
<template>
<div class="single-widget single-widget-post-sidebar">
<h3 class="font-f-2">Search</h3>
<div class="recent-post">
<div class="">
<div class="recent-img">
<img src="/assets/img/image/blog-details-side1.png" alt="">
</div>
</div>
<div class="recent-post-content">
<div class="blog-date-time">
<ul class="blog-date">
<li><img src="/assets/img/icons/date.svg" alt=""> 11/02/2022</li>
</ul>
</div>
<h6><a href="blog-details.html" class="font-f-2">Leadership Burnout: What causes it and how avoid
it.</a></h6>
</div>
</div>
<div class="recent-post">
<div class="">
<div class="recent-img">
<img src="/assets/img/image/blog-details-side2.png" alt="">
</div>
</div>
<div class="recent-post-content">
<div class="blog-date-time">
<ul class="blog-date">
<li><img src="/assets/img/icons/date.svg" alt=""> 11/02/2022</li>
</ul>
</div>
<h6><a href="blog-details.html" class="font-f-2">New Consulting For All Kind Offer Finance...</a>
</h6>
</div>
</div>
<div class="recent-post">
<div class="">
<div class="recent-img">
<img src="/assets/img/image/blog-details-side3.png" alt="">
</div>
</div>
<div class="recent-post-content">
<div class="blog-date-time">
<ul class="blog-date">
<li><img src="/assets/img/icons/date.svg" alt=""> 11/02/2022</li>
</ul>
</div>
<h6><a href="blog-details.html" class="font-f-2">What we are capable to usually discovered...</a>
</h6>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
</script>
<template>
<div class="single-widget widget_search">
<h3 class="font-f-2">Search</h3>
<div class="search-form-widget">
<form action="#">
<input type="search" placeholder="Type keyword here">
<button type="submit" class="search-icon"><i class="fa-solid fa-magnifying-glass"></i></button>
</form>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
</script>
<template>
<div class="sidebar-img-box-all text-center">
<div class="sidebar-img">
<img src="/assets/img/image/sidebar-box-img.png" alt="">
</div>
<div class="sidebar-box-hadding">
<div class="space16"></div>
<h4><a href="#" class="font-f-2 font-20 line-height-20 weight-500 font-w">Alex Blake</a></h4>
<div class="space24"></div>
<p class="font-f-2 font-16 line-height-26 weight-400 font-w">When it comes to business, listen to
Henry David Thoreau: things usually dont happen overnight instead, to find success takes a lot of
time, effort, and courage. Opus includes everything you need to build a beautiful website.</p>
</div>
<div class="details-side-box-icon">
<div class="space24"></div>
<ul>
<li><a href="#"><i class="fa-brands fa-twitter"></i></a></li>
<li><a href="#"><i class="fa-brands fa-facebook-f"></i></a></li>
<li><a href="#"><i class="fa-brands fa-instagram"></i></a></li>
</ul>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,23 @@
<script setup lang="ts">
</script>
<template>
<div class="single-widget widget_tag_cloud">
<h3 class="font-f-2">Tags</h3>
<div class="space10"></div>
<div class="tagcloud">
<a href="#" class="font-f-2">Healthcare</a>
<a href="#" class="font-f-2">Consult</a>
<a href="#" class="font-f-2">Cahless</a>
<a href="#" class="font-f-2">Cyberattacks</a>
<a href="#" class="font-f-2">Meeting</a>
<a href="#" class="font-f-2">Leadership</a>
<a href="#" class="font-f-2">Health Insurace</a>
</div>
</div>
</template>
<style scoped>
</style>

88
app/layouts/admin.vue Normal file
View File

@@ -0,0 +1,88 @@
<template>
<div class="d-flex" id="wrapper">
<!-- Sidebar -->
<div class="bg-dark text-white p-3" id="sidebar-wrapper" style="min-width: 250px; min-height: 100vh;">
<div class="sidebar-heading text-center py-4 fs-4 fw-bold border-bottom">Admin Panel</div>
<div class="list-group list-group-flush my-3">
<NuxtLink to="/admin" class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-tachometer-alt me-2"></i> Dashboard
</NuxtLink>
<NuxtLink to="/admin/settings"
class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-cogs me-2"></i> Settings
</NuxtLink>
<NuxtLink to="/admin/security"
class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-shield-alt me-2"></i> Güvenlik &
</NuxtLink>
<NuxtLink to="/admin/users"
class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-users me-2"></i> Kullanıcılar
</NuxtLink>
<div class="sidebar-heading text-white-50 mt-3 mb-1 small text-uppercase">Blog</div>
<NuxtLink to="/admin/blog/categories"
class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-tags me-2"></i> Kategoriler
</NuxtLink>
<NuxtLink to="/admin/blog/tags"
class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-hashtag me-2"></i> Etiketler
</NuxtLink>
<NuxtLink to="/admin/blog/posts"
class="list-group-item list-group-item-action bg-transparent text-white border-0">
<i class="fas fa-newspaper me-2"></i> Postlar
</NuxtLink>
<NuxtLink to="/" class="list-group-item list-group-item-action bg-transparent text-white border-0 mt-5">
<i class="fas fa-home me-2"></i> Siteye Dön
</NuxtLink>
<button @click="handleLogout"
class="list-group-item list-group-item-action bg-transparent text-danger border-0">
<i class="fas fa-sign-out-alt me-2"></i> Çıkış
</button>
</div>
</div>
<!-- Page Content -->
<div id="page-content-wrapper" class="w-100">
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom px-4">
<button class="btn btn-primary" id="menu-toggle">Toggle Menu</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ms-auto mt-2 mt-lg-0">
<li class="nav-item active">
<span class="nav-link">Hoşgeldin, Admin</span>
</li>
</ul>
</div>
</nav>
<div class="container-fluid px-4 py-4">
<slot />
</div>
</div>
</div>
</template>
<script setup lang="ts">
const { signOut } = useAuth();
const handleLogout = async () => {
await signOut({ callbackUrl: '/auth/login' });
};
</script>
<style scoped>
#sidebar-wrapper {
transition: all 0.3s;
}
.list-group-item-action:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
color: #fff !important;
}
.router-link-active {
background-color: rgba(255, 255, 255, 0.2) !important;
border-radius: 5px;
}
</style>

72
app/layouts/default.vue Normal file
View File

@@ -0,0 +1,72 @@
<template>
<div>
<slot />
<div class="progress-wrap">
<svg class="progress-circle svg-content" width="100%" height="100%" viewBox="-1 -1 102 102">
<path d="M50,1 a49,49 0 0,1 0,98 a49,49 0 0,1 0,-98"/>
</svg>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue';
import { useHeroStore } from '@/stores/hero';
import { useSettingStore } from '@/stores/setting';
const heroStore = useHeroStore();
const settingStore = useSettingStore();
useHead(computed(() => {
const title = settingStore.settings?.title ?? heroStore.hero?.title ?? 'Blog details Sidebar Right || Avigo Multipurpose Theme';
const meta: Array<Record<string, string>> = [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
];
if (settingStore.settings?.meta_description) {
meta.push({ name: 'description', content: settingStore.settings.meta_description });
}
return {
title,
meta
}
}));
onMounted(() => {
// page-progress
const progressPath = document.querySelector(".progress-wrap path") as SVGPathElement | null;
if (progressPath) {
const pathLength = progressPath.getTotalLength();
// use webkitTransition (lowercase) to satisfy TypeScript and cast style where needed
progressPath.style.transition = (progressPath.style as any).webkitTransition = "none";
progressPath.style.strokeDasharray = pathLength + " " + pathLength;
progressPath.style.strokeDashoffset = String(pathLength);
progressPath.getBoundingClientRect();
progressPath.style.transition = (progressPath.style as any).webkitTransition = "stroke-dashoffset 10ms linear";
const updateProgress = () => {
const scroll = $(window).scrollTop();
const height = $(document).height() - $(window).height();
const prog = pathLength - (scroll * pathLength) / height;
progressPath!.style.strokeDashoffset = String(prog);
};
updateProgress();
$(window).scroll(updateProgress);
const offset = 50;
const duration = 550;
jQuery(window).on("scroll", () => {
if (jQuery(window).scrollTop() > offset) {
jQuery(".progress-wrap").addClass("active-progress");
} else {
jQuery(".progress-wrap").removeClass("active-progress");
}
});
jQuery(".progress-wrap").on("click", (event: any) => {
event.preventDefault();
jQuery("html, body").animate({ scrollTop: 0 }, duration);
return false;
});
}
})
</script>

11
app/middleware/admin.ts Normal file
View File

@@ -0,0 +1,11 @@
export default defineNuxtRouteMiddleware((to, from) => {
const { status, data } = useAuth();
if (status.value !== 'authenticated') {
return navigateTo('/auth/login?callbackUrl=' + to.fullPath);
}
if (!data.value?.user?.is_admin) {
return navigateTo('/');
}
});

View File

@@ -0,0 +1,8 @@
// Middleware to redirect authenticated users away from guest-only pages
export default defineNuxtRouteMiddleware((to, from) => {
const { status } = useAuth();
if (status.value === 'authenticated') {
return navigateTo('/');
}
});

View File

@@ -0,0 +1,350 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Kategori Yönetimi</h2>
</div>
<!-- Filter / Search -->
<div class="card mb-4">
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<select v-model="filterStatus" class="form-select" @change="fetchCategories(1)">
<option value="">Aktif Kategoriler</option>
<option value="only">Silinenler (Çöp Kutusu)</option>
<option value="with">Tümü (Silinenler Dahil)</option>
</select>
</div>
</div>
</div>
</div>
<!-- Categories List Card -->
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center bg-white">
<h5 class="mb-0">Kategoriler</h5>
<button class="btn btn-success btn-sm" @click="openModal(null)">
<i class="fas fa-plus me-2"></i> Yeni Kategori
</button>
</div>
<div class="card-body">
<div v-if="loading" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Yükleniyor...</span>
</div>
</div>
<div v-else-if="categories.length === 0" class="text-center py-5 text-muted">
Kayıt bulunamadı.
</div>
<div v-else class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>ID</th>
<th>Başlık</th>
<th>Slug</th>
<th>ıklama</th>
<th>Durum</th>
<th class="text-end">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="category in categories" :key="category.ID">
<td>{{ category.ID }}</td>
<td>
<!-- Indentation for hierarchy if available, otherwise flat -->
<div :style="{ paddingLeft: (category.parent_id ? 20 : 0) + 'px' }">
<span v-if="category.parent_id" class="text-muted me-1"></span>
{{ category.title }}
</div>
</td>
<td><span class="badge bg-light text-dark">{{ category.slug }}</span></td>
<td>{{ category.description }}</td>
<td>
<span v-if="category.DeletedAt" class="badge bg-danger">Silindi</span>
<span v-else class="badge bg-success">Aktif</span>
</td>
<td class="text-end">
<div v-if="!category.DeletedAt">
<button class="btn btn-sm btn-outline-primary me-2" @click="openModal(category)">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger" @click="deleteCategory(category.ID)">
<i class="fas fa-trash"></i>
</button>
</div>
<div v-else>
<button class="btn btn-sm btn-outline-success me-2" @click="restoreCategory(category.ID)">
<i class="fas fa-trash-restore"></i>
</button>
<button class="btn btn-sm btn-outline-danger" @click="hardDeleteCategory(category.ID)">
<i class="fas fa-times-circle"></i> Kalıcı Sil
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<nav v-if="pagination.total > pagination.per_page" class="mt-4">
<ul class="pagination justify-content-center">
<li class="page-item" :class="{ disabled: pagination.page === 1 }">
<button class="page-link" @click="fetchCategories(pagination.page - 1)">Önceki</button>
</li>
<li v-for="page in Math.ceil(pagination.total / pagination.per_page)" :key="page"
class="page-item" :class="{ active: pagination.page === page }">
<button class="page-link" @click="fetchCategories(page)">{{ page }}</button>
</li>
<li class="page-item" :class="{ disabled: pagination.page >= Math.ceil(pagination.total / pagination.per_page) }">
<button class="page-link" @click="fetchCategories(pagination.page + 1)">Sonraki</button>
</li>
</ul>
</nav>
</div>
</div>
<!-- Create/Edit Modal -->
<div class="modal fade" id="categoryModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ isEditing ? 'Kategoriyi Düzenle' : 'Yeni Kategori' }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form @submit.prevent="saveCategory">
<div class="mb-3">
<label class="form-label">Başlık</label>
<input v-model="formData.title" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">ıklama</label>
<textarea v-model="formData.description" class="form-control" rows="3"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Üst Kategori (Opsiyonel)</label>
<select v-model="formData.parent_id" class="form-select">
<option :value="null">Ana Kategori</option>
<!-- Only show active categories as parents to avoid confusion -->
<option v-for="cat in activeCategories" :key="cat.ID" :value="cat.ID"
:disabled="cat.ID === formData.ID">
{{ cat.title }}
</option>
</select>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary me-2"
data-bs-dismiss="modal">İptal</button>
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Category } from '~~/types/category';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const categories = ref<Category[]>([]);
const activeCategories = ref<Category[]>([]); // For parent selection
const loading = ref(false);
const isEditing = ref(false);
const formData = ref<Partial<Category>>({});
const filterStatus = ref(''); // '' = active, 'only' = trashed, 'with' = all
let categoryModal: any = null;
const pagination = ref({
page: 1,
per_page: 10,
total: 0
});
// -- API --
const fetchCategories = async (page = 1) => {
loading.value = true;
try {
const query: any = { page }; // Pagination if supported by admin endpoint
if (filterStatus.value) {
query.trashed = filterStatus.value;
}
// Use Admin endpoint for management
const url = '/api/v1/admin/categories';
const res = await $fetch<any>(url, {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
query
});
if (res.data) {
categories.value = res.data;
pagination.value = res.meta || { page: 1, per_page: 100, total: res.data.length }; // Fallback if no meta
} else if (Array.isArray(res)) {
// Fallback for non-paginated response (if API changes or for active list)
categories.value = res;
pagination.value = { page: 1, per_page: 100, total: res.length };
} else {
categories.value = [];
}
// Fetch active categories for parent selection (always needs active ones)
if (filterStatus.value === '') {
activeCategories.value = categories.value;
} else {
// If we are viewing trashed, we still need active categories for the dropdown
// We can fetch them separately/lightweight if needed, or just allow 'null' parent
// checking if we have them cached or fetch explicitly
// For now, let's just fetch active ones if we don't have them
const activeRes = await $fetch<Category[]>('/api/v1/categories', {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
activeCategories.value = activeRes || [];
}
} catch (e) {
console.error(e);
categories.value = [];
} finally {
loading.value = false;
}
};
const openModal = (category: Category | null) => {
isEditing.value = !!category;
formData.value = category ? { ...category } : { title: '', description: '', parent_id: null };
// Check if modal instance exists
const el = document.getElementById('categoryModal');
if (el) {
// @ts-ignore
categoryModal = new bootstrap.Modal(el);
categoryModal.show();
}
};
const saveCategory = async () => {
loading.value = true;
try {
const url = isEditing.value
? `/api/v1/categories/${formData.value.ID}`
: '/api/v1/categories';
const method = isEditing.value ? 'PUT' : 'POST';
await $fetch(url, {
method,
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
body: formData.value
});
Swal.fire('Başarılı', `Kategori ${isEditing.value ? 'güncellendi' : 'eklendi'}.`, 'success');
if (categoryModal) categoryModal.hide();
fetchCategories(pagination.value.page);
} catch (error) {
console.error(error);
Swal.fire('Hata', 'İşlem başarısız.', 'error');
} finally {
loading.value = false;
}
};
const deleteCategory = async (id: number) => {
const result = await Swal.fire({
title: 'Emin misiniz?',
text: "Kategori silinecek (Çöp kutusuna taşınacak).",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Evet, Sil',
cancelButtonText: 'İptal',
confirmButtonColor: '#d33'
});
if (!result.isConfirmed) return;
try {
await $fetch(`/api/v1/categories/${id}`, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi', 'Kategori silindi.', 'success');
fetchCategories(pagination.value.page);
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
};
const restoreCategory = async (id: number) => {
try {
await $fetch(`/api/v1/admin/categories/${id}/restore`, {
method: 'POST',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Başarılı', 'Kategori geri yüklendi.', 'success');
fetchCategories(pagination.value.page);
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Geri yükleme başarısız.', 'error');
}
};
const hardDeleteCategory = async (id: number) => {
const result = await Swal.fire({
title: 'Kalıcı Olarak Sil?',
text: "Bu işlem geri alınamaz!",
icon: 'error',
showCancelButton: true,
confirmButtonText: 'Evet, Kalıcı Sil',
cancelButtonText: 'İptal',
confirmButtonColor: '#d33'
});
if (!result.isConfirmed) return;
try {
await $fetch(`/api/v1/admin/categories/${id}/hard`, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi', 'Kategori kalıcı olarak silindi.', 'success');
fetchCategories(pagination.value.page);
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
};
onMounted(() => {
fetchCategories();
});
</script>

View File

@@ -0,0 +1,325 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Gönderi Düzenle</h2>
<NuxtLink to="/admin/blog/posts" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i> Geri Dön
</NuxtLink>
</div>
<div v-if="loadingData" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Yükleniyor...</span>
</div>
</div>
<div v-else class="card">
<div class="card-body">
<form @submit.prevent="updatePost">
<div class="row">
<div class="col-md-8">
<!-- Title -->
<div class="mb-3">
<label class="form-label">Başlık</label>
<input v-model="formData.title" type="text" class="form-control" required>
</div>
<!-- Content -->
<div class="mb-3">
<label class="form-label">İçerik</label>
<textarea v-model="formData.content" class="form-control" rows="10" required></textarea>
<div class="form-text">HTML içeriği girebilirsiniz.</div>
</div>
<!-- Tags -->
<div class="card mb-3">
<div class="card-header">Etiketler</div>
<div class="card-body" style="max-height: 200px; overflow-y: auto;">
<div v-if="availableTags.length === 0" class="text-muted small">Etiket bulunamadı.</div>
<div v-for="tag in availableTags" :key="tag.ID" class="form-check">
<input class="form-check-input" type="checkbox" :value="tag.ID" v-model="selectedTags" :id="`tag-${tag.ID}`">
<label class="form-check-label" :for="`tag-${tag.ID}`">
{{ tag.name }}
</label>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<!-- Categories -->
<div class="card mb-3">
<div class="card-header">Kategoriler</div>
<div class="card-body" style="max-height: 300px; overflow-y: auto;">
<div v-for="cat in categories" :key="cat.ID" class="form-check">
<input class="form-check-input" type="checkbox" :value="cat.ID" v-model="formData.category_ids" :id="`cat-${cat.ID}`">
<label class="form-check-label" :for="`cat-${cat.ID}`">
{{ cat.title }}
</label>
</div>
</div>
</div>
<!-- Image Upload & Options -->
<div class="card mb-3">
<div class="card-header">Öne Çıkan Görsel</div>
<div class="card-body">
<div class="mb-3 text-center">
<div v-if="imagePreview" class="mb-3">
<img :src="imagePreview" class="img-fluid rounded" style="max-height: 200px;">
<button type="button" class="btn btn-sm btn-danger mt-2" @click="removeImage">Değiştir / Kaldır</button>
</div>
<div v-else>
<div class="mb-3">
<i class="fas fa-image fa-3x text-muted"></i>
</div>
<input type="file" class="form-control" @change="handleFileUpload" accept="image/*">
</div>
</div>
<div v-if="imageFile">
<hr>
<h6>Resim Ayarları</h6>
<div class="row g-2">
<div class="col-6">
<label class="form-label small">Genişlik (px)</label>
<input v-model="imageOptions.width" type="number" class="form-control form-control-sm" placeholder="Opsiyonel">
</div>
<div class="col-6">
<label class="form-label small">Yükseklik (px)</label>
<input v-model="imageOptions.height" type="number" class="form-control form-control-sm" placeholder="Opsiyonel">
</div>
<div class="col-6">
<label class="form-label small">Kalite (1-100)</label>
<input v-model="imageOptions.quality" type="number" class="form-control form-control-sm" min="1" max="100">
</div>
<div class="col-6">
<label class="form-label small">Format</label>
<select v-model="imageOptions.format" class="form-select form-select-sm">
<option value="jpeg">JPEG</option>
<option value="png">PNG</option>
<option value="webp">WebP</option>
<option value="avif">AVIF</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Update Button -->
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary" :disabled="saving">
<span v-if="saving" class="spinner-border spinner-border-sm me-2"></span>
{{ saving ? 'Güncelleniyor...' : 'Güncelle' }}
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Category } from '~~/types/category';
import type { Post } from '~~/types/post';
import type { Tag, TagResponse } from '~~/types/tag';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const route = useRoute();
const router = useRouter();
const loadingData = ref(true);
const saving = ref(false);
const categories = ref<Category[]>([]);
const availableTags = ref<Tag[]>([]);
const selectedTags = ref<number[]>([]);
const imageFile = ref<File | null>(null);
const imagePreview = ref<string | null>(null);
const formData = ref({
title: '',
content: '',
category_ids: [] as number[],
});
const imageOptions = ref({
width: null as number | null,
height: null as number | null,
quality: 80,
format: 'jpeg'
});
const postId = route.params.id as string;
const fetchData = async () => {
try {
const [catRes, tagRes] = await Promise.all([
$fetch<Category[]>('/api/v1/categories', {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
}),
$fetch<TagResponse>('/api/v1/admin/tags', { // Fetching all tags
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
params: { limit: 100 }
})
]);
categories.value = catRes || [];
availableTags.value = tagRes?.data || [];
// After fetching data, fetch post
await fetchPost();
} catch (e) {
console.error('Veriler yüklenemedi', e);
loadingData.value = false;
}
};
const fetchPost = async () => {
try {
const res = await $fetch<Post>(`/api/v1/posts/${postId}`, {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
if (res) {
formData.value.title = res.title;
formData.value.content = res.content;
formData.value.category_ids = res.categories?.map(c => c.ID) || [];
// Pre-select tags
if (res.tags) {
selectedTags.value = res.tags.map(t => t.ID);
}
// Image
if (res.images) {
// Should parse if JSON or string
let imgPath = res.images;
if (imgPath.startsWith('[') || imgPath.startsWith('{')) {
try {
const parsed = JSON.parse(imgPath);
if (Array.isArray(parsed) && parsed.length > 0) imgPath = parsed[0];
} catch(e) {}
}
imagePreview.value = getImageUrl(imgPath);
}
}
} catch (e) {
console.error('Post yüklenemedi', e);
Swal.fire('Hata', 'Gönderi bulunamadı veya yüklenemedi.', 'error');
router.push('/admin/blog/posts');
} finally {
loadingData.value = false;
}
};
const getImageUrl = (path: string) => {
if (path.startsWith('http')) return path;
if (path.startsWith('/')) return `${config.public.BASE_API_URL}${path}`;
return `${config.public.BASE_API_URL}/${path}`;
};
const handleFileUpload = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) {
const file = target.files[0];
imageFile.value = file;
imagePreview.value = URL.createObjectURL(file);
// Default format based on uploaded file or keep jpeg
if (file.type === 'image/png') imageOptions.value.format = 'png';
else if (file.type === 'image/webp') imageOptions.value.format = 'webp';
else imageOptions.value.format = 'jpeg';
}
};
const removeImage = () => {
imageFile.value = null;
imagePreview.value = null;
};
const updatePost = async () => {
saving.value = true;
try {
const data = new FormData();
data.append('title', formData.value.title);
data.append('content', formData.value.content);
if (formData.value.category_ids.length > 0) {
data.append('category_ids', formData.value.category_ids.join(','));
}
if (selectedTags.value.length > 0) {
data.append('tag_ids', selectedTags.value.join(','));
}
// Optimize & Append Image if exists
if (imageFile.value) {
const optimizeData = new FormData();
optimizeData.append('file', imageFile.value);
if (imageOptions.value.width) optimizeData.append('width', imageOptions.value.width.toString());
if (imageOptions.value.height) optimizeData.append('height', imageOptions.value.height.toString());
optimizeData.append('quality', imageOptions.value.quality.toString());
optimizeData.append('format', imageOptions.value.format);
// Fetch as Blob from server/api/optimize
const optimizedBlob = await $fetch<Blob>('/api/optimize', {
method: 'POST',
body: optimizeData,
responseType: 'blob'
});
const ext = imageOptions.value.format === 'jpg' ? 'jpeg' : imageOptions.value.format;
const filename = `img-${Date.now()}.${ext === 'jpeg' ? 'jpg' : ext}`;
const optimizedFile = new File([optimizedBlob], filename, { type: `image/${ext}` });
data.append('images', optimizedFile);
}
await $fetch(`/api/v1/posts/${postId}`, {
method: 'PUT',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
body: data
});
Swal.fire({
title: 'Başarılı',
text: 'Gönderi güncellendi.',
icon: 'success',
showCancelButton: true,
confirmButtonText: 'Listeye Dön',
cancelButtonText: 'Kalsın'
}).then((result) => {
if (result.isConfirmed) {
router.push('/admin/blog/posts');
}
});
} catch (error: any) {
console.error(error);
Swal.fire('Hata', error.data?.message || 'Bir hata oluştu.', 'error');
} finally {
saving.value = false;
}
};
onMounted(() => {
fetchData();
});
</script>

View File

@@ -0,0 +1,253 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Yeni Gönderi Ekle</h2>
<NuxtLink to="/admin/blog/posts" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i> Geri Dön
</NuxtLink>
</div>
<div class="card">
<div class="card-body">
<form @submit.prevent="savePost">
<div class="row">
<div class="col-md-8">
<!-- Title -->
<div class="mb-3">
<label class="form-label">Başlık</label>
<input v-model="formData.title" type="text" class="form-control" required>
</div>
<!-- Content -->
<div class="mb-3">
<label class="form-label">İçerik</label>
<textarea v-model="formData.content" class="form-control" rows="10" required></textarea>
<div class="form-text">HTML içeriği girebilirsiniz.</div>
</div>
<!-- Tags -->
<div class="card mb-3">
<div class="card-header">Etiketler</div>
<div class="card-body" style="max-height: 200px; overflow-y: auto;">
<div v-if="availableTags.length === 0" class="text-muted small">Etiket bulunamadı.</div>
<div v-for="tag in availableTags" :key="tag.ID" class="form-check">
<input class="form-check-input" type="checkbox" :value="tag.ID" v-model="selectedTags" :id="`tag-${tag.ID}`">
<label class="form-check-label" :for="`tag-${tag.ID}`">
{{ tag.name }}
</label>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<!-- Categories -->
<div class="card mb-3">
<div class="card-header">Kategoriler</div>
<div class="card-body" style="max-height: 300px; overflow-y: auto;">
<div v-if="categories.length === 0" class="text-muted small">Kategori bulunamadı.</div>
<div v-for="cat in categories" :key="cat.ID" class="form-check">
<input class="form-check-input" type="checkbox" :value="cat.ID" v-model="formData.category_ids" :id="`cat-${cat.ID}`">
<label class="form-check-label" :for="`cat-${cat.ID}`">
{{ cat.title }}
</label>
</div>
</div>
</div>
<!-- Image Upload & Options -->
<div class="card mb-3">
<div class="card-header">Öne Çıkan Görsel</div>
<div class="card-body">
<div class="mb-3 text-center">
<div v-if="imagePreview" class="mb-3">
<img :src="imagePreview" class="img-fluid rounded" style="max-height: 200px;">
<button type="button" class="btn btn-sm btn-danger mt-2" @click="removeImage">Kaldır</button>
</div>
<div v-else>
<div class="mb-3">
<i class="fas fa-image fa-3x text-muted"></i>
</div>
<input type="file" class="form-control" @change="handleFileUpload" accept="image/*">
</div>
</div>
<div v-if="imageFile">
<hr>
<h6>Resim Ayarları</h6>
<div class="row g-2">
<div class="col-6">
<label class="form-label small">Genişlik (px)</label>
<input v-model="imageOptions.width" type="number" class="form-control form-control-sm">
</div>
<div class="col-6">
<label class="form-label small">Yükseklik (px)</label>
<input v-model="imageOptions.height" type="number" class="form-control form-control-sm" placeholder="Opsiyonel">
</div>
<div class="col-6">
<label class="form-label small">Kalite (1-100)</label>
<input v-model="imageOptions.quality" type="number" class="form-control form-control-sm" min="1" max="100">
</div>
<div class="col-6">
<label class="form-label small">Format</label>
<select v-model="imageOptions.format" class="form-select form-select-sm">
<option value="jpeg">JPEG</option>
<option value="png">PNG</option>
<option value="webp">WebP</option>
<option value="avif">AVIF</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Publish Button -->
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
{{ loading ? 'İşleniyor...' : 'Yayınla' }}
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Category } from '~~/types/category';
import type { Tag, TagResponse } from '~~/types/tag';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const router = useRouter();
const loading = ref(false);
const categories = ref<Category[]>([]);
const availableTags = ref<Tag[]>([]);
const selectedTags = ref<number[]>([]);
const imageFile = ref<File | null>(null);
const imagePreview = ref<string | null>(null);
const formData = ref({
title: '',
content: '',
category_ids: [] as number[],
});
const imageOptions = ref({
width: null as number | null,
height: null as number | null,
quality: 80,
format: 'jpeg'
});
// Fetch Categories and Tags
const fetchData = async () => {
try {
const [catRes, tagRes] = await Promise.all([
$fetch<Category[]>('/api/v1/categories', {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
}),
$fetch<TagResponse>('/api/v1/admin/tags', { // Fetching all tags (admin endpoint for safety/completeness)
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
params: { limit: 100 } // Get a reasonable amount of tags
})
]);
categories.value = catRes || [];
availableTags.value = tagRes?.data || [];
} catch (e) {
console.error(e);
}
};
const handleFileUpload = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) {
const file = target.files[0];
imageFile.value = file;
imagePreview.value = URL.createObjectURL(file);
// Default format based on uploaded file or keep jpeg
if (file.type === 'image/png') imageOptions.value.format = 'png';
else if (file.type === 'image/webp') imageOptions.value.format = 'webp';
else imageOptions.value.format = 'jpeg';
}
};
const removeImage = () => {
imageFile.value = null;
imagePreview.value = null;
};
const savePost = async () => {
loading.value = true;
try {
const data = new FormData();
data.append('title', formData.value.title);
data.append('content', formData.value.content);
if (formData.value.category_ids.length > 0) {
data.append('category_ids', formData.value.category_ids.join(','));
}
if (selectedTags.value.length > 0) {
data.append('tag_ids', selectedTags.value.join(','));
}
// Optimize Image and append to FormData
if (imageFile.value) {
const optimizeData = new FormData();
optimizeData.append('file', imageFile.value);
if (imageOptions.value.width) optimizeData.append('width', imageOptions.value.width.toString());
if (imageOptions.value.height) optimizeData.append('height', imageOptions.value.height.toString());
optimizeData.append('quality', imageOptions.value.quality.toString());
optimizeData.append('format', imageOptions.value.format);
const optimizedBlob = await $fetch<Blob>('/api/optimize', {
method: 'POST',
body: optimizeData,
responseType: 'blob'
});
const ext = imageOptions.value.format === 'jpg' ? 'jpeg' : imageOptions.value.format;
const filename = `img-${Date.now()}.${ext === 'jpeg' ? 'jpg' : ext}`;
const optimizedFile = new File([optimizedBlob], filename, { type: `image/${ext}` });
data.append('images', optimizedFile);
}
await $fetch('/api/v1/posts', {
method: 'POST',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
body: data
});
Swal.fire('Başarılı', 'Gönderi başarıyla oluşturuldu.', 'success');
router.push('/admin/blog/posts');
} catch (error: any) {
console.error(error);
Swal.fire('Hata', error.data?.message || 'Bir hata oluştu.', 'error');
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchData();
});
</script>

View File

@@ -0,0 +1,252 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Gönderi Yönetimi</h2>
<NuxtLink to="/admin/blog/posts/create" class="btn btn-primary">
<i class="fas fa-plus me-2"></i> Yeni Gönderi
</NuxtLink>
</div>
<!-- Filter / Search -->
<div class="card mb-4">
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<select v-model="filterStatus" class="form-select" @change="fetchPosts(1)">
<option value="">Tümü</option>
<option value="only">Silinenler (Çöp Kutusu)</option>
<option value="with">Tümü (Silinenler Dahil)</option>
</select>
</div>
<!-- Future: Add search input here -->
</div>
</div>
</div>
<!-- Posts List -->
<div class="card mb-4">
<div class="card-body">
<div v-if="loading" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Yükleniyor...</span>
</div>
</div>
<div v-else-if="posts.length === 0" class="text-center py-5 text-muted">
Kayıt bulunamadı.
</div>
<div v-else class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>ID</th>
<th>Resim</th>
<th>Başlık</th>
<th>Kategoriler</th>
<th>Etiketler</th>
<th>Durum</th>
<th>Tarih</th>
<th class="text-end">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="post in posts" :key="post.ID">
<td>{{ post.ID }}</td>
<td>
<img v-if="getFirstImage(post.images)" :src="getImageUrl(getFirstImage(post.images)!)"
alt="thumbnail" class="rounded" style="width: 50px; height: 50px; object-fit: cover;">
<div v-else class="bg-light rounded d-flex align-items-center justify-content-center text-muted"
style="width: 50px; height: 50px;">
<i class="fas fa-image"></i>
</div>
</td>
<td>
<div class="fw-bold">{{ post.title }}</div>
<small class="text-muted">{{ post.slug }}</small>
</td>
<td>
<span v-for="cat in post.categories" :key="cat.ID" class="badge bg-info text-dark me-1">
{{ cat.title }}
</span>
</td>
<td>
<span v-for="tag in post.tags" :key="tag.ID" class="badge bg-secondary me-1">
{{ tag.name }}
</span>
</td>
<td>
<span v-if="post.DeletedAt" class="badge bg-danger">Silindi</span>
<span v-else class="badge bg-success">Aktif</span>
</td>
<td>
<div>{{ formatDate(post.CreatedAt) }}</div>
</td>
<td class="text-end">
<div v-if="!post.DeletedAt">
<NuxtLink :to="`/admin/blog/posts/${post.ID}`" class="btn btn-sm btn-outline-primary me-2">
<i class="fas fa-edit"></i>
</NuxtLink>
<button class="btn btn-sm btn-outline-danger" @click="deletePost(post.ID)">
<i class="fas fa-trash"></i>
</button>
</div>
<div v-else>
<button class="btn btn-sm btn-outline-success" @click="restorePost(post.ID)">
<i class="fas fa-trash-restore"></i> Geri Yükle
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<nav v-if="pagination.total > pagination.per_page" class="mt-4">
<ul class="pagination justify-content-center">
<li class="page-item" :class="{ disabled: pagination.page === 1 }">
<button class="page-link" @click="fetchPosts(pagination.page - 1)">Önceki</button>
</li>
<li v-for="page in Math.ceil(pagination.total / pagination.per_page)" :key="page"
class="page-item" :class="{ active: pagination.page === page }">
<button class="page-link" @click="fetchPosts(page)">{{ page }}</button>
</li>
<li class="page-item" :class="{ disabled: pagination.page >= Math.ceil(pagination.total / pagination.per_page) }">
<button class="page-link" @click="fetchPosts(pagination.page + 1)">Sonraki</button>
</li>
</ul>
</nav>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Post, PostResponse } from '~~/types/post';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const posts = ref<Post[]>([]);
const loading = ref(false);
const filterStatus = ref(''); // '' = active (default from API perspective usually?), or we need to handle default
// Default listing in doc: /api/v1/admin/posts
// Trashed only: ?trashed=only
// With trashed: ?trashed=with
const pagination = ref({
page: 1,
per_page: 10,
total: 0
});
const fetchPosts = async (page = 1) => {
loading.value = true;
try {
const query: any = { page, limit: pagination.value.per_page };
if (filterStatus.value) {
query.trashed = filterStatus.value;
}
const res = await $fetch<PostResponse>('/api/v1/admin/posts', {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
query
});
posts.value = res.data || [];
pagination.value = res.meta;
} catch (e) {
console.error(e);
posts.value = [];
} finally {
loading.value = false;
}
};
const deletePost = async (id: number) => {
const result = await Swal.fire({
title: 'Emin misiniz?',
text: "Bu gönderi silinecek (Çöp kutusuna taşınacak).",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Evet, Sil',
cancelButtonText: 'İptal',
confirmButtonColor: '#d33'
});
if (!result.isConfirmed) return;
try {
await $fetch(`/api/v1/posts/${id}`, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi', 'Gönderi silindi.', 'success');
fetchPosts(pagination.value.page);
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
};
const restorePost = async (id: number) => {
// Implement restore logic if API supports it, otherwise basic delete is usually soft delete.
// If current delete IS soft delete, usually there's a restore endpoint or update with deleted_at=null.
// Assuming for now we just show it for concept, but strict restore endpoint might be needed.
// For now, let's keep it simple. If no explicit restore endpoint documented, maybe we can't restore easily from UI yet.
// But wait, the user asked to implement based on admin_post.md.
// Use case "sadece soft delete olmuslar listesi" implies we can see them.
// Usually a PUT to /api/v1/admin/posts/{id}/restore or similar.
// Since not documented, I will skip implementation of ACTUAL restore call but keep the button or maybe comment it out
// until confirmed. Or I can assume standard behaviour if I knew the backend.
// Let's assume standard soft delete REST pattern often allows toggle or specific endpoint.
// I'll leave the button but functionality might be missing.
// Actually, let's look at the docs again.
// Docs only show GET lists.
// I will remove Restore button functionality for now to avoid errors, or just show alert 'Not implemented'.
Swal.fire('Bilgi', 'Geri yükleme özelliği henüz API tarafında dökümante edilmedi.', 'info');
};
const getFirstImage = (imagesStr: string): string | null => {
try {
// API returns "images": "uploads/posts/..." OR JSON string "[\"...\"]"
// Let's try to parse if it looks like JSON
if (imagesStr.startsWith('[') || imagesStr.startsWith('{')) {
const parsed = JSON.parse(imagesStr);
if (Array.isArray(parsed) && parsed.length > 0) return parsed[0];
}
return imagesStr;
} catch (e) {
return imagesStr;
}
};
const getImageUrl = (path: string) => {
if (path.startsWith('http')) return path;
if (path.startsWith('/')) return `${config.public.BASE_API_URL}${path}`;
return `${config.public.BASE_API_URL}/${path}`;
};
const formatDate = (dateStr: string) => {
return new Date(dateStr).toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
onMounted(() => {
fetchPosts();
});
</script>

View File

@@ -0,0 +1,317 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Etiket Yönetimi</h2>
<button class="btn btn-primary" @click="openModal()">
<i class="fas fa-plus me-2"></i> Yeni Etiket
</button>
</div>
<div class="card mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" :class="{ active: filterTrashed === 'without' }" @click="changeFilter('without')">
Aktif
</button>
<button type="button" class="btn btn-outline-secondary" :class="{ active: filterTrashed === 'only' }" @click="changeFilter('only')">
Çöp Kutusu
</button>
<button type="button" class="btn btn-outline-secondary" :class="{ active: filterTrashed === 'with' }" @click="changeFilter('with')">
Hepsi
</button>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<div v-if="loading" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Yükleniyor...</span>
</div>
</div>
<div v-else class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>ID</th>
<th>Etiket Adı</th>
<th>Oluşturulma</th>
<th>Durum</th>
<th class="text-end">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="tag in tags" :key="tag.ID">
<td>#{{ tag.ID }}</td>
<td>
<span class="fw-bold">{{ tag.name }}</span>
</td>
<td>{{ new Date(tag.CreatedAt).toLocaleDateString('tr-TR') }}</td>
<td>
<span v-if="tag.DeletedAt" class="badge bg-danger">Silinmiş</span>
<span v-else class="badge bg-success">Aktif</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm">
<button v-if="!tag.DeletedAt" class="btn btn-outline-primary" @click="openModal(tag)" title="Düzenle">
<i class="fas fa-edit"></i>
</button>
<button v-if="!tag.DeletedAt" class="btn btn-outline-danger" @click="deleteTag(tag.ID)" title="Sil">
<i class="fas fa-trash"></i>
</button>
<button v-if="tag.DeletedAt" class="btn btn-outline-success" @click="restoreTag(tag.ID)" title="Geri Yükle">
<i class="fas fa-trash-restore"></i>
</button>
<button v-if="tag.DeletedAt" class="btn btn-outline-dark" @click="hardDeleteTag(tag.ID)" title="Kalıcı Sil">
<i class="fas fa-times"></i>
</button>
</div>
</td>
</tr>
<tr v-if="tags.length === 0">
<td colspan="5" class="text-center py-4">Kayıt bulunamadı.</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<nav v-if="totalPages > 1" class="mt-4">
<ul class="pagination justify-content-center">
<li class="page-item" :class="{ disabled: currentPage === 1 }">
<button class="page-link" @click="changePage(currentPage - 1)">Önceki</button>
</li>
<li v-for="page in totalPages" :key="page" class="page-item" :class="{ active: currentPage === page }">
<button class="page-link" @click="changePage(page)">{{ page }}</button>
</li>
<li class="page-item" :class="{ disabled: currentPage === totalPages }">
<button class="page-link" @click="changePage(currentPage + 1)">Sonraki</button>
</li>
</ul>
</nav>
</div>
</div>
<!-- Tag Modal -->
<div class="modal fade" id="tagModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ isEditing ? 'Etiket Düzenle' : 'Yeni Etiket Ekle' }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form @submit.prevent="saveTag">
<div class="mb-3">
<label class="form-label">Etiket Adı</label>
<input v-model="tagForm.name" type="text" class="form-control" required>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary me-2" data-bs-dismiss="modal">İptal</button>
<button type="submit" class="btn btn-primary" :disabled="saving">
<span v-if="saving" class="spinner-border spinner-border-sm me-2"></span>
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Tag, TagResponse } from '~~/types/tag';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const loading = ref(false);
const saving = ref(false);
const tags = ref<Tag[]>([]);
const currentPage = ref(1);
const totalPages = ref(1);
const filterTrashed = ref<'without' | 'with' | 'only'>('without');
const tagForm = ref({
ID: 0,
name: ''
});
const isEditing = ref(false);
let tagModal: any = null;
const fetchTags = async () => {
loading.value = true;
try {
const res = await $fetch<TagResponse>('/api/v1/admin/tags', {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
params: {
page: currentPage.value,
limit: 10,
...(filterTrashed.value !== 'without' && { trashed: filterTrashed.value })
}
});
if (res && res.data) {
tags.value = res.data;
totalPages.value = Math.ceil(res.meta.total / res.meta.per_page);
} else {
tags.value = [];
}
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Etiketler yüklenirken bir hata oluştu.', 'error');
} finally {
loading.value = false;
}
};
const changePage = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page;
fetchTags();
}
};
const changeFilter = (filter: 'without' | 'with' | 'only') => {
filterTrashed.value = filter;
currentPage.value = 1;
fetchTags();
};
const openModal = (tag: Tag | null = null) => {
if (tag) {
isEditing.value = true;
tagForm.value = { ID: tag.ID, name: tag.name };
} else {
isEditing.value = false;
tagForm.value = { ID: 0, name: '' };
}
const modalEl = document.getElementById('tagModal');
if (modalEl) {
// @ts-ignore
tagModal = new bootstrap.Modal(modalEl);
tagModal.show();
}
};
const saveTag = async () => {
saving.value = true;
try {
let url = '/api/v1/tags';
let method: 'POST' | 'PUT' = 'POST';
if (isEditing.value && tagForm.value.ID) {
url = `/api/v1/tags/${tagForm.value.ID}`;
method = 'PUT';
}
await $fetch(url, {
method: method,
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
body: { name: tagForm.value.name }
});
if (tagModal) tagModal.hide();
Swal.fire('Başarılı', `Etiket ${isEditing.value ? 'güncellendi' : 'eklendi'}.`, 'success');
fetchTags();
} catch (error: any) {
console.error(error);
Swal.fire('Hata', error.data?.message || 'İşlem başarısız.', 'error');
} finally {
saving.value = false;
}
};
const deleteTag = async (id: number) => {
const result = await Swal.fire({
title: 'Emin misiniz?',
text: "Bu etiketi çöp kutusuna taşımak istediğinize emin misiniz?",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Evet, Sil',
cancelButtonText: 'İptal'
});
if (result.isConfirmed) {
try {
await $fetch(`/api/v1/tags/${id}`, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi', 'Etiket çöp kutusuna taşındı.', 'success');
fetchTags();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
}
};
const hardDeleteTag = async (id: number) => {
const result = await Swal.fire({
title: 'DİKKAT! Kalıcı Silme',
text: "Bu işlem geri alınamaz! Etiketi tamamen silmek istediğinize emin misiniz?",
icon: 'error',
showCancelButton: true,
confirmButtonText: 'Evet, Tamamen Sil',
cancelButtonText: 'İptal'
});
if (result.isConfirmed) {
try {
await $fetch(`/api/v1/admin/tags/${id}/hard`, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi', 'Etiket kalıcı olarak silindi.', 'success');
fetchTags();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Kalıcı silme işlemi başarısız.', 'error');
}
}
};
const restoreTag = async (id: number) => {
try {
await $fetch(`/api/v1/admin/tags/${id}/restore`, {
method: 'POST',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Geri Yüklendi', 'Etiket başarıyla geri yüklendi.', 'success');
fetchTags();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Geri yükleme işlemi başarısız.', 'error');
}
};
onMounted(() => {
fetchTags();
});
</script>

14
app/pages/admin/index.vue Normal file
View File

@@ -0,0 +1,14 @@
<template>
<div>
admin index
</div>
</template>
<script lang="ts" setup>
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
</script>
<style></style>

View File

@@ -0,0 +1,501 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Güvenlik & Yönetimi</h2>
</div>
<ul class="nav nav-tabs mb-4" id="securityTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="whitelist-tab" data-bs-toggle="tab" data-bs-target="#whitelist"
type="button" role="tab" aria-controls="whitelist" aria-selected="true">CORS Whitelist</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="blacklist-tab" data-bs-toggle="tab" data-bs-target="#blacklist"
type="button" role="tab" aria-controls="blacklist" aria-selected="false">CORS Blacklist</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="ratelimit-tab" data-bs-toggle="tab" data-bs-target="#ratelimit"
type="button" role="tab" aria-controls="ratelimit" aria-selected="false">Rate Limit</button>
</li>
</ul>
<div class="tab-content" id="securityTabContent">
<!-- WHITELIST TAB -->
<div class="tab-pane fade show active" id="whitelist" role="tabpanel" aria-labelledby="whitelist-tab">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center bg-white">
<h5 class="mb-0">Whitelist Origins</h5>
<div class="d-flex align-items-center">
<div class="form-check form-switch me-3 mb-0">
<input class="form-check-input" type="checkbox" v-model="showDeleted"
id="whitelistDeleted">
<label class="form-check-label" for="whitelistDeleted">Silinenler</label>
</div>
<button class="btn btn-success btn-sm" @click="openModal('whitelist')"><i
class="fas fa-plus me-2"></i> Yeni
Ekle</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Origin</th>
<th>ıklama</th>
<th>Durum</th>
<th class="text-end" width="150">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="item in whitelistItems" :key="item.ID">
<td>{{ item.origin }}</td>
<td>{{ item.description }}</td>
<td>
<span class="badge" :class="item.is_active ? 'bg-success' : 'bg-secondary'">
{{ item.is_active ? 'Aktif' : 'Pasif' }}
</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<template v-if="!item.DeletedAt">
<button class="btn btn-outline-primary"
@click="openModal('whitelist', item)" title="Düzenle">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger"
@click="deleteItem('whitelist', item.ID)" title="Sil">
<i class="fas fa-trash"></i>
</button>
</template>
<button v-else class="btn btn-outline-success"
@click="restoreItem('whitelist', item.ID)" title="Geri Yükle">
<i class="fas fa-trash-restore"></i>
</button>
</div>
</td>
</tr>
<tr v-if="whitelistItems.length === 0">
<td colspan="4" class="text-center py-4">Kayıt bulunamadı.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- BLACKLIST TAB -->
<div class="tab-pane fade" id="blacklist" role="tabpanel" aria-labelledby="blacklist-tab">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center bg-white">
<h5 class="mb-0">Blacklist Origins</h5>
<div class="d-flex align-items-center">
<div class="form-check form-switch me-3 mb-0">
<input class="form-check-input" type="checkbox" v-model="showDeleted"
id="blacklistDeleted">
<label class="form-check-label" for="blacklistDeleted">Silinenler</label>
</div>
<button class="btn btn-success btn-sm" @click="openModal('blacklist')"><i
class="fas fa-plus me-2"></i>
Yeni
Ekle</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Origin</th>
<th>Sebep</th>
<th>Durum</th>
<th class="text-end" width="150">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="item in blacklistItems" :key="item.ID">
<td>{{ item.origin }}</td>
<td>{{ item.reason }}</td>
<td>
<span class="badge" :class="item.is_active ? 'bg-danger' : 'bg-secondary'">
{{ item.is_active ? 'Aktif' : 'Pasif' }}
</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<template v-if="!item.DeletedAt">
<button class="btn btn-outline-primary"
@click="openModal('blacklist', item)" title="Düzenle">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger"
@click="deleteItem('blacklist', item.ID)" title="Sil">
<i class="fas fa-trash"></i>
</button>
</template>
<button v-else class="btn btn-outline-success"
@click="restoreItem('blacklist', item.ID)" title="Geri Yükle">
<i class="fas fa-trash-restore"></i>
</button>
</div>
</td>
</tr>
<tr v-if="blacklistItems.length === 0">
<td colspan="4" class="text-center py-4">Kayıt bulunamadı.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- RATE LIMIT TAB -->
<div class="tab-pane fade" id="ratelimit" role="tabpanel" aria-labelledby="ratelimit-tab">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center bg-white">
<h5 class="mb-0">Rate Limits</h5>
<div class="d-flex align-items-center">
<div class="form-check form-switch me-3 mb-0">
<input class="form-check-input" type="checkbox" v-model="showDeleted"
id="ratelimitDeleted">
<label class="form-check-label" for="ratelimitDeleted">Silinenler</label>
</div>
<button class="btn btn-success btn-sm" @click="openModal('ratelimit')"><i
class="fas fa-plus me-2"></i>
Yeni
Ekle</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>İsim</th>
<th>Max İstek / Süre (sn)</th>
<th>ıklama</th>
<th>Durum</th>
<th class="text-end" width="150">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="item in rateLimitItems" :key="item.ID">
<td>{{ item.name }}</td>
<td>{{ item.max_requests }} / {{ item.window_seconds }}sn</td>
<td>{{ item.description }}</td>
<td>
<span class="badge" :class="item.is_active ? 'bg-success' : 'bg-secondary'">
{{ item.is_active ? 'Aktif' : 'Pasif' }}
</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<template v-if="!item.DeletedAt">
<button class="btn btn-outline-primary"
@click="openModal('ratelimit', item)" title="Düzenle">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger"
@click="deleteItem('ratelimit', item.ID)" title="Sil">
<i class="fas fa-trash"></i>
</button>
</template>
<button v-else class="btn btn-outline-success"
@click="restoreItem('ratelimit', item.ID)" title="Geri Yükle">
<i class="fas fa-trash-restore"></i>
</button>
</div>
</td>
</tr>
<tr v-if="rateLimitItems.length === 0">
<td colspan="5" class="text-center py-4">Kayıt bulunamadı.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="securityModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ modalTitle }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form @submit.prevent="saveItem">
<!-- Common Fields -->
<div v-if="activeTab === 'whitelist' || activeTab === 'blacklist'" class="mb-3">
<label class="form-label">Origin (URL)</label>
<input v-model="formData.origin" type="text" class="form-control"
placeholder="http://example.com" required>
</div>
<div v-if="activeTab === 'whitelist'" class="mb-3">
<label class="form-label">ıklama</label>
<input v-model="formData.description" type="text" class="form-control">
</div>
<div v-if="activeTab === 'blacklist'" class="mb-3">
<label class="form-label">Sebep</label>
<input v-model="formData.reason" type="text" class="form-control">
</div>
<!-- Rate Limit Fields -->
<div v-if="activeTab === 'ratelimit'" class="mb-3">
<label class="form-label">İsim (Endpoint/Key)</label>
<input v-model="formData.name" type="text" class="form-control" required>
</div>
<div v-if="activeTab === 'ratelimit'" class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Max İstek</label>
<input v-model="formData.max_requests" type="number" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Süre (Saniye)</label>
<input v-model="formData.window_seconds" type="number" class="form-control"
required>
</div>
</div>
<div v-if="activeTab === 'ratelimit'" class="mb-3">
<label class="form-label">ıklama</label>
<input v-model="formData.description" type="text" class="form-control">
</div>
<div class="mb-3">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" v-model="formData.is_active"
id="isActiveCheck">
<label class="form-check-label" for="isActiveCheck">Aktif</label>
</div>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary me-2"
data-bs-dismiss="modal">İptal</button>
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { CorsWhitelistItem, CorsBlacklistItem, RateLimitItem } from '~~/types/security';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const loading = ref(false);
const activeTab = ref<'whitelist' | 'blacklist' | 'ratelimit'>('whitelist');
const showDeleted = ref(false); // Global toggle for simplicity, or could be per tab
const whitelistItems = ref<CorsWhitelistItem[]>([]);
const blacklistItems = ref<CorsBlacklistItem[]>([]);
const rateLimitItems = ref<RateLimitItem[]>([]);
const formData = ref<any>({});
const isEditing = ref(false);
let securityModal: any = null;
const modalTitle = computed(() => {
const action = isEditing.value ? 'Düzenle' : 'Ekle';
if (activeTab.value === 'whitelist') return `Whitelist ${action}`;
if (activeTab.value === 'blacklist') return `Blacklist ${action}`;
return `Rate Limit ${action}`;
});
// Watch for tab or filter changes
watch([activeTab, showDeleted], () => {
fetchItems();
});
// -- API HANDLERS --
const fetchItems = async () => {
try {
const query = showDeleted.value ? { deleted: 'true' } : {};
const headers = { Authorization: `Bearer ${(authData.value as any)?.accessToken}` };
if (activeTab.value === 'whitelist') {
const res = await $fetch<any>('/api/v1/admin/cors/whitelist', { baseURL: config.public.BASE_API_URL, headers, query });
whitelistItems.value = res.items || [];
} else if (activeTab.value === 'blacklist') {
const res = await $fetch<any>('/api/v1/admin/cors/blacklist', { baseURL: config.public.BASE_API_URL, headers, query });
blacklistItems.value = res.items || [];
} else {
const res = await $fetch<any>('/api/v1/admin/rate-limit', { baseURL: config.public.BASE_API_URL, headers, query });
rateLimitItems.value = res.items || [];
}
} catch (error) {
console.error('Fetch error:', error);
// Fallback or empty list on error
if (activeTab.value === 'whitelist') whitelistItems.value = [];
else if (activeTab.value === 'blacklist') blacklistItems.value = [];
else rateLimitItems.value = [];
}
};
const openModal = (tab: 'whitelist' | 'blacklist' | 'ratelimit', item: any = null) => {
activeTab.value = tab;
if (item) {
isEditing.value = true;
formData.value = { ...item };
} else {
isEditing.value = false;
formData.value = { is_active: true };
}
const modalEl = document.getElementById('securityModal');
if (modalEl) {
// @ts-ignore
securityModal = new bootstrap.Modal(modalEl);
securityModal.show();
}
};
const saveItem = async () => {
loading.value = true;
try {
let endpoint = '';
if (activeTab.value === 'whitelist') endpoint = '/api/v1/admin/cors/whitelist';
else if (activeTab.value === 'blacklist') endpoint = '/api/v1/admin/cors/blacklist';
else endpoint = '/api/v1/admin/rate-limit';
let method: 'POST' | 'PUT' = 'POST';
if (isEditing.value && formData.value.ID) {
endpoint += `/${formData.value.ID}`;
method = 'PUT';
}
// Convert numbers for RateLimit
if (activeTab.value === 'ratelimit') {
formData.value.max_requests = Number(formData.value.max_requests);
formData.value.window_seconds = Number(formData.value.window_seconds);
}
await $fetch(endpoint, {
method,
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
body: formData.value
});
if (securityModal) securityModal.hide();
Swal.fire('Başarılı', 'İşlem başarıyla tamamlandı.', 'success');
fetchItems();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'İşlem başarısız.', 'error');
} finally {
loading.value = false;
}
};
const deleteItem = async (tab: string, id: number) => {
const result = await Swal.fire({
title: 'Silme Tipi?',
text: "Kayıt nasıl silinsin?",
icon: 'warning',
showCancelButton: true,
showDenyButton: true,
confirmButtonText: 'Soft Sil (Arşivle)',
denyButtonText: 'Hard Sil (Kalıcı)',
cancelButtonText: 'İptal'
});
if (result.isDismissed) return;
const isHard = result.isDenied;
try {
let endpoint = '';
if (tab === 'whitelist') endpoint = `/api/v1/admin/cors/whitelist/${id}`;
else if (tab === 'blacklist') endpoint = `/api/v1/admin/cors/blacklist/${id}`;
else endpoint = `/api/v1/admin/rate-limit/${id}`;
if (isHard) endpoint += '/hard';
await $fetch(endpoint, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi!', isHard ? 'Kayıt kalıcı olarak silindi.' : 'Kayıt arşivlendi (soft delete).', 'success');
fetchItems();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
};
const restoreItem = async (tab: string, id: number) => {
const result = await Swal.fire({
title: 'Emin misiniz?',
text: "Kayıt geri yüklenecek.",
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Geri Yükle',
cancelButtonText: 'İptal'
});
if (!result.isConfirmed) return;
try {
let endpoint = '';
if (tab === 'whitelist') endpoint = `/api/v1/admin/cors/whitelist/${id}/restore`;
else if (tab === 'blacklist') endpoint = `/api/v1/admin/cors/blacklist/${id}/restore`;
else endpoint = `/api/v1/admin/rate-limit/${id}/restore`;
await $fetch(endpoint, {
method: 'POST', // Assuming POST for restore action
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Başarılı', 'Kayıt geri yüklendi.', 'success');
fetchItems();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Geri yükleme başarısız. Endpoint mevcut olmayabilir.', 'error');
}
};
onMounted(() => {
// Bootstrap tabs events
const tabEls = document.querySelectorAll('button[data-bs-toggle="tab"]');
tabEls.forEach(tabEl => {
tabEl.addEventListener('shown.bs.tab', (event: any) => {
const targetId = event.target.getAttribute('data-bs-target');
if (targetId === '#whitelist') activeTab.value = 'whitelist';
if (targetId === '#blacklist') activeTab.value = 'blacklist';
if (targetId === '#ratelimit') activeTab.value = 'ratelimit';
// fetchItems() is called by watcher
});
});
fetchItems(); // Initial fetch
});
</script>
```

View File

@@ -0,0 +1,578 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Ayarlar</h2>
</div>
<ul class="nav nav-tabs mb-4" id="settingsTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="general-tab" data-bs-toggle="tab" data-bs-target="#general" type="button"
role="tab" aria-controls="general" aria-selected="true">Genel Ayarlar</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="hero-tab" data-bs-toggle="tab" data-bs-target="#hero" type="button" role="tab"
aria-controls="hero" aria-selected="false">Hero (Banner) Ayarları</button>
</li>
</ul>
<div class="tab-content" id="settingsTabContent">
<!-- GENERAL SETTINGS TAB -->
<div class="tab-pane fade show active" id="general" role="tabpanel" aria-labelledby="general-tab">
<div class="card">
<div class="card-body">
<form @submit.prevent="updateSettings">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Site Başlığı</label>
<input v-model="settingForm.title" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Slogan</label>
<input v-model="settingForm.slogan" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Telefon</label>
<input v-model="settingForm.phone" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Email</label>
<input v-model="settingForm.email" type="email" class="form-control">
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Adres</label>
<textarea v-model="settingForm.address" class="form-control" rows="2"></textarea>
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Harita Embed Code</label>
<textarea v-model="settingForm.map_embed" class="form-control" rows="3"></textarea>
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Copyright Metni</label>
<input v-model="settingForm.copyright" type="text" class="form-control">
</div>
</div>
<h5 class="mt-4">SEO Ayarları</h5>
<hr>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Meta Başlık</label>
<input v-model="settingForm.meta_title" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Meta ıklama</label>
<input v-model="settingForm.meta_description" type="text" class="form-control">
</div>
</div>
<h5 class="mt-4">Sosyal Medya</h5>
<hr>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Facebook</label>
<input v-model="settingForm.facebook" type="text" class="form-control">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Twitter / X</label>
<input v-model="settingForm.x" type="text" class="form-control">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Instagram</label>
<input v-model="settingForm.instagram" type="text" class="form-control">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Linkedin</label>
<input v-model="settingForm.linkedin" type="text" class="form-control">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Pinterest</label>
<input v-model="settingForm.pinterest" type="text" class="form-control">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Whatsapp</label>
<input v-model="settingForm.whatsapp" type="text" class="form-control">
</div>
</div>
<h5 class="mt-4">Logolar</h5>
<hr>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Beyaz Logo (Dark Mod için)</label>
<input @change="handleFileUpload($event, 'w_logo')" type="file" class="form-control">
<div v-if="settingForm.w_logo && typeof settingForm.w_logo === 'string'" class="mt-2">
<img :src="config.public.BASE_API_URL + settingForm.w_logo" height="50" class="bg-dark p-1">
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Siyah Logo (Light Mod için)</label>
<input @change="handleFileUpload($event, 'b_logo')" type="file" class="form-control">
<div v-if="settingForm.b_logo && typeof settingForm.b_logo === 'string'" class="mt-2">
<img :src="config.public.BASE_API_URL + settingForm.b_logo" height="50" class="bg-light border p-1">
</div>
</div>
</div>
<h5 class="mt-4">Resim Optimizasyon Ayarları (Logo İçin)</h5>
<hr>
<div class="row bg-light p-3 rounded mx-1">
<div class="col-md-3 mb-2">
<label class="form-label">Format</label>
<select v-model="imageSettings.format" class="form-select">
<option value="avif">AVIF</option>
<option value="webp">WEBP</option>
<option value="png">PNG</option>
<option value="jpg">JPG</option>
</select>
</div>
<div class="col-md-3 mb-2">
<label class="form-label">Kalite (1-100)</label>
<input v-model="imageSettings.quality" type="number" min="1" max="100" class="form-control">
</div>
<div class="col-md-3 mb-2">
<label class="form-label">Genişlik (px)</label>
<input v-model="imageSettings.width" type="number" class="form-control" placeholder="Opsiyonel">
</div>
<div class="col-md-3 mb-2">
<label class="form-label">Yükseklik (px)</label>
<input v-model="imageSettings.height" type="number" class="form-control" placeholder="Opsiyonel">
</div>
</div>
<div class="mt-4 text-end">
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
<!-- HERO SETTINGS TAB -->
<div class="tab-pane fade" id="hero" role="tabpanel" aria-labelledby="hero-tab">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center bg-white">
<h5 class="mb-0">Hero Listesi</h5>
<button class="btn btn-success btn-sm" @click="openHeroModal()"><i class="fas fa-plus me-2"></i> Yeni
Ekle</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Resim</th>
<th>Başlık</th>
<th>Alt Başlıklar</th>
<th>Durum</th>
<th class="text-end" width="100">İşlemler</th>
</tr>
</thead>
<tbody>
<tr v-for="hero in heroes" :key="hero.ID">
<td>
<img v-if="hero.image" :src="config.public.BASE_API_URL + hero.image"
style="max-width: 60px; height: 40px; object-fit: cover;" class="rounded border">
<span v-else>-</span>
</td>
<td>{{ hero.title }}</td>
<td>
<small class="d-block text-muted">{{ hero.text1 }}</small>
<small class="d-block text-muted">{{ hero.text2 }}</small>
</td>
<td>
<span class="badge" :class="hero.is_active ? 'bg-success' : 'bg-secondary'">
{{ hero.is_active ? 'Aktif' : 'Pasif' }}
</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<button class="btn btn-outline-primary" @click="openHeroModal(hero)" title="Düzenle">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger" @click="deleteHero(hero.ID)" title="Sil">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
<tr v-if="heroes.length === 0">
<td colspan="5" class="text-center py-4">Kayıt bulunamadı.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- HERO MODAL -->
<div class="modal fade" id="heroModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ isEditingHero ? 'Hero Düzenle' : 'Yeni Hero Ekle' }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form @submit.prevent="saveHero">
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label">Başlık</label>
<input v-model="heroForm.title" type="text" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Text 1</label>
<input v-model="heroForm.text1" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Text 2</label>
<input v-model="heroForm.text2" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Text 4</label>
<input v-model="heroForm.text4" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Text 5</label>
<input v-model="heroForm.text5" type="text" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Renk</label>
<div class="input-group">
<input v-model="heroForm.color" type="color" class="form-control form-control-color"
title="Renk Seçin">
<input v-model="heroForm.color" type="text" class="form-control" placeholder="#ffffff">
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Durum</label>
<div class="form-check form-switch mt-2">
<input class="form-check-input" type="checkbox" v-model="heroForm.is_active" id="heroActive">
<label class="form-check-label" for="heroActive">Aktif</label>
</div>
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Görsel</label>
<input @change="handleHeroFileUpload($event)" type="file" class="form-control">
<div v-if="heroForm.image && typeof heroForm.image === 'string'" class="mt-2">
<small>Mevcut Görsel:</small>
<img :src="config.public.BASE_API_URL + heroForm.image" height="80" class="d-block mt-1 rounded">
</div>
</div>
<div class="col-md-12 mb-3">
<div class="p-3 bg-light rounded border">
<h6>Resim Optimizasyon Ayarları</h6>
<div class="row">
<div class="col-md-3 mb-2">
<label class="form-label">Format</label>
<select v-model="imageSettings.format" class="form-select">
<option value="avif">AVIF</option>
<option value="webp">WEBP</option>
<option value="png">PNG</option>
<option value="jpg">JPG</option>
</select>
</div>
<div class="col-md-3 mb-2">
<label class="form-label">Kalite</label>
<input v-model="imageSettings.quality" type="number" min="1" max="100" class="form-control">
</div>
<div class="col-md-3 mb-2">
<label class="form-label">Genişlik</label>
<input v-model="imageSettings.width" type="number" class="form-control" placeholder="Opsiyonel">
</div>
<div class="col-md-3 mb-2">
<label class="form-label">Yükseklik</label>
<input v-model="imageSettings.height" type="number" class="form-control"
placeholder="Opsiyonel">
</div>
</div>
</div>
</div>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary me-2" data-bs-dismiss="modal">İptal</button>
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Setting } from '~~/types/setting';
import type { Hero } from '~~/types/hero';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const loading = ref(false);
// --- SETTINGS ---
const settingForm = ref<Partial<Setting>>({});
const w_logo_file = ref<File | null>(null);
const b_logo_file = ref<File | null>(null);
const imageSettings = ref({
format: 'avif',
quality: 80,
width: null as number | null,
height: null as number | null
});
// Fetch Settings
const fetchSettings = async () => {
try {
const data = await $fetch<Setting>('/api/v1/setting', {
baseURL: config.public.BASE_API_URL,
});
if (data) {
settingForm.value = { ...data };
}
} catch (error) {
console.error('Settings fetch error:', error);
}
};
const handleFileUpload = (event: Event, type: 'w_logo' | 'b_logo') => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) {
if (type === 'w_logo') w_logo_file.value = target.files[0];
if (type === 'b_logo') b_logo_file.value = target.files[0];
}
};
const optimizeImage = async (file: File): Promise<File> => {
const formData = new FormData();
formData.append('file', file);
formData.append('format', imageSettings.value.format);
formData.append('quality', String(imageSettings.value.quality));
if (imageSettings.value.width) formData.append('width', String(imageSettings.value.width));
if (imageSettings.value.height) formData.append('height', String(imageSettings.value.height));
const optimizedBlob = await $fetch<Blob>('/api/optimize', {
method: 'POST',
body: formData,
responseType: 'blob'
});
const ext = imageSettings.value.format === 'jpg' ? 'jpeg' : imageSettings.value.format;
const filename = `img-${Date.now()}-${Math.floor(Math.random() * 10000)}.${ext === 'jpeg' ? 'jpg' : ext}`;
const optimizedFile = new File([optimizedBlob], filename, {
type: `image/${ext}`
});
return optimizedFile;
};
const updateSettings = async () => {
loading.value = true;
try {
const formData = new FormData();
Object.keys(settingForm.value).forEach(key => {
const value = (settingForm.value as any)[key];
if (value !== null && value !== undefined && key !== 'w_logo' && key !== 'b_logo' && key !== 'ID' && key !== 'CreatedAt' && key !== 'UpdatedAt' && key !== 'DeletedAt') {
formData.append(key, String(value));
}
});
if (w_logo_file.value) {
const optimized = await optimizeImage(w_logo_file.value);
formData.append('w_logo', optimized);
}
if (b_logo_file.value) {
const optimized = await optimizeImage(b_logo_file.value);
formData.append('b_logo', optimized);
}
formData.append('is_active', 'true');
let url = '/api/v1/setting';
let method: 'POST' | 'PUT' = 'POST';
if (settingForm.value.ID) {
url = `/api/v1/setting/${settingForm.value.ID}`;
method = 'PUT';
}
await $fetch(url, {
method: method,
baseURL: config.public.BASE_API_URL,
body: formData,
headers: {
Authorization: `Bearer ${(authData.value as any)?.accessToken}`
}
});
Swal.fire('Başarılı', 'Ayarlar güncellendi.', 'success');
fetchSettings(); // Refresh
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Güncelleme sırasında hata oluştu.', 'error');
} finally {
loading.value = false;
}
};
// --- HERO ---
const heroes = ref<Hero[]>([]);
const heroForm = ref<Partial<Hero>>({});
const heroImageFile = ref<File | null>(null);
const isEditingHero = ref(false);
let heroModal: any = null;
const fetchHeroes = async () => {
try {
const response = await $fetch<any>('/api/v1/heroes', {
baseURL: config.public.BASE_API_URL,
});
if (response && Array.isArray(response.data)) {
heroes.value = response.data;
} else if (Array.isArray(response)) {
heroes.value = response;
} else if (response && typeof response === 'object') {
heroes.value = [response]; // Wrap single object
} else {
heroes.value = [];
}
} catch (error) {
console.error('Hero fetch error:', error);
}
};
const openHeroModal = (hero: Hero | null = null) => {
if (hero) {
isEditingHero.value = true;
heroForm.value = { ...hero };
} else {
isEditingHero.value = false;
heroForm.value = { is_active: true };
}
heroImageFile.value = null;
// Initialize Modal
const modalEl = document.getElementById('heroModal');
if (modalEl) {
// @ts-ignore
heroModal = new bootstrap.Modal(modalEl);
heroModal.show();
}
};
const handleHeroFileUpload = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) {
heroImageFile.value = target.files[0];
}
};
const saveHero = async () => {
loading.value = true;
try {
const formData = new FormData();
Object.keys(heroForm.value).forEach(key => {
const value = (heroForm.value as any)[key];
if (value !== null && value !== undefined && key !== 'image' && key !== 'ID' && key !== 'CreatedAt' && key !== 'UpdatedAt' && key !== 'DeletedAt') {
formData.append(key, String(value));
}
});
if (heroImageFile.value) {
const optimized = await optimizeImage(heroImageFile.value);
formData.append('image', optimized);
}
// Ensure is_active sent as string 'true'/'false' if backend expects it
// formData.set('is_active', String(heroForm.value.is_active));
let url = '/api/v1/hero';
let method: 'POST' | 'PUT' = 'POST';
if (isEditingHero.value && heroForm.value.ID) {
url = `/api/v1/hero/${heroForm.value.ID}`;
method = 'PUT';
}
await $fetch(url, {
method: method,
baseURL: config.public.BASE_API_URL,
body: formData,
headers: {
Authorization: `Bearer ${(authData.value as any)?.accessToken}`
}
});
if (heroModal) heroModal.hide();
Swal.fire('Başarılı', `Hero ${isEditingHero.value ? 'güncellendi' : 'eklendi'}.`, 'success');
fetchHeroes();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'İşlem başarısız.', 'error');
} finally {
loading.value = false;
}
};
const deleteHero = async (id: number) => {
const result = await Swal.fire({
title: 'Emin misiniz?',
text: "Bu hero kaydını silmek istediğinize emin misiniz?",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Evet, Sil',
cancelButtonText: 'İptal'
});
if (result.isConfirmed) {
try {
await $fetch(`/api/v1/hero/${id}`, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: {
Authorization: `Bearer ${(authData.value as any)?.accessToken}`
}
});
Swal.fire('Silindi!', 'Kayıt başarıyla silindi.', 'success');
fetchHeroes();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
}
};
onMounted(() => {
fetchSettings();
fetchHeroes();
});
</script>
<style scoped>
/* Tabs styling */
.nav-tabs .nav-link {
color: #495057;
}
.nav-tabs .nav-link.active {
font-weight: bold;
color: #0d6efd;
}
</style>

View File

@@ -0,0 +1,457 @@
<template>
<div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Kullanıcı Yönetimi</h2>
</div>
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center bg-white">
<h5 class="mb-0">Kullanıcı Listesi</h5>
<div class="d-flex align-items-center">
<div class="form-check form-switch me-3 mb-0">
<input class="form-check-input" type="checkbox" v-model="showDeleted" id="userDeleted">
<label class="form-check-label" for="userDeleted">Silinenler</label>
</div>
<button class="btn btn-success btn-sm" @click="openModal(null)"><i class="fas fa-plus me-2"></i> Yeni
Kullanıcı</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>ID</th>
<th>Avatar</th>
<th>Kullanıcı Adı</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.ID">
<td>{{ user.ID }}</td>
<td>
<img :src="getAvatarUrl(user)" alt="Avatar" class="rounded-circle border" width="40" height="40"
style="object-fit: cover;"
onerror="this.src='https://ui-avatars.com/api/?name=User&background=random'">
</td>
<td>{{ user.username }}</td>
<td>
{{ user.email }}
<span v-if="user.email_verified" class="badge bg-success ms-1"><i class="fas fa-check"></i></span>
<span v-else class="badge bg-warning ms-1" title="Email Doğrulanmadı"><i
class="fas fa-exclamation"></i></span>
</td>
<td>
<span v-if="user.profiles && user.profiles.length > 0">
{{ user.profiles[0]?.first_name }} {{ user.profiles[0]?.last_name }}
</span>
<span v-else>-</span>
</td>
<td>
<span class="badge" :class="user.is_admin ? 'bg-primary' : 'bg-secondary'">
{{ user.is_admin ? 'Admin' : 'User' }}
</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<template v-if="!user.DeletedAt">
<button class="btn btn-outline-primary" @click="openModal(user)" title="Düzenle">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger" @click="deleteUser(user.ID)" title="Sil">
<i class="fas fa-trash"></i>
</button>
</template>
<template v-else>
<button class="btn btn-outline-success" @click="restoreUser(user.ID)" title="Geri Yükle">
<i class="fas fa-trash-restore"></i>
</button>
<button class="btn btn-outline-danger" @click="deleteUser(user.ID)" title="Kalıcı Sil">
<i class="fas fa-ban"></i>
</button>
</template>
</div>
</td>
</tr>
<tr v-if="users.length === 0">
<td colspan="6" class="text-center py-4">Kayıt bulunamadı.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="userModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ isEditing ? 'Kullanıcı Düzenle' : 'Yeni Kullanıcı Ekle' }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form @submit.prevent="saveUser">
<div class="mb-3">
<label class="form-label">Kullanıcı Adı</label>
<input v-model="formData.username" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input v-model="formData.email" type="email" class="form-control" required>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">İsim</label>
<input v-model="formData.first_name" type="text" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Soyisim</label>
<input v-model="formData.last_name" type="text" class="form-control" required>
</div>
</div>
<div class="mb-3">
<label class="form-label">Avatar</label>
<div class="d-flex align-items-center mb-2" v-if="formData.avatar_url">
<img :src="config.public.BASE_API_URL + formData.avatar_url" alt="Avatar Preview"
class="rounded-circle me-3"
style="width: 50px; height: 50px; object-fit: cover; border: 1px solid #dee2e6;">
<button type="button" class="btn btn-sm btn-outline-danger"
@click="formData.avatar_url = ''">Kaldır</button>
</div>
<input type="file" class="form-control" @change="handleFileUpload" accept="image/*">
<input v-model="formData.avatar_url" type="text" class="form-control mt-2"
placeholder="veya URL girin (/uploads/...)">
<!-- Image Optimization Settings -->
<div class="mt-3 p-3 border rounded bg-light">
<div class="d-flex justify-content-between align-items-center mb-2">
<small class="fw-bold text-primary"><i class="fas fa-magic me-1"></i> Resim Optimizasyonu</small>
<span class="badge bg-secondary" style="font-size: 0.7em">İsteğe Bağlı</span>
</div>
<div class="row g-2">
<div class="col-6 col-md-3">
<label class="form-label small text-muted">Format</label>
<select v-model="imageSettings.format" class="form-select form-select-sm">
<option value="avif">AVIF</option>
<option value="webp">WEBP</option>
<option value="png">PNG</option>
<option value="jpg">JPG</option>
</select>
</div>
<div class="col-6 col-md-3">
<label class="form-label small text-muted">Kalite (1-100)</label>
<input v-model="imageSettings.quality" type="number" class="form-control form-control-sm" min="1"
max="100">
</div>
<div class="col-6 col-md-3">
<label class="form-label small text-muted">Genişlik (px)</label>
<input v-model="imageSettings.width" type="number" class="form-control form-control-sm">
</div>
<div class="col-6 col-md-3">
<label class="form-label small text-muted">Yükseklik (px)</label>
<input v-model="imageSettings.height" type="number" class="form-control form-control-sm">
</div>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Şifre <span v-if="isEditing" class="text-muted small">(Boş bırakılırsa
değişmez)</span></label>
<input v-model="formData.password" type="password" class="form-control" :required="!isEditing"
minlength="6">
</div>
<div class="row mb-3">
<div class="col-md-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" v-model="formData.is_admin" id="isAdminCheck">
<label class="form-check-label" for="isAdminCheck">Admin Yetkisi</label>
</div>
</div>
<div class="col-md-6">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" v-model="formData.email_verified"
id="emailVerifiedCheck">
<label class="form-check-label" for="emailVerifiedCheck">Email Onaylı</label>
</div>
</div>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary me-2" data-bs-dismiss="modal">İptal</button>
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { User } from '~~/types/user';
import Swal from 'sweetalert2';
definePageMeta({
layout: 'admin',
middleware: 'admin'
});
const config = useRuntimeConfig();
const { data: authData } = useAuth();
const loading = ref(false);
const users = ref<User[]>([]);
const showDeleted = ref(false);
const formData = ref<any>({});
const isEditing = ref(false);
const avatarFile = ref<File | null>(null);
let userModal: any = null;
// -- API HANDLERS --
const fetchUsers = async () => {
try {
const endpoint = showDeleted.value ? '/api/v1/users/list/deleted' : '/api/v1/users/list';
const res = await $fetch<any>(endpoint, {
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
users.value = res.users || [];
} catch (error) {
console.error('Fetch error:', error);
users.value = [];
}
};
watch(showDeleted, () => {
fetchUsers();
});
const openModal = (user: User | null = null) => {
if (user) {
isEditing.value = true;
// Flatten the structure for the form
// Check if avatar_url is on user or profile
const avatarUrl = user.avatar_url || user.profiles?.[0]?.avatar_url || '';
formData.value = {
...user,
first_name: user.profiles?.[0]?.first_name || '',
last_name: user.profiles?.[0]?.last_name || '',
avatar_url: avatarUrl,
password: '' // Don't prefill password
};
} else {
isEditing.value = false;
formData.value = { is_admin: false, email_verified: false };
}
const modalEl = document.getElementById('userModal');
if (modalEl) {
// Reset image settings
imageSettings.value = {
format: 'avif',
quality: 80,
width: null,
height: null
};
// @ts-ignore
userModal = new bootstrap.Modal(modalEl);
userModal.show();
}
};
const getAvatarUrl = (user: User) => {
// Priority: avatar_url (from User or Profile) -> UI Avatar
// Check user.avatar_url first (if backend sends it on user object)
// Then check profiles[0].avatar
const avatarPath = user.avatar_url || user.profiles?.[0]?.avatar_url;
if (avatarPath) {
// If it's a full URL (e.g. googlelh3...) return as is
if (avatarPath.startsWith('http')) return avatarPath;
// If local path, prepend base API URL
return config.public.BASE_API_URL + avatarPath;
}
// Default: UI Avatars
const name = (user.profiles?.[0]?.first_name && user.profiles?.[0]?.last_name)
? `${user.profiles[0].first_name} ${user.profiles[0].last_name}`
: user.username;
return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=random&color=fff`;
};
const handleFileUpload = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) {
avatarFile.value = target.files[0];
}
};
const imageSettings = ref({
format: 'avif',
quality: 80,
width: null as number | null,
height: null as number | null
});
const optimizeImage = async (file: File): Promise<File> => {
const fd = new FormData();
fd.append('file', file);
fd.append('format', imageSettings.value.format);
fd.append('quality', String(imageSettings.value.quality));
if (imageSettings.value.width) fd.append('width', String(imageSettings.value.width));
if (imageSettings.value.height) fd.append('height', String(imageSettings.value.height));
const optimizedBlob = await $fetch<Blob>('/api/optimize', {
method: 'POST',
body: fd,
responseType: 'blob'
});
const ext = imageSettings.value.format === 'jpg' ? 'jpeg' : imageSettings.value.format;
const filename = `avatar-${Date.now()}.${ext === 'jpeg' ? 'jpg' : ext}`;
return new File([optimizedBlob], filename, { type: `image/${ext}` });
};
const saveUser = async () => {
loading.value = true;
try {
if (isEditing.value) {
// Context: PUT /api/v1/users/:id
// Using Multipart/Form-Data for strict compliance with backend requirements
const userId = formData.value.ID;
const fd = new FormData();
// Append Avatar if selected
if (avatarFile.value) {
try {
const optimized = await optimizeImage(avatarFile.value);
fd.append('avatar', optimized);
} catch (e) {
console.error(e);
fd.append('avatar', avatarFile.value);
}
}
// Append other fields
// Explicitly mapping known fields to ensure order and presence matches expectation,
// or just iterating keys. Iterating keys is safer for "all fields".
// However, we must ensure boolean -> string conversion.
Object.keys(formData.value).forEach(key => {
const val = formData.value[key];
// Filter out non-form fields and sensitive/system fields
if (key !== 'ID' && key !== 'profiles' && key !== 'avatar_url' && key !== 'CreatedAt' && key !== 'UpdatedAt' && key !== 'DeletedAt') {
if (key === 'password' && !val) return; // Skip empty password
fd.append(key, String(val));
}
});
await $fetch(`/api/v1/users/${userId}`, {
method: 'PUT',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` },
body: fd
});
// Reset file
avatarFile.value = null;
Swal.fire('Başarılı', 'Kullanıcı güncellendi.', 'success');
} else {
// Context: Create (Register) - POST JSON
await $fetch('/api/v1/auth/register', {
method: 'POST',
baseURL: config.public.BASE_API_URL,
body: formData.value
});
Swal.fire('Başarılı', 'Kullanıcı oluşturuldu.', 'success');
}
if (userModal) userModal.hide();
fetchUsers();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'İşlem başarısız.', 'error');
} finally {
loading.value = false;
}
};
const deleteUser = async (id: number) => {
const isHardDelete = showDeleted.value; // If showing deleted, then next action is hard delete
const result = await Swal.fire({
title: isHardDelete ? 'Kalıcı Silinecek?' : 'Silinecek?',
text: isHardDelete ? "Bu işlem geri alınamaz!" : "Kullanıcı arşivlenecek.",
icon: 'warning',
showCancelButton: true,
confirmButtonText: isHardDelete ? 'Evet, Kalıcı Sil' : 'Evet, Sil',
cancelButtonText: 'İptal',
confirmButtonColor: '#d33'
});
if (!result.isConfirmed) return;
try {
let endpoint = `/api/v1/users/${id}`;
if (isHardDelete) endpoint += '/hard';
await $fetch(endpoint, {
method: 'DELETE',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Silindi!', 'İşlem başarılı.', 'success');
fetchUsers();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Silme işlemi başarısız.', 'error');
}
};
const restoreUser = async (id: number) => {
const result = await Swal.fire({
title: 'Geri Yükle?',
text: "Kullanıcı aktif edilecek.",
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Evet',
cancelButtonText: 'İptal'
});
if (!result.isConfirmed) return;
try {
await $fetch(`/api/v1/users/${id}/restore`, {
method: 'POST',
baseURL: config.public.BASE_API_URL,
headers: { Authorization: `Bearer ${(authData.value as any)?.accessToken}` }
});
Swal.fire('Başarılı', 'Kullanıcı geri yüklendi.', 'success');
fetchUsers();
} catch (error) {
console.error(error);
Swal.fire('Hata', 'Geri yükleme başarısız.', 'error');
}
};
onMounted(() => {
fetchUsers();
});
</script>

177
app/pages/auth/login.vue Normal file
View File

@@ -0,0 +1,177 @@
<template>
<div class="login-area pt-120 pb-120">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-6 col-md-8">
<div class="login-form-wrap shadow-lg p-5 rounded bg-white">
<div class="login-header text-center mb-4">
<h2 class="mb-2">Giriş Yap</h2>
<p>
Hesabınız yok mu?
<NuxtLink to="/auth/register" class="text-primary fw-bold">Kayıt Olun</NuxtLink>
</p>
</div>
<form @submit.prevent="handleLogin">
<div class="mb-3">
<label for="email" class="form-label">E-posta Adresi</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
<input type="email" class="form-control" id="email" v-model="form.email"
placeholder="E-posta adresinizi girin" required>
</div>
<div v-if="errors.email" class="text-danger small mt-1">{{ errors.email }}</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Şifre</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input type="password" class="form-control" id="password" v-model="form.password"
placeholder="Şifrenizi girin" required>
</div>
<div v-if="errors.password" class="text-danger small mt-1">{{ errors.password }}</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="remember-me">
<label class="form-check-label" for="remember-me">Beni Hatırla</label>
</div>
<a href="#" class="text-muted small">Şifremi Unuttum?</a>
</div>
<!-- Turnstile Component -->
<ClientOnly>
<div class="mb-4 d-flex justify-content-center">
<NuxtTurnstile v-model="turnstileToken" />
</div>
</ClientOnly>
<button type="submit" class="btn btn-primary w-100 py-2" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2" role="status"
aria-hidden="true"></span>
{{ loading ? 'Giriş Yapılıyor...' : 'Giriş Yap' }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { loginSchema, type LoginInput } from '~~/utils/validations';
import Swal from 'sweetalert2';
definePageMeta({
middleware: 'guest-only',
auth: { unauthenticatedOnly: true, navigateAuthenticatedTo: '/' }
});
const { signIn } = useAuth();
const router = useRouter();
// Reactive form state
const form = reactive<LoginInput>({
email: '',
password: ''
});
// Validation errors state
const errors = reactive<Partial<Record<keyof LoginInput, string>>>({});
const loading = ref(false);
const turnstileToken = ref('');
const validate = () => {
const result = loginSchema.safeParse(form);
// Clear previous errors
Object.keys(errors).forEach(key => delete errors[key as keyof LoginInput]);
if (!result.success) {
result.error.errors.forEach(err => {
if (err.path[0]) {
errors[err.path[0] as keyof LoginInput] = err.message;
}
});
return false;
}
return true;
};
const handleLogin = async () => {
if (!validate()) return;
if (!turnstileToken.value) {
Swal.fire({
icon: 'warning',
title: 'Güvenlik Kontrolü',
text: 'Lütfen Turnstile güvenlik kontrolünü tamamlayın.',
timer: 3000,
toast: true,
position: 'top-end',
showConfirmButton: false
});
return;
}
loading.value = true;
try {
const result = await signIn('credentials', {
email: form.email,
password: form.password,
redirect: false,
});
if (result?.error) {
Swal.fire({
icon: 'error',
title: 'Giriş Başarısız',
text: 'E-posta veya şifre hatalı.',
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000
});
} else {
Swal.fire({
icon: 'success',
title: 'Giriş Başarılı',
text: 'Yönlendiriliyorsunuz...',
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 1500
});
setTimeout(() => {
router.push('/');
}, 1000);
}
} catch (error) {
console.error(error);
Swal.fire({
icon: 'error',
title: 'Hata',
text: 'Bir şeyler ters gitti, lütfen tekrar deneyin.',
});
} finally {
loading.value = false;
}
};
</script>
<style scoped>
.login-area {
background-color: #f8f9fa;
min-height: 80vh;
display: flex;
align-items: center;
}
.login-form-wrap {
border-top: 5px solid #0d6efd;
}
</style>

222
app/pages/auth/register.vue Normal file
View File

@@ -0,0 +1,222 @@
<template>
<div class="register-area pt-120 pb-120">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8 col-md-10">
<div class="register-form-wrap shadow-lg p-5 rounded bg-white">
<div class="register-header text-center mb-4">
<h2 class="mb-2">Hesap Oluşturun</h2>
<p>
Zaten hesabınız var mı?
<NuxtLink to="/auth/login" class="text-primary fw-bold">Giriş Yapın</NuxtLink>
</p>
</div>
<form @submit.prevent="handleRegister">
<div class="mb-3">
<label for="username" class="form-label">Kullanıcı Adı</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text" class="form-control" id="username" v-model="form.username"
placeholder="Kullanıcı adı seçin" required>
</div>
<div v-if="errors.username" class="text-danger small mt-1">{{ errors.username }}</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="first_name" class="form-label">Ad</label>
<input type="text" class="form-control" id="first_name" v-model="form.first_name"
placeholder="Adınız" required>
<div v-if="errors.first_name" class="text-danger small mt-1">{{ errors.first_name }}
</div>
</div>
<div class="col-md-6 mb-3">
<label for="last_name" class="form-label">Soyad</label>
<input type="text" class="form-control" id="last_name" v-model="form.last_name"
placeholder="Soyadınız" required>
<div v-if="errors.last_name" class="text-danger small mt-1">{{ errors.last_name }}
</div>
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">E-posta Adresi</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
<input type="email" class="form-control" id="email" v-model="form.email"
placeholder="E-posta adresiniz" required>
</div>
<div v-if="errors.email" class="text-danger small mt-1">{{ errors.email }}</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="password" class="form-label">Şifre</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input type="password" class="form-control" id="password"
v-model="form.password" placeholder="Şifreniz" required>
</div>
<div v-if="errors.password" class="text-danger small mt-1">{{ errors.password }}
</div>
</div>
<div class="col-md-6 mb-3">
<label for="passwordConfirm" class="form-label">Şifre Tekrar</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input type="password" class="form-control" id="passwordConfirm"
v-model="form.passwordConfirm" placeholder="Şifrenizi tekrar girin"
required>
</div>
<div v-if="errors.passwordConfirm" class="text-danger small mt-1">{{
errors.passwordConfirm }}</div>
</div>
</div>
<!-- Turnstile Component -->
<ClientOnly>
<div class="mb-4 d-flex justify-content-center">
<NuxtTurnstile v-model="turnstileToken" />
</div>
</ClientOnly>
<button type="submit" class="btn btn-success w-100 py-2" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2" role="status"
aria-hidden="true"></span>
{{ loading ? 'Kayıt Yapılıyor...' : 'Kayıt Ol' }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { registerSchema, type RegisterInput } from '~~/utils/validations';
import Swal from 'sweetalert2';
definePageMeta({
middleware: 'guest-only',
auth: { unauthenticatedOnly: true, navigateAuthenticatedTo: '/' }
});
const config = useRuntimeConfig();
const router = useRouter();
const form = reactive<RegisterInput>({
username: '',
first_name: '',
last_name: '',
email: '',
password: '',
passwordConfirm: ''
});
const errors = reactive<Partial<Record<keyof RegisterInput, string>>>({});
const loading = ref(false);
const turnstileToken = ref('');
const validate = () => {
// Zod validasyonu
const result = registerSchema.safeParse(form);
// Clear previous errors
Object.keys(errors).forEach(key => delete errors[key as keyof RegisterInput]);
if (!result.success) {
result.error.errors.forEach(err => {
if (err.path[0]) {
errors[err.path[0] as keyof RegisterInput] = err.message;
}
});
return false;
}
return true;
};
const handleRegister = async () => {
if (!validate()) return;
if (!turnstileToken.value) {
Swal.fire({
icon: 'warning',
title: 'Güvenlik Kontrolü',
text: 'Lütfen Turnstile güvenlik kontrolünü tamamlayın.',
timer: 3000,
toast: true,
position: 'top-end',
showConfirmButton: false
});
return;
}
loading.value = true;
try {
const apiUrl = config.public.NUXT_PUBLIC_API_BASE || 'http://127.0.0.1:8080';
// Backend API çağrısı
const response = await fetch(`${apiUrl}/api/v1/auth/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: form.username,
first_name: form.first_name,
last_name: form.last_name,
email: form.email,
password: form.password
})
});
if (response.ok) {
const data = await response.json();
Swal.fire({
icon: 'success',
title: 'Kayıt Başarılı!',
text: data.message || 'Lütfen e-posta adresinizi doğrulayın.',
confirmButtonText: 'Giriş Yap'
}).then((result) => {
if (result.isConfirmed) {
router.push('/auth/login');
}
});
} else {
const errorData = await response.json();
Swal.fire({
icon: 'error',
title: 'Kayıt Hatası',
text: errorData.message || 'Kayıt işlemi sırasında bir hata oluştu.',
});
}
} catch (error) {
console.error(error);
Swal.fire({
icon: 'error',
title: 'Hata',
text: 'Sunucu ile iletişim hatası.',
});
} finally {
loading.value = false;
}
};
</script>
<style scoped>
.register-area {
background-color: #f8f9fa;
min-height: 80vh;
display: flex;
align-items: center;
}
.register-form-wrap {
border-top: 5px solid #198754;
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<div class="resend-verify-area pt-120 pb-120">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-6 col-md-8">
<div class="resend-form-wrap shadow-lg p-5 rounded bg-white">
<div class="header text-center mb-4">
<h2 class="mb-2">Doğrulama E-postası Gönder</h2>
<p class="text-muted">
E-posta adresinizi girin, doğrulama bağlantısını tekrar gönderelim.
</p>
</div>
<form @submit.prevent="handleResend">
<div class="mb-3">
<label for="email" class="form-label">E-posta Adresi</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
<input type="email" class="form-control" id="email" v-model="email"
placeholder="E-posta adresinizi girin" required>
</div>
<div v-if="error" class="text-danger small mt-1">{{ error }}</div>
</div>
<!-- Turnstile Component -->
<ClientOnly>
<div class="mb-4 d-flex justify-content-center">
<NuxtTurnstile v-model="turnstileToken" />
</div>
</ClientOnly>
<button type="submit" class="btn btn-primary w-100 py-2" :disabled="loading">
<span v-if="loading" class="spinner-border spinner-border-sm me-2" role="status"
aria-hidden="true"></span>
{{ loading ? 'Gönderiliyor...' : 'Gönder' }}
</button>
</form>
<div class="text-center mt-4">
<NuxtLink to="/auth/login" class="text-decoration-none">
<i class="fas fa-arrow-left me-1"></i> Giriş sayfasına dön
</NuxtLink>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { z } from 'zod'; // Import z directly if not exported from validations, or use from validations if available
import Swal from 'sweetalert2';
definePageMeta({
middleware: 'guest-only',
auth: { unauthenticatedOnly: true, navigateAuthenticatedTo: '/' }
});
const config = useRuntimeConfig();
const email = ref('');
const error = ref('');
const loading = ref(false);
const turnstileToken = ref('');
const emailSchema = z.string().email({ message: 'Geçerli bir e-posta adresi giriniz' });
const validate = () => {
const result = emailSchema.safeParse(email.value);
if (!result.success) {
error.value = result.error.errors[0].message;
return false;
}
error.value = '';
return true;
};
const handleResend = async () => {
if (!validate()) return;
if (!turnstileToken.value) {
Swal.fire({
icon: 'warning',
title: 'Güvenlik Kontrolü',
text: 'Lütfen Turnstile güvenlik kontrolünü tamamlayın.',
timer: 3000,
toast: true,
position: 'top-end',
showConfirmButton: false
});
return;
}
loading.value = true;
try {
const apiUrl = config.public.NUXT_PUBLIC_API_BASE || 'http://127.0.0.1:8080';
const response = await fetch(`${apiUrl}/api/v1/auth/resend-verification`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email.value
})
});
if (response.ok) {
Swal.fire({
icon: 'success',
title: 'Başarılı!',
text: 'Doğrulama bağlantısı e-posta adresinize gönderildi.',
confirmButtonText: 'Tamam'
});
email.value = ''; // Reset form
turnstileToken.value = ''; // Reset token ideally, but might need manual reset
} else {
const data = await response.json();
Swal.fire({
icon: 'error',
title: 'Hata',
text: data.message || data.error || 'İşlem başarısız oldu.',
});
}
} catch (err) {
console.error(err);
Swal.fire({
icon: 'error',
title: 'Hata',
text: 'Sunucu ile iletişim hatası.',
});
} finally {
loading.value = false;
}
};
</script>
<style scoped>
.resend-verify-area {
background-color: #f8f9fa;
min-height: 80vh;
display: flex;
align-items: center;
}
.resend-form-wrap {
border-top: 5px solid #0d6efd;
}
</style>

114
app/pages/auth/verify.vue Normal file
View File

@@ -0,0 +1,114 @@
<template>
<div class="verify-area pt-120 pb-120">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-6 col-md-8">
<div class="verify-content text-center shadow-lg p-5 rounded bg-white">
<div v-if="loading" class="text-center py-5">
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Yükleniyor...</span>
</div>
<p class="mt-3 text-muted fs-5">E-posta adresiniz doğrulanıyor...</p>
</div>
<div v-else-if="success">
<div class="mb-4">
<div class="success-icon d-inline-flex align-items-center justify-content-center bg-success text-white rounded-circle"
style="width: 80px; height: 80px;">
<i class="fas fa-check fa-3x"></i>
</div>
</div>
<h3 class="mb-3 text-success">Doğrulama Başarılı!</h3>
<p class="text-muted mb-4 fs-5">Hesabınız başarıyla doğrulandı. Artık giriş yapabilirsiniz.
</p>
<NuxtLink to="/auth/login" class="btn btn-primary btn-lg w-100">
Giriş Yap
</NuxtLink>
</div>
<div v-else>
<div class="mb-4">
<div class="error-icon d-inline-flex align-items-center justify-content-center bg-danger text-white rounded-circle"
style="width: 80px; height: 80px;">
<i class="fas fa-times fa-3x"></i>
</div>
</div>
<h3 class="mb-3 text-danger">Doğrulama Hatası</h3>
<p class="text-muted mb-4 fs-5">{{ displayError }}</p>
<NuxtLink to="/auth/login" class="btn btn-outline-primary">
Giriş sayfasına dön
</NuxtLink>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const route = useRoute();
const config = useRuntimeConfig();
definePageMeta({
middleware: 'guest-only',
auth: { unauthenticatedOnly: true, navigateAuthenticatedTo: '/' }
});
const loading = ref(true);
const success = ref(false);
const errorMessage = ref('');
const displayError = computed(() => {
return errorMessage.value || 'Doğrulama işlemi sırasında bir hata oluştu.';
});
onMounted(async () => {
const token = route.query.token as string;
if (!token) {
loading.value = false;
errorMessage.value = "Geçersiz doğrulama bağlantısı (token eksik).";
return;
}
try {
const apiUrl = config.public.NUXT_PUBLIC_API_BASE || 'http://127.0.0.1:8080';
// Backend API: GET /api/v1/auth/verify-email?token=...
const response = await fetch(`${apiUrl}/api/v1/auth/verify-email?token=${token}`, {
method: 'GET',
headers: {
'accept': 'application/json'
}
});
if (response.ok) {
success.value = true;
} else {
const data = await response.json();
errorMessage.value = data.message || 'Token geçersiz veya süresi dolmuş.';
}
} catch (error) {
console.error("Verify error:", error);
errorMessage.value = 'Sunucu ile bağlantı kurulamadı.';
} finally {
loading.value = false;
}
});
</script>
<style scoped>
.verify-area {
background-color: #f8f9fa;
min-height: 80vh;
display: flex;
align-items: center;
}
.verify-content {
border-top: 5px solid #0d6efd;
}
</style>

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

@@ -0,0 +1,61 @@
<script setup lang="ts">
import Paginate from "~/components/Paginate.vue";
import FooterArea from "~/components/FooterArea.vue";
import SubsribeStart from "~/components/SubsribeStart.vue";
import TagsClouds from "~/components/TagsClouds.vue";
import WidgetSidebarImgBox from "~/components/home/WidgetSidebarImgBox.vue";
import WidgetRecentPost from "~/components/home/WidgetRecentPost.vue";
import WidgetCategories from "~/components/home/WidgetCategories.vue";
import WidgetSearch from "~/components/home/WidgetSearch.vue";
import BlogPage from "~/components/home/BlogPage.vue";
import PagesHeroHeader from "~/components/home/PagesHeroHeader.vue";
import MobileHeader from "~/components/MobileHeader.vue";
import HeaderSection from "~/components/HeaderSection.vue";
import Preloader from "~/components/home/Preloader.vue";
import PaginaContainer from "~/components/PaginaContainer.vue";
</script>
<template>
<div>
<Preloader/>
<!--=====progress START=======-->
<PaginaContainer/>
<!--=====progress END=======-->
<!--=====HEADER START=======-->
<HeaderSection/>
<!--=====HEADER END=======-->
<!--=====Mobile header start=======-->
<MobileHeader/>
<!--=====Mobile header end=======-->
<!--=====pages hero header end=======-->
<PagesHeroHeader/>
<!--=====pages hero header end=======-->
<div class="blog-page sp2">
<div class="container">
<div class="row">
<BlogPage/>
<div class="col-lg-4 col-md-6">
<div class="widgets lg-ml-15">
<WidgetSearch/>
<WidgetCategories/>
<WidgetSidebarImgBox/>
<TagsClouds/>
</div>
</div>
</div>
</div>
</div>
<!--=====SUBSRIBE START=======-->
<SubsribeStart/>
<!--=====SUBSRIBE END=======-->
<!--=====Footer start=======-->
<FooterArea/>
<!--=====Footer end=======-->
<!--================== sidebar===================== -->
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,20 @@
import type { Setting } from "~~/types/setting";
export default defineNuxtPlugin(async () => {
const { useSettingStore } = await import('@/stores/setting')
const settingStore = useSettingStore()
const config = useRuntimeConfig();
const apiUrl = config.public.BASE_API_URL;
try {
const data = await $fetch<Setting>(`${apiUrl}/api/v1/setting`)
if (data) {
settingStore.setSettings(data)
}
} catch (error) {
console.error('Failed to fetch settings:', error)
}
})

View File

@@ -0,0 +1,20 @@
import type { Hero } from "~~/types/hero";
export default defineNuxtPlugin(async () => {
const { useHeroStore } = await import('@/stores/hero')
const heroStore = useHeroStore()
const config = useRuntimeConfig();
const apiUrl = config.public.BASE_API_URL;
try {
const data = await $fetch<Hero>(`${apiUrl}/api/v1/hero`)
if (data) {
heroStore.setHero(data)
}
} catch (error) {
console.error('Failed to fetch settings:', error)
}
})

11
app/plugins/auth-error.ts Normal file
View File

@@ -0,0 +1,11 @@
export default defineNuxtPlugin((nuxtApp) => {
const { data, signOut } = useAuth();
// Watch for session changes
watch(data, async (session) => {
if (session?.error === 'RefreshAccessTokenError') {
console.warn('Refresh token expired or invalid. Signing out...');
await signOut({ callbackUrl: '/auth/login' });
}
}, { immediate: true });
});

View File

@@ -0,0 +1,7 @@
// Converted to a no-op: we register Font Awesome using an SSR-aware plugin `app/plugins/fontawesome.ts`.
// Keep this client-only file present (if you relied on it elsewhere) but do not inject the runtime script
// to avoid hydration mismatches.
export default defineNuxtPlugin(() => {
if (process.server) return
// No-op on client — FontAwesome is registered via app/plugins/fontawesome.ts
})

View File

@@ -0,0 +1,39 @@
// SSR-friendly Font Awesome plugin
// Registers the Vue component and a small set of icons so server-render and client-render match.
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
// Import a conservative set of icons used in the admin UI
import {
faHouse,
faUsers,
faCogs,
faNewspaper,
faTags,
faHashtag,
faRightFromBracket,
faPlus,
faWandMagic,
faGaugeHigh,
faShieldAlt
} from '@fortawesome/free-solid-svg-icons'
export default defineNuxtPlugin((nuxtApp) => {
// Add selected icons to the library
library.add(
faHouse,
faUsers,
faCogs,
faNewspaper,
faTags,
faHashtag,
faRightFromBracket,
faPlus,
faWandMagic,
faGaugeHigh,
faShieldAlt
)
// Globally register component
nuxtApp.vueApp.component('FontAwesomeIcon', FontAwesomeIcon)
})

View File

@@ -0,0 +1,5 @@
import Swal from 'sweetalert2'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.provide('swal', Swal)
})

18
app/stores/hero.ts Normal file
View File

@@ -0,0 +1,18 @@
import { defineStore } from 'pinia';
import type { Hero } from '~~/types/hero';
interface HeroState {
hero: Hero | null;
}
export const useHeroStore = defineStore('hero', {
state: (): HeroState => ({
hero: null,
}),
actions: {
setHero(newHero: Hero | null): void {
this.hero = newHero;
}
}
});

18
app/stores/setting.ts Normal file
View File

@@ -0,0 +1,18 @@
import { defineStore } from 'pinia';
import type { Setting } from '~~/types/setting';
interface SettingState {
settings: Setting | null;
}
export const useSettingStore = defineStore('setting', {
state: (): SettingState => ({
settings: null,
}),
actions: {
setSettings(newSettings: Setting | null): void {
this.settings = newSettings;
}
}
});

10
content.config.ts Normal file
View File

@@ -0,0 +1,10 @@
import { defineContentConfig, defineCollection } from '@nuxt/content'
export default defineContentConfig({
collections: {
content: defineCollection({
type: 'page',
source: '**/*.md'
})
}
})

0
content/LoginRegiter.md Normal file
View File

654
content/admin_cors.md Normal file
View File

@@ -0,0 +1,654 @@
White List
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/cors/whitelist' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/cors/whitelist
Server response
Code Details
200
Response body
Download
{
"count": 3,
"items": [
{
"ID": 3,
"CreatedAt": "2026-02-15T07:43:24.505+03:00",
"UpdatedAt": "2026-02-15T07:43:24.505+03:00",
"DeletedAt": null,
"origin": "http://localhost:8080",
"description": "default seeded whitelist",
"is_active": true,
"created_by": "seed"
},
{
"ID": 2,
"CreatedAt": "2026-02-15T07:43:24.488+03:00",
"UpdatedAt": "2026-02-15T07:43:24.488+03:00",
"DeletedAt": null,
"origin": "http://localhost:5173",
"description": "default seeded whitelist",
"is_active": true,
"created_by": "seed"
},
{
"ID": 1,
"CreatedAt": "2026-02-15T07:43:24.473+03:00",
"UpdatedAt": "2026-02-15T07:43:24.473+03:00",
"DeletedAt": null,
"origin": "http://localhost:3000",
"description": "default seeded whitelist",
"is_active": true,
"created_by": "seed"
}
]
}
Response headers
content-length: 696
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:36:01 GMT
whitelist add
curl -X 'POST' \
'http://localhost:8080/api/v1/admin/cors/whitelist' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: application/json' \
-d '{
"description": "test",
"is_active": true,
"origin": "http://test.com"
}'
Request URL
http://localhost:8080/api/v1/admin/cors/whitelist
Server response
Code Details
201
Response body
Download
{
"item": {
"ID": 4,
"CreatedAt": "2026-02-15T20:37:14.89782Z",
"UpdatedAt": "2026-02-15T20:37:14.89782Z",
"DeletedAt": null,
"origin": "http://test.com",
"description": "test",
"is_active": true,
"created_by": "beyhan@beyhan.dev"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 214
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:37:14 GMT
vary: Origin
whitelist Update
curl -X 'PUT' \
'http://localhost:8080/api/v1/admin/cors/whitelist/4' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: application/json' \
-d '{
"description": "Test",
"is_active": true,
"origin": "http://updatetest.com"
}'
Request URL
http://localhost:8080/api/v1/admin/cors/whitelist/4
Server response
Code Details
200
Response body
Download
{
"item": {
"ID": 4,
"CreatedAt": "2026-02-15T23:37:14.897+03:00",
"UpdatedAt": "2026-02-15T20:38:29.294829Z",
"DeletedAt": null,
"origin": "http://updatetest.com",
"description": "Test",
"is_active": true,
"created_by": "beyhan@beyhan.dev"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 224
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:38:28 GMT
vary: Origin
whitelist delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/cors/whitelist/4' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/cors/whitelist/4
Server response
Code Details
200
Response body
Download
{
"id": 4,
"message": "soft deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 33
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:39:22 GMT
vary: Origin
whitelist hard delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/cors/whitelist/4/hard' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/cors/whitelist/4/hard
Server response
Code Details
200
Response body
Download
{
"id": 4,
"message": "hard deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 33
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:40:08 GMT
vary: Origin
########################################################################
Blacklist add
curl -X 'POST' \
'http://localhost:8080/api/v1/admin/cors/blacklist' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: application/json' \
-d '{
"is_active": true,
"origin": "http://post.test.com",
"reason": "spamer"
}'
Request URL
http://localhost:8080/api/v1/admin/cors/blacklist
Server response
Code Details
201
Response body
Download
{
"item": {
"ID": 1,
"CreatedAt": "2026-02-15T20:42:27.229052Z",
"UpdatedAt": "2026-02-15T20:42:27.229052Z",
"DeletedAt": null,
"origin": "http://post.test.com",
"reason": "spamer",
"is_active": true,
"created_by": "beyhan@beyhan.dev"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 218
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:42:26 GMT
vary: Origin
Blacklist List
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/cors/blacklist' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/cors/blacklist
Server response
Code Details
200
Response body
Download
{
"count": 1,
"items": [
{
"ID": 1,
"CreatedAt": "2026-02-15T23:42:27.229+03:00",
"UpdatedAt": "2026-02-15T23:42:27.229+03:00",
"DeletedAt": null,
"origin": "http://post.test.com",
"reason": "spamer",
"is_active": true,
"created_by": "beyhan@beyhan.dev"
}
]
}
Response headers
content-length: 235
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:43:26 GMT
Blacklist Update
curl -X 'PUT' \
'http://localhost:8080/api/v1/admin/cors/blacklist/1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: application/json' \
-d '{
"is_active": true,
"origin": "http://update.test.com",
"reason": "Sapammer Update"
}'
Request URL
http://localhost:8080/api/v1/admin/cors/blacklist/1
Server response
Code Details
200
Response body
Download
{
"item": {
"ID": 1,
"CreatedAt": "2026-02-15T23:42:27.229+03:00",
"UpdatedAt": "2026-02-15T20:44:35.812266Z",
"DeletedAt": null,
"origin": "http://update.test.com",
"reason": "Sapammer Update",
"is_active": true,
"created_by": "beyhan@beyhan.dev"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 231
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:44:35 GMT
vary: Origin
Blacklist Delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/cors/blacklist/1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/cors/blacklist/1
Server response
Code Details
200
Response body
Download
{
"id": 1,
"message": "soft deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 33
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:45:14 GMT
vary: Origin
Blacklist Hard Delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/cors/blacklist/1/hard' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/cors/blacklist/1/hard
Server response
Code Details
200
Response body
Download
{
"id": 1,
"message": "hard deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 33
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:45:40 GMT
vary: Origin
Rate Limit List
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/rate-limit' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/rate-limit
Server response
Code Details
200
Response body
Download
{
"count": 2,
"items": [
{
"ID": 2,
"CreatedAt": "2026-02-15T07:43:24.453+03:00",
"UpdatedAt": "2026-02-15T07:43:24.453+03:00",
"DeletedAt": null,
"name": "login",
"description": "Login endpoint default rate limit",
"max_requests": 10,
"window_seconds": 60,
"is_active": true,
"updated_by": "seed"
},
{
"ID": 1,
"CreatedAt": "2026-02-15T07:43:24.439+03:00",
"UpdatedAt": "2026-02-15T07:43:24.439+03:00",
"DeletedAt": null,
"name": "register",
"description": "Register endpoint default rate limit",
"max_requests": 5,
"window_seconds": 60,
"is_active": true,
"updated_by": "seed"
}
]
}
Response headers
content-length: 534
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:46:22 GMT
Rate Limit Create
curl -X 'POST' \
'http://localhost:8080/api/v1/admin/rate-limit' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: application/json' \
-d '{
"description": "test için",
"is_active": true,
"max_requests": 100,
"name": "test",
"window_seconds": 100
}'
Request URL
http://localhost:8080/api/v1/admin/rate-limit
Server response
Code Details
201
Response body
Download
{
"item": {
"ID": 3,
"CreatedAt": "2026-02-15T20:47:20.555797Z",
"UpdatedAt": "2026-02-15T20:47:20.555797Z",
"DeletedAt": null,
"name": "test",
"description": "test için",
"max_requests": 100,
"window_seconds": 100,
"is_active": true,
"updated_by": "beyhan@beyhan.dev"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 249
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:47:20 GMT
vary: Origin
Rate Limit Update
curl -X 'PUT' \
'http://localhost:8080/api/v1/admin/rate-limit/3' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: application/json' \
-d '{
"description": "update",
"is_active": true,
"max_requests": 34,
"name": "update",
"window_seconds": 34
}'
Request URL
http://localhost:8080/api/v1/admin/rate-limit/3
Server response
Code Details
200
Response body
Download
{
"item": {
"ID": 3,
"CreatedAt": "2026-02-15T23:47:20.555+03:00",
"UpdatedAt": "2026-02-15T20:48:22.836239Z",
"DeletedAt": null,
"name": "update",
"description": "update",
"max_requests": 34,
"window_seconds": 34,
"is_active": true,
"updated_by": "beyhan@beyhan.dev"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 247
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:48:22 GMT
vary: Origin
Rate Limit Delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/rate-limit/3' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/rate-limit/3
Server response
Code Details
200
Response body
Download
{
"id": 3,
"message": "soft deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 33
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:48:56 GMT
vary: Origin
Rate Limit Hard Delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/rate-limit/3/hard' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/admin/rate-limit/3/hard
Server response
Code Details
200
Response body
Download
{
"id": 3,
"message": "hard deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 33
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 20:49:21 GMT
vary: Origin

380
content/admin_kategori.md Normal file
View File

@@ -0,0 +1,380 @@
css ler /public/css/ kalsorunde
* Bootstrap v5.2.0-beta1 kullanarak yapabilirsin
sweetaler2
sadece soft delete edilmis
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/categories?trashed=only' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/admin/categories?trashed=only
Server response
Code Details
200
Response body
Download
{
"data": [
{
"ID": 11,
"CreatedAt": "2026-02-16T02:27:17.611+03:00",
"UpdatedAt": "2026-02-16T02:46:47.319+03:00",
"DeletedAt": "2026-02-16T02:56:25.664+03:00",
"title": "rftghyujkiiiüüğğççöö",
"slug": "rftghyujkiiiuuggccoo",
"description": "sfdfsd",
"parent_id": 1
}
],
"meta": {
"page": 1,
"per_page": 10,
"total": 1
}
}
Response headers
content-length: 300
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 10:45:03 GMT
#############
admin kategoriler list hepsi
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/categories?trashed=with' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/admin/categories?trashed=with
Server response
Code Details
200
Response body
Download
{
"data": [
{
"ID": 13,
"CreatedAt": "2026-02-16T02:48:15.651+03:00",
"UpdatedAt": "2026-02-16T02:48:15.651+03:00",
"DeletedAt": null,
"title": "sdadasd",
"slug": "sdadasd",
"description": "asdasdad"
},
{
"ID": 12,
"CreatedAt": "2026-02-16T02:29:37.895+03:00",
"UpdatedAt": "2026-02-16T02:40:55.819+03:00",
"DeletedAt": null,
"title": "update",
"slug": "update",
"description": "update"
},
{
"ID": 11,
"CreatedAt": "2026-02-16T02:27:17.611+03:00",
"UpdatedAt": "2026-02-16T02:46:47.319+03:00",
"DeletedAt": "2026-02-16T02:56:25.664+03:00",
"title": "rftghyujkiiiüüğğççöö",
"slug": "rftghyujkiiiuuggccoo",
"description": "sfdfsd",
"parent_id": 1
},
{
"ID": 10,
"CreatedAt": "2026-02-16T02:26:53.621+03:00",
"UpdatedAt": "2026-02-16T02:55:27.599+03:00",
"DeletedAt": null,
"title": "34345345",
"slug": "34345345",
"description": "sfdfsd",
"parent_id": 8
},
{
"ID": 8,
"CreatedAt": "2026-02-15T07:43:42.09+03:00",
"UpdatedAt": "2026-02-15T07:43:42.09+03:00",
"DeletedAt": null,
"title": "Finans",
"slug": "finans",
"description": "Finans kategorisi seed verisi",
"children": [
{
"ID": 10,
"CreatedAt": "2026-02-16T02:26:53.621+03:00",
"UpdatedAt": "2026-02-16T02:55:27.599+03:00",
"DeletedAt": null,
"title": "34345345",
"slug": "34345345",
"description": "sfdfsd",
"parent_id": 8
}
]
},
{
"ID": 7,
"CreatedAt": "2026-02-15T07:43:42.07+03:00",
"UpdatedAt": "2026-02-15T07:43:42.07+03:00",
"DeletedAt": null,
"title": "Saglik",
"slug": "saglik",
"description": "Saglik kategorisi seed verisi"
},
{
"ID": 6,
"CreatedAt": "2026-02-15T07:43:42.053+03:00",
"UpdatedAt": "2026-02-15T07:43:42.053+03:00",
"DeletedAt": null,
"title": "Spor",
"slug": "spor",
"description": "Spor kategorisi seed verisi"
},
{
"ID": 5,
"CreatedAt": "2026-02-15T07:43:42.038+03:00",
"UpdatedAt": "2026-02-16T02:48:47.358+03:00",
"DeletedAt": null,
"title": "Egitim",
"slug": "egitim",
"description": "Egitim kategorisi seed verisi",
"parent_id": 2
},
{
"ID": 4,
"CreatedAt": "2026-02-15T07:43:42.023+03:00",
"UpdatedAt": "2026-02-15T07:43:42.023+03:00",
"DeletedAt": null,
"title": "Yasam",
"slug": "yasam",
"description": "Yasam kategorisi seed verisi"
},
{
"ID": 3,
"CreatedAt": "2026-02-15T07:43:42.005+03:00",
"UpdatedAt": "2026-02-15T07:43:42.005+03:00",
"DeletedAt": null,
"title": "Guncel",
"slug": "guncel",
"description": "Guncel kategorisi seed verisi"
}
],
"meta": {
"page": 1,
"per_page": 10,
"total": 12
}
}
Response headers
content-length: 2197
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 10:43:49 GMT
#################################################
kategorileri ekleme
curl -X 'POST' \
'http://localhost:8080/api/v1/categories' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMjA1MDM2LCJpYXQiOjE3NzExOTc4MzZ9.p1hHNkMya9mEII-Bkc9TkMtBeWt6k3bp2e-pR5zw_EI' \
-H 'Content-Type: application/json' \
-d '{
"description": "sfdfsd",
"parent_id": null,
"title": "öçşiğüIi"
}'
Request URL
http://localhost:8080/api/v1/categories
Server response
Code Details
201
Response body
Download
{
"ID": 12,
"CreatedAt": "2026-02-15T23:29:37.89581Z",
"UpdatedAt": "2026-02-15T23:29:37.89581Z",
"DeletedAt": null,
"title": "öçşiğüIi",
"slug": "ocsiguii",
"description": "sfdfsd"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 173
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 23:29:37 GMT
vary: Origin
Responses
Code Description
201
Created
{
"children": [
"string"
],
"description": "string",
"id": 0,
"parent_id": 0,
"title": "string"
}
400
#################################################
kategoriler update
curl -X 'PUT' \
'http://localhost:8080/api/v1/categories/12' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMjA1MDM2LCJpYXQiOjE3NzExOTc4MzZ9.p1hHNkMya9mEII-Bkc9TkMtBeWt6k3bp2e-pR5zw_EI' \
-H 'Content-Type: application/json' \
-d '{
"description": "update",
"parent_id": null,
"title": "update"
}'
Request URL
http://localhost:8080/api/v1/categories/12
Server response
Code Details
200
Response body
Download
{
"ID": 12,
"CreatedAt": "2026-02-16T02:29:37.895+03:00",
"UpdatedAt": "2026-02-15T23:40:55.819633Z",
"DeletedAt": null,
"title": "update",
"slug": "update",
"description": "update"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 168
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 23:40:55 GMT
vary: Origin
############################
kategorileri silme
curl -X 'DELETE' \
'http://localhost:8080/api/v1/categories/12' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMjA1MDM2LCJpYXQiOjE3NzExOTc4MzZ9.p1hHNkMya9mEII-Bkc9TkMtBeWt6k3bp2e-pR5zw_EI'
Request URL
http://localhost:8080/api/v1/categories/12
Server response
Code Details
200
Response body
Download
{
"message": "category deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 30
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 23:42:58 GMT
vary: Origin
####################
restore etme
curl -X 'POST' \
'http://localhost:8080/api/v1/admin/categories/11/restore' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0' \
-d ''
Request URL
http://localhost:8080/api/v1/admin/categories/11/restore
Server response
Code Details
200
Response body
Download
{
"category_id": 11,
"message": "category restored successfully"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 61
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 10:46:40 GMT
vary: Origin
###########
hard delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/categories/11/hard' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/admin/categories/11/hard

475
content/admin_oanel.md Normal file
View File

@@ -0,0 +1,475 @@
css ler /public/css/ kalsorunde
* Bootstrap v5.2.0-beta1 kullanarak yapabilirsin
settings index.vue de tab li bir yapi ile settings ve hero yu yapalim
solda bir sidebar menu ile yapalim
Admin Panel Settings
settings ve hero da resimleri gondermeden once en boy format kalite ayarlarini yapacaz "sharp": "^0.34.5",
bu kutuplahnetyi kulanalim seçmelerde default olarak avif formati olsun
# Login olmak#
is_admin = true olanalar sadece girebilir adminb kismina
curl -X 'POST' \
'http://localhost:8080/api/v1/auth/login' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"email": "beyhan@beyhan.dev",
"password": "1923btO**"
}'
Request URL
http://localhost:8080/api/v1/auth/login
Server response
Code Details
200
Response body
Download
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgzOTc5LCJpYXQiOjE3NzExNzY3Nzl9.bVzWG3KHsKkqm11A1uNru5H3g5kZtq30lQxgasCqhBk",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJyZWZyZXNoIiwic3ViIjoiMSIsImV4cCI6MTc3Mzc2ODc3OSwiaWF0IjoxNzcxMTc2Nzc5fQ.wRT0ECN-UsF4ileQHUXl0evePXlaCuG8vb11wWt75Es",
"user": {
"email": "beyhan@beyhan.dev",
"first_name": "Beyhan",
"id": 1,
"is_admin": true,
"last_name": "Oğur",
"username": "beyhano"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 765
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:32:58 GMT
vary: Origin
#####################################
#Settings Yeni Ekle#
'''
curl -X 'POST' \
'http://localhost:8080/api/v1/setting' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgwODI4LCJpYXQiOjE3NzExNzM2Mjh9.l51p91--l7JcUbBPHwwVjqRgoT7KXFtU-33HLr4YfBE' \
-H 'Content-Type: multipart/form-data' \
-F 'title=Title' \
-F 'meta_title=Meta Title' \
-F 'meta_description=Meta Description' \
-F 'phone=Phone' \
-F 'url=URL' \
-F 'email=Email' \
-F 'facebook=Facebook' \
-F 'x=x' \
-F 'instagram=Instagram' \
-F 'whatsapp=Whatsapp' \
-F 'pinterest=Pinterest' \
-F 'linkedin=Linkedin' \
-F 'slogan=Slogan' \
-F 'address=Address' \
-F 'copyright=Copyright' \
-F 'map_embed=Map Embed' \
-F 'is_active=true' \
-F 'w_logo=@845660.png;type=image/png' \
-F 'b_logo=@845660.png;type=image/png'
Request URL
http://localhost:8080/api/v1/setting
Server response
Code Details
201
Response body
Download
{
"ID": 2,
"CreatedAt": "2026-02-15T17:24:34.125454Z",
"UpdatedAt": "2026-02-15T17:24:34.125454Z",
"DeletedAt": null,
"title": "Title",
"meta_title": "Meta Title",
"meta_description": "Meta Description",
"phone": "Phone",
"url": "URL",
"email": "Email",
"facebook": "Facebook",
"x": "x",
"instagram": "Instagram",
"whatsapp": "Whatsapp",
"pinterest": "Pinterest",
"linkedin": "Linkedin",
"slogan": "Slogan",
"address": "Address",
"copyright": "Copyright",
"map_embed": "Map Embed",
"w_logo": "/uploads/settings/w_1771176274_845660.png",
"b_logo": "/uploads/settings/b_1771176274_845660.png",
"is_active": true
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 564
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:24:33 GMT
vary: Origin
'''
#Settings Güncelle#
'''
curl -X 'PUT' \
'http://localhost:8080/api/v1/setting/1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgwODI4LCJpYXQiOjE3NzExNzM2Mjh9.l51p91--l7JcUbBPHwwVjqRgoT7KXFtU-33HLr4YfBE' \
-H 'Content-Type: multipart/form-data' \
-F 'title=Title' \
-F 'meta_title=Meta Title' \
-F 'meta_description=Meta Description' \
-F 'phone=Phone' \
-F 'url=URL' \
-F 'email=Email' \
-F 'facebook=Facebook' \
-F 'x=x' \
-F 'instagram=Instagram' \
-F 'whatsapp=Whatsapp' \
-F 'pinterest=Pinterest' \
-F 'linkedin=Linkedin' \
-F 'slogan=Slogan' \
-F 'address=Address' \
-F 'copyright=Copyright' \
-F 'map_embed=Map Embed' \
-F 'is_active=true' \
-F 'w_logo=@18d11cf76c60f0b453aaea8da0838d3b.png;type=image/png' \
-F 'b_logo=@1657955547black-google-icon.png;type=image/png'
Request URL
http://localhost:8080/api/v1/setting/1
Server response
Code Details
200
Response body
Download
{
"ID": 1,
"CreatedAt": "2026-02-15T19:43:23.226+03:00",
"UpdatedAt": "2026-02-15T17:27:21.266645Z",
"DeletedAt": null,
"title": "Title",
"meta_title": "Meta Title",
"meta_description": "Meta Description",
"phone": "Phone",
"url": "URL",
"email": "Email",
"facebook": "Facebook",
"x": "x",
"instagram": "Instagram",
"whatsapp": "Whatsapp",
"pinterest": "Pinterest",
"linkedin": "Linkedin",
"slogan": "Slogan",
"address": "Address",
"copyright": "Copyright",
"map_embed": "Map Embed",
"w_logo": "/uploads/settings/w_1771176441_18d11cf76c60f0b453aaea8da0838d3b.png",
"b_logo": "/uploads/settings/b_1771176441_1657955547black-google-icon.png",
"is_active": true
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 613
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:27:20 GMT
vary: Origin
'''
#Settings Silme#
'''
curl -X 'DELETE' \
'http://localhost:8080/api/v1/setting/1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgwODI4LCJpYXQiOjE3NzExNzM2Mjh9.l51p91--l7JcUbBPHwwVjqRgoT7KXFtU-33HLr4YfBE'
Request URL
http://localhost:8080/api/v1/setting/1
Server response
Code Details
200
Response body
Download
{
"message": "setting deleted successfully"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 38
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:26:38 GMT
vary: Origin
'''
#Settings Listeleme#
'''
curl -X 'GET' \
'http://localhost:8080/api/v1/setting' \
-H 'accept: application/json'
Request URL
http://localhost:8080/api/v1/setting
Server response
Code Details
200
Response body
Download
{
"ID": 1,
"CreatedAt": "2026-02-15T19:43:23.226+03:00",
"UpdatedAt": "2026-02-15T20:27:21.266+03:00",
"DeletedAt": null,
"title": "Title",
"meta_title": "Meta Title",
"meta_description": "Meta Description",
"phone": "Phone",
"url": "URL",
"email": "Email",
"facebook": "Facebook",
"x": "x",
"instagram": "Instagram",
"whatsapp": "Whatsapp",
"pinterest": "Pinterest",
"linkedin": "Linkedin",
"slogan": "Slogan",
"address": "Address",
"copyright": "Copyright",
"map_embed": "Map Embed",
"w_logo": "/uploads/settings/w_1771176441_18d11cf76c60f0b453aaea8da0838d3b.png",
"b_logo": "/uploads/settings/b_1771176441_1657955547black-google-icon.png",
"is_active": true
}
Response headers
content-length: 615
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:28:37 GMT
'''
#####################################
admin için Hero ayarlari
#Hero Yeni Ekle#
'''
curl -X 'POST' \
'http://localhost:8080/api/v1/hero' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgwODI4LCJpYXQiOjE3NzExNzM2Mjh9.l51p91--l7JcUbBPHwwVjqRgoT7KXFtU-33HLr4YfBE' \
-H 'Content-Type: multipart/form-data' \
-F 'title=Title' \
-F 'text1=Text1' \
-F 'text2=Text2' \
-F 'text4=Text4' \
-F 'text5=Text5' \
-F 'color=Color' \
-F 'is_active=true' \
-F 'image=@1200x630bb.png;type=image/png'
Request URL
http://localhost:8080/api/v1/hero
Server response
Code Details
201
Response body
Download
{
"ID": 2,
"CreatedAt": "2026-02-15T17:13:52.46361Z",
"UpdatedAt": "2026-02-15T17:13:52.46361Z",
"DeletedAt": null,
"color": "Color",
"title": "Title",
"text1": "Text1",
"text2": "Text2",
"text4": "Text4",
"text5": "Text5",
"image": "/uploads/heroes/1771175632_1200x630bb.png",
"is_active": true
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 272
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:13:51 GMT
vary: Origin
'''
#Hero Güncelle#
'''
curl -X 'PUT' \
'http://localhost:8080/api/v1/hero/2' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgwODI4LCJpYXQiOjE3NzExNzM2Mjh9.l51p91--l7JcUbBPHwwVjqRgoT7KXFtU-33HLr4YfBE' \
-H 'Content-Type: multipart/form-data' \
-F 'title=Title' \
-F 'text1=Text1' \
-F 'text2=Text2' \
-F 'text4=Text4' \
-F 'text5=Text5' \
-F 'color=Color' \
-F 'is_active=true' \
-F 'image=@1632286445-en-sqdgame-main-playgrou-5BVA_cover.jpg;type=image/jpeg'
Request URL
http://localhost:8080/api/v1/hero/2
Server response
Code Details
200
Response body
Download
{
"ID": 2,
"CreatedAt": "2026-02-15T20:13:52.463+03:00",
"UpdatedAt": "2026-02-15T17:17:21.182814Z",
"DeletedAt": null,
"color": "Color",
"title": "Title",
"text1": "Text1",
"text2": "Text2",
"text4": "Text4",
"text5": "Text5",
"image": "/uploads/heroes/1771175841_1632286445-en-sqdgame-main-playgrou-5BVA_cover.jpg",
"is_active": true
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 312
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:17:21 GMT
vary: Origin
'''
#Hero Silme#
'''
curl -X 'DELETE' \
'http://localhost:8080/api/v1/hero/1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTgwODI4LCJpYXQiOjE3NzExNzM2Mjh9.l51p91--l7JcUbBPHwwVjqRgoT7KXFtU-33HLr4YfBE'
Request URL
http://localhost:8080/api/v1/hero/1
Server response
Code Details
200
Response body
Download
{
"message": "hero deleted successfully"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 39
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:18:39 GMT
vary: Origin
'''
#Hero Listeleme#
'''
curl -X 'GET' \
'http://localhost:8080/api/v1/hero' \
-H 'accept: application/json'
Request URL
http://localhost:8080/api/v1/hero
Server response
Code Details
200
Response body
Download
{
"ID": 2,
"CreatedAt": "2026-02-15T20:13:52.463+03:00",
"UpdatedAt": "2026-02-15T20:17:21.182+03:00",
"DeletedAt": null,
"color": "Color",
"title": "Title",
"text1": "Text1",
"text2": "Text2",
"text4": "Text4",
"text5": "Text5",
"image": "/uploads/heroes/1771175841_1632286445-en-sqdgame-main-playgrou-5BVA_cover.jpg",
"is_active": true
}
Response headers
content-length: 314
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 17:20:30 GMT
'''
##############################################################################################

1175
content/admin_post.md Normal file

File diff suppressed because it is too large Load Diff

934
content/admin_tags.md Normal file
View File

@@ -0,0 +1,934 @@
css ler /public/css/ kalsorunde
* Bootstrap v5.2.0-beta1 kullanarak yapabilirsin
tags ekleme
curl -X 'POST' \
'http://localhost:8080/api/v1/tags' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0' \
-H 'Content-Type: application/json' \
-d '{
"name": "test4444"
}'
Request URL
http://localhost:8080/api/v1/tags
Server response
Code Details
201
Response body
Download
{
"ID": 13,
"CreatedAt": "2026-02-17T09:50:12.778586Z",
"UpdatedAt": "2026-02-17T09:50:12.778586Z",
"DeletedAt": null,
"name": "test4444"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 128
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:50:12 GMT
vary: Origi
#################
tags güncelleme
curl -X 'PUT' \
'http://localhost:8080/api/v1/tags/13' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0' \
-H 'Content-Type: application/json' \
-d '{
"name": "test4444_update"
}'
Request URL
http://localhost:8080/api/v1/tags/13
Server response
Code Details
200
Response body
Download
{
"ID": 13,
"CreatedAt": "2026-02-17T12:50:12.778+03:00",
"UpdatedAt": "2026-02-17T09:51:54.295883Z",
"DeletedAt": null,
"name": "test4444_update"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 137
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:51:53 GMT
vary: Origin
#################
tags silme
curl -X 'DELETE' \
'http://localhost:8080/api/v1/tags/13' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/tags/13
Server response
Code Details
200
Response body
Download
{
"message": "tag deleted"
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 25
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:55:04 GMT
vary: Origin
#################
Listele hepsi
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/tags?trashed=with' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/admin/tags?trashed=with
Server response
Code Details
200
Response body
Download
{
"data": [
{
"ID": 13,
"CreatedAt": "2026-02-17T12:50:12.778+03:00",
"UpdatedAt": "2026-02-17T12:51:54.295+03:00",
"DeletedAt": "2026-02-17T12:55:04.871+03:00",
"name": "test4444_update"
},
{
"ID": 12,
"CreatedAt": "2026-02-15T07:43:42.295+03:00",
"UpdatedAt": "2026-02-15T07:43:42.295+03:00",
"DeletedAt": null,
"name": "testing",
"posts": [
{
"ID": 1,
"CreatedAt": "2026-02-15T07:43:42.673+03:00",
"UpdatedAt": "2026-02-15T07:43:42.673+03:00",
"DeletedAt": null,
"title": "Seed Post 1",
"images": "uploads/posts/post_1_75e8c4.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 1.",
"slug": "seed-post-1"
},
{
"ID": 9,
"CreatedAt": "2026-02-15T07:43:44.525+03:00",
"UpdatedAt": "2026-02-15T07:43:44.525+03:00",
"DeletedAt": null,
"title": "Seed Post 9",
"images": "uploads/posts/post_9_03c3b8.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 9.",
"slug": "seed-post-9"
},
{
"ID": 21,
"CreatedAt": "2026-02-15T07:43:46.483+03:00",
"UpdatedAt": "2026-02-15T07:43:46.483+03:00",
"DeletedAt": null,
"title": "Seed Post 21",
"images": "uploads/posts/post_21_626d54.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 21.",
"slug": "seed-post-21"
},
{
"ID": 31,
"CreatedAt": "2026-02-15T07:43:48.197+03:00",
"UpdatedAt": "2026-02-15T07:43:48.197+03:00",
"DeletedAt": null,
"title": "Seed Post 31",
"images": "uploads/posts/post_31_7a6f61.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 31.",
"slug": "seed-post-31"
}
]
},
{
"ID": 11,
"CreatedAt": "2026-02-15T07:43:42.277+03:00",
"UpdatedAt": "2026-02-15T07:43:42.277+03:00",
"DeletedAt": null,
"name": "security",
"posts": [
{
"ID": 2,
"CreatedAt": "2026-02-15T07:43:42.829+03:00",
"UpdatedAt": "2026-02-15T07:43:42.829+03:00",
"DeletedAt": null,
"title": "Seed Post 2",
"images": "uploads/posts/post_2_a15fd6.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 2.",
"slug": "seed-post-2"
},
{
"ID": 18,
"CreatedAt": "2026-02-15T07:43:45.891+03:00",
"UpdatedAt": "2026-02-15T07:43:45.891+03:00",
"DeletedAt": null,
"title": "Seed Post 18",
"images": "uploads/posts/post_18_ec88ac.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 18.",
"slug": "seed-post-18"
},
{
"ID": 20,
"CreatedAt": "2026-02-15T07:43:46.25+03:00",
"UpdatedAt": "2026-02-15T07:43:46.25+03:00",
"DeletedAt": null,
"title": "Seed Post 20",
"images": "uploads/posts/post_20_09d967.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 20.",
"slug": "seed-post-20"
},
{
"ID": 30,
"CreatedAt": "2026-02-15T07:43:48.058+03:00",
"UpdatedAt": "2026-02-15T07:43:48.058+03:00",
"DeletedAt": null,
"title": "Seed Post 30",
"images": "uploads/posts/post_30_d116c0.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 30.",
"slug": "seed-post-30"
},
{
"ID": 37,
"CreatedAt": "2026-02-15T07:43:49.511+03:00",
"UpdatedAt": "2026-02-15T07:43:49.511+03:00",
"DeletedAt": null,
"title": "Seed Post 37",
"images": "uploads/posts/post_37_573af9.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 37.",
"slug": "seed-post-37"
}
]
},
{
"ID": 10,
"CreatedAt": "2026-02-15T07:43:42.259+03:00",
"UpdatedAt": "2026-02-15T07:43:42.259+03:00",
"DeletedAt": null,
"name": "devops",
"posts": [
{
"ID": 3,
"CreatedAt": "2026-02-15T07:43:42.966+03:00",
"UpdatedAt": "2026-02-15T07:43:42.966+03:00",
"DeletedAt": null,
"title": "Seed Post 3",
"images": "uploads/posts/post_3_291f5f.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 3.",
"slug": "seed-post-3"
},
{
"ID": 5,
"CreatedAt": "2026-02-15T07:43:43.624+03:00",
"UpdatedAt": "2026-02-15T07:43:43.624+03:00",
"DeletedAt": null,
"title": "Seed Post 5",
"images": "uploads/posts/post_5_954662.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 5.",
"slug": "seed-post-5"
},
{
"ID": 14,
"CreatedAt": "2026-02-15T07:43:45.377+03:00",
"UpdatedAt": "2026-02-15T07:43:45.377+03:00",
"DeletedAt": null,
"title": "Seed Post 14",
"images": "uploads/posts/post_14_c21ae0.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 14.",
"slug": "seed-post-14"
},
{
"ID": 18,
"CreatedAt": "2026-02-15T07:43:45.891+03:00",
"UpdatedAt": "2026-02-15T07:43:45.891+03:00",
"DeletedAt": null,
"title": "Seed Post 18",
"images": "uploads/posts/post_18_ec88ac.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 18.",
"slug": "seed-post-18"
},
{
"ID": 22,
"CreatedAt": "2026-02-15T07:43:46.611+03:00",
"UpdatedAt": "2026-02-15T07:43:46.611+03:00",
"DeletedAt": null,
"title": "Seed Post 22",
"images": "uploads/posts/post_22_2aa79a.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 22.",
"slug": "seed-post-22"
},
{
"ID": 28,
"CreatedAt": "2026-02-15T07:43:47.546+03:00",
"UpdatedAt": "2026-02-15T07:43:47.546+03:00",
"DeletedAt": null,
"title": "Seed Post 28",
"images": "uploads/posts/post_28_a10a99.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 28.",
"slug": "seed-post-28"
},
{
"ID": 35,
"CreatedAt": "2026-02-15T07:43:49.258+03:00",
"UpdatedAt": "2026-02-15T07:43:49.258+03:00",
"DeletedAt": null,
"title": "Seed Post 35",
"images": "uploads/posts/post_35_30ee16.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 35.",
"slug": "seed-post-35"
},
{
"ID": 39,
"CreatedAt": "2026-02-15T07:43:49.767+03:00",
"UpdatedAt": "2026-02-17T12:28:44.936+03:00",
"DeletedAt": null,
"title": "Seed Post 39",
"images": "uploads/posts/post_39_0e773c.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 39.",
"slug": "seed-post-39"
}
]
},
{
"ID": 9,
"CreatedAt": "2026-02-15T07:43:42.242+03:00",
"UpdatedAt": "2026-02-15T07:43:42.242+03:00",
"DeletedAt": null,
"name": "cloud",
"posts": [
{
"ID": 9,
"CreatedAt": "2026-02-15T07:43:44.525+03:00",
"UpdatedAt": "2026-02-15T07:43:44.525+03:00",
"DeletedAt": null,
"title": "Seed Post 9",
"images": "uploads/posts/post_9_03c3b8.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 9.",
"slug": "seed-post-9"
},
{
"ID": 10,
"CreatedAt": "2026-02-15T07:43:44.653+03:00",
"UpdatedAt": "2026-02-15T07:43:44.653+03:00",
"DeletedAt": null,
"title": "Seed Post 10",
"images": "uploads/posts/post_10_19b004.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 10.",
"slug": "seed-post-10"
},
{
"ID": 12,
"CreatedAt": "2026-02-15T07:43:44.905+03:00",
"UpdatedAt": "2026-02-15T07:43:44.905+03:00",
"DeletedAt": null,
"title": "Seed Post 12",
"images": "uploads/posts/post_12_ddfaa3.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 12.",
"slug": "seed-post-12"
},
{
"ID": 21,
"CreatedAt": "2026-02-15T07:43:46.483+03:00",
"UpdatedAt": "2026-02-15T07:43:46.483+03:00",
"DeletedAt": null,
"title": "Seed Post 21",
"images": "uploads/posts/post_21_626d54.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 21.",
"slug": "seed-post-21"
},
{
"ID": 22,
"CreatedAt": "2026-02-15T07:43:46.611+03:00",
"UpdatedAt": "2026-02-15T07:43:46.611+03:00",
"DeletedAt": null,
"title": "Seed Post 22",
"images": "uploads/posts/post_22_2aa79a.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 22.",
"slug": "seed-post-22"
},
{
"ID": 26,
"CreatedAt": "2026-02-15T07:43:47.278+03:00",
"UpdatedAt": "2026-02-15T07:43:47.278+03:00",
"DeletedAt": null,
"title": "Seed Post 26",
"images": "uploads/posts/post_26_ce05c4.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 26.",
"slug": "seed-post-26"
},
{
"ID": 29,
"CreatedAt": "2026-02-15T07:43:47.674+03:00",
"UpdatedAt": "2026-02-15T07:43:47.674+03:00",
"DeletedAt": null,
"title": "Seed Post 29",
"images": "uploads/posts/post_29_5a1d1f.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 29.",
"slug": "seed-post-29"
},
{
"ID": 30,
"CreatedAt": "2026-02-15T07:43:48.058+03:00",
"UpdatedAt": "2026-02-15T07:43:48.058+03:00",
"DeletedAt": null,
"title": "Seed Post 30",
"images": "uploads/posts/post_30_d116c0.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 30.",
"slug": "seed-post-30"
},
{
"ID": 31,
"CreatedAt": "2026-02-15T07:43:48.197+03:00",
"UpdatedAt": "2026-02-15T07:43:48.197+03:00",
"DeletedAt": null,
"title": "Seed Post 31",
"images": "uploads/posts/post_31_7a6f61.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 31.",
"slug": "seed-post-31"
},
{
"ID": 34,
"CreatedAt": "2026-02-15T07:43:49.078+03:00",
"UpdatedAt": "2026-02-15T07:43:49.078+03:00",
"DeletedAt": null,
"title": "Seed Post 34",
"images": "uploads/posts/post_34_6f7963.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 34.",
"slug": "seed-post-34"
}
]
},
{
"ID": 8,
"CreatedAt": "2026-02-15T07:43:42.227+03:00",
"UpdatedAt": "2026-02-15T07:43:42.227+03:00",
"DeletedAt": null,
"name": "docker",
"posts": [
{
"ID": 4,
"CreatedAt": "2026-02-15T07:43:43.484+03:00",
"UpdatedAt": "2026-02-15T07:43:43.484+03:00",
"DeletedAt": null,
"title": "Seed Post 4",
"images": "uploads/posts/post_4_3d6f3f.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 4.",
"slug": "seed-post-4"
},
{
"ID": 6,
"CreatedAt": "2026-02-15T07:43:43.759+03:00",
"UpdatedAt": "2026-02-15T07:43:43.759+03:00",
"DeletedAt": null,
"title": "Seed Post 6",
"images": "uploads/posts/post_6_ad3af8.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 6.",
"slug": "seed-post-6"
},
{
"ID": 8,
"CreatedAt": "2026-02-15T07:43:44.393+03:00",
"UpdatedAt": "2026-02-15T07:43:44.393+03:00",
"DeletedAt": null,
"title": "Seed Post 8",
"images": "uploads/posts/post_8_80cbb5.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 8.",
"slug": "seed-post-8"
},
{
"ID": 9,
"CreatedAt": "2026-02-15T07:43:44.525+03:00",
"UpdatedAt": "2026-02-15T07:43:44.525+03:00",
"DeletedAt": null,
"title": "Seed Post 9",
"images": "uploads/posts/post_9_03c3b8.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 9.",
"slug": "seed-post-9"
},
{
"ID": 32,
"CreatedAt": "2026-02-15T07:43:48.329+03:00",
"UpdatedAt": "2026-02-15T07:43:48.329+03:00",
"DeletedAt": null,
"title": "Seed Post 32",
"images": "uploads/posts/post_32_214285.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 32.",
"slug": "seed-post-32"
},
{
"ID": 39,
"CreatedAt": "2026-02-15T07:43:49.767+03:00",
"UpdatedAt": "2026-02-17T12:28:44.936+03:00",
"DeletedAt": null,
"title": "Seed Post 39",
"images": "uploads/posts/post_39_0e773c.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 39.",
"slug": "seed-post-39"
}
]
},
{
"ID": 7,
"CreatedAt": "2026-02-15T07:43:42.212+03:00",
"UpdatedAt": "2026-02-15T07:43:42.212+03:00",
"DeletedAt": null,
"name": "api",
"posts": [
{
"ID": 5,
"CreatedAt": "2026-02-15T07:43:43.624+03:00",
"UpdatedAt": "2026-02-15T07:43:43.624+03:00",
"DeletedAt": null,
"title": "Seed Post 5",
"images": "uploads/posts/post_5_954662.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 5.",
"slug": "seed-post-5"
},
{
"ID": 7,
"CreatedAt": "2026-02-15T07:43:44.243+03:00",
"UpdatedAt": "2026-02-15T07:43:44.243+03:00",
"DeletedAt": null,
"title": "Seed Post 7",
"images": "uploads/posts/post_7_d973fd.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 7.",
"slug": "seed-post-7"
},
{
"ID": 16,
"CreatedAt": "2026-02-15T07:43:45.629+03:00",
"UpdatedAt": "2026-02-15T07:43:45.629+03:00",
"DeletedAt": null,
"title": "Seed Post 16",
"images": "uploads/posts/post_16_6a0c58.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 16.",
"slug": "seed-post-16"
},
{
"ID": 27,
"CreatedAt": "2026-02-15T07:43:47.412+03:00",
"UpdatedAt": "2026-02-15T07:43:47.412+03:00",
"DeletedAt": null,
"title": "Seed Post 27",
"images": "uploads/posts/post_27_c75542.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 27.",
"slug": "seed-post-27"
},
{
"ID": 29,
"CreatedAt": "2026-02-15T07:43:47.674+03:00",
"UpdatedAt": "2026-02-15T07:43:47.674+03:00",
"DeletedAt": null,
"title": "Seed Post 29",
"images": "uploads/posts/post_29_5a1d1f.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 29.",
"slug": "seed-post-29"
},
{
"ID": 31,
"CreatedAt": "2026-02-15T07:43:48.197+03:00",
"UpdatedAt": "2026-02-15T07:43:48.197+03:00",
"DeletedAt": null,
"title": "Seed Post 31",
"images": "uploads/posts/post_31_7a6f61.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 31.",
"slug": "seed-post-31"
}
]
},
{
"ID": 6,
"CreatedAt": "2026-02-15T07:43:42.192+03:00",
"UpdatedAt": "2026-02-15T07:43:42.192+03:00",
"DeletedAt": null,
"name": "jwt",
"posts": [
{
"ID": 13,
"CreatedAt": "2026-02-15T07:43:45.044+03:00",
"UpdatedAt": "2026-02-15T07:43:45.044+03:00",
"DeletedAt": null,
"title": "Seed Post 13",
"images": "uploads/posts/post_13_b37f04.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 13.",
"slug": "seed-post-13"
},
{
"ID": 19,
"CreatedAt": "2026-02-15T07:43:46.017+03:00",
"UpdatedAt": "2026-02-15T07:43:46.017+03:00",
"DeletedAt": null,
"title": "Seed Post 19",
"images": "uploads/posts/post_19_25715c.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 19.",
"slug": "seed-post-19"
},
{
"ID": 24,
"CreatedAt": "2026-02-15T07:43:46.876+03:00",
"UpdatedAt": "2026-02-15T07:43:46.876+03:00",
"DeletedAt": null,
"title": "Seed Post 24",
"images": "uploads/posts/post_24_97be20.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 24.",
"slug": "seed-post-24"
},
{
"ID": 34,
"CreatedAt": "2026-02-15T07:43:49.078+03:00",
"UpdatedAt": "2026-02-15T07:43:49.078+03:00",
"DeletedAt": null,
"title": "Seed Post 34",
"images": "uploads/posts/post_34_6f7963.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 34.",
"slug": "seed-post-34"
}
]
},
{
"ID": 5,
"CreatedAt": "2026-02-15T07:43:42.176+03:00",
"UpdatedAt": "2026-02-15T07:43:42.176+03:00",
"DeletedAt": null,
"name": "redis",
"posts": [
{
"ID": 2,
"CreatedAt": "2026-02-15T07:43:42.829+03:00",
"UpdatedAt": "2026-02-15T07:43:42.829+03:00",
"DeletedAt": null,
"title": "Seed Post 2",
"images": "uploads/posts/post_2_a15fd6.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 2.",
"slug": "seed-post-2"
},
{
"ID": 4,
"CreatedAt": "2026-02-15T07:43:43.484+03:00",
"UpdatedAt": "2026-02-15T07:43:43.484+03:00",
"DeletedAt": null,
"title": "Seed Post 4",
"images": "uploads/posts/post_4_3d6f3f.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 4.",
"slug": "seed-post-4"
},
{
"ID": 12,
"CreatedAt": "2026-02-15T07:43:44.905+03:00",
"UpdatedAt": "2026-02-15T07:43:44.905+03:00",
"DeletedAt": null,
"title": "Seed Post 12",
"images": "uploads/posts/post_12_ddfaa3.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 12.",
"slug": "seed-post-12"
},
{
"ID": 20,
"CreatedAt": "2026-02-15T07:43:46.25+03:00",
"UpdatedAt": "2026-02-15T07:43:46.25+03:00",
"DeletedAt": null,
"title": "Seed Post 20",
"images": "uploads/posts/post_20_09d967.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 20.",
"slug": "seed-post-20"
},
{
"ID": 23,
"CreatedAt": "2026-02-15T07:43:46.742+03:00",
"UpdatedAt": "2026-02-15T07:43:46.742+03:00",
"DeletedAt": null,
"title": "Seed Post 23",
"images": "uploads/posts/post_23_4ff46d.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 23.",
"slug": "seed-post-23"
},
{
"ID": 34,
"CreatedAt": "2026-02-15T07:43:49.078+03:00",
"UpdatedAt": "2026-02-15T07:43:49.078+03:00",
"DeletedAt": null,
"title": "Seed Post 34",
"images": "uploads/posts/post_34_6f7963.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 34.",
"slug": "seed-post-34"
},
{
"ID": 35,
"CreatedAt": "2026-02-15T07:43:49.258+03:00",
"UpdatedAt": "2026-02-15T07:43:49.258+03:00",
"DeletedAt": null,
"title": "Seed Post 35",
"images": "uploads/posts/post_35_30ee16.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 35.",
"slug": "seed-post-35"
}
]
},
{
"ID": 4,
"CreatedAt": "2026-02-15T07:43:42.16+03:00",
"UpdatedAt": "2026-02-15T07:43:42.16+03:00",
"DeletedAt": null,
"name": "mysql",
"posts": [
{
"ID": 1,
"CreatedAt": "2026-02-15T07:43:42.673+03:00",
"UpdatedAt": "2026-02-15T07:43:42.673+03:00",
"DeletedAt": null,
"title": "Seed Post 1",
"images": "uploads/posts/post_1_75e8c4.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 1.",
"slug": "seed-post-1"
},
{
"ID": 4,
"CreatedAt": "2026-02-15T07:43:43.484+03:00",
"UpdatedAt": "2026-02-15T07:43:43.484+03:00",
"DeletedAt": null,
"title": "Seed Post 4",
"images": "uploads/posts/post_4_3d6f3f.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 4.",
"slug": "seed-post-4"
},
{
"ID": 8,
"CreatedAt": "2026-02-15T07:43:44.393+03:00",
"UpdatedAt": "2026-02-15T07:43:44.393+03:00",
"DeletedAt": null,
"title": "Seed Post 8",
"images": "uploads/posts/post_8_80cbb5.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 8.",
"slug": "seed-post-8"
},
{
"ID": 21,
"CreatedAt": "2026-02-15T07:43:46.483+03:00",
"UpdatedAt": "2026-02-15T07:43:46.483+03:00",
"DeletedAt": null,
"title": "Seed Post 21",
"images": "uploads/posts/post_21_626d54.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 21.",
"slug": "seed-post-21"
},
{
"ID": 22,
"CreatedAt": "2026-02-15T07:43:46.611+03:00",
"UpdatedAt": "2026-02-15T07:43:46.611+03:00",
"DeletedAt": null,
"title": "Seed Post 22",
"images": "uploads/posts/post_22_2aa79a.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 22.",
"slug": "seed-post-22"
},
{
"ID": 23,
"CreatedAt": "2026-02-15T07:43:46.742+03:00",
"UpdatedAt": "2026-02-15T07:43:46.742+03:00",
"DeletedAt": null,
"title": "Seed Post 23",
"images": "uploads/posts/post_23_4ff46d.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 23.",
"slug": "seed-post-23"
},
{
"ID": 28,
"CreatedAt": "2026-02-15T07:43:47.546+03:00",
"UpdatedAt": "2026-02-15T07:43:47.546+03:00",
"DeletedAt": null,
"title": "Seed Post 28",
"images": "uploads/posts/post_28_a10a99.jpg",
"content": "Bu bir test icerigidir. Gonderi numarasi 28.",
"slug": "seed-post-28"
}
]
}
],
"meta": {
"page": 1,
"per_page": 10,
"total": 13
}
}
Response headers
content-length: 16263
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:56:17 GMT
##############
listele sadece sotf delete
curl -X 'GET' \
'http://localhost:8080/api/v1/admin/tags?trashed=only' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/admin/tags?trashed=only
Server response
Code Details
200
Response body
Download
{
"data": [
{
"ID": 13,
"CreatedAt": "2026-02-17T12:50:12.778+03:00",
"UpdatedAt": "2026-02-17T12:51:54.295+03:00",
"DeletedAt": "2026-02-17T12:55:04.871+03:00",
"name": "test4444_update"
}
],
"meta": {
"page": 1,
"per_page": 10,
"total": 1
}
}
Response headers
content-length: 219
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:57:29 GMT
Responses
######
hard delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/admin/tags/13/hard' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0'
Request URL
http://localhost:8080/api/v1/admin/tags/13/hard
Server response
Code Details
200
Response body
Download
{
"message": "tag permanently deleted",
"tag_id": 13
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 49
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:58:06 GMT
vary: Origin
Responses
####################
restore
curl -X 'POST' \
'http://localhost:8080/api/v1/admin/tags/14/restore' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMzI1MjM0LCJpYXQiOjE3NzEzMTgwMzR9.PlL8aXxN-6U6yZE1nNQJ_sTdnzwni12p_JxoB1RRfy0' \
-d ''
Request URL
http://localhost:8080/api/v1/admin/tags/14/restore
Server response
Code Details
200
Response body
Download
{
"message": "tag restored successfully",
"tag_id": 14
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 51
content-type: application/json; charset=utf-8
date: Tue,17 Feb 2026 09:59:31 GMT
vary: Origin
Responses
Code Description

305
content/admin_users.md Normal file
View File

@@ -0,0 +1,305 @@
admin users List
curl -X 'GET' \
'http://localhost:8080/api/v1/users/list' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/users/list
Server response
Code Details
200
Response body
Download
{
"count": 1,
"users": [
{
"ID": 1,
"CreatedAt": "2026-02-15T19:38:23.59+03:00",
"UpdatedAt": "2026-02-15T19:38:23.59+03:00",
"DeletedAt": null,
"username": "beyhano",
"email": "beyhan@beyhan.dev",
"email_verified": true,
"email_verified_at": "2026-02-18T19:39:41+03:00",
"is_admin": true,
"profiles": [
{
"ID": 1,
"CreatedAt": "2026-02-15T19:38:23.602+03:00",
"UpdatedAt": "2026-02-15T19:38:23.602+03:00",
"DeletedAt": null,
"user_id": 1,
"first_name": "Beyhan",
"last_name": "Oğur"
}
]
}
]
}
Response headers
content-length: 449
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 21:11:44 GMT
user add
curl -X 'POST' \
'http://localhost:8080/api/v1/auth/register' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"email": "test@test.com",
"first_name": "test",
"last_name": "tset",
"password": "password",
"username": "testuser"
}'
Request URL
http://localhost:8080/api/v1/auth/register
Server response
Code Details
201
Response body
Download
{
"message": "registration successful, please verify your email before login",
"user": {
"email": "test@test.com",
"email_verified": false,
"first_name": "test",
"id": 2,
"is_admin": false,
"last_name": "tset",
"username": "testuser"
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 217
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 21:14:11 GMT
vary: Origin
user soft delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/users/2' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/users/2
Server response
Code Details
200
Response body
Download
{
"message": "user soft-deleted successfully",
"user_id": 2
}
user soft delete list
curl -X 'GET' \
'http://localhost:8080/api/v1/users/list/deleted' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/users/list/deleted
Server response
Code Details
200
Response body
Download
{
"count": 1,
"users": [
{
"ID": 2,
"CreatedAt": "2026-02-16T00:14:11.53+03:00",
"UpdatedAt": "2026-02-16T00:14:11.53+03:00",
"DeletedAt": "2026-02-16T00:15:34.342+03:00",
"username": "testuser",
"email": "test@test.com",
"email_verified": false,
"is_admin": false,
"profiles": [
{
"ID": 2,
"CreatedAt": "2026-02-16T00:14:11.546+03:00",
"UpdatedAt": "2026-02-16T00:14:11.546+03:00",
"DeletedAt": null,
"user_id": 2,
"first_name": "test",
"last_name": "tset"
}
]
}
]
}
Response headers
content-length: 424
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 21:16:16 GMT
soft delete user restore
curl -X 'POST' \
'http://localhost:8080/api/v1/users/2/restore' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-d ''
Request URL
http://localhost:8080/api/v1/users/2/restore
Server response
Code Details
200
Response body
Download
{
"message": "user restored successfully",
"user_id": 2
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 52
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 21:17:10 GMT
vary: Origin
user hard delete
curl -X 'DELETE' \
'http://localhost:8080/api/v1/users/2/hard' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E'
Request URL
http://localhost:8080/api/v1/users/2/hard
Server response
Code Details
200
Response body
Download
{
"message": "user permanently deleted",
"user_id": 2
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 50
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 21:18:01 GMT
vary: Origin
user update
curl -X 'PUT' \
'http://localhost:8080/api/v1/users/3' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImJleWhhbkBiZXloYW4uZGV2IiwiaXNfYWRtaW4iOnRydWUsImZpcnN0X25hbWUiOiJCZXloYW4iLCJsYXN0X25hbWUiOiJPxJ91ciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzdWIiOiIxIiwiZXhwIjoxNzcxMTk0OTI4LCJpYXQiOjE3NzExODc3Mjh9.E6Kd65yQoIysJEDoHA-pVqLi4JvG4FBUeRdv8RiVh4E' \
-H 'Content-Type: multipart/form-data' \
-F 'username=Username' \
-F 'email=ewrwerwrwer@fgddfg.com' \
-F 'is_admin=false' \
-F 'password=12345678' \
-F 'first_name=First Name' \
-F 'last_name=Last Name' \
-F 'email_verified=true' \
-F 'avatar=@1632286445-en-sqdgame-main-playgrou-5BVA_cover.jpg;type=image/jpeg'
Request URL
http://localhost:8080/api/v1/users/3
Server response
Code Details
200
Response body
Download
{
"message": "user updated",
"user": {
"ID": 3,
"CreatedAt": "2026-02-16T00:23:49.301+03:00",
"UpdatedAt": "2026-02-16T01:05:17.776+03:00",
"DeletedAt": null,
"username": "Username",
"email": "ewrwerwrwer@fgddfg.com",
"email_verified": true,
"email_verified_at": "2026-02-16T01:05:17.698+03:00",
"is_admin": false,
"profiles": [
{
"ID": 3,
"CreatedAt": "2026-02-16T00:23:49.315+03:00",
"UpdatedAt": "2026-02-16T01:05:17.808+03:00",
"DeletedAt": null,
"user_id": 3,
"avatar_url": "/uploads/avatars/1771193117_1632286445-en-sqdgame-main-playgrou-5BVA_cover.jpg",
"first_name": "First Name",
"last_name": "Last Name"
}
]
}
}
Response headers
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,X-Requested-With
access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
access-control-allow-origin: http://localhost:8080
access-control-max-age: 600
content-length: 576
content-type: application/json; charset=utf-8
date: Sun,15 Feb 2026 22:05:17 GMT
vary: Origin

1
content/index.md Normal file
View File

@@ -0,0 +1 @@
## index MD

6
eslint.config.mjs Normal file
View File

@@ -0,0 +1,6 @@
// @ts-check
import withNuxt from './.nuxt/eslint.config.mjs'
export default withNuxt(
// Your custom configs here
)

111
nuxt.config.ts Normal file
View File

@@ -0,0 +1,111 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
srcDir: 'app',
devtools: { enabled: true },
auth: {
// Full auth endpoint URL (origin + /api/auth). This prevents /session recursion.
// Prefer setting AUTH_ORIGIN in your environment (e.g. https://example.com)
baseURL: `${(
process.env.AUTH_ORIGIN ||
process.env.BASE_SITE_URL ||
`http://localhost:${process.env.PORT || 3000}`
).replace(/\/$/, '')}/api/auth`,
},
turnstile: {
siteKey: process.env.CLOUD_FLARE_SITE_KEY,
},
runtimeConfig: {
//private (server-side only - güvenli)
turnstile: {
// This can be overridden at runtime via the NUXT_TURNSTILE_SECRET_KEY
// environment variable.
secretKey: process.env.CLOUD_FLARE_SECRET,
},
yourOrigin: "",
apiSecret: "",
authOrigin: process.env.AUTH_ORIGIN,
jwtSecret: process.env.JWT_SECRET,
googleToken: process.env.NUXT_GOOGLE_TOKEN,
authSecret: process.env.NUXT_AUTH_SECRET,
githubClientId: process.env.GITHUB_CLIENT_ID,
githubClientSecret: process.env.GITHUB_CLIENT_SECRET,
googleClientId: process.env.GOOGLE_CLIENT_ID,
googleClientSecret: process.env.GOOGLE_CLIENT_SECRET,
public: {
//public (client-side accessible)
BASE_API_URL: process.env.NUXT_PUBLIC_API_BASE,
BASE_SITE_URL: process.env.NUXT_PUBLIC_API_BASE,
yourOrigin: "",
apiBase: "",
},
},
modules: [
//'@nuxt/content',
'@nuxt/a11y',
'@nuxt/eslint',
'@nuxt/fonts',
'@nuxt/hints',
'@nuxt/icon',
'@nuxt/image',
'@nuxt/scripts',
"@sidebase/nuxt-auth",
"@pinia/nuxt",
'@nuxtjs/turnstile',
],
app: {
head: {
title: 'Avigo Multipurpose Theme',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
],
link: [
{ rel: 'stylesheet', href: '/assets/css/bootstrap.min.css' },
{ rel: 'stylesheet', href: '/assets/css/font-awesome-pro.css' },
{ rel: 'stylesheet', href: '/assets/css/magnific-popup.css' },
{ rel: 'stylesheet', href: '/assets/css/owl.carousel.min.css' },
{ rel: 'stylesheet', href: '/assets/css/slick-slider.css' },
{ rel: 'stylesheet', href: '/assets/css/meanmenu.css' },
{ rel: 'stylesheet', href: '/assets/css/typography.css' },
{ rel: 'stylesheet', href: '/assets/css/preloader.css' },
{ rel: 'stylesheet', href: '/assets/css/mobile-menu.css' },
{ rel: 'stylesheet', href: '/assets/css/header.css' },
{ rel: 'stylesheet', href: '/assets/css/footer.css' },
{ rel: 'stylesheet', href: '/assets/css/aos.css' },
{ rel: 'stylesheet', href: '/assets/css/fonts.css' },
{ rel: 'stylesheet', href: '/assets/css/blog-page.css' },
{ rel: 'stylesheet', href: '/assets/css/nice-select.css' },
{ rel: 'stylesheet', href: '/assets/css/modal-video.min.css' },
{ rel: 'stylesheet', href: '/assets/css/responsive.css' },
{ rel: 'stylesheet', href: '/assets/css/comon.css' },
{ rel: 'stylesheet', href: '/assets/css/avigo-unit.css' },
{ rel: 'stylesheet', href: '/assets/css/avigo-core.css' },
{ rel: 'stylesheet', href: '/style.css' }
],
script: [
{ src: '/assets/js/jquery-3-6-0.min.js', defer: true },
{ src: '/assets/js/bootstrap.min.js', defer: true },
// fontawesome will be loaded client-side via a plugin to avoid SSR hydration mismatches
{ src: '/assets/js/owl.carousel.min.js', defer: true },
{ src: 'https://unpkg.com/aos@next/dist/aos.js', defer: true, crossorigin: 'anonymous' },
{ src: '/assets/js/jquery.magnific-popup.min.js', defer: true },
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/waypoints/4.0.1/jquery.waypoints.min.js', defer: true, crossorigin: 'anonymous' },
{ src: 'https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js', defer: true, integrity: 'sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3', crossorigin: 'anonymous' },
{ src: '/assets/js/slick-slider.js', defer: true },
{ src: '/assets/js/mobile-menu.js', defer: true },
{ src: '/assets/js/tilt.jquery.js', defer: true },
{ src: '/assets/js/jquery.lineProgressbar.js', defer: true },
{ src: '/assets/js/jquery.nice-select.js', defer: true },
{ src: '/assets/js/mobile-meanmenu.js', defer: true },
{ src: '/assets/js/modal-video.min.js', defer: true },
{ src: '/assets/js/main.js', defer: true },
]
}
},
vite: {
build: {
sourcemap: false,
},
},
})

44
package.json Normal file
View File

@@ -0,0 +1,44 @@
{
"name": "dj_nuxt",
"type": "module",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxt/a11y": "^1.0.0-alpha.1",
"@nuxt/content": "^3.11.2",
"@nuxt/eslint": "^1.15.1",
"@nuxt/fonts": "^0.13.0",
"@nuxt/hints": "^1.0.0-alpha.6",
"@nuxt/icon": "^2.2.1",
"@nuxt/image": "^2.0.0",
"@nuxt/scripts": "^0.13.2",
"@nuxtjs/turnstile": "^1.1.1",
"@pinia/nuxt": "^0.11.3",
"@sidebase/nuxt-auth": "^1.2.0",
"@unhead/vue": "^2.1.4",
"better-sqlite3": "^12.6.2",
"next-auth": "~4.21.1",
"nuxt": "^4.3.1",
"pinia": "^3.0.4",
"sharp": "^0.34.5",
"sweetalert2": "^11.26.18",
"typescript": "^5.9.3",
"vue": "^3.5.28",
"vue-router": "^4.6.4",
"vue-tsc": "^3.2.4",
"zod": "^4.3.6",
"@fortawesome/fontawesome-svg-core": "^6.5.0",
"@fortawesome/vue-fontawesome": "^3.0.0-4",
"@fortawesome/free-solid-svg-icons": "^6.5.0"
},
"devDependencies": {
"@types/jquery": "^3.5.33",
"@types/node": "^25.2.3"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,873 @@
.breadcrumb-area{
background: #ddd;
}
/*Pagination*/
.theme-pagination ul{
margin: 0;
padding: 0;
list-style: none;
}
.theme-pagination li{
display: inline-block;
}
.theme-pagination li a {
width: 55px;
height: 55px;
text-align: center;
line-height: 55px;
border: 1px solid #E1E3E8;
border-radius: 8px;
margin: 0 4px;
transition: all 0.3s;
display: block;
color: #000;
}
.theme-pagination li a:hover, .theme-pagination li a.active {
background: #7977C6;
transition: all 0.3s;
color: #ffffff;
}
/*Common Widget*/
.single-widget {
padding: 32px;
border-radius: 8px;
margin-bottom: 30px;
background: #FFFFFF;
box-shadow: 0px 0px 48px rgba(0, 0, 0, 0.09);
border-radius: 15px;
}
.single-widget ul{
margin: 0;
padding: 0;
list-style: none;
}
.single-widget li a {
background: transparent;
border-radius: 7px;
font-weight: 400;
font-size: 16px;
line-height: 24px;
display: flex;
/* display: inline-block; */
align-items: center;
color: #161540;
justify-content: space-between;
padding: 17px 20px;
transition: all 0.3s;
background: #F6F7FA;
margin-bottom: 16px;
width: 100%;
transition: all.4s;
}
.single-widget li a:hover {
background-color: #006fff;
color: #fff;
transition: all.3s;
}
.single-widget li a span {
transform: rotate(90deg);
transition: all.3s;
}
.single-widget li:hover span {
transform: rotate(0);
transition: all.3s;
}
.single-widget h3 {
font-weight: 500;
font-size: 20px;
line-height: 20px;
color: #202C4A;
position: relative;
z-index: 2;
padding-left: 10px;
margin-bottom: 16px;
}
/* .single-widget h3:after {
position: absolute;
content: "";
height: 100%;
width: 2px;
background: #7977C6;
left: 0;
top: 0;
z-index: 1;
} */
.widgets {
position: sticky;
top: 74px;
}
/*Search widget*/
.search-form-widget form{
position: relative;
z-index: 2;
border-radius: 7px;
overflow: hidden;
background:#fff;
border: none;
}
.search-form-widget form input{
width: 90%;
padding: 14px;
border: none;
font-size: 16px;
color: #000;
}
.search-form-widget form input::placeholder{
color: #000
}
.search-form-widget form input:focus{
outline: none;
}
button.search-icon {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 10%;
border: none;
}
/*recent post */
.recent-post {
position: relative;
z-index: 2;
padding-bottom: 15px;
}
.recent-post {
display: flex;
align-items: start;
}
.recent-img img {
object-fit: cover;
height: 70px;
width: 100px;
border-radius: 4px;
}
.recent-post-content a {
font-weight: 500;
font-size: 16px;
line-height: 22px;
color: #202C4A;
}
.blog-date-time ul{
margin: 0;
padding: 0;
list-style: none;
}
.blog-date-time ul li a {
font-weight: 400;
font-size: 14px;
line-height: 24px;
color: #161540;
}
/*Blog posts*/
.blog-date-time ul {
display: inline-block;
margin: 0;
padding: 0;
list-style: none;
}
.blog-date-time ul li a {
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: #161540;
}
.author-bio {
display: flex;
align-items: center;
}
.author-name, .blog-category, .blog-date, .blog-coments {
display: flex;
align-items: center;
margin-right: 15px;
}
.author-name a, .blog-category a, .blog-date a, .blog-coments a {
color: #202C4A;
font-weight: 400;
font-size: 16px;
}
.author-name a svg, .blog-category a svg, .blog-date a svg, .blog-coments a svg {
margin-right: 4px;
}
.post-format-video .blog-img {
position: relative;
z-index: 2;
}
.post-format-quote {
background: #e7e8f4;
border-radius: 8px;
padding: 30px;
margin-bottom: 30px;
}
.post-format-quote p {font-size: 20px;line-height: 32px;}
.post-audio iframe{
width: 100%;
}
.video-btn {
position: absolute;
top: calc(50% - 50px);
left: calc(50% - 50px);
z-index: 9;
}
.video-btn a {
font-size: 30px;
display: block;
width: 100px;
height: 100px;
background: #7977C6;
text-align: center;
line-height: 100px;
color: #ffffff;
border-radius: 50%;
}
.video-btn a:hover {
color: #ffffff;
}
.post-format-gallery .owl-nav button {
font-size: 30px;
display: block;
width: 70px;
height: 70px;
background: #2F80ED !important;
text-align: center;
line-height: 100px;
color: #ffffff !important;
border-radius:15px;
position: absolute;
top: calc(50% - 35px);
left: 10px;
z-index: 9;
}
.post-format-gallery .owl-nav button.owl-next {
left: auto;
right: 10px;
}
.blog-date-time {
margin-bottom: 6px;
}
.single-blog-contents h3 {
font-weight: 400;
font-size: 32px;
line-height: 40px;
color: #161540;
margin: 30px 0 20px;
}
.single-blog-contents p {
margin-bottom: 30px;
font-size: 20px;
line-height: 34px;
color: #5C5B79;
}
.single-blog-quote {
background: rgba(242, 57, 54, 0.03);
border-radius: 8px;
padding: 30px 120px 30px 30px;
}
.single-blog-quote h4 {
font-weight: 400;
font-size: 24px;
line-height: 34px;
color: #161540;
}
.single-blog-quote a {
font-weight: 400;
font-size: 16px;
line-height: 16px;
color: #5C5B79;
}
.theme-border {
height: 1px;
border-bottom: 1px solid #E1E3E8;
margin: 20px 0;
}
.single-blog h3 a {
font-weight: 700;
font-size: 24px;
line-height: 34px;
color: #161540;
margin-bottom: 0;
transition: all 0.3s;
display: block;
}
.single-blog p {
font-size: 16px;
line-height: 28px;
color: #5C5B79;
margin-bottom: 0;
}
.post-blog{
margin-bottom: 40px;
}
.post-blog:last-child{
margin-bottom: 0;
}
.post-format-gallery .owl-nav button {
font-size: 30px;
display: block;
width: 70px;
height: 70px;
background: #2F80ED !important;
text-align: center;
line-height: 100px;
color: #ffffff !important;
border-radius: 7px;
position: absolute;
top: calc(50% - 35px);
left: 10px;
z-index: 9;
}
.post-format-gallery .owl-nav button.owl-next {
left: auto;
right: 10px;
}
.theme-btn-14 {
background: #7977C6;
box-shadow: 0 4px 25px rgba(121, 119, 198, 0.2);
border-radius: 7px;
font-family: "satoshi";
font-weight: 600;
font-size: 16px;
line-height: 24px;
display: inline-block;
text-align: center;
color: #ffffff;
padding: 13px 24px;
border: none;
}
/*
++++++++++++++++++++++++++++++++++
==== =====blog page all ====== ======
+++++++++++++++++++++++++++++++++
*/
.coffee-nav ul li a {
color: #2F80ED;
text-decoration: none;
background: rgba(47, 128, 237, 0.2);
border-radius: 7px;
/* padding: 13px 20px; */
font-size: 15px;
font-weight: 700;
transition: all.6s;
cursor: pointer;
display: inline-block;
min-height: 50px;
min-width: 165px;
/* width: 200px; */
text-align: start;
line-height: 50px;
padding-left: 20px;
}
.coffee-nav ul li a:hover,
.coffee-nav ul li a.active {
background: #2F80ED;
box-shadow: 0px 4px 10px rgba(47, 128, 237, 0.2);
color: #fff;
}
.coffee-nav ul {
list-style: none;
padding: 15px 0 25px 0;
justify-content: center;
}
ul {
list-style: none;
}
.coffee-nav ul li {
display: inline-block;
margin: 8px;
margin-top: 20px;
}
.coffee-nav {
background-color: #fff;
margin: 0 auto;
border-radius: 5px;
margin-top: 50px;
margin-bottom: 10px;
}
.shop-1 {
margin-top: 0;
}
ul.nav.nav-tabs {
background: #F6F7FA;
border-radius: 7px;
}
.faq-massge-box-all {
background: #2F80ED;
border-radius: 7px;
padding: 50px 48px;
margin: 0 90px;
}
.massge-single-inputs input {
background: none;
border: none;
border-bottom: 1px solid #fff;
width: 100%;
padding: 17px;
font-size: 16px;
color: #fff;
}
.massge-single-inputs input::placeholder {
color: #fff;
}
.massge-single-inputs textarea {
background: none;
border: none;
border-bottom: 1px solid #fff;
width: 100%;
padding: 17px;
font-size: 20px;
color: #fff;
}
.massge-single-inputs textarea::placeholder {
color: #fff;
}
.massge-btn a {
color: #0072ff;
background-color: #fff;
border-radius: 5px;
padding: 15px 20px;
font-size: 15px;
font-weight: 700;
line-height: 16px;
display: inline-block;
}
.massge-button {
text-align: end;
margin-top: 30px;
}
.about-slider-box.about-slider-box2 {
background-color: #F6F7FA;
}
.accordion-button.accordion-button2.accordion-button3 {
background-color: #fff;
}
/*
++++++++++++++++++++++++++++++++++
==== =====blog page all ====== ======
+++++++++++++++++++++++++++++++++
*/
/*
++++++++++++++++++++++++++++++++++
==== =====blog details all ====== ======
+++++++++++++++++++++++++++++++++
*/
.author-bio > div {
padding: 0 20px 0 0;
}
p.details-p-hadding {
color: #202C4A;
font-weight: 500;
font-size: 18px;
line-height: 18px;
}
.blog-massge-box {
background-color: #202C4A;
border-radius: 15px;
padding: 32px 24px;
margin: 30px 0;
}
.blog-massge-box p {
color: #fff;
line-height: 30px;
font-size: 18px;
font-style: italic;
font-weight: 400;
}
.blog-post-img img {
width: 100%;
}
.border-details {
border: 1px solid #1c243923;
}
.details-tag-list ul li a {
padding: 15px 20px;
border-radius: 7px;
background: rgba(32, 44, 74, 0.1);
display: inline-block;
font-size: 16px;
font-weight: 500;
color: #202C4A;
line-height: 16px;
transition: all.3s;
}
.details-tag-list ul li a:hover {
padding: 15px 20px;
border-radius: 7px;
background: #202C4A;
display: inline-block;
font-size: 16px;
font-weight: 500;
color: #fff;
line-height: 16px;
transition: all.3s;
}
.details-tag-list ul li {
display: inline-block;
}
.blog-details-tag {
display: flex;
align-items: center;
}
.blog-details-tags {
padding: 40px 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.blog-details-icons {
display: flex;
align-items: center;
}
.blogp-details-icon-list ul li a {
background: #2f81ed20;
height: 40px;
width: 40px;
display: inline-block;
text-align: center;
line-height: 40px;
color: #202C4A;
border-radius: 50%;
transition: all.3s;
}
.blogp-details-icon-list ul li a:hover {
color: #fff;
background-color: #006fff;
transition: all.3s;
}
.blogp-details-icon-list ul li {
display: inline-block;
}
.commet-single-box {
display: flex;
align-items: start;
position: relative;
margin-top: 40px;
padding: 40px 24px;
background-color: #F6F7FA;
border-radius: 15px;
}
.single-commet-img {
height: 100px;
width: 100px;
margin-right: 20px;
}
.replly-btn a {
display: inline-block;
background-color: #202C4A;
padding: 12px 14px;
border-radius: 7px;
font-size: 16px;
line-height: 16px;
color: #fff;
font-weight: 700;
transition: all.4s;
}
.replly-btn a:hover {
background-color: #006fff;
transition: all.4s;
}
.replly-btn {
position: absolute;
top: 16px;
right: 16px;
}
.contact-input input {
width: 49%;
padding: 13px;
border: none;
background-color: #F6F7FA;
border-radius: 4px;
margin-top: 16px;
margin-right: 5px;
}
.contact-input textarea {
width: 99%;
padding: 13px;
border: none;
background-color: #F6F7FA;
border-radius: 4px;
margin-top: 16px;
margin-right: 5px;
}
.details-form-btn {
text-align: end;
}
.search-form-widget form {
border: none;
background: #F3F3F3;
}
.search-form-widget form input {
background-color: #F3F3F3;
}
ul.blog-date li {
color: #525975;
line-height: 16px;
font-weight: 400;
font-size: 16px;
display: flex;
}
ul.blog-date li img {
padding-right: 2px;
}
.recent-img {
height: 100px;
width: 100px;
margin-right: 20px;
}
.tagcloud a {
padding: 16px 20px;
background: rgba(32, 44, 74, 0.1);
border-radius: 7px;
color: #202C4A;
}
.tagcloud a:hover {
background-color: #202C4A;
color: #fff;
}
.sidebar-img-box-all {
background: #202C4A;
box-shadow: 0px 0px 48px rgba(0, 0, 0, 0.09);
border-radius: 15px;
padding: 32px;
margin-bottom: 36px;
}
.sidebar-box-hadding h4 a:hover {
color: #fff;
}
.details-side-box-icon ul li a {
display: inline-block;
height: 40px;
width: 40px;
border-radius: 50%;
line-height: 40px;
text-align: center;
background-color: #f6f7fa2b;
color: #fff;
transition: all.3s;
}
.details-side-box-icon ul li {
display: inline-block;
}
.details-side-box-icon ul li a:hover {
background-color: #006fff;
transition: all.3s;
}
.blog-realeted {
margin-top: 120px;
}
.blog-boxs-img img {
width: 100%;
}
.blog-page-boxs.blog-page-boxs1 {
margin-bottom: 45px;
}
@media screen and (max-width: 769px) {
.blog-page-boxs.blog-page-boxs1 {
margin-bottom: 30px;
}
.blog-page-boxs {
margin-bottom: 30px;
}
}
@media screen and (max-width: 426px) {
.blog-details-tags {
padding: 40px 0;
display: block;
align-items: center;
justify-content: space-between;
}
.blog-details-tag {
display: flex;
align-items: center;
margin-bottom: 20px;
}
}
.blog-details-img img {
width: 100%;
}
/*
++++++++++++++++++++++++++++++++++
==== =====blog details all ====== ======
+++++++++++++++++++++++++++++++++
*/
.video-play-button2 {
position: absolute;
z-index: 10;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
box-sizing: content-box;
display: block;
width: 32px;
height: 44px;
/* background: #fa183d; */
border-radius: 50%;
padding: 18px 20px 18px 28px;
}
.video-play-button2:before {
content: "";
position: absolute;
z-index: 0;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
display: block;
width: 80px;
height: 80px;
background: #2F80ED;
border-radius: 50%;
animation: pulse-border 1500ms ease-out infinite;
}
.video-play-button2:after {
content: "";
position: absolute;
z-index: 1;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
display: block;
width: 70px;
height: 70px;
background: #2F80ED;
border-radius: 50%;
transition: all 200ms;
}
.video-play-button2:hover:after {
background-color: darken(#fa183d, 10%);
}
.video-play-button2 img {
position: relative;
z-index: 3;
max-width: 100%;
width: auto;
height: auto;
}
.video-play-button2 span {
display: block;
position: relative;
z-index: 3;
width: 0;
height: 0;
border-left: 13px solid #fff;
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
margin-top: 11px;
margin-left: 7px;
}
@keyframes pulse-border {
0% {
transform: translateX(-50%) translateY(-50%) translateZ(0) scale(1);
opacity: 1;
}
100% {
transform: translateX(-50%) translateY(-50%) translateZ(0) scale(1.5);
opacity: 0;
}
}
.space-left-30 {
padding-left: 30px;
}

7
public/assets/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

840
public/assets/css/comon.css Normal file
View File

@@ -0,0 +1,840 @@
/*----- all-button -----*/
.button1 button {
color: #fff;
font-size: 16px;
background: #0263e0;
line-height: 16px;
padding: 20px 30px;
display: inline-block;
transition: all 0.3s;
font-weight: 700;
border-radius: 10px;
box-shadow: 0px 20px 40px rgba(2, 99, 224, 0.15);
border: none;
}
.button1 button:hover {
transition: all 0.3s;
transform: scale(1.01);
}
.shopes1 {
position: absolute;
right: 0;
top: 50px;
}
.shopes2 {
position: absolute;
left: 0;
bottom: 50px;
}
/*----- all-button -----*/
.hadding2 h1,
h2,
h3,
h4,
h5,
h6 {
color: #202c4a;
}
.hadding2 h1,
a h2,
a h3,
a h4,
a h5,
a h6 a {
color: #202c4a;
}
.hadding2 h4 a {
color: #202c4a;
}
.hadding2 p {
color: #525975;
}
.hadding2 span.after {
position: relative;
}
.hadding2 span.after::after {
content: "";
}
.img-border15 img {
border-radius: 15px;
}
.img100 img {
width: 100%;
}
a.home2-a {
color: #202c4a;
}
.home2-btn a:hover {
color: #fff;
transform: scale(1.03);
transition: all.3s;
}
.hadding3 h1,
h2,
h3,
h4,
h5,
h6 {
color: #09100e;
}
.hadding3 p.p3 {
color: #5e6261;
}
.hadding3 p.p4 {
color: #4D4B51;
}
.hadding3 p {
color: #747474;
}
.hadding3 h4 a {
color: #09100e;
}
.hadding3 span {
display: inline-block;
margin-bottom: 10px;
}
img.img-border {
border-radius: 7px;
}
.hadding4 h1,
h2,
h3,
h4,
h5,
h6 {
color: #030406;
}
.hadding4 h1 {
text-transform: capitalize;
}
p.hadding4-p1 p {
color: rgba(3, 4, 6, 0.7);
}
p.hadding4-p2 p {
color: #4f4f51;
}
.hadding4 h4 a {
color: #030406;
}
.hadding5 span.after {
position:relative;
}
.hadding5 h1 span.after::after {
content: "";
position: absolute;
bottom: 4px;
left: 0px;
width: 100%;
height: 16px;
z-index: -2;
background-image: url(../img/shapes/hadding5-span.svg);
}
.bg5 {
}
/*------------------------------------------
------------ all buttons--------------------
-------------------------------- */
.home4-button a {
display: inline-block;
padding: 16px 25px;
color: #fff;
background-color: #006fff;
border-radius: 5px;
position: relative;
font-size: 16px;
line-height: 16px;
font-weight: 700;
}
.home4-button a:nth {
margin-left: 10px;
}
a.home4-btn2 {
margin-left: 10px;
}
.home4-button a:hover::after {
top: 4px;
left: 4px;
transition: all.4s;
}
.home4-button a:hover {
background: #fff;
transition: all.4s;
color: #030406;
}
a.home4-btn-f {
position: relative;
}
a.home4-btn-f::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #0072ff;
z-index: -2;
border-radius: 5px;
transition: all.4s;
}
a.home4-btn-f:hover::after {
top: 4px;
left: 4px;
transition: all.4s;
}
a.cta-btn4 {
background: none;
}
a.cta-btn4:hover {
background: none;
color: #fff;
}
a.home4-btn-plan {
position: relative;
}
a.home4-btn-plan::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
width: 100%;
height: 100%;
background-color: #030406;
z-index: -2;
border-radius: 5px;
transition: all.4s;
}
a.home4-btn-plan:hover::after {
top: 4px;
left: 4px;
transition: all.4s;
}
.home4-button.home4-pricing-btn a {
width: 100%;
text-align: center;
}
.home4-pricing-main-img {
position: absolute;
top: 32px;
right: 32px;
}
.home2-pricing-box::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
#030406;
border-radius: 10px;
z-index: -5;
transition: all.3s;
opacity: 0;
visibility: hidden;
}
.home2-pricing-box:hover::before {
visibility: visible;
opacity: 1;
transition: all.3s;
top: 10px;
left: 10px;;
}
.home2-pricing-box.active::before {
visibility: visible;
opacity: 1;
transition: all.3s;
top: 10px;
left: 10px;;
}
.home2-pricing-box:hover {
border: none;
}
.button5 a {
font-size: 16px;
font-weight: 700;
background-color: #24BA96;
line-height: 16px;
color: #fff;
padding: 16px 24px;
display: inline-block;
border-radius: 4px;
transition: all.3s;
}
.button5 a:hover {
font-size: 16px;
font-weight: 700;
background-color: #fff;
line-height: 16px;
color: #24BA96;
padding: 16px 24px;
border-radius: 4px;
transition: all.3s;
box-shadow: 4px 4px 48px rgba(0, 0, 0, 0.14);
}
.button5 a:hover svg {
color: #Fff;
}
.button5 a:hover span {
background-color: #24BA96;
}
.button5 a svg {
color: #24BA96;
transition: all.3s;
}
.button5 a span {
background-color: #fff;
height: 20px;
display: inline-block;
width: 20px;
border-radius: 50%;
text-align: center;
line-height: 18px;
font-size: 12px;
transform: rotate(-45deg);
margin-left: 3px;
}
.button5-btn2 a:hover {
font-size: 16px;
font-weight: 700;
background-color: #24BA96;
line-height: 16px;
color: #fff;
padding: 16px 24px;
border-radius: 4px;
transition: all.3s;
}
.button5-btn2 a {
font-size: 16px;
font-weight: 700;
background-color: #fff;
line-height: 16px;
color: #24BA96;
padding: 16px 24px;
border-radius: 4px;
transition: all.3s;
box-shadow: 4px 4px 48px rgba(0, 0, 0, 0.14);
}
.button5-btn2 a svg {
color: #Fff;
}
.d-none.d-md-block.button5-btn2:hover a svg {
color: #24BA96;
}
.button5-btn2 a:hover span {
background-color: #fff;
}
.button5-btn2 a span {
background-color: #24BA96;
height: 20px;
display: inline-block;
width: 20px;
border-radius: 50%;
text-align: center;
line-height: 18px;
font-size: 12px;
transform: rotate(-45deg);
margin-left: 3px;
}
.button5 button {
font-size: 16px;
font-weight: 700;
background-color: #24BA96;
line-height: 16px;
color: #fff;
padding: 16px 24px;
border: none;
border-radius: 4px;
}
.button5 button:hover {
font-size: 16px;
font-weight: 700;
background-color: #fff;
line-height: 16px;
color: #24BA96;
padding: 16px 24px;
border: none;
border-radius: 4px;
box-shadow: 4px 4px 48px rgba(0, 0, 0, 0.14);
transition: all.3s;
}
.button5 button svg {
color: #24BA96;
transition: all.3s;
}
.button5 button:hover svg {
color: #fff;
transition: all.3s;
}
.button5 button span {
background-color: #fff;
height: 20px;
display: inline-block;
width: 20px;
border-radius: 50%;
text-align: center;
transition: all.3s;
line-height: 20px;
font-size: 12px;
transform: rotate(-45deg);
margin-left: 3px;
}
.button5 button:hover span {
background-color: #24BA96;
}
/*------------------------------------------
------------ all buttons--------------------
-------------------------------- */
.video-play-button {
position: absolute;
z-index: 10;
top: 50%;
left: 50%;
top: 8px;
transform: translateX(-50%) translateY(-50%);
box-sizing: content-box;
display: block;
width: 32px;
height: 44px;
/* background: #fa183d; */
border-radius: 50%;
padding: 18px 20px 18px 28px;
}
.video-play-button:before {
content: "";
position: absolute;
z-index: 0;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
display: block;
width: 60px;
height: 60px;
background: #24BA96;
border-radius: 50%;
animation: pulse-border 1500ms ease-out infinite;
}
.padding50 {
padding: 20px 0 80px 0;
}
.video-play-button:after {
content: "";
position: absolute;
z-index: 1;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
display: block;
width: 50px;
height: 50px;
background: #24BA96;
border-radius: 50%;
transition: all 200ms;
}
.video-play-button:hover:after {
background-color: darken(#fa183d, 10%);
}
.video-play-button img {
position: relative;
z-index: 3;
max-width: 100%;
width: auto;
height: auto;
}
.video-play-button span {
display: block;
position: relative;
z-index: 3;
width: 0;
height: 0;
border-left: 13px solid #fff;
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
margin-top: 11px;
margin-left: 7px;
}
@keyframes pulse-border {
0% {
transform: translateX(-50%) translateY(-50%) translateZ(0) scale(1);
opacity: 1;
}
100% {
transform: translateX(-50%) translateY(-50%) translateZ(0) scale(1.5);
opacity: 0;
}
}
.video-overlay {
position: fixed;
z-index: -1;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.80);
opacity: 0;
transition: all ease 500ms;
}
.video-overlay.open {
position: fixed;
z-index: 1000;
opacity: 1;
}
.video-overlay-close {
position: absolute;
z-index: 1000;
top: 15px;
right: 20px;
font-size: 36px;
line-height: 1;
font-weight: 400;
color: #24BA96;
text-decoration: none;
cursor: pointer;
transition: all 200ms;
}
.video-overlay-close:hover {
color: #24BA96;
}
/*============================
++++PAGE-PROGRESS-SATRT+++++
=============================*/
.blok:nth-of-type(odd) {
background-color: white;
}
.blok:nth-of-type(even) {
background-color: black;
}
@-webkit-keyframes border-transform {
0%,
100% {
border-radius: 63% 37% 54% 46% / 55% 48% 52% 45%;
}
14% {
border-radius: 40% 60% 54% 46% / 49% 60% 40% 51%;
}
28% {
border-radius: 54% 46% 38% 62% / 49% 70% 30% 51%;
}
42% {
border-radius: 61% 39% 55% 45% / 61% 38% 62% 39%;
}
56% {
border-radius: 61% 39% 67% 33% / 70% 50% 50% 30%;
}
70% {
border-radius: 50% 50% 34% 66% / 56% 68% 32% 44%;
}
84% {
border-radius: 46% 54% 50% 50% / 35% 61% 39% 65%;
}
}
/* #Progress
================================================== */
/*============================
++++PAGE-PROGRESS-SATRT+++++
=============================*/
.blok:nth-of-type(odd) {
background-color: white;
}
.blok:nth-of-type(even) {
background-color: black;
}
@-webkit-keyframes border-transform {
0%,
100% {
border-radius: 63% 37% 54% 46% / 55% 48% 52% 45%;
}
14% {
border-radius: 40% 60% 54% 46% / 49% 60% 40% 51%;
}
28% {
border-radius: 54% 46% 38% 62% / 49% 70% 30% 51%;
}
42% {
border-radius: 61% 39% 55% 45% / 61% 38% 62% 39%;
}
56% {
border-radius: 61% 39% 67% 33% / 70% 50% 50% 30%;
}
70% {
border-radius: 50% 50% 34% 66% / 56% 68% 32% 44%;
}
84% {
border-radius: 46% 54% 50% 50% / 35% 61% 39% 65%;
}
}
/* #Progress
================================================== */
.progress-wrap {
position: fixed;
right: 30px;
bottom: 30px;
height: 56px;
width: 56px;
cursor: pointer;
display: block;
border-radius: 50px;
box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.1);
z-index: 10000;
opacity: 0;
visibility: hidden;
transform: translateY(15px);
-webkit-transition: all 200ms linear;
transition: all 200ms linear;
}
.progress-wrap.active-progress {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.progress-wrap::after {
position: absolute;
font-family: "FontAwesome";
content: "\f062";
text-align: center;
line-height: 56px;
font-size: 18px;
color: #03256c;
left: 0;
top: 0;
height: 56px;
width: 56px;
cursor: pointer;
display: block;
z-index: 1;
-webkit-transition: all 200ms linear;
transition: all 200ms linear;
}
.progress-wrap:hover::after {
opacity: 0;
}
.progress-wrap::before {
position: absolute;
font-family: "FontAwesome";
content: "\f062";
text-align: center;
line-height: 56px;
font-size: 18px;
opacity: 0;
left: 0;
top: 0;
height: 56px;
width: 56px;
cursor: pointer;
display: block;
z-index: 2;
-webkit-transition: all 200ms linear;
transition: all 200ms linear;
}
.progress-wrap:hover::before {
opacity: 1;
}
.progress-wrap svg path {
fill: none;
}
.progress-wrap svg.progress-circle path {
stroke: #03256c; /* --- Lijn progres kleur --- */
stroke-width: 4;
box-sizing: border-box;
-webkit-transition: all 200ms linear;
transition: all 200ms linear;
}
/*============================
++++PAGE-PROGRESS-END+++++
=============================*/
/*========+ PRELOADER +=========*/
#preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #061a45;
z-index: 9999;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #B1C8E8;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #B1C8E8;
-webkit-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #B1C8E8;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
/*========+ PRELOADER +=========*/
.border15 {
border-radius: 15px;
}
.border15 img {
border-radius: 15px;
}
.theme-pagination ul{
margin: 0;
padding: 0;
list-style: none;
}
.theme-pagination li{
display: inline-block;
}
.theme-pagination li a {
width: 55px;
height: 55px;
text-align: center;
line-height: 55px;
border: 1px solid #E1E3E8;
background: var(--background, #F4F8F7);
border-radius: 8px;
margin: 0 4px;
transition: all 0.3s;
display: block;
color: #000;
}
.theme-pagination li a:hover, .theme-pagination li a.active {
background: var(--business-consulting, #029AFF);
transition: all 0.3s;
color: #ffffff;
}

File diff suppressed because one or more lines are too long

366
public/assets/css/fonts.css Normal file
View File

@@ -0,0 +1,366 @@
/*! Generated by Font Squirrel (https://www.fontsquirrel.com) on March 28, 2023 */
/* -------basier font------- */
@font-face {
font-family: "basier";
src: url("../fonts/") format("woff2"),
url("../fonts/basiersquare-regular-webfont.woff") format("woff");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "basier";
src: url("../fonts/basiercircle-regular-webfont.woff2") format("woff2"),
url("../fonts/basiercircle-regular-webfont.woff") format("woff");
font-weight: 400;
font-style: normal;
}
/* -------basier font------- */
/* -------butler font------- */
@font-face {
font-family: "butler";
src: url("../fonts/butler_extrabold-webfont.woff2") format("woff2"),
url("../fonts/butler_extrabold-webfont.woff") format("woff");
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: "butler";
src: url("../fonts/butler_light-webfont.woff2") format("woff2"),
url("../fonts/butler_light-webfont.woff") format("woff");
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: "butler";
src: url("../fonts/butler_medium-webfont.woff2") format("woff2"),
url("../fonts/butler_medium-webfont.woff") format("woff");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "butler";
src: url("../fonts/butler_regular-webfont.woff2") format("woff2"),
url("../fonts/butler_regular-webfont.woff") format("woff");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "butler";
src: url("../fonts/butler_ultra_light-webfont.woff2") format("woff2"),
url("../fonts/butler_ultra_light-webfont.woff") format("woff");
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: "butler";
src: url("../fonts/butler_black-webfont.woff2") format("woff2"),
url("../fonts/butler_black-webfont.woff") format("woff");
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: "butler";
src: url("../fonts/butler_bold-webfont.woff2") format("woff2"),
url("../fonts/butler_bold-webfont.woff") format("woff");
font-weight: 700;
font-style: normal;
}
/* -------butler font------- */
/* -------samsung_sharp font------- */
@font-face {
font-family: "samsung_sharp";
src: url("../fonts/") format("woff2"),
url("../fonts/samsungsharpsans-medium-webfont.woff") format("woff");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "samsung_sharp";
src: url("../fonts/samsungsharpsans-webfont.woff2") format("woff2"),
url("../fonts/samsungsharpsans-webfont.woff") format("woff");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "samsung_sharp";
src: url("../fonts/samsungsharpsans-bold-webfont.woff2") format("woff2"),
url("../fonts/samsungsharpsans-bold-webfont.woff") format("woff");
font-weight: 700;
font-style: normal;
}
/* -------samsung_sharp font------- */
/* -------gilroy font------- */
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-extrabold-webfont.woff2") format("woff2"),
url("../fonts/gilroy-extrabold-webfont.woff") format("woff");
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-extrabolditalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-extrabolditalic-webfont.woff") format("woff");
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-heavy-webfont.woff2") format("woff2"),
url("../fonts/gilroy-heavy-webfont.woff") format("woff");
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-heavyitalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-heavyitalic-webfont.woff") format("woff");
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-light-webfont.woff2") format("woff2"),
url("../fonts/gilroy-light-webfont.woff") format("woff");
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-lightitalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-lightitalic-webfont.woff") format("woff");
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-medium-webfont.woff2") format("woff2"),
url("../fonts/gilroy-medium-webfont.woff") format("woff");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-mediumitalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-mediumitalic-webfont.woff") format("woff");
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-black-webfont.woff2") format("woff2"),
url("../fonts/gilroy-black-webfont.woff") format("woff");
font-weight: 1000;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("gilroy-blackitalic-webfont.woff2") format("woff2"),
url("gilroy-blackitalic-webfont.woff") format("woff");
font-weight: 1000;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-bold-webfont.woff2") format("woff2"),
url("../fonts/gilroy-bold-webfont.woff") format("woff");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-bolditalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-bolditalic-webfont.woff") format("woff");
font-weight: 700;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-semibold-webfont.woff2") format("woff2"),
url("../fonts/gilroy-semibold-webfont.woff") format("woff");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-semibolditalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-semibolditalic-webfont.woff") format("woff");
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-thin-webfont.woff2") format("woff2"),
url("../fonts/gilroy-thin-webfont.woff") format("woff");
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-thinitalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-thinitalic-webfont.woff") format("woff");
font-weight: 200;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-ultralight-webfont.woff2") format("woff2"),
url("../fonts/gilroy-ultralight-webfont.woff") format("woff");
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-ultralightitalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-ultralightitalic-webfont.woff") format("woff");
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-regular-webfont.woff2") format("woff2"),
url("../fonts/gilroy-regular-webfont.woff") format("woff");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "gilroy";
src: url("../fonts/gilroy-regularitalic-webfont.woff2") format("woff2"),
url("../fonts/gilroy-regularitalic-webfont.woff") format("woff");
font-weight: 400;
font-style: italic;
}
/* -------CircularStd font------- */
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-Black.eot");
src: url("../fonts/CircularStd-Black.eot?#iefix") format("embedded-opentype"),
url("../fonts/CircularStd-Black.woff") format("woff"),
url("../fonts/CircularStd-Black.ttf") format("truetype"),
url("../fonts/CircularStd-Black.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-BlackItalic.eot");
src: url("../fonts/CircularStd-BlackItalic.eot?#iefix")
format("embedded-opentype"),
url("../fonts/CircularStd-BlackItalic.woff") format("woff"),
url("../fonts/CircularStd-BlackItalic.ttf") format("truetype"),
url("../fonts/CircularStd-BlackItalic.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-Bold.eot");
src: url("../fonts/CircularStd-Bold.eot?#iefix") format("embedded-opentype"),
url("../fonts/CircularStd-Bold.woff") format("woff"),
url("../fonts/CircularStd-Bold.ttf") format("truetype"),
url("../fonts/CircularStd-Bold.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-BoldItalic.eot");
src: url("../fonts/CircularStd-BoldItalic.eot?#iefix")
format("embedded-opentype"),
url("../fonts/CircularStd-BoldItalic.woff") format("woff"),
url("../fonts/CircularStd-BoldItalic.ttf") format("truetype"),
url("../fonts/CircularStd-BoldItalic.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-Medium.eot");
src: url("../fonts/CircularStd-Medium.eot?#iefix") format("embedded-opentype"),
url("../fonts/CircularStd-Medium.woff") format("woff"),
url("../fonts/CircularStd-Medium.ttf") format("truetype"),
url("../fonts/CircularStd-Medium.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-MediumItalic.eot");
src: url("../fonts/CircularStd-MediumItalic.eot?#iefix")
format("embedded-opentype"),
url("../fonts/CircularStd-MediumItalic.woff") format("woff"),
url("../fonts/CircularStd-MediumItalic.ttf") format("truetype"),
url("../fonts/CircularStd-MediumItalic.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-Book.eot");
src: url("../fonts/CircularStd-Book.eot?#iefix") format("embedded-opentype"),
url("../fonts/CircularStd-Book.woff") format("woff"),
url("../fonts/CircularStd-Book.ttf") format("truetype"),
url("../fonts/CircularStd-Book.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "CircularStd";
src: url("../fonts/CircularStd-BookItalic.eot");
src: url("../fonts/CircularStd-BookItalic.eot?#iefix")
format("embedded-opentype"),
url("../fonts/CircularStd-BookItalic.woff") format("woff"),
url("../fonts/CircularStd-BookItalic.ttf") format("truetype"),
url("../fonts/CircularStd-BookItalic.svg#bcc26993292869431e54c666aafa8fcd")
format("svg");
font-weight: 400;
font-style: italic;
}

View File

@@ -0,0 +1,241 @@
/*----===========FOOTER AREA CSS----============*/
.single-footer ul {
list-style: none;
}
.single-footer ul li a {
color: #09120e;
font-size: 16px;
padding: 10px 0;
display: inline-block;
transition: all.3s;
}
.single-footer ul li a:hover {
color: #006fff;
transition: all.3s;
margin-left: 5px;
}
.footer-area3 .single-footer ul li a:hover {
color: #ff008a;
}
.single-footer h3 {
margin-left: 0;
margin-bottom: 20px;
}
.copyright2 {
border-top: 1px solid #E9ECEA;
padding: 16px 0;
margin-top: 30px;
}
.copyright4 {
border-top: 1px solid #737373;
padding: 16px 0;
margin-top: 30px;
}
.copyright5 {
border-top: 1px solid #192D6A;
padding: 16px 0;
margin-top: 30px;
}
.social1 ul {
list-style: none;
}
.social1 ul li {
display: inline-block;
}
.social1 ul li a {
background: #f2f2f2;
height: 40px;
width: 40px;
display: inherit;
border-radius: 50%;
text-align: center;
color: #006fff;
line-height: 40px;
transition: all.4s;
}
.social1 ul li a:hover {
background-color: #006fff;
color: #fff;
transition: all.4s;
}
@media screen and (max-width: 426px) {
.social.social1 {
display: flex;
justify-content: center;
}
.social1.comon-footer-icons {
margin-top: 24px;
}
}
.foonter-contact-icon-1 > div {
background: #2f81ed17;
height: 40px;
width: 40px;
border-radius: 50%;
text-align: center;
line-height: 40px;
}
.foonter-contact-p a {
color: #525975;
display: block;
font-size: 16px;
font-weight: 400;
}
.foonter-contact-1 {
display: flex;
align-items: start;
}
.foonter-contact-p {
padding-left: 8px;
}
.foonter-contact-1 {
padding-top: 24px;
}
/*----===========FOOTER AREA CSS----============*/
/*----===========HOME4 FOOTER AREA CSS----============*/
.footer-area4 {
background-image: url(../img/footer/footer4.png);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.single-footer4 ul li a {
color: #B2B4B7;
}
.single-footer4 p {
color: #B2B4B7;
}
.social.social4 ul {
margin: 0;
padding: 0;
}
.social.social4 {
margin-top: 30px;
}
.social.social4 ul li a {
font-size: 20px;
color: #000410;
background: #fff;
/* padding: 5px; */
height: 24px;
width: 24px;
display: inline-block;
border-radius: 5px;
text-align: center;
transition: all.3s;
}
.social.social4 ul li a:hover {
transition: all.3s;
background-color: #006fff;
transform: scale(1.1);
}
.social.social4 ul li {
display: inline-block;
margin-right: 7px;
}
/*----===========HOME4 FOOTER AREA CSS----============*/
/*----===========HOME5 FOOTER AREA CSS----============*/
.footer-area5 {
background-image: url(../img/bg/footer5-bg.png);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.footer5-p {
color: #CCD0DE;
}
.footer5-from {
position: relative;
margin-top: 50px;
}
.social1 ul {
list-style: none;
padding: 0;
margin-top: 0;
}
.social.social1.social5.text-start ul li a {
background-color: #192D6A;
color: #fff;
}
.social.social1.social5.text-start ul li a:hover {
background-color: #24BA96;
}
.foorer5-list li a {
color: rgba(255, 255, 255, 0.90);
transition: all.3s;
display: block;
padding: 10px 0;
}
.foorer5-list li a:hover {
color: #fff;
margin-left: 5px;
}
ul.foorer5-list {
padding: 0;
margin-top: 0;
}
.foonter-contact-p.foonter-contact-p5 a {
color: rgba(255, 255, 255, 0.90);
}
.foonter-contact-p.foonter-contact-p5 a {
display: block;
margin-left: 5px;
}
.footer5-from-input input {
border: none;
background: none;
padding: 13px;
border-bottom: 1px solid #fff;
color: #fff;
width: 160px;
}
.button5.button5-footer5 {
position: absolute;
right: 0;
top: 0;
}
.footer5-p p {
color: rgba(255, 255, 255, 0.90);
}
.foonter-contact-icon-1 {
margin-right: 10px;
}
/*----===========HOME5 FOOTER AREA CSS----============*/

View File

@@ -0,0 +1,640 @@
.header-area {
padding: 20px 0;
}
.main-menu ul {
margin: 0;
padding: 0;
list-style: none;
}
.main-menu li {
display: inline-block;
}
.site-logo {
max-width: 280px;
height: auto;
display: flex;
align-items: center;
}
.site-logo a {
display: block;
}
.main-menu li a {
color: #000;
padding: 10px 20px;
display: block;
}
.mean-container .mean-nav > ul {
display: block !important;
}
.main-menu ul li {
display: inline-block;
margin-right: 40px;
position: relative;
}
.home1-site-logo span {
background: #cce4ff;
padding: 6px 8px;
font-size: 13px;
line-height: 13px;
margin-left: 8px;
color: #0263e0;
border-radius: 7px;
}
@media only screen and (min-width: 992px) and (max-width: 1199px) {
.main-menu ul li {
margin-right: 22px;
}
}
.main-menu ul li:last-child {
margin-right: 0;
}
.main-menu ul li.has-dropdown > a {
padding-right: 14px;
}
.main-menu ul li.has-dropdown > a::after {
position: absolute;
content: "\f107";
right: -4px;
top: 22px;
font-size: 14px;
font-size: 14px;
color: #000;
font-family: "Font Awesome 6 Pro";
}
.main-menu ul li a {
color: #0f1826;
font-size: 16px;
line-height: 20px;
font-weight: 450;
padding: 20px 0;
display: inline-block;
transition: 0.3s;
}
.main-menu ul li .active {
color: #f9f9f9;
}
.main-menu ul li:hover > a {
color: #000;
}
.main-menu ul li:hover > a::after {
color: #000;
}
.main-menu ul li:hover .sub-menu {
visibility: visible;
opacity: 1;
top: 100%;
}
.main-menu ul li .sub-menu {
position: absolute;
left: 0;
top: 110%;
min-width: 220px;
background-color: #fff;
opacity: 0;
visibility: hidden;
z-index: 99;
-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.16);
-moz-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.16);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.16);
padding: 20px 0px 15px;
transition: 0.3s;
border-top: 4px solid #000;
}
.main-menu ul li .sub-menu li {
margin: 0;
padding: 0;
display: inline;
position: relative;
width: 100%;
display: block;
}
.main-menu ul li .sub-menu li a {
font-size: 16px;
font-weight: 450;
display: block;
padding: 10px 25px;
transition: 0.3s;
color: #000;
text-transform: capitalize;
}
.main-menu ul li .sub-menu li .active {
color: #000;
}
.main-menu ul li .sub-menu li:hover > a {
color: #0263e0;
margin-left: 5px;
}
.main-menu ul li .sub-menu li > .sub-menu {
left: 120%;
top: 0;
visibility: hidden;
opacity: 0;
}
.main-menu ul li .sub-menu li:hover > .sub-menu {
left: 100%;
visibility: visible;
opacity: 1;
}
.meanmenu-reveal {
display: none !important;
}
.mean-container .mean-nav {
margin-top: 0 !important;
}
.mean-container .mean-bar {
padding: 0 0 !important;
}
.sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.slidebar-content {
padding: 20px 40px;
}
.sidebar-header {
margin-bottom: 20px;
}
.sidebar-logo {
max-width: 250px;
}
.sidebar-close {
font-size: 20px;
}
.sidebar-logo {
max-width: 250px;
}
.sidebar-close {
font-size: 20px;
}
.sidebar-socials ul {
padding: 0;
margin: 0;
list-style: none;
}
.sidebar-socials li {
display: inline-block;
}
.sidebar-socials {
margin-top: 30px;
}
.sidebar-socials li a {
display: block;
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
color: #fff;
background: #000;
}
.sidebar-socials li a:hover {
background: #f9f9f9;
}
.sidebar-single-contact-info {
position: relative;
z-index: 2;
padding-left: 80px;
min-height: 60px;
margin-top: 24px;
}
.sidebar-contact-info-icon {
position: absolute;
left: 0;
top: 0;
background: #f9f9f9;
width: 60px;
height: 60px;
border-radius: 50%;
text-align: center;
line-height: 60px;
font-size: 20px;
}
.mobile-menu.fix.mean-container {
clear: both;
overflow: hidden;
}
.sidebar-main {
position: fixed;
width: 100%;
height: 100%;
background: #f1f1f1;
transition: all 0.3s;
right: -100%;
top: 0;
visibility: hidden;
opacity: 0;
}
.sidebar-main.active {
transition: all 0.3s;
visibility: visible;
opacity: 1;
right: 0;
}
.mobile-menu-bar {
height: 30px;
width: 40px;
text-align: center;
line-height: 30px;
font-size: 20px;
}
.sidebar-contact-info-title p {
font-size: 18px;
}
.sidebar-contact-info-title a {
font-size: 20px;
color: #000;
font-weight: 700;
}
.sidebar-single-contact-info {
border-bottom: 1px solid #ddd;
padding-bottom: 30px;
}
a.cta-btn {
color: #fff;
font-size: 14px;
position: relative;
line-height: 14px;
padding: 17px 20px;
display: inline-block;
transition: all 0.3s;
border-radius: 10px;
overflow: hidden;
box-shadow: 0px 20px 40px rgba(2, 99, 224, 0.15);
}
a.cta-btn::before {
content: "";
position: absolute;
background: #0263e0;
height: 100%;
width: 100%;
top: 0;
left: 0;
z-index: -2;
border-radius: 7px;
}
a.cta-btn::after {
content: "";
position: absolute;
background: #202C4A;
height: 100%;
width: 0%;
top: 0;
left: 0;
z-index: -1;
border-radius: 7px;
transition: all.4s;
}
a.cta-btn:hover::after {
width: 100%;
transition: all.4s;
}
a.cta-btn:hover {
transition: all 0.3s;
transform: translatey(-5px);
}
a.login-btn {
color: #0263e0;
display: inline-block;
margin-right: 5px;
}
/*------------------------ home 2 -------------------------*/
a.home2-site-btn-1 {
color: #202c4a;
display: inline-block;
margin-right: 10px;
}
/*------------------------ home 2 -------------------------*/
.main-menu3 ul li a {
font-size: 18px;
font-weight: 400;
line-height: 18px;
color: #fff;
}
.main-menu3 ul li.has-dropdown > a::after {
position: absolute;
content: "";
right: -4px;
top: 22px;
font-size: 14px;
color: #fff;
font-family: "Font Awesome 6 Pro";
}
.main-menu3 ul li a:hover {
color: #fff;
}
a.home3-cta {
background: #ff008a;
color: #fff;
display: inline-block;
padding: 17px 50px;
border-radius: 5px;
}
/*
++++++++++++++++++++++++++++++++++
==== =====nav menu all ====== ======
+++++++++++++++++++++++++++++++++
*/
.main-menu-ex li {
display: inline-block;
}
.main-menu-ex li a {
color: #000;
font-size: 16px;
display: block;
font-weight: 500;
padding: 10px 15px;
transition: all.3s;
}
.main-menu-ex li a:hover {
color: #0263e0;
transition: all.3s;
}
.main-menu-ex li {
position: relative;
z-index: 9;
}
.main-menu-ex li ul {
position: absolute;
left: 0;
top: 50px;
background: #fff;
width: 200px;
border-radius: 4px;
box-shadow: rgb(0 0 0 / 20%) 0px 20px 30px;
padding: 10px;
}
.main-menu-ex li li {
display: block;
}
.main-menu-ex li li a {
padding: 10px 20px;
color: #0e1124;
font-weight: 500;
transition: all.4s;
border-radius: 4px;
position: relative;
}
.main-menu-ex li li a:hover {
color: #fff;
transition: all.4s;
}
.main-menu-ex li li a::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: rotatey(60deg);
color: #fff;
border-radius: 4px;
transition: all.4s;
z-index: -1;
}
.main-menu-ex li li a:hover::after {
transform: rotatey(0deg);
background-color:
#2F80ED;
border-radius: 4px;
}
.main-menu-ex li li a .main-menu-ex li li a:hover {
background-color: #00bf71;
transform: rotatey(0deg);
border-radius: 4px;
}
.main-menu-ex li > ul {
opacity: 0;
visibility: hidden;
top: 60px;
left: 0;
transition: all 0.3s;
transform: translateY(20px);
}
.main-menu-ex li li > ul {
left: 100%;
right: auto;
}
.main-menu-ex li li:hover > ul {
top: 0;
}
.main-menu-ex li:hover > ul {
visibility: visible;
opacity: 1;
top: 50px;
transition: all 0.3s;
z-index: 99;
transform: translateY(0);
}
.main-menu-ex.home4-menu li li a {
padding: 10px 20px;
color: #0e1124;
font-weight: 500;
transition: all.4s;
border-radius: 4px;
position: relative;
}
.main-menu-ex.home4-menu li li a:hover {
color: #fff;
transition: all.4s;
}
.main-menu-ex.home4-menu li li a::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateY(-10px);
color: #fff;
border-radius: 4px;
transition: all.4s;
z-index: -1;
}
.main-menu-ex.home4-menu li li a:hover::after {
transform: translateY(0);
background-color: #ff3e55;
border-radius: 4px;
}
.main-menu-ex.home2-menu li li a {
padding: 10px 20px;
color: #0e1124;
font-weight: 500;
transition: all.4s;
border-radius: 4px;
position: relative;
}
.main-menu-ex.home2-menu li li a:hover {
color: #fff;
transition: all.4s;
}
.main-menu-ex.home2-menu li li a::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateY(-10px);
color: #fff;
border-radius: 4px;
transition: all.4s;
z-index: -1;
}
.main-menu-ex.home2-menu li li a:hover::after {
transform: translateY(0);
background-color: #00bf71;
border-radius: 4px;
}
li.has-dropdown1 {
position: relative;
}
li.has-dropdown1 span {
position: absolute;
right: 10px;
}
.main-menu-ex.main-menu-ex3 li a:hover {
color: #ff008a;
}
.main-menu-ex.main-menu-ex3 li li a:hover {
color: #fff;
background-color: #ff008a;
}
.main-menu-ex.main-menu-ex4 li a {
color: #fff;
}
.main-menu-ex.main-menu-ex4 li a:hover {
color: #2F80ED;
}
.main-menu-ex.main-menu-ex4 li li a {
color: #0e1124;
}
.main-menu-ex.main-menu-ex4 li li a:hover {
background-color: #0263e0;
}
.main-menu-ex.main-menu-ex4 li li a:hover {
color: #fff;
}
.main-menu-ex.main-menu-ex5 li li a:hover {
background-color: #24BA96;
}
.main-menu-ex.main-menu-ex5 li li a:hover {
color: #fff;
}
.main-menu-ex.main-menu-ex5 li a:hover {
color: #00bf71;
}
/*
++++++++++++++++++++++++++++++++++
==== =====nav menu all ====== ======
+++++++++++++++++++++++++++++++++
*/
.header-area {
transition: all.6s;
}
.header-area.sticky {
position: fixed;
top: 0;
left: 0;
width: 100%;
transform: translate3d(0, 0, 0);
z-index: 111;
-webkit-animation-name: fade-in-down;
animation-name: fade-in-down;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
background-color: #ffffff;
transition: all 0.3s ease-in-out;
z-index: 99;
}
.header-area.header-area2.sticky {
position: fixed;
top: 0;
left: 0;
width: 100%;
transform: translate3d(0, 0, 0);
z-index: 111;
-webkit-animation-name: fade-in-down;
animation-name: fade-in-down;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
background-color: #0A122A;
transition: all 0.3s ease-in-out;
z-index: 99;
}
.header-site-btn {
display: flex;
align-items: center;
}

View File

@@ -0,0 +1,351 @@
/* Magnific Popup CSS */
.mfp-bg {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1042;
overflow: hidden;
position: fixed;
background: #0b0b0b;
opacity: 0.8; }
.mfp-wrap {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1043;
position: fixed;
outline: none !important;
-webkit-backface-visibility: hidden; }
.mfp-container {
text-align: center;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
padding: 0 8px;
box-sizing: border-box; }
.mfp-container:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle; }
.mfp-align-top .mfp-container:before {
display: none; }
.mfp-content {
position: relative;
display: inline-block;
vertical-align: middle;
margin: 0 auto;
text-align: left;
z-index: 1045; }
.mfp-inline-holder .mfp-content,
.mfp-ajax-holder .mfp-content {
width: 100%;
cursor: auto; }
.mfp-ajax-cur {
cursor: progress; }
.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
cursor: -moz-zoom-out;
cursor: -webkit-zoom-out;
cursor: zoom-out; }
.mfp-zoom {
cursor: pointer;
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in; }
.mfp-auto-cursor .mfp-content {
cursor: auto; }
.mfp-close,
.mfp-arrow,
.mfp-preloader,
.mfp-counter {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none; }
.mfp-loading.mfp-figure {
display: none; }
.mfp-hide {
display: none !important; }
.mfp-preloader {
color: #CCC;
position: absolute;
top: 50%;
width: auto;
text-align: center;
margin-top: -0.8em;
left: 8px;
right: 8px;
z-index: 1044; }
.mfp-preloader a {
color: #CCC; }
.mfp-preloader a:hover {
color: #FFF; }
.mfp-s-ready .mfp-preloader {
display: none; }
.mfp-s-error .mfp-content {
display: none; }
button.mfp-close,
button.mfp-arrow {
overflow: visible;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
display: block;
outline: none;
padding: 0;
z-index: 1046;
box-shadow: none;
touch-action: manipulation; }
button::-moz-focus-inner {
padding: 0;
border: 0; }
.mfp-close {
width: 44px;
height: 44px;
line-height: 44px;
position: absolute;
right: 0;
top: 0;
text-decoration: none;
text-align: center;
opacity: 0.65;
padding: 0 0 18px 10px;
color: #FFF;
font-style: normal;
font-size: 28px;
font-family: Arial, Baskerville, monospace; }
.mfp-close:hover,
.mfp-close:focus {
opacity: 1; }
.mfp-close:active {
top: 1px; }
.mfp-close-btn-in .mfp-close {
color: #333; }
.mfp-image-holder .mfp-close,
.mfp-iframe-holder .mfp-close {
color: #FFF;
right: -6px;
text-align: right;
padding-right: 6px;
width: 100%; }
.mfp-counter {
position: absolute;
top: 0;
right: 0;
color: #CCC;
font-size: 12px;
line-height: 18px;
white-space: nowrap; }
.mfp-arrow {
position: absolute;
opacity: 0.65;
margin: 0;
top: 50%;
margin-top: -55px;
padding: 0;
width: 90px;
height: 110px;
-webkit-tap-highlight-color: transparent; }
.mfp-arrow:active {
margin-top: -54px; }
.mfp-arrow:hover,
.mfp-arrow:focus {
opacity: 1; }
.mfp-arrow:before,
.mfp-arrow:after {
content: '';
display: block;
width: 0;
height: 0;
position: absolute;
left: 0;
top: 0;
margin-top: 35px;
margin-left: 35px;
border: medium inset transparent; }
.mfp-arrow:after {
border-top-width: 13px;
border-bottom-width: 13px;
top: 8px; }
.mfp-arrow:before {
border-top-width: 21px;
border-bottom-width: 21px;
opacity: 0.7; }
.mfp-arrow-left {
left: 0; }
.mfp-arrow-left:after {
border-right: 17px solid #FFF;
margin-left: 31px; }
.mfp-arrow-left:before {
margin-left: 25px;
border-right: 27px solid #3F3F3F; }
.mfp-arrow-right {
right: 0; }
.mfp-arrow-right:after {
border-left: 17px solid #FFF;
margin-left: 39px; }
.mfp-arrow-right:before {
border-left: 27px solid #3F3F3F; }
.mfp-iframe-holder {
padding-top: 40px;
padding-bottom: 40px; }
.mfp-iframe-holder .mfp-content {
line-height: 0;
width: 100%;
max-width: 900px; }
.mfp-iframe-holder .mfp-close {
top: -40px; }
.mfp-iframe-scaler {
width: 100%;
height: 0;
overflow: hidden;
padding-top: 56.25%; }
.mfp-iframe-scaler iframe {
position: absolute;
display: block;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
background: #000; }
/* Main image in popup */
img.mfp-img {
width: auto;
max-width: 100%;
height: auto;
display: block;
line-height: 0;
box-sizing: border-box;
padding: 40px 0 40px;
margin: 0 auto; }
/* The shadow behind the image */
.mfp-figure {
line-height: 0; }
.mfp-figure:after {
content: '';
position: absolute;
left: 0;
top: 40px;
bottom: 40px;
display: block;
right: 0;
width: auto;
height: auto;
z-index: -1;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
background: #444; }
.mfp-figure small {
color: #BDBDBD;
display: block;
font-size: 12px;
line-height: 14px; }
.mfp-figure figure {
margin: 0; }
.mfp-bottom-bar {
margin-top: -36px;
position: absolute;
top: 100%;
left: 0;
width: 100%;
cursor: auto; }
.mfp-title {
text-align: left;
line-height: 18px;
color: #F3F3F3;
word-wrap: break-word;
padding-right: 36px; }
.mfp-image-holder .mfp-content {
max-width: 100%; }
.mfp-gallery .mfp-image-holder .mfp-figure {
cursor: pointer; }
@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
/**
* Remove all paddings around the image on small screen
*/
.mfp-img-mobile .mfp-image-holder {
padding-left: 0;
padding-right: 0; }
.mfp-img-mobile img.mfp-img {
padding: 0; }
.mfp-img-mobile .mfp-figure:after {
top: 0;
bottom: 0; }
.mfp-img-mobile .mfp-figure small {
display: inline;
margin-left: 5px; }
.mfp-img-mobile .mfp-bottom-bar {
background: rgba(0, 0, 0, 0.6);
bottom: 0;
margin: 0;
top: auto;
padding: 3px 5px;
position: fixed;
box-sizing: border-box; }
.mfp-img-mobile .mfp-bottom-bar:empty {
padding: 0; }
.mfp-img-mobile .mfp-counter {
right: 5px;
top: 3px; }
.mfp-img-mobile .mfp-close {
top: 0;
right: 0;
width: 35px;
height: 35px;
line-height: 35px;
background: rgba(0, 0, 0, 0.6);
position: fixed;
text-align: center;
padding: 0; } }
@media all and (max-width: 900px) {
.mfp-arrow {
-webkit-transform: scale(0.75);
transform: scale(0.75); }
.mfp-arrow-left {
-webkit-transform-origin: 0;
transform-origin: 0; }
.mfp-arrow-right {
-webkit-transform-origin: 100%;
transform-origin: 100%; }
.mfp-container {
padding-left: 6px;
padding-right: 6px; } }

View File

@@ -0,0 +1,175 @@
/*! #######################################################################
MeanMenu 2.0.7
--------
To be used with jquery.meanmenu.js by Chris Wharton (http://www.meanthemes.com/plugins/meanmenu/)
####################################################################### */
/* hide the link until viewport size is reached */
a.meanmenu-reveal {
display: none;
}
/* when under viewport size, .mean-container is added to body */
.mean-container .mean-bar {
float: left;
width: 100%;
position: relative;
background: #f9f9f9;
padding: 4px 0;
min-height: 42px;
z-index: 999999;
}
.mean-container a.meanmenu-reveal {
width: 22px;
height: 22px;
padding: 13px 13px 11px 13px;
position: absolute;
top: 0;
right: 0;
cursor: pointer;
color: #000;
text-decoration: none;
font-size: 16px;
text-indent: -9999em;
line-height: 22px;
font-size: 1px;
display: block;
font-family: Arial, Helvetica, sans-serif;
font-weight: 700;
}
.mean-container a.meanmenu-reveal span {
display: block;
background: #000;
height: 3px;
margin-top: 3px;
}
.mean-container .mean-nav {
float: left;
width: 100%;
background: #f9f9f9;
margin-top: 44px;
}
.mean-container .mean-nav ul {
padding: 0;
margin: 0;
width: 100%;
list-style-type: none;
}
.mean-container .mean-nav ul li {
position: relative;
float: left;
width: 100%;
}
.mean-container .mean-nav ul li a {
display: block;
float: left;
width: 90%;
padding: 1em 5%;
margin: 0;
text-align: left;
color: #000;
border-top: 1px solid #383838;
border-top: 1px solid #f1f1f1;
text-decoration: none;
text-transform: uppercase;
}
.mean-container .mean-nav ul li li a {
width: 80%;
padding: 1em 10%;
border-top: 1px solid #f1f1f1;
border-top: 1px solid #f1f1f1;
opacity: 0.75;
filter: alpha(opacity=75);
text-shadow: none !important;
visibility: visible;
}
.mean-container .mean-nav ul li.mean-last a {
border-bottom: none;
margin-bottom: 0;
}
.mean-container .mean-nav ul li li li a {
width: 70%;
padding: 1em 15%;
}
.mean-container .mean-nav ul li li li li a {
width: 60%;
padding: 1em 20%;
}
.mean-container .mean-nav ul li li li li li a {
width: 50%;
padding: 1em 25%;
}
.mean-container .mean-nav ul li a:hover {
background: #252525;
background: rgba(255,255,255,0.1);
}
.mean-container .mean-nav ul li a.mean-expand {
margin-top: 1px;
width: 26px;
height: 32px;
padding: 12px !important;
text-align: center;
position: absolute;
right: 0;
top: 0;
z-index: 2;
font-weight: 700;
background: rgba(255,255,255,0.1);
border: none !important;
border-left: 1px solid rgba(255,255,255,0.4) !important;
border-bottom: 1px solid rgba(255,255,255,0.2) !important;
}
.mean-container .mean-nav ul li a.mean-expand:hover {
background: #fff;
}
.mean-container .mean-nav ul li a.mean-expand{
border: 1px solid #f1f1f1 !important;
}
.mean-container .mean-push {
float: left;
width: 100%;
padding: 0;
margin: 0;
clear: both;
}
.mean-nav .wrapper {
width: 100%;
padding: 0;
margin: 0;
}
.mobile-menu.fix.mean-container {
margin: 50px 0;
}
/* Fix for box sizing on Foundation Framework etc. */
.mean-container .mean-bar, .mean-container .mean-bar * {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.mean-remove {
display: none !important;
}

View File

@@ -0,0 +1,224 @@
/*
Plugin Name: Multi Drop Down Mobile menu
version: 1.0
Author: Sujon Mahamud
Desc:
*/
/*Base css*/
@media screen and (max-width:769px) {
*{
margin: 0;
padding: 0;
}
body{
font-size: 16px;
font-family: 'arial', sans-serif;
}
a, a:hover{
outline: none;
text-decoration: none;
}
ul, li{
list-style: none;
}
/*Menu Css*/
.mobile-logo a{
font-size: 20px;
font-weight: 700;
color: #000;
text-decoration: none;
}
.mobile-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 17px 0;
z-index: 9;
background: #fff;
}
.mobile-header-elements {
display: flex;
align-items: center;
justify-content: space-between;
}
.mobile-nav-icon {
font-size: 23px;
border: 1px solid;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
color: #000;
}
.mobile-sidebar {
position: fixed;
height: 100%;
width: 100%;
background:#000;
z-index: 99;
padding: 40px 30px;
left: -100%;
top: 0;
visibility: hidden;
opacity: 0;
transition: all .3s;
}
.mobile-sidebar .contact-icon{
background: #fff;
}
.mobile-sidebar.mobile-menu-active{
left: 0;
visibility: visible;
opacity: 1;
transition: all .3s;
}
.menu-close {
position: absolute;
right: 20px;
top: 20px;
font-size: 25px;
color: #fff;
cursor: pointer;
}
.mobile-nav li a {
font-size: 18px;
line-height: 24px;
color: #fff;
padding: 10px 0;
display: block;
}
ul.mobile-nav-list {
margin-bottom: 40px;
margin-top: 30px;
}
li.has-children {
position: relative;
z-index: 2;
}
span.mobile-nav-menu-icon {
position: absolute;
right: 0;
top: 0;
height: 50px;
width: 50px;
text-align: center;
line-height: 50px;
color: #fff;
}
ul.mobile-menu-sub {
display: none;
position: relative;
left: 0;
padding-left: 10px;
transition: all .3s;
}
ul.mobile-menu-sub.sub-menu-active{
display: block;
position: relative;
transition: all .3s;
}
.mobile-nav li {
position: relative;
z-index: 2;
}
span.submenu-button {
position: absolute;
width: 100%;
height: 44px;
top: 0;
left: 0;
z-index: 3;
transition: all .3s;
}
span.submenu-button:after, span.submenu-button:before {
position: absolute;
content: "";
height: 20px;
width: 2px;
background: #fff;
top: 12px;
right: 9px;
transition: all .3s;
}
span.submenu-button:before {
height: 2px;
width: 20px;
right: 0px;
top: 22px;
}
span.submenu-button.submenu-opened:after {
visibility: hidden;
opacity: 0;
}
.sub-menu{
display: none;
padding-left: 15px;
}
span.multi-drop-icon {
position: absolute;
right: 10px;
width: 20px;
height: 100%;
top: 0;
text-align: center;
display: flex;
align-items: center;
justify-content: flex-end;
}
ul.mobile-nav-list {
max-height: 500px;
overflow-y: scroll;
}
}

1
public/assets/css/modal-video.min.css vendored Normal file
View File

@@ -0,0 +1 @@
@keyframes modal-video{from{opacity:0}to{opacity:1}}@keyframes modal-video-inner{from{transform:translate(0, 100px)}to{transform:translate(0, 0)}}.modal-video{position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);z-index:1000000;cursor:pointer;opacity:1;animation-timing-function:ease-out;animation-duration:.3s;animation-name:modal-video;-webkit-transition:opacity .3s ease-out;-moz-transition:opacity .3s ease-out;-ms-transition:opacity .3s ease-out;-o-transition:opacity .3s ease-out;transition:opacity .3s ease-out}.modal-video-close{opacity:0}.modal-video-close .modal-video-movie-wrap{-webkit-transform:translate(0, 100px);-moz-transform:translate(0, 100px);-ms-transform:translate(0, 100px);-o-transform:translate(0, 100px);transform:translate(0, 100px)}.modal-video-body{max-width:960px;width:100%;height:100%;margin:0 auto;padding:0 10px;display:flex;justify-content:center;box-sizing:border-box}.modal-video-inner{display:flex;justify-content:center;align-items:center;width:100%;height:100%}@media (orientation: landscape){.modal-video-inner{padding:10px 60px;box-sizing:border-box}}.modal-video-movie-wrap{width:100%;height:0;position:relative;padding-bottom:56.25%;background-color:#333;animation-timing-function:ease-out;animation-duration:.3s;animation-name:modal-video-inner;-webkit-transform:translate(0, 0);-moz-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-ms-transition:-ms-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal-video-movie-wrap iframe{position:absolute;top:0;left:0;width:100%;height:100%}.modal-video-close-btn{position:absolute;z-index:2;top:-45px;right:0;display:inline-block;width:35px;height:35px;overflow:hidden;border:none;background:transparent}@media (orientation: landscape){.modal-video-close-btn{top:0;right:-45px}}.modal-video-close-btn:before{transform:rotate(45deg)}.modal-video-close-btn:after{transform:rotate(-45deg)}.modal-video-close-btn:before,.modal-video-close-btn:after{content:'';position:absolute;height:2px;width:100%;top:50%;left:0;margin-top:-1px;background:#fff;border-radius:5px;margin-top:-6px}

View File

@@ -0,0 +1,138 @@
.nice-select {
-webkit-tap-highlight-color: transparent;
background-color: #fff;
border-radius: 5px;
border: solid 1px #e8e8e8;
box-sizing: border-box;
clear: both;
cursor: pointer;
display: block;
float: left;
font-family: inherit;
font-size: 14px;
font-weight: normal;
height: 42px;
line-height: 40px;
outline: none;
padding-left: 18px;
padding-right: 30px;
position: relative;
text-align: left !important;
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
white-space: nowrap;
width: auto; }
.nice-select:hover {
border-color: #dbdbdb; }
.nice-select:active, .nice-select.open, .nice-select:focus {
border-color: #999; }
.nice-select:after {
border-bottom: 2px solid #999;
border-right: 2px solid #999;
content: '';
display: block;
height: 5px;
margin-top: -4px;
pointer-events: none;
position: absolute;
right: 12px;
top: 50%;
-webkit-transform-origin: 66% 66%;
-ms-transform-origin: 66% 66%;
transform-origin: 66% 66%;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-transition: all 0.15s ease-in-out;
transition: all 0.15s ease-in-out;
width: 5px; }
.nice-select.open:after {
-webkit-transform: rotate(-135deg);
-ms-transform: rotate(-135deg);
transform: rotate(-135deg); }
.nice-select.open .list {
opacity: 1;
pointer-events: auto;
-webkit-transform: scale(1) translateY(0);
-ms-transform: scale(1) translateY(0);
transform: scale(1) translateY(0); }
.nice-select.disabled {
border-color: #ededed;
color: #999;
pointer-events: none; }
.nice-select.disabled:after {
border-color: #cccccc; }
.nice-select.wide {
width: 100%; }
.nice-select.wide .list {
left: 0 !important;
right: 0 !important; }
.nice-select.right {
float: right; }
.nice-select.right .list {
left: auto;
right: 0; }
.nice-select.small {
font-size: 12px;
height: 36px;
line-height: 34px; }
.nice-select.small:after {
height: 4px;
width: 4px; }
.nice-select.small .option {
line-height: 34px;
min-height: 34px; }
.nice-select .list {
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 0 1px rgba(68, 68, 68, 0.11);
box-sizing: border-box;
margin-top: 4px;
opacity: 0;
overflow: hidden;
padding: 0;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
-webkit-transform-origin: 50% 0;
-ms-transform-origin: 50% 0;
transform-origin: 50% 0;
-webkit-transform: scale(0.75) translateY(-21px);
-ms-transform: scale(0.75) translateY(-21px);
transform: scale(0.75) translateY(-21px);
-webkit-transition: all 0.2s cubic-bezier(0.5, 0, 0, 1.25), opacity 0.15s ease-out;
transition: all 0.2s cubic-bezier(0.5, 0, 0, 1.25), opacity 0.15s ease-out;
z-index: 9; }
.nice-select .list:hover .option:not(:hover) {
background-color: transparent !important; }
.nice-select .option {
cursor: pointer;
font-weight: 400;
line-height: 40px;
list-style: none;
min-height: 40px;
outline: none;
padding-left: 18px;
padding-right: 29px;
text-align: left;
-webkit-transition: all 0.2s;
transition: all 0.2s; }
.nice-select .option:hover, .nice-select .option.focus, .nice-select .option.selected.focus {
background-color: #f6f6f6; }
.nice-select .option.selected {
font-weight: bold; }
.nice-select .option.disabled {
background-color: transparent;
color: #999;
cursor: default; }
.no-csspointerevents .nice-select .list {
display: none; }
.no-csspointerevents .nice-select.open .list {
display: block; }

View File

@@ -0,0 +1,6 @@
/**
* Owl Carousel v2.3.4
* Copyright 2013-2018 David Deutsch
* Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE
*/
.owl-carousel,.owl-carousel .owl-item{-webkit-tap-highlight-color:transparent;position:relative}.owl-carousel{display:none;width:100%;z-index:1}.owl-carousel .owl-stage{position:relative;-ms-touch-action:pan-Y;touch-action:manipulation;-moz-backface-visibility:hidden}.owl-carousel .owl-stage:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0}.owl-carousel .owl-stage-outer{position:relative;overflow:hidden;-webkit-transform:translate3d(0,0,0)}.owl-carousel .owl-item,.owl-carousel .owl-wrapper{-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0)}.owl-carousel .owl-item{min-height:1px;float:left;-webkit-backface-visibility:hidden;-webkit-touch-callout:none}.owl-carousel .owl-item img{display:block;width:100%}.owl-carousel .owl-dots.disabled,.owl-carousel .owl-nav.disabled{display:none}.no-js .owl-carousel,.owl-carousel.owl-loaded{display:block}.owl-carousel .owl-dot,.owl-carousel .owl-nav .owl-next,.owl-carousel .owl-nav .owl-prev{cursor:pointer;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel .owl-nav button.owl-next,.owl-carousel .owl-nav button.owl-prev,.owl-carousel button.owl-dot{background:0 0;color:inherit;border:none;padding:0!important;font:inherit}.owl-carousel.owl-loading{opacity:0;display:block}.owl-carousel.owl-hidden{opacity:0}.owl-carousel.owl-refresh .owl-item{visibility:hidden}.owl-carousel.owl-drag .owl-item{-ms-touch-action:pan-y;touch-action:pan-y;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-grab{cursor:move;cursor:grab}.owl-carousel.owl-rtl{direction:rtl}.owl-carousel.owl-rtl .owl-item{float:right}.owl-carousel .animated{animation-duration:1s;animation-fill-mode:both}.owl-carousel .owl-animated-in{z-index:0}.owl-carousel .owl-animated-out{z-index:1}.owl-carousel .fadeOut{animation-name:fadeOut}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.owl-height{transition:height .5s ease-in-out}.owl-carousel .owl-item .owl-lazy{opacity:0;transition:opacity .4s ease}.owl-carousel .owl-item .owl-lazy:not([src]),.owl-carousel .owl-item .owl-lazy[src^=""]{max-height:0}.owl-carousel .owl-item img.owl-lazy{transform-style:preserve-3d}.owl-carousel .owl-video-wrapper{position:relative;height:100%;background:#000}.owl-carousel .owl-video-play-icon{position:absolute;height:80px;width:80px;left:50%;top:50%;margin-left:-40px;margin-top:-40px;background:url(owl.video.play.png) no-repeat;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;transition:transform .1s ease}.owl-carousel .owl-video-play-icon:hover{-ms-transform:scale(1.3,1.3);transform:scale(1.3,1.3)}.owl-carousel .owl-video-playing .owl-video-play-icon,.owl-carousel .owl-video-playing .owl-video-tn{display:none}.owl-carousel .owl-video-tn{opacity:0;height:100%;background-position:center center;background-repeat:no-repeat;background-size:contain;transition:opacity .4s ease}.owl-carousel .owl-video-frame{position:relative;z-index:1;height:100%;width:100%}

View File

View File

@@ -0,0 +1,16 @@
/*=================
home1-start
===================*/
@media screen and (max-width: 768px) {
.home1-hero-hadding {
padding: 150px 0 20px 0;
}
.home1-main-hero-img {
padding-top: 75px;
margin-right: 10px;
}
}
/*=================
home1-end
===================*/

View File

@@ -0,0 +1,119 @@
/* Slider */
.slick-slider
{
position: relative;
display: block;
box-sizing: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-khtml-user-select: none;
-ms-touch-action: pan-y;
touch-action: pan-y;
-webkit-tap-highlight-color: transparent;
}
.slick-list
{
position: relative;
display: block;
overflow: hidden;
margin: 0;
padding: 0;
}
.slick-list:focus
{
outline: none;
}
.slick-list.dragging
{
cursor: pointer;
cursor: hand;
}
.slick-slider .slick-track,
.slick-slider .slick-list
{
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.slick-track
{
position: relative;
top: 0;
left: 0;
display: block;
margin-left: auto;
margin-right: auto;
}
.slick-track:before,
.slick-track:after
{
display: table;
content: '';
}
.slick-track:after
{
clear: both;
}
.slick-loading .slick-track
{
visibility: hidden;
}
.slick-slide
{
display: none;
float: left;
height: 100%;
min-height: 1px;
}
[dir='rtl'] .slick-slide
{
float: right;
}
.slick-slide img
{
display: block;
}
.slick-slide.slick-loading img
{
display: none;
}
.slick-slide.dragging img
{
pointer-events: none;
}
.slick-initialized .slick-slide
{
display: block;
}
.slick-loading .slick-slide
{
visibility: hidden;
}
.slick-vertical .slick-slide
{
display: block;
height: auto;
border: 1px solid transparent;
}
.slick-arrow.slick-hidden {
display: none;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More