40 Commits

Author SHA1 Message Date
925b6197f2 add. Closes #8
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 3m59s
2025-06-22 08:53:02 +04:00
5c97679188 fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 3m40s
2025-06-21 10:06:45 +04:00
6eb987ffdb fix DockerFile
Some checks failed
Deploy Nuxt App / deploy (push) Has been cancelled
2025-06-21 06:06:07 +00:00
f2f7798e83 :wqMerge branch 'production' of https://gitea.miduway.space/levis/ebook into production 2025-06-21 05:41:27 +00:00
e396883830 add pages
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 2m40s
2025-06-21 09:40:31 +04:00
a783918241 :Merge branch 'production' of https://gitea.miduway.space/levis/ebook into production 2025-06-20 13:57:16 +00:00
f8a632b6df fix
Some checks failed
Deploy Nuxt App / deploy (push) Failing after 13m20s
2025-06-20 17:56:43 +04:00
d92fc4cf6e fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 2m27s
2025-06-20 16:53:15 +04:00
8529e66e33 fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m53s
2025-06-20 16:11:27 +04:00
6c8a6e7c35 Merge branch 'production' of https://gitea.miduway.space/levis/ebook into production 2025-06-20 10:45:02 +00:00
117e579f2e fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m50s
2025-06-20 14:42:10 +04:00
8ba72bc8a4 fix
Some checks failed
Deploy Nuxt App / deploy (push) Has been cancelled
2025-06-20 14:41:42 +04:00
1823cd87b6 Merge branch 'production' of https://gitea.miduway.space/levis/ebook into production 2025-06-20 08:25:01 +00:00
94d008ca7d add html-validator + fix html ceo code
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m31s
2025-06-20 12:21:00 +04:00
9950665146 Merge branch 'production' of https://gitea.miduway.space/levis/ebook into production 2025-06-19 18:35:02 +00:00
cbabdf8ccb fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m52s
2025-06-19 22:30:52 +04:00
b73a9f5b3f Обновить README.md
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m47s
2025-06-19 18:17:46 +00:00
2909160b17 Обновить .gitea/workflows/deploy.yml
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m47s
2025-06-19 18:08:01 +00:00
2ef59fee78 Обновить README.md
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 2m26s
2025-06-19 18:01:41 +00:00
84cf03bd61 fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m47s
2025-06-19 21:40:30 +04:00
3cd6a975f9 Merge branch 'production' of https://gitea.miduway.space/levis/ebook into production
Some checks failed
Deploy Nuxt App / deploy (push) Has been cancelled
2025-06-19 21:39:01 +04:00
15a7ffe120 fix
Some checks failed
Deploy Nuxt App / deploy (push) Has been cancelled
2025-06-19 17:37:47 +00:00
b54de4ace0 update deploy.yml
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m46s
2025-06-19 17:25:32 +00:00
7a283ababc remove .nuxt and .data
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m44s
2025-06-19 21:17:07 +04:00
eacb8a70d0 fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m41s
2025-06-19 21:14:28 +04:00
bc3d085c02 fix
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m45s
2025-06-19 21:11:08 +04:00
4112bca029 Merge branch 'production' of https://gitea.miduway.space/levis/ebook into production
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 1m53s
2025-06-19 21:07:57 +04:00
76aa42dc25 fix DockerFile
All checks were successful
Deploy Nuxt App / deploy (push) Successful in 2m39s
2025-06-19 16:59:26 +00:00
551f68a78a fix DockerFile 2025-06-19 16:08:21 +00:00
46a59c7b91 fix 2025-06-18 10:04:01 +04:00
4cdb90eaf7 fix
All checks were successful
Deploy Application / deploy (push) Successful in 8s
2025-06-18 08:41:39 +04:00
4934e8be67 fix
All checks were successful
Deploy Application / deploy (push) Successful in 7s
2025-06-18 08:40:17 +04:00
886f21e0a2 fix
All checks were successful
Deploy Application / deploy (push) Successful in 7s
2025-06-18 08:35:55 +04:00
495638c922 fix
All checks were successful
Deploy Application / deploy (push) Successful in 7s
2025-06-18 08:33:33 +04:00
453376c024 Update config
All checks were successful
Deploy Application / deploy (push) Successful in 8s
2025-06-18 07:50:52 +04:00
4e35841df3 fix deploy
All checks were successful
Deploy Application / deploy (push) Successful in 8s
2025-06-17 17:52:41 +04:00
09edacf376 Merge pull request 'migrate on nuxt3' (#7) from migration-to-nuxt into production
Reviewed-on: #7
2025-06-17 13:49:35 +00:00
727fa976bd migrate on nuxt3 2025-06-17 17:46:03 +04:00
7634a3d616 fix
All checks were successful
Deploy Application / deploy (push) Successful in 6s
2025-06-17 11:27:13 +04:00
a30c08c50c del clg
All checks were successful
Deploy Application / deploy (push) Successful in 5s
2025-06-15 19:23:58 +04:00
105 changed files with 1426 additions and 6473 deletions

16
.env Normal file
View File

@ -0,0 +1,16 @@
DB_USER=
DB_HOST=
DB_NAME=
DB_PASSWORD=
DB_PORT=
YOOKASSA_SHOP_ID=
YOOKASSA_SECRET_KEY=
APP_URL=
# SMTP Configuration for Yandex
SMTP_USER=levishub@yandex.com
SMTP_PASS=avhpihoudpyvibtx
DEFAULT_TO_EMAIL=miduway@yandex.ru

18
.env.example Normal file
View File

@ -0,0 +1,18 @@
DB_USER=
DB_HOST=
DB_NAME=
DB_PASSWORD=
DB_PORT=
YOOKASSA_SHOP_ID=
YOOKASSA_SECRET_KEY=
APP_URL=
# SMTP Configuration for Yandex
SMTP_USER=levishub@yandex.ru
SMTP_PASS=avhpihoudpyvibtx
DEFAULT_TO_EMAIL=miduway@yandex.ru

View File

@ -1,4 +1,4 @@
name: Deploy Application name: Deploy Nuxt App
on: [push] on: [push]
jobs: jobs:
@ -8,27 +8,37 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
token: 0406afe7de6547e850dd62c84976c6def23a5193 token: ${{ secrets.TOKEN }}
- name: Install curl - name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '22'
- name: Install dependencies and build
run: | run: |
if command -v apt-get &> /dev/null; then npm install
apt-get update && apt-get install -y curl npm run build
elif command -v apk &> /dev/null; then
apk add --no-cache curl
elif command -v yum &> /dev/null; then
yum install -y curl
fi
- name: Upload config files to server - name: Prepare deployment files
run: |
tar -czf deploy.tar.gz dist Dockerfile docker-compose.yml
- name: Upload files via Gitea API
env: env:
GITEA_API: 'https://gitea.miduway.space/api/v1/repos/levis/ebook/raw/main' GITEA_API: "https://gitea.miduway.space/api/v1/repos/levis/ebook/raw/main"
GITEA_TOKEN: '0406afe7de6547e850dd62c84976c6def23a5193' GITEA_TOKEN: ${{ secrets.TOKEN }}
run: | run: |
for file in docker-compose.yml Dockerfile; do for file in Dockerfile docker-compose.yml; do
echo "Uploading $file"
curl -X PUT \ curl -X PUT \
-H "Authorization: token $GITEA_TOKEN" \ -H "Authorization: token $TOKEN" \
-H "Content-Type: text/plain" \
-T "$file" \ -T "$file" \
"$GITEA_API/$file" "$GITEA_API/$file?branch=main"
done done
curl -X PUT \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/gzip" \
-T "deploy.tar.gz" \
"$GITEA_API/deploy.tar.gz?branch=main"

11
.gitignore vendored
View File

@ -11,7 +11,15 @@ node_modules
dist dist
dist-ssr dist-ssr
*.local *.local
.nuxt
.output
.data
.nuxt
.nitro
.cache
dist
.history
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
@ -22,6 +30,5 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
package-lock.json
frontend.env frontend.env

13
.htmlvalidate.json Normal file
View File

@ -0,0 +1,13 @@
{
"root": true,
"extends": ["html-validate:recommended", "html-validate:standard"],
"rules": {
"heading-level": "error",
"require-img-alt": "error",
"element-permitted-content": "error",
"require-meta-title": "error",
"require-meta-description": "error",
"no-duplicate-id": "error",
"prefer-native-element": "error"
}
}

View File

@ -1,2 +0,0 @@
npm run lint
npm run format

View File

@ -1,3 +0,0 @@
{
"recommendations": ["Vue.volar"]
}

View File

@ -1,13 +1,7 @@
FROM node:20 as builder FROM node:22-slim
WORKDIR /usr/src/app
WORKDIR /app
COPY package*.json ./ COPY package*.json ./
RUN npm install RUN npm install
COPY . . COPY . .
RUN npm run build RUN npm run build
CMD ["node", "dist/server/index.mjs"]
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY ebook.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1,27 +1,76 @@
# Проект сайт vino Galante # Nuxt Minimal Starter
## Описание проекта Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
Интернет сайт Vino Galente создан для продажи книг за авторством... ## Setup
### Установка, для разработки Make sure to install dependencies:
Копируем env ```bash
# npm
``` npm install
cp frontend.env frontend.env.example
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
``` ```
Устанавливаем зависимости ## Development Server
``` Start the development server on `http://localhost:3000`:
yarn
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
``` ```
Запуск dev ## Production
``` Build the application for production:
vite
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
``` ```
Разворачивание, docker 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.

15
app.vue Normal file
View File

@ -0,0 +1,15 @@
<template>
<NuxtLayout>
<!-- <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap"
rel="stylesheet"
/> -->
<NuxtPage />
</NuxtLayout>
</template>

View File

@ -1,6 +1,8 @@
@import 'tailwindcss'; @tailwind base;
@tailwind components;
@tailwind utilities;
@theme { :root {
--color-primary: rgba(255, 255, 255, 1); --color-primary: rgba(255, 255, 255, 1);
--color-secondary: rgba(229, 30, 125, 1); --color-secondary: rgba(229, 30, 125, 1);
--color-three: rgba(245, 75, 126, 1); --color-three: rgba(245, 75, 126, 1);
@ -20,30 +22,27 @@
--color-bg-disabled: oklch(92.8% 0.006 264.531); --color-bg-disabled: oklch(92.8% 0.006 264.531);
} }
/* Кастомные классы шрифтов */
.lato-thin { .lato-thin {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 100; font-weight: 100;
font-style: normal; font-style: normal;
} }
.lato-light { .lato-light {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 300; font-weight: 300;
font-style: normal; font-style: normal;
} }
.lato-regular { .lato-regular {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
} }
.lato-bold { .lato-bold {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 700; font-weight: 700;
font-style: normal; font-style: normal;
} }
.lato-black { .lato-black {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 900; font-weight: 900;
@ -55,25 +54,21 @@
font-weight: 100; font-weight: 100;
font-style: italic; font-style: italic;
} }
.lato-light-italic { .lato-light-italic {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 300; font-weight: 300;
font-style: italic; font-style: italic;
} }
.lato-regular-italic { .lato-regular-italic {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 400; font-weight: 400;
font-style: italic; font-style: italic;
} }
.lato-bold-italic { .lato-bold-italic {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 700; font-weight: 700;
font-style: italic; font-style: italic;
} }
.lato-black-italic { .lato-black-italic {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
font-weight: 900; font-weight: 900;

3
assets/icon/arrow.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="14" height="20" viewBox="0 0 14 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9401 11.1109L2.9533 20L0.457031 17.7781L9.19574 10L0.457031 2.22187L2.9533 0L12.9401 8.88906C13.2711 9.18373 13.457 9.58334 13.457 10C13.457 10.4167 13.2711 10.8163 12.9401 11.1109Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 354 B

View File

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 251 KiB

View File

Before

Width:  |  Height:  |  Size: 409 KiB

After

Width:  |  Height:  |  Size: 409 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 613 KiB

After

Width:  |  Height:  |  Size: 613 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

78
components/EmailForm.vue Normal file
View File

@ -0,0 +1,78 @@
<template>
<div class="max-w-md mx-auto p-6 rounded-lg shadow-md text-gray-900">
<h2 class="text-2xl font-bold mb-6 text-primary text-center">Заполните контактные данные</h2>
<form @submit.prevent="onSubmit" class="space-y-4">
<div>
<label for="to" class="block text-sm font-medium text-primary mb-2"> Ваш e-mail: </label>
<input
id="to"
v-model="to"
type="email"
required
placeholder="email@example.com"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900"
/>
</div>
<UiButton type="submit" :disabled="isLoading" class="w-[400px]">
<span v-if="isLoading">Перехожу...</span>
<span v-else>Перейти к оплате</span>
</UiButton>
</form>
<div v-if="status" class="mt-4 p-3 rounded-md" :class="statusClass">
{{ status }}
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const mail = useMail()
const to = ref('')
const status = ref('')
const isLoading = ref(false)
const statusClass = computed(() => {
if (status.value.includes('Ошибка')) {
return 'bg-red-100 text-red-700 border border-red-200'
}
if (status.value.includes('отправлено') || status.value.includes('подписка')) {
return 'bg-green-100 text-green-700 border border-green-200'
}
return 'bg-blue-100 text-blue-700 border border-blue-200'
})
async function onSubmit() {
if (!to.value) {
status.value = 'Пожалуйста, введите ваш e-mail'
return
}
isLoading.value = true
status.value = ''
try {
const emailData = {
to: to.value,
subject: 'Новая подписка на рассылку книги!',
text: `Пользователь с e-mail: ${to.value} подписался на рассылку книги.`,
}
await mail.send(emailData)
status.value = 'Успешная подписка! Проверьте вашу почту.'
// Очищаем форму
to.value = ''
} catch (error) {
console.error('Ошибка отправки письма:', error)
status.value = `Ошибка отправки: ${error.message || 'Неизвестная ошибка'}`
} finally {
isLoading.value = false
}
}
</script>

View File

@ -0,0 +1,17 @@
export const colorVariants = {
primary: [
"bg-accent-50",
"text-primary",
"hover:bg-accent-100",
"active:bg-accent-150",
],
secondary: [
"bg-transparent",
"text-primary",
"hover:bg-accent-50",
"border-accent-50",
"border",
"hover:bg-accent-100",
"active:bg-accent-150",
],
};

View File

@ -1,6 +1,9 @@
<template> <template>
<component <component
:is="tag" :is="tag"
:type="tag === 'button' ? 'button' : ''"
:to="path"
:href="path"
class="px-14 py-4 rounded-[20px] text-[30px] cursor-pointer shadow-[0px_16px_50px_-16px_rgba(229,30,125,1)]" class="px-14 py-4 rounded-[20px] text-[30px] cursor-pointer shadow-[0px_16px_50px_-16px_rgba(229,30,125,1)]"
:class="[baseStyle, size]" :class="[baseStyle, size]"
data-ui="ui-button" data-ui="ui-button"
@ -11,7 +14,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { toRefs, computed } from 'vue' import { toRefs, computed } from 'vue'
import { colorVariants } from './UiButton.params.ts' import { colorVariants } from './UiButton.params.js'
const props = defineProps({ const props = defineProps({
tag: { tag: {
@ -29,6 +32,10 @@ const props = defineProps({
type: String, type: String,
default: 'font-bold', default: 'font-bold',
}, },
path: {
type: String,
default: '',
},
}) })
const { tag, variants, size } = toRefs(props) const { tag, variants, size } = toRefs(props)

28
config/head.ts Normal file
View File

@ -0,0 +1,28 @@
export default {
meta: [
{ charset: "utf-8" },
{
name: "viewport",
content:
"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no",
},
],
link: [
{
rel: "preconnect",
href: "https://fonts.googleapis.com",
},
{
rel: "preconnect",
href: "https://fonts.gstatic.com",
},
{
rel: "stylesheet",
href: "https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap",
},
{
rel: "stylesheet",
href: "https://fonts.googleapis.com/css2?family=Russo+One&display=swap",
},
],
};

11
config/index.ts Normal file
View File

@ -0,0 +1,11 @@
import modules from "./modules";
import sitemap from "./sitemap";
export default {
modules,
sitemap,
typescript: {
strict: true,
typeCheck: true,
},
};

13
config/modules.ts Normal file
View File

@ -0,0 +1,13 @@
export default [
'@nuxt/eslint',
'@nuxt/icon',
'@nuxt/image',
'@nuxt/content',
// '@nuxt/fonts',
'@pinia/nuxt',
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'@nuxtjs/html-validator',
// '@nuxtjs/robots',
// '@nuxtjs/sitemap',
]

12
config/sitemap.ts Normal file
View File

@ -0,0 +1,12 @@
// ./sitemap.config.ts
export default {
siteUrl: 'https://ebook.miduway.space',
// Примеры маршрутов, можно заменить на API-запрос
urls: [
{ loc: '/books/1' },
{ loc: '/books/1/title-1' },
{ loc: '/books/2/title-2' },
{ loc: '/books/2' },
],
}

10
content.config.ts Normal file
View File

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

69
content/books/1.md Normal file
View File

@ -0,0 +1,69 @@
<div style="color: #f54b7e; font-weight: bold; font-size: 1.5em;">Глава 1. Перезагрузка</div>
### Введение
&ensp;&ensp;Название первой главы - “Перезагрузка”. Как ты думаешь, почему? Потому что всё начинается с твоей головы и только с твоей головы. Суть этой главы - выработать в тебе правильное отношение к мужчинам. Правильное значит такое, которое тебе позволит нами управлять, хотя и неявно. Если ты хочешь иметь успех у мужчин, тебе просто необходимо правильно к нам относиться. Тебе нужна правильная система взглядов. Ты должна знать, по каким законам функционирует система “мужчина”. В противном случае ты обречена на провал, независимо от внешних данных, финансовой ситуации и всего остального.<br/>
Пример. С чего начинается обучение водительскому мастерству? Правильно, с теории. На курсах вождения тебе объясняют, по каким правилам и законам движутся автомобили. Зная эти правила, ты можешь безопасно передвигаться в мегаполисе. Т.е. используя машину, ты добираешься из пункта “А” в пункт “Б”. Т.е. ты достигаешь своей цели быть в пункте “Б”, приехать на работу, например.<br/>
&ensp;&ensp;То же самое и в соблазнении. Все те убеждения соблазнительницы, с которыми ты ознакомишься в этой главе, такие же правила безопасности дорожного движения. Если твои нынешние убеждения относительно парней сильно разнятся с истинными, то ты просто не можешь иметь настоящий успех у мужчин. Мне очень повезло в жизни - не знаю почему, но я встретился с одной очень хорошей правильной девушкой. Девушка очень красивая, но с парнями на тот момент у нее был явный недобор. Все, что ей мешало иметь колоссальный успех у парней - неправильное отношение к мужчинам. Например, она считала, что мужчина первым должен проявлять активность, а девушка при этом вообще ничего не должна делать. Прошло полгода, теперь она проводит в день по три свидания, ей дарят золото, за ней ухаживают очень классные парни, такие, от которых у обычной девушки текут слюнки. Ей уже поступило несколько предложений о замужестве.
Что же позволило ей так возвыситься над обычными девушками? Ответ ты найдешь в этой главе.
### <span style="color: #f54b7e;">Убеждения соблазнительницы</span>
_"Мы формируем свои убеждения в детстве,_<br/>
_а потом движемся по жизни, воссоздавая_<br/>
_ситуации, которые подошли бы нашим убеждениям."_ <br/>
уиза Хей_
&ensp;&ensp;Ты наверно думаешь, что успешная соблазнительница - это такая очень красивая девушка, понимающая все тонкости в моде и умеющая соблазнительно кокетничать? У нее обязательно должны быть длинные красивые ноги и осиная талия? Вся такая Мэрилин Монро? Тогда я не завидую твоей способности общаться с мужчинами. Сейчас я разобью эти глупые убеждения, созданные рекламой и всей этой телевизионной шелухой. Но мне надо, чтобы ты сама думала, сама использовала свой самый главный актив - свой мозг. Поэтому ответь на вопрос - какова была личная жизнь Мэрилин Монро? Ответила? Спасибо. Теперь ответь на следующий вопрос - какова личная жизнь типичной очень красивой девушки? Ответила? Спасибо. А теперь правильные ответы.<br/>
&ensp;&ensp;Мэрилин Монро, эта актриса, певица и, самое главное, секс-символ Америки 50 ÷ 60 годов прошлого столетия, была 3 раза замужем и умерла в очень молодом возрасте (всего 36 лет) от передозировки снотворного. Мдаа.<br/>
Первый муж - Джим Дагерти, то ли моряк, то ли еще не пойми кто. Мэрилин было всего 16 лет, когда они поженились. Совсем ребенок еще. Характеризовала брак как ошибку молодости. Второй брак. Какой-то Джо Ди Маджо, звезда бейсбола. Брак просуществовал всего 9 месяцев. Мужик был необычайно ревнив и ревновал Мэрилин даже к публике. Ему было сложно терпеть ее популярность. Если брак даже и год не продержался, значит это был изначально один большой косяк, а не брак. Ладно, кратко о последнем браке. Третий муж - Артур Миллер. Драматург. Вроде более-менее брак. Продержался 4.5 года. Ходят слухи о четвертом браке и о том, что на Мэрилин залезал президент США Кеннеди. И вроде как причина смерти Мэрилин - убийство, т.к. она начала заявлять свои права на президента, и тот, чтобы устранить проблему, устранил любовницу.
Из этой истории, так напоминающей кальку современной жизни, делаем следующие выводы:
1. Сама по себе красота, даже если это дьявольски привлекательная красота, ничего не дает.
2. Так, желаемая всеми популярность абсолютно не гарантирует счастья в личной жизни.<br/><br/>
Пока остановимся на этих двух выводах.<br/>
&ensp;&ensp;Еще в недавнем прошлом моей слабостью были модели. Они такие красивые, утонченные, у них длинные изящные пальцы - не девушки, а сплошное загляденье. И главное, они недоступны для обычных парней. Сколько раз я видел, как мужики сворачивали головы, провожая взглядом мою очередную спутницу. Конечно! В школу моделей берут всех, в модели - единицы. Самое интересное - наблюдать за реакцией парней. Вначале они бросают взгляд на девушку, примерно через секунду на меня. Если я делаю морду кирпичом - убирают взгляд, если нет - снова смотрят на девушку. Их можно понять, ведь по-настоящему красивая девушка - это истинное произведение искусства. Сам Бог ваяет настоящих моделей. И, естественно, у меня было много моделей и не только из моего города. И как ты думаешь, что я тебе скажу о них? Многие из них несчастны в личной жизни. Это просто парадоксально! Как могут быть несчастны такие красавицы?!<br/>
&ensp;&ensp;Вначале я удивлялся, потом привык. Да ты хотя бы посмотри на мисс Вселенная Оксану Федорову. Полнейшая неразбериха в личной жизни. Она же мисс Вселенная! Представляешь?! Официально она самая красивая девушка планеты Земля в 2002-м году.<br/>
&ensp;&ensp;Она могла выбрать любого. А красавица Дениз Ричардс? В 2002-м в возрасте 31 года вышла замуж за актера Чарли Шина. Как можно было выходить замуж за такого дегенерата? Чарли Шин известен своими многочисленными скандалами в личной и общественной жизни, он наркоман. В 2011 году Шин был уволен компаниями CBS и Warner Bros. Television. Мдаа. С 2011 года этот актер живет с Бри Олсон. Бри Олсон -порноактриса. Кто такая порноактриса? Порноактриса это всего лишь помойное ведро, она отсосет у любого, на кого укажет режиссер. Надо иметь весьма убогий разум, чтобы жить со шлюхой. И вот Дениз, живое воплощение женской красоты, выбрала для себя такого недочеловека. Прожив четыре года, они разошлись. Дениз осталась с двумя детьми.<br/>
&ensp;&ensp;Во истину покажите мне красивую, нет, очень красивую девушку, и я покажу вам трагедию. К чему это я все? А к тому, что главное в тебе - это твои мозги. Главное в тебе - это твои установки и ценности, которыми ты руководствуешься в жизни, твои глубинные убеждения. И если ты будешь женственной, нежной девушкой и, самое главное, умной, ты стопроцентно найдешь себе достойного человека. В этом просто нет никаких сомнений.<br/>
&ensp;&ensp;Теперь посмотрим на жен успешных людей. На женщин, у которых в личной жизни все тип-топ. Кого же взять на рассмотрение? А давай-ка возьмем царицу нашей страны - Светлану Владимировну Медведеву (фотографии смотри в интернете). Это жена действующего президента нашей страны Дмитрия Анатольевича Медведева. Родилась 15-го марта 1965 года в Кронштадте, детство провела в деревне Коваши. По одним данным вышла замуж в 24 года, по другим в 28. Ну что тут сказать? Смотри фотографии и делай выводы сама. В жизни сплошь и рядом девушки с самой заурядной внешностью удачно выходят замуж. Почему? Читай дальше.
_&ensp;&ensp;Задание. Найди информацию, например, биографии о пяти женщинах, успешных в личной жизни и о пяти женщинах, несчастных в личной жизни. Проанализируй их и сделай хотя бы по 5 выводов о том, что же объединяет женщин успешных и что объединяет женщин не успешных. Таким образом, в сумме ты получишь 10 выводов. Обращай внимание не только на убеждения или установки, но и на то, какое воспитание получили девочки в детстве, в каких семьях росли, какова судьба их матерей и т.д. После 10 общих выводов в тебе должна родиться одна главная мысль, один общий вывод. Сделай это, потом читай дальше._
<br/>
&ensp;&ensp;Я верю в тебя и поэтому знаю, что общий вывод, который ты сделаешь сама для себя, будет таким: 99% успеха в соблазнении зависит от твоих ценностей, доминантных мыслей, убеждений, отношения к жизни, к мужчинам, т.е. всего того, что находится в твоей голове. И почти не зависит от природных внешних данных, места рождения, текущей финансовой ситуации и т.п. Сейчас я приведу убеждения успешной соблазнительницы, которые ты должна выучить как Отче наш и которые ты обязана проверить на практике. Все до единого! Вот этот ключ, открывающей тебе дорогу в долину любых отношений, которые ты только захочешь иметь. Они позволят тебе играть с мужчинами как кошка с мышкой и брать от них то, о чем обычная девушка не может и мечтать. Вот они:
1. Всё всегда хорошо
2. Другие девушки не помеха для меня
3. Парней много, я одна
4. Использовать мужчин естественно и приятно
5. Отшивать мужчин - это естественно
6. Ты имеешь ровно то, что заслужила
7. Ты достойна гораздо большего
8. Все мечты достижимы
9. Проявлять активность девушке естественно
10. Мужчины до замужества - тренажеры
11. Мужчина должен относиться уважительно
12. Среди мужчин есть идиоты
13. Желание секса от мужчины - это нормально
14. Мужчины общаются, когда им что-то нужно
15. Пока не было секса - ты не проиграла
16. Удовольствие от общения = всё по плану
17. Ты ничего не должна мужчине
18. Соблазнение - интересная игра
19. Ахиллесовы пяты мужчин: эго и секс
20. Мужчины - хорошие создания
21. Мужчины не примитивны
22. Мужчинам нужно не только секс
23. Смысл жизни - развитие
24. Запасной аэродром - это нормально
25. Сексом мужчину не удержать
26. Общение должно приносить радость
27. Мужчины любят высокоранговых женщин
28. Мир - отражение тебя самой
&ensp;&ensp;А теперь остановимся подробнее на каждом из этих убеждений. Надо сказать, что ты можешь высечь все эти убеждения на камне и использовать этот камень у себя вместо спальной подушки. Ты должна на собственном опыте убедиться в правоте этих убеждений. Эти убеждения - не что-то волшебное. Они просто сама суть жизнь, реальное положение вещей. Всё перечисленное - факты. Зная настоящие правила игры, ты получаешь шанс выиграть в этой игре. Только так, а руководствуясь ложными убеждениями ты всегда будешь оставаться в дурах.<br/>
&ensp;&ensp;Если ты хочешь достичь определенной цели - тебе нужны истинные знания. Только истинные знания позволят достичь цели. Все неистинные знания (ложь) забирают твое время, силы и уводят от нужного результата. Если бы ты могла жить вечно и при этом сохранить свою привлекательность, тебе не понадобилась бы эта книга. Просто экспериментируя и набивая себе шишки, исследовав все ложные тропы, ты нашла бы одну истинную и достигла бы нужного тебе результата. Но в жизни не так. У успешных спортсменов - выдающиеся тренера. У успешных бизнесменов - выдающиеся наставники. Человек даже писать и читать без учителя не научится. И в соблазнении у тебя есть учителя, только это слабые и некомпетентные учителя, поэтому учась у них, ты останешься такой же некомпетентной. Кто твои учителя? Правильно, это те люди, с которыми ты общаешься на темы соблазнения, т.е. твои подруги. Возможно, мама. Это уже лучше. Потому что она-то уж точно желает тебе добра, а насчет подруг - большой вопрос. В общем, послушай человека, достигшего определенных успехов в этом вопросе, т.е. меня. Даже если ты просто прочитаешь эту книгу, тебе станет гораздо легче. А уж если ты и задания будешь выполнять, а потом изучишь и вторую мою книгу… Истинно тебе говорю - ты будешь смеяться над своим текущим уровнем в общении с парнями.<br/>
&ensp;&ensp;Ты что-нибудь знаешь про метод тотальной бомбежки? Есть такой метод. Сделай себе шпаргалку с этими убеждениями, заглядывай в нее каждые полчаса и размышляй над тем убеждением, которое первым бросится тебе в глаза. Думай и думай - задавай себе вопросы - “А почему так? Какая мне польза от этого убеждения? Какие истории из моей жизни или жизни моих подруг подтверждают правильность этих убеждений?” Твой мозг должен вскипеть от такой работы. Думай и думай над этими убеждениями. Выучи их наизусть так, чтобы они от зубов твоих отлетали. После этого ты должна “вылизать”, “обсосать” каждое из этих убеждений, обсмаковать, понять в тончайших деталях, а это уже делается только на практике и только с помощью практики. Обязательно проверь их на практике, впитай в себя, а не то грош цена всем твоим стараниям.<br/>
&ensp;&ensp;Многие из этих убеждений очень глубокие, глубже и мощнее наших океанов, и для того, чтобы они стали частью тебя, тебе потребуется много практики и несколько лет жизни. Но это стоит того. Всё, что ты тратишь на улучшение - это только время и старания. Больше ничего. И это ничтожная цена за то, кем ты станешь. После всего пары лет упорной работы ты настолько изменишься в лучшую сторону, что будешь смотреть на себя теперешнюю как на слепого котенка. Тебе будет смешно. Ты будешь смотреть на своих сверстниц и ощущать огромнейшую разницу между тобой и ними. Тебе захочется им помочь, ты будешь смотреть на них, как на слепых котят, которые бьются головой об ножку стула и не могут ее обойти. Ты также будешь насквозь видеть мужчин, ты будешь чувствовать, на каком этапе игра, все будет под твоим контролем. Ты выйдешь на недостижимый для миллионов девушек уровень. У тебя откроются глаза. Ты ни за что не захочешь возвращаться в свое текущее состояние. Но для этого тебе надо начать действовать, да, черт подери, тупо начать действовать! Первый этап работы - перепрошивка мозга.<br/>
&ensp;&ensp;Итак, убеждения.

35
content/books/2.md Normal file
View File

@ -0,0 +1,35 @@
<div style="color: #f54b7e; font-weight: bold; font-size: 1.5em;">Глава 2. Ближний бой</div>
### <span style="color: #f54b7e;">Введение</span>
&ensp;&ensp;Эта глава — самая важная глава в моей второй книге. Умение калибровать, несомненно, тоже важно. Но калибровать — природный дар девушек, вы очень быстро этому и обучаетесь, и изначально почти умеете. Именно поэтому во второй книге я сделал акцент не на калибровке, а именно на умении так влюбить в себя парня, что он будет помнить тебя даже перед лицом смерти. Техники влюбления… здесь я рассказываю далеко не очевидные вещи. Но они очень важны, очень. Крутую соблазнительницу от неопытной девушки отличают действия. Если они обе одинаково красивы, одинаково стильны, одинаково умны и приветливы, то всё, что их отличает, это действия. Соблазнительница просто действует по-другому, она делает другие вещи. Вот и всё. И я еще раз повторяю, ты не сможешь сама догадаться до тех фишек, которые проворачивает соблазнительница. Те фишки, о которых ты узнаешь из моей книги, я смог синтезировать, только на основе моего опыта и природной склонности к анализу. Физико-математический лицей и 4 года аспирантуры сделали свое дело. У меня есть природная склонность анализировать, я люблю думать. Мне нравится сам процесс мышления. Чтобы начать думать в правильном русле, нужны правильные знания, правильный опыт. На приобретение этого пресловутого опыта в лучшем случае может уйти год. У меня ушло больше, не было наставника. Приходилось до многих вещей доходить самому[1] .
Но видимо у тебя хорошая карма, если в своих руках ты держишь эту книгу. Поэтому я не буду тебе рассказывать всякую чушь, которую пишут авторы-женщины в своих псевдо-учебниках по скрытому управлению мужчинами, соблазнению и т.д. Я пишу самую суть правды. Хотя, может это и не так уж плохо, что в теме соблазнения мужчин, все авторы — сплошные лохушки, пишущие о ширине твоего каблука, который должен быть на первом свидании. Что тебе идет, то и носи. Вот и вся правда про одежду. На фоне авторов, ничего не смыслящих в соблазнении, я выгляжу круче. Я удовлетворяю свой эгоизм, свою самовлюбленность и прочие черные стороны своей личности. Мой уровень, то качество материала, который я выдаю, становятся еще выше и качественнее на фоне псевдоспециалистов. Обычные авторы, которые пишут про соблазнение — это псевдоспециалисты. Спасибо вам, авторы-лохи, вы позволяете ощутить мне свою исключительность. А любому мужчине это очень приятно.
### <span style="color: #f54b7e;">Техника влюбления</span>
_Здесь даются реальные знания._<br/>
арвард_
&ensp;&ensp;Механизм мужской и женской влюбленностей<br/>
Механизмы мужской и женской влюбленностей несколько разнятся. Т.е. мужчины и женщины, парни и девушки влюбляются по-разному. У девушек крышу сразу не сносит, а у парней крышу сносит именно сразу. В этом и заключается принципиальное различие в механизмах. Безусловно, эта разница — классика, поэтому на этот счет есть вариации. Однако, по своей сути, так оно и есть. На первых порах отношений девушка способна трезво оценивать парня. Она еще не влюбилась, она оценивает, присматривается, приценивается к парню. Он ей нравится, но она может трезво мыслить. Видишь эти два графика ниже по тексту[2]? Теперь ты понимаешь различие в механизмах.
Когда отношения с девушкой только начинаются, когда еще не было секса, парень испытывает некое подобие эйфории и одновременной зависимости от девушки. Я опять же говорю о классическом случае. Парень пытается быть максимально хорошим для нее. Она ему нравится, он запал на нее. Все его мысли — о ней. Девушка же влюбляется с задержкой по времени, когда парень пройдет ее фильтры. Когда у нее “щелкнет” в голове, тогда она и влюбляется.
![Рисунок 5 — Различия между мужской и женской схемами влюбления](image2.png)
Рисунок 5 — Различия между мужской и женской схемами влюбления<br/>
_Иллюстрация создана автором книги_
&ensp;&ensp;Смотри, как выгодно для тебя устроила природа. Ты еще ничего не сделала в плане развития отношений, но уже имеешь бонус. Красота!! Спасибо матери-природе. Такой механизм — следствие заботы о выживании рода, вида. Вид должен выжить. Поэтому тебе дается время на фильтрацию самцов, чтобы ты могла выбрать лучшего и получить его семя. Самый живучий самец, самец, обошедший своих сородичей, самый жизнеспобный, способен дать тебе лучшее семя среди своих представителей. Так ты сможешь родить наиболее жизнеспособного ребенка, самого живучего. Забеременев же от слабого самца, ты родишь слабое потомство. Именно из-за такого механизма выживания вида парень сразу испытывает влечение к девушке, но после первого секса сразу же остывает к ней. Его задача выполнена, он оплодотворил самку. Его следующая задача — найти новую самку и оплодотворить ее. Поэтому он также быстро “западает” на следующую, трахает ее, остывает к ней и ищет новую. Парень выполняет задачу продолжения рода: совокупляется с максимальным количеством девушек.
Но для нас с тобой сейчас самое важное — работать в зоне t — фильтрации. Как раз в период, который тебе подарила мать-природа, ты должна работать над отношениями. Тебе дали бонус в виде времени, не просри его. Те фишки, которые я тебе даю, имеют максимальную эффективность как раз в период фильтрации самцов. Как только вы переспите, период фильтрации закончится. И эффективность любых манипулятивных фишек, даже от Вино Галанте, упадет в разы. Это же так просто!! Ты пропустила парня через свои фильтры. Мать-природа позаботилась о том, чтобы сразу после этого ему захотелось другую самку. Как только ему захотелось другую, тебе становится архисложно влиять на парня. Природа, природа, против нее не поспоришь.
Сколько времени длится период фильтрации самцов? Запомни, в соблазнении отсутствует время как таковое, в соблазнении присутствует качество. Поэтому период фильтрации может длиться один час, а может год или два. Здесь все зависит от твоего уровня. Но период фильтрации заканчивается первым сексом. Потому что ты уже отфильтровала парня. Раз ты его отфильтровала, как же может продолжаться фильтрация? Правильно, никак. А начинается период первой коммуникацией, считай, что словом “Привет”. Видишь, никаких времен, только условия.
### <span style="color: #f54b7e;">Главный секрет влюбления</span>
&ensp;&ensp;Для того, чтобы парень влюбился в тебя, необходимо и достаточно выполнения всего четырех условий. На самом деле, когда ты работаешь по влюблению в себя парня, ты действуешь согласно всего четырем правилам. Все эти четыре правила или условия являются необходимыми и достаточными. Необходимые, значит, требуется выполнение всех четырех условий, чтобы в тебя влюбились. Достаточные, значит, требуется выполнение только этих четырех условий и никаких больше.
Для влюбления парня **необходимы и достаточны 4 условия**:
- **Подстройка ментальная** — совпадение мышления.
- **Подстройка по ценностям** — общие принципы жизни.
- **Поведение** — действия, которые цепляют.
- **Достаточное качество девушки** — внешность, интеллект, харизма.
&ensp;&ensp;Выбей этот секрет влюбления на огромном камне и используй этот камень вместо подушки. Только что я рассказал тебе главный секрет влюбления в себя мужчины. Или девушки, здесь нет никакой разницы, кстати. Есть всего четыре вещи: ментальная подстройка, подстройка по ценностям, твое качество и твое поведение. Всё, больше ничего нет. Больше ничего не требуется, чтобы влюбить в себя абсолютно любого парня. Самое сложное, над чем приходится работать обычной девушке — ее качество. Если же ты родилась с внешностью Адрианы Лимы или Миранды Керр, да еще и с головой на плечах. Тогда тебе крупно повезло, работать придется не так уж и много. Ты — девушка высокого качества. Четвертый пункт удовлетворен.

BIN
content/books/image2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,10 +1,7 @@
import { globalIgnores } from "eslint/config"; import { globalIgnores } from 'eslint/config'
import { import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
defineConfigWithVueTs, import pluginVue from 'eslint-plugin-vue'
vueTsConfigs, import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
} from "@vue/eslint-config-typescript";
import pluginVue from "eslint-plugin-vue";
import skipFormatting from "@vue/eslint-config-prettier/skip-formatting";
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: // To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript' // import { configureVueProject } from '@vue/eslint-config-typescript'
@ -13,18 +10,19 @@ import skipFormatting from "@vue/eslint-config-prettier/skip-formatting";
export default defineConfigWithVueTs( export default defineConfigWithVueTs(
{ {
name: "app/files-to-lint", name: 'app/files-to-lint',
files: ["**/*.{ts,mts,tsx,vue}"], files: ['**/*.{ts,mts,tsx,vue}'],
}, },
globalIgnores([ globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**', './src/generated/**']),
"**/dist/**",
"**/dist-ssr/**",
"**/coverage/**",
"./src/generated/**",
]),
pluginVue.configs["flat/essential"], pluginVue.configs['flat/essential'],
vueTsConfigs.recommended, vueTsConfigs.recommended,
skipFormatting skipFormatting,
); {
rules: {
'vue/multi-word-component-names': 'off',
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
},
},
)

View File

@ -1,2 +0,0 @@
VITE_MODE=DEVELOP
VITE_API_BASE_PATH=http://localhost:5173/

View File

@ -1,21 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="/src/style.css" rel="stylesheet" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap"
rel="stylesheet"
/>
<link href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap" rel="stylesheet" />
<title>e-book</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -25,7 +25,7 @@
<script setup lang="ts"> <script setup lang="ts">
import UiParagraph from '@/components/Typography/UiParagraph.vue' import UiParagraph from '@/components/Typography/UiParagraph.vue'
import UiNav from '@/components/UiNav/UiNav.vue' import UiNav from '@/components/UiNav/UiNav.vue'
import { footerNavigation, refLitres } from '@/layout/UiFooter/_data' import { footerNavigation, refLitres } from '@/layouts/UiFooter/_data'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()

View File

@ -0,0 +1,3 @@
export { default as footerNavigation } from '@/layouts/UiFooter/_data/footer-navigation.data'
export { default as refLitres } from '@/layouts/UiFooter/_data/ref-litres.data'

View File

@ -0,0 +1 @@
export { default as headerNavigation } from '@/layouts/UiHeader/_data/header-navigation.data'

View File

@ -1,6 +1,6 @@
<template> <template>
<div <div
class="min-h-svh overflow-x-hidden lato-regular relative overflow-visible before:content-[''] before:absolute before:bottom-0 before:h-[520px] before:w-full before:bg-[url(/src/assets/img/webp/footer-flowers.webp)] before:bg-no-repeat before:bg-bottom before:bg-cover before:z-40" class="min-h-svh overflow-x-hidden lato-regular relative overflow-visible before:content-[''] before:absolute before:bottom-0 before:h-full before:w-full before:bg-[url(/assets/img/png/footer-flowers-2.png)] before:bg-no-repeat before:bg-bottom before:bg-contain before:z-40"
:class=" :class="
route.path === '/books/1' || route.path === '/books/2' route.path === '/books/1' || route.path === '/books/2'
? 'bg-[linear-gradient(135deg,rgba(17,17,30,1)_10%,rgba(183,32,76,1)_170%)]' ? 'bg-[linear-gradient(135deg,rgba(17,17,30,1)_10%,rgba(183,32,76,1)_170%)]'
@ -9,15 +9,15 @@
> >
<div <div
v-if="route.path === '/'" v-if="route.path === '/'"
class="relative z-10 after:content-[''] after:absolute after:top-0 after:right-0 after:w-[820px] after:h-[535px] after:bg-[url(/src/assets/img/webp/gradient.webp)] after:bg-no-repeat after:bg-[right_top] after:bg-contain" class="relative z-10 after:content-[''] after:absolute after:top-0 after:right-0 after:w-[820px] after:h-[535px] after:bg-[url(/assets/img/webp/gradient.webp)] after:bg-no-repeat after:bg-[right_top] after:bg-contain"
/> />
<div <div
v-if="route.path === '/'" v-if="route.path === '/'"
class="relative z-20 after:content-[''] after:absolute after:top-0 after:right-0 after:w-[1120px] after:h-[1800px] after:bg-[url(/src/assets/img/webp/group-flowers.webp)] after:bg-no-repeat after:bg-[right_top] after:bg-cover" class="relative z-20 after:content-[''] after:absolute after:top-0 after:right-0 after:w-[1120px] after:h-[1800px] after:bg-[url(/assets/img/webp/group-flowers.webp)] after:bg-no-repeat after:bg-[right_top] after:bg-cover"
/> />
<div <div
v-if="route.path === '/'" v-if="route.path === '/'"
class="relative z-40 before:content-[''] before:absolute before:top-0 before:left-0 before:w-[1120px] before:h-[1000px] before:bg-[url(/src/assets/img/webp/hero-flowers.webp)] before:bg-no-repeat before:bg-left before:bg-contain" class="relative z-40 before:content-[''] before:absolute before:top-0 before:left-0 before:w-[1120px] before:h-[1000px] before:bg-[url(/assets/img/webp/hero-flowers.webp)] before:bg-no-repeat before:bg-left before:bg-contain"
/> />
<UiHeader class="relative z-50" /> <UiHeader class="relative z-50" />
<UiMain class="!min-h-[1200px] text-primary relative"> <UiMain class="!min-h-[1200px] text-primary relative">

65
nuxt.config.ts Normal file
View File

@ -0,0 +1,65 @@
import config from './config'
import { fileURLToPath, URL } from 'node:url'
import head from './config/head'
// import sitemap from "./config/sitemap";
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-05-15',
devtools: { enabled: true },
devServer: {
host: 'localhost',
port: 4002,
},
nitro: {
output: {
dir: './dist',
},
},
alias: {
'@': fileURLToPath(new URL('./', import.meta.url)),
},
css: ['@/assets/css/tailwind.css'],
...config,
htmlValidator: {
usePrettier: true,
logLevel: 'error',
failOnError: false,
},
app: {
head: {
title: 'Vino Galante',
htmlAttrs: {
lang: 'ru',
},
...head,
},
},
modules: [
...config.modules,
[
'nuxt-mail',
{
smtp: {
host: 'smtp.yandex.ru',
port: 465,
secure: true,
auth: {
user: process.env.SMTP_USER || 'levishub@yandex.com',
pass: process.env.SMTP_PASS || 'avhpihoudpyvibtx',
},
},
message: {
to: process.env.DEFAULT_TO_EMAIL || 'default@example.com',
},
},
],
],
})

5918
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +1,56 @@
{ {
"name": "e-book", "name": "e-book",
"private": true, "private": true,
"version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "build": "nuxt build",
"build": "vue-tsc -b && vite build", "dev": "nuxt dev",
"preview": "vite preview", "generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"prepare": "husky", "prepare": "husky",
"lint": "node_modules/.bin/eslint . --fix", "lint": "node_modules/.bin/eslint . --fix",
"format": "node_modules/.bin/prettier --write ./src/" "format": "node_modules/.bin/prettier --write ./"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.8", "@a2seven/yoo-checkout": "^1.1.4",
"@vueuse/head": "^2.0.0", "@nuxt/content": "^3.6.0",
"pinia": "^3.0.3", "@nuxt/fonts": "^0.11.4",
"@nuxt/icon": "^1.13.0",
"@nuxt/image": "^1.10.0",
"@nuxtjs/html-validator": "^2.1.0",
"@nuxtjs/robots": "^5.2.10",
"@nuxtjs/sitemap": "^7.4.0",
"@pinia/nuxt": "^0.5.5",
"@tailwindcss/postcss": "^4.1.10",
"better-sqlite3": "^11.10.0",
"crypto-js": "^4.2.0",
"date-fns": "^4.1.0",
"husky": "^9.1.7",
"nodemailer": "^7.0.3",
"nuxt": "^3.17.5",
"nuxt-mail": "^6.0.2",
"nuxt-schema-org": "^5.0.5",
"pg": "^8.16.2",
"postgres": "^3.4.7",
"swiper": "^11.2.8", "swiper": "^11.2.8",
"tailwindcss": "^4.1.8", "vue": "^3.5.16"
"vue": "^3.5.13",
"vue-router": "4"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node22": "^22.0.1", "@nuxt/eslint": "^1.4.1",
"@types/node": "^22.14.0", "@nuxt/test-utils": "^3.11.3",
"@vitejs/plugin-vue": "^5.2.3", "@nuxtjs/tailwindcss": "^6.11.4",
"@types/node": "^22.0.0",
"@types/pg": "^8.15.4",
"@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0", "@vue/eslint-config-typescript": "^14.5.0",
"@vue/tsconfig": "^0.7.0", "autoprefixer": "^10.4.18",
"eslint": "^9.22.0", "eslint": "^9.29.0",
"eslint-plugin-vue": "~10.0.0", "eslint-plugin-vue": "^10.0.0",
"husky": "^9.1.7", "postcss": "^8.5.6",
"jiti": "^2.4.2",
"npm-run-all2": "^7.0.2",
"playwright": "^1.52.0",
"prettier": "3.5.3", "prettier": "3.5.3",
"tailwindcss": "^3.4.17",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"vite": "^6.3.5",
"vite-plugin-vue-devtools": "^7.7.2",
"vue-tsc": "^2.2.8" "vue-tsc": "^2.2.8"
}, }
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
} }

View File

@ -1,15 +1,15 @@
<template> <template>
<template v-if="book"> <template v-if="book">
<div class="relative z-50 min-h-screen text-white mb-[208px]"> <div class="relative z-50 min-h-screen text-white mb-[208px]">
<template v-if="!route.params.titlesSlug"> <template v-if="!route.params.chapter && !route.params.titlesSlug">
<!--верхний блок--> <!--верхний блок-->
<section <section
class="flex flex-row relative z-40 before:content-[''] before:absolute before:top-[-140px] before:bg-top before:left-0 before:w-[1200px] before:h-[1000px] before:bg-[url(/src/assets/img/webp/vino-galante.webp)] before:bg-no-repeat before:bg-contain mt-40" class="flex min-h-[600px] flex-row relative z-40 before:content-[''] before:absolute before:top-[-140px] before:bg-top before:left-0 before:w-[1280px] before:h-[1000px] before:bg-[url(/assets/img/webp/vino-galante.webp)] before:bg-no-repeat before:bg-contain mt-40"
> >
<!--левый блок контента--> <!--левый блок контента-->
<section class="relative top-[-20px] min-w-[570px]"> <section class="relative top-[-30px] min-w-[570px]">
<div class="flex flex-col items-center"> <div class="flex flex-col items-center h-[600px]">
<img :src="book.img" :alt="book.buttonText" width="100%" height="100%" /> <img :src="book.img" :alt="book.buttonText" />
</div> </div>
</section> </section>
<!--правый блок контента--> <!--правый блок контента-->
@ -33,15 +33,20 @@
</section> </section>
</section> </section>
<!--средний блок--> <!--средний блок-->
<section class="flex flex-row items-center ml-18 justify-between"> <section class="flex flex-row items-center ml-12 gap-24 relative z-50">
<!--левый--> <!--левый-->
<div class="flex flex-col items-center min-h-[310px]"> <div class="flex flex-col items-center min-h-[310px]">
<div class="flex flex-row"> <div class="flex flex-row">
<UiParagraph class="[&]:text-6xl">{{ book.price }}&nbsp;</UiParagraph> <UiParagraph class="[&]:text-6xl">{{ book.price }}&nbsp;</UiParagraph>
<img src="/img/svg/books/ruble.svg" alt="ruble" /> <img src="/img/svg/books/ruble.svg" alt="ruble" />
</div> </div>
<div class="mr-10 flex items-center flex-col gap-3"> <div class="flex items-center flex-col gap-3">
<UiButton class="max-w-[380px] !font-normal !px-2 !py-4 mt-24"> <UiButton
@click="handleSelect"
:id="book.id"
variants="primary"
class="max-w-[440px] !font-normal !px-2 !py-4 mt-16"
>
{{ book.buttonText }} {{ book.buttonText }}
</UiButton> </UiButton>
<UiParagraph size="200"> <UiParagraph size="200">
@ -50,39 +55,29 @@
</div> </div>
</div> </div>
<!--правый--> <!--правый-->
<div class="min-h-[310px]"> <div class="min-h-[310px] pt-6">
<!--о книге--> <!--о книге-->
<div> <div>
<ul class="flex flex-row mr-14 items-center justify-between lg:whitespace-nowrap"> <ul class="flex flex-row mr-14 items-center justify-between lg:whitespace-nowrap">
<li class="flex flex-row mr-14 gap-3 items-center"> <li class="flex flex-row mr-14 gap-3 items-center">
<img <img src="/img/svg/books/book-pages.svg" alt="страниц" width="24" height="24" />
src="/img/svg/books/book-pages.svg"
alt="страниц"
width="100%"
height="100%"
/>
<UiParagraph size="250" as="span"> <UiParagraph size="250" as="span">
{{ book.pages }} {{ book.pages }}
</UiParagraph> </UiParagraph>
</li> </li>
<li class="flex flex-row mr-14 gap-3 items-center"> <li class="flex flex-row mr-14 gap-3 items-center shrink-0">
<img <img
src="/img/svg/books/book-illustrations.svg" src="/img/svg/books/book-illustrations.svg"
alt="иллюстраций" alt="иллюстраций"
width="100%" width="24"
height="100%" height="24"
/> />
<UiParagraph size="250" as="span"> <UiParagraph size="250" as="span">
{{ book.illust }} {{ book.illust }}
</UiParagraph> </UiParagraph>
</li> </li>
<li class="flex flex-row mr-14 gap-3 items-center"> <li class="flex flex-row mr-14 gap-3 items-center">
<img <img src="/img/svg/books/book-formats.svg" alt="формат" width="24" height="24" />
src="/img/svg/books/book-formats.svg"
alt="формат"
width="100%"
height="100%"
/>
<UiParagraph size="250" as="span"> <UiParagraph size="250" as="span">
{{ book.format }} {{ book.format }}
</UiParagraph> </UiParagraph>
@ -91,51 +86,51 @@
</div> </div>
<!--навигация по книге--> <!--навигация по книге-->
<div class="mt-24"> <div class="mt-28 pt-2">
<ul class="flex flex-row mr-32 items-end justify-between lg:whitespace-nowrap"> <ul class="flex flex-row mr-32 items-baseline justify-between lg:whitespace-nowrap">
<li class="flex flex-row gap-3 items-center"> <li class="flex flex-row h-24 w-[105px] items-center">
<RouterLink to="#" class="flex flex-col items-center gap-3 cursor-pointer"> <NuxtLink
<img src="/img/svg/books/read.svg" alt="Читай отрывок" width="50" height="50" /> :to="`/books/${route.params.slug}/chapters/${route.params.slug}/`"
class="flex flex-col gap-8 items-center cursor-pointer"
>
<img src="/img/svg/books/read.svg" alt="Читай отрывок" width="62" height="53" />
<UiParagraph size="250" as="span"> Читай отрывок </UiParagraph> <UiParagraph size="250" as="span"> Читай отрывок </UiParagraph>
</RouterLink> </NuxtLink>
</li> </li>
<li class="flex flex-row gap-3 items-center"> <li class="flex flex-row items-center h-24 w-[105px]">
<RouterLink to="#" class="flex flex-col items-center gap-3 cursor-pointer"> <NuxtLink to="#" class="flex flex-col items-center gap-8 cursor-pointer">
<img <img
src="/img/svg/books/download.svg" src="/img/svg/books/download.svg"
alt="Скачай отрывок" alt="Скачай отрывок"
width="50" width="62"
height="50" height="53"
/> />
<UiParagraph size="250" as="span"> Скачай отрывок </UiParagraph> <UiParagraph size="250" as="span"> Скачай отрывок </UiParagraph>
</RouterLink> </NuxtLink>
</li> </li>
<li class="flex flex-row gap-3 items-center"> <li class="flex flex-row items-center h-24 w-[105px]">
<RouterLink <NuxtLink
:to="{ :to="`/books/${route.params.slug}/${book.hrefTitles}`"
name: 'book-titles', class="flex flex-col items-center gap-8 cursor-pointer"
params: {
slug: route.params.slug,
titlesSlug: book.hrefTitles,
},
}"
class="flex flex-col items-center gap-3 cursor-pointer"
> >
<img src="/img/svg/books/titles.svg" alt="Содержание" width="50" height="50" /> <img src="/img/svg/books/down2.svg" alt="Содержание" width="62" height="53" />
<UiParagraph size="250" as="span"> Содержание </UiParagraph> <UiParagraph size="250" as="span"> Содержание </UiParagraph>
</RouterLink> </NuxtLink>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</section> </section>
<!--нижний блок--> <!--нижний блок-->
<section class="ml-18 mt-32"> <section class="ml-20 mt-32">
<div> <div>
<UiHeading tag="H2" size="300" class="text-three"> Что ты узнаешь </UiHeading> <UiHeading tag="h2" size="300" class="text-three"> Что ты узнаешь </UiHeading>
<ul class="flex mt-20 flex-row items-center justify-between"> <ul class="flex mt-20 flex-row items-center justify-between">
<li <li
class="flex flex-col-reverse justify-end w-38 gap-4 h-64 items-center transition-transform transform hover:scale-110" class="flex flex-col-reverse justify-end w-32 gap-4 h-64 items-center transition-transform transform hover:scale-110"
v-for="({ svg, text }, index) in book.whoUKnows" v-for="({ svg, text }, index) in book.whoUKnows"
:key="index" :key="index"
> >
@ -155,7 +150,7 @@
</div> </div>
</section> </section>
</template> </template>
<RouterView /> <NuxtPage />
</div> </div>
</template> </template>
<div v-else class="text-white text-center py-20">Книга не найдена.</div> <div v-else class="text-white text-center py-20">Книга не найдена.</div>
@ -163,11 +158,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { useRoute, RouterView } from 'vue-router' import { useRoute, useRouter } from '#app'
import UiHeading from '@/components/Typography/UiHeading.vue' import UiHeading from '@/components/Typography/UiHeading.vue'
import UiParagraph from '@/components/Typography/UiParagraph.vue' import UiParagraph from '@/components/Typography/UiParagraph.vue'
import UiButton from '@/components/UiButton/UiButton.vue' import UiButton from '@/components/UiButton/UiButton.vue'
import { useHead } from '@vueuse/head' import { useSelectedBook } from '@/store/useSelectedBook'
interface BookDetail { interface BookDetail {
id: number id: number
@ -193,6 +188,8 @@ interface BookDetail {
const route = useRoute() const route = useRoute()
const router = useRouter()
const currentBookData = ref<BookDetail | null>(null) const currentBookData = ref<BookDetail | null>(null)
const book = computed(() => currentBookData.value) const book = computed(() => currentBookData.value)
@ -207,6 +204,10 @@ const loadBookData = async (slug: string) => {
} }
} }
const handleSelect = () => {
router.push('/cart/')
}
watch( watch(
() => route.params.slug, () => route.params.slug,
async (newSlug) => { async (newSlug) => {

View File

@ -0,0 +1,175 @@
<template>
<div v-if="content" class="relative z-50 min-h-screen text-white mb-[208px] ml-4">
<section class="flex flex-col relative z-40 mt-40 ml-18 mr-18">
<div v-html="renderedContent" class="prose prose-invert max-w-none"></div>
</section>
</div>
<div v-else class="text-white text-center py-20">Глава не найдена.</div>
</template>
<script setup lang="ts">
// Получаем параметры из URL
const route = useRoute()
const bookSlug = route.params.slug as string
const chapterSlug = route.params.chapter as string
// Загружаем контент из markdown файла
const { data: content } = await useAsyncData(`book-${bookSlug}-${chapterSlug}`, () => {
return $fetch(`/api/content/books/${chapterSlug}`)
})
// Конвертируем markdown в HTML (простая реализация)
const renderedContent = computed(() => {
if (!content.value?.content) return ''
let html = content.value.content
// Заменяем заголовки
html = html.replace(
/^### (.*$)/gim,
'<h3 style="color: #f54b7e; font-weight: bold; margin-top: 1.5rem; margin-bottom: 0.5rem;">$1</h3>',
)
html = html.replace(
/^## (.*$)/gim,
'<h2 style="color: #f54b7e; font-weight: bold; margin-top: 2rem; margin-bottom: 1rem;">$1</h2>',
)
html = html.replace(
/^# (.*$)/gim,
'<h1 style="color: #f54b7e; font-weight: bold; font-size: 1.5em; margin-bottom: 1rem;">$1</h1>',
)
// Заменяем параграфы
html = html.replace(
/^&ensp;&ensp;(.*$)/gim,
'<p style="margin-bottom: 1rem; line-height: 1.6;">$1</p>',
)
// Заменяем жирный текст (двойные звездочки)
html = html.replace(/\*\*(.*?)\*\*/g, '<strong style=" font-weight: bold;">$1</strong>')
// Заменяем курсив (звездочки)
html = html.replace(/\*(.*?)\*/g, '<em style="font-style: italic;">$1</em>')
// Заменяем курсив (подчеркивания)
html = html.replace(/\_(.*?)\_/g, '<em style="font-style: italic;">$1</em>')
// Заменяем изображения - исправляем пути
html = html.replace(
/!\[([^\]]*)\]\(([^)]+)\)/g,
'<img src="/img/books/$2" alt="$1" style="max-width: 100%; height: auto; margin: 1rem 0; display: block;" />',
)
// Заменяем списки
html = html.replace(/^- (.*$)/gim, '<li style="margin-bottom: 0.5rem;">$1</li>')
html = html.replace(/(<li.*<\/li>)/s, '<ul style="margin-bottom: 1rem;">$1</ul>')
// Заменяем <br/> теги на HTML <br>
html = html.replace(/<br\/>/g, '<br>')
// Заменяем переносы строк
html = html.replace(/\n/g, '<br>')
return html
})
// Устанавливаем мета-теги
useHead({
title: `Глава ${chapterSlug} | Книга ${bookSlug} | Vino Galante`,
meta: [
{
name: 'description',
content: `Читайте главу ${chapterSlug} из книги ${bookSlug} автора Vino Galante`,
},
],
link: [
{
rel: 'canonical',
href: `https://ebook.miduway.space/books/${bookSlug}/chapters/${chapterSlug}`,
},
],
})
// Обработка ошибок, если файл не найден
if (!content.value) {
throw createError({
statusCode: 404,
statusMessage: 'Глава не найдена',
})
}
</script>
<style>
/* Дополнительные стили для контента */
.prose {
color: white;
max-width: none;
}
.prose h1 {
color: #f54b7e;
font-weight: bold;
font-size: 1.5em;
margin-bottom: 1rem;
}
.prose h2 {
color: #f54b7e;
font-weight: bold;
margin-top: 2rem;
margin-bottom: 1rem;
}
.prose h3 {
color: #f54b7e;
font-weight: bold;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
}
.prose p {
margin-bottom: 1rem;
line-height: 1.6;
}
.prose ul {
margin-bottom: 1rem;
}
.prose li {
margin-bottom: 0.5rem;
}
.prose strong {
font-weight: bold;
}
.prose em {
font-style: italic;
}
.prose blockquote {
border-left: 4px solid #f54b7e;
padding-left: 1rem;
margin: 1rem 0;
font-style: italic;
}
.prose img {
max-width: 100%;
height: auto;
margin: 1rem 0;
display: block;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
/* Стили для подписей к изображениям */
.prose img + br + em {
display: block;
text-align: center;
font-style: italic;
color: #f54b7e;
margin-top: 0.5rem;
margin-bottom: 1rem;
}
</style>

View File

@ -8,7 +8,7 @@
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6">
<div v-for="(section, index) in titles.sections" :key="index" class="flex flex-col gap-4"> <div v-for="(section, index) in titles.sections" :key="index" class="flex flex-col gap-4">
<!-- Main section title --> <!-- Main section title -->
<UiHeading tag="H2" size="300" class="text-three [&]:font-normal"> <UiHeading tag="h2" size="300" class="text-three [&]:font-normal">
{{ section.title }} {{ section.title }}
</UiHeading> </UiHeading>
@ -70,7 +70,6 @@ import { ref, computed, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import UiHeading from '@/components/Typography/UiHeading.vue' import UiHeading from '@/components/Typography/UiHeading.vue'
import UiParagraph from '@/components/Typography/UiParagraph.vue' import UiParagraph from '@/components/Typography/UiParagraph.vue'
import { useHead } from '@vueuse/head'
interface SubsectionTitle { interface SubsectionTitle {
text: string text: string
@ -115,8 +114,6 @@ const loadTitlesData = async (slug: string) => {
watch( watch(
() => route.params.titlesSlug, () => route.params.titlesSlug,
async (newSlug) => { async (newSlug) => {
console.log('Route params changed:', route.params)
console.log('New titlesSlug:', newSlug)
if (newSlug) { if (newSlug) {
await loadTitlesData(newSlug as string) await loadTitlesData(newSlug as string)
} }

View File

@ -0,0 +1,21 @@
{
"items": [
{
"id": 1,
"name": "Как влюбить в себя любого \n Книга I. \n Откровения бывшего Казановы",
"src": "/img/books/book1.png",
"buy": "добавить Книгу I",
"price": 520,
"message": "💡 Купи обе книги и получи \n скидку 10% - 936 за комплект"
},
{
"id": 2,
"name": "Как влюбить в себя любого \n Книга II. \n Тонкая игра",
"src": "/img/books/book2.png",
"buy": "добавить Книгу II",
"price": 520,
"message": "💡 Купи обе книги и получи \n скидку 10% - 936 за комплект"
}
],
"message": "💡 Купи обе книги и получи скидку 10% - 936 за комплект"
}

113
pages/cart/index.vue Normal file
View File

@ -0,0 +1,113 @@
<template>
<section class="relative z-50 ml-4">
<UiHeading tag="h1" size="300" class="font-bold mb-16"> Корзина </UiHeading>
<div class="-ml-6">
<div
v-for="({ name, src, price, message, buy, id }, index) in cartList.items"
:key="index"
class="flex items-center mb-24"
>
<div class="w-40 h-40">
<img :src="`${src}`" alt="book" />
</div>
<UiParagraph size="300" class="whitespace-pre mb-10 mr-12 w-80">{{ name }}</UiParagraph>
<UiParagraph size="300" class="whitespace-pre mb-10 mr-20">{{ message }}</UiParagraph>
<UiButton
:id="id"
class="mb-10"
v-if="store.getQuantity(id) === 0"
@click="handleAddToCart(id)"
>{{ buy }}</UiButton
>
<template v-else>
<div class="flex items-center gap-8 mr-20">
<button
class="w-8 h-8 flex items-center justify-center rounded-full border border-white text-white text-2xl"
@click="handleRemove(id)"
aria-label="Уменьшить количество"
>
</button>
<span class="text-white text-2xl">{{ store.getQuantity(id) }}</span>
<button
class="w-8 h-8 flex items-center justify-center rounded-full border border-white text-white text-2xl"
@click="handleIncrement(id)"
aria-label="Увеличить количество"
>
+
</button>
</div>
<span class="text-white text-2xl font-bold mr-20 whitespace-nowrap">
{{ Number(price) * store.getQuantity(id) }}
</span>
<button
class="w-8 h-8 flex items-center justify-center rounded-full border border-white text-white text-xl"
@click="handleRemove(id)"
aria-label="Удалить товар"
>
×
</button>
</template>
</div>
<div class="flex items-center justify-end mt-8">
<UiParagraph is="span" size="300">Общая стоимость</UiParagraph>
<template v-if="isSpecialPrice">
<span class="text-white text-2xl font-bold ml-4 line-through select-none">
{{ regularTotalPrice }}
</span>
<span class="text-primary text-3xl font-bold ml-4">
{{ store.getTotalPrice(cartList.items) }}
</span>
</template>
<template v-else>
<span class="text-primary text-3xl font-bold ml-4">
{{ store.getTotalPrice(cartList.items) }}
</span>
</template>
</div>
</div>
<div class="mt-10 flex flex-col items-center justify-center">
<UiButton class="w-[660px]"> перейти к оформлению </UiButton>
<UiParagraph is="span" size="300" class="mb-10 mt-5"
>После оплаты книги сразу будут доступны для скачивания</UiParagraph
>
</div>
</section>
</template>
<script setup lang="ts">
import cartList from './_data/cart.json'
import UiHeading from '~/components/Typography/UiHeading.vue'
import UiParagraph from '~/components/Typography/UiParagraph.vue'
import { useSelectedBook } from '@/store/useSelectedBook'
import { computed } from 'vue'
const store = useSelectedBook()
function handleIncrement(id: number) {
store.increment(id)
}
function handleDecrement(id: number) {
store.decrement(id)
}
function handleRemove(id: number) {
store.reset(id)
}
function handleAddToCart(id: number) {
store.addToCart(id)
}
const selectedBooksCount = computed(() => store.cartQuantities.filter((i) => i.quantity > 0).length)
const isSpecialPrice = computed(() => selectedBooksCount.value === 2)
const regularTotalPrice = computed(() => {
// Сумма без скидки
return store.cartQuantities.reduce((sum, item) => {
const book = cartList.items.find((b: any) => b.id === item.id)
return sum + (book ? Number(book.price) * item.quantity : 0)
}, 0)
})
</script>

19
pages/email.vue Normal file
View File

@ -0,0 +1,19 @@
<template>
<div class="relative z-50 text-black min-h-screen py-12">
<div class="container mx-auto px-4">
<div class="text-center mb-8">
<UiHeading tag="h1" size="500" class="font-bold text-primary mb-4"
>Контактные данные</UiHeading
>
</div>
<EmailForm />
</div>
</div>
</template>
<script setup>
import UiHeading from '~/components/Typography/UiHeading.vue'
// Страница использует компонент EmailForm
</script>

View File

@ -1,6 +1,6 @@
<template> <template>
<section class="feedback-section"> <section class="feedback-section">
<UiHeading tag="H2" size="300" class="text-three mb-4 pl-16 pr-24"> <UiHeading tag="h2" size="300" class="text-three mb-4 pl-16 pr-24">
Что говорят читатели Что говорят читатели
</UiHeading> </UiHeading>
@ -18,7 +18,9 @@
class="swiper-slide feedback-slide !w-[356px] !h-[325px]" class="swiper-slide feedback-slide !w-[356px] !h-[325px]"
> >
<div class="feedback-card !w-full !h-full"> <div class="feedback-card !w-full !h-full">
<UiParagraph size="250" class="card-text">{{ feedback.text }}</UiParagraph> <UiParagraph size="250" class="card-text">{{
feedback.text
}}</UiParagraph>
</div> </div>
</div> </div>
</div> </div>
@ -30,40 +32,40 @@
Это поможет другим и вдохновит на новые главы. <br /> Это поможет другим и вдохновит на новые главы. <br />
Спасибо! Спасибо!
</UiParagraph> </UiParagraph>
<UiButton @click="console.log(1)" class="mt-10" variants="secondary">Написать отзыв</UiButton> <UiButton class="mt-10" variants="secondary">Написать отзыв</UiButton>
</div> </div>
</section> </section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from 'vue' // Добавляем onMounted import { onMounted } from "vue";
import Swiper from 'swiper' import Swiper from "swiper";
import { Pagination, Autoplay } from 'swiper/modules' import { Pagination, Autoplay } from "swiper/modules";
import UiHeading from '@/components/Typography/UiHeading.vue' import UiHeading from "@/components/Typography/UiHeading.vue";
import UiParagraph from '@/components/Typography/UiParagraph.vue' import UiParagraph from "@/components/Typography/UiParagraph.vue";
import feedbackData from './_data/feedback.data' import feedbackData from "./_data/feedback.data";
import 'swiper/css' import "swiper/css";
import 'swiper/css/pagination' import "swiper/css/pagination";
import UiButton from '@/components/UiButton/UiButton.vue' import UiButton from "@/components/UiButton/UiButton.vue";
onMounted(() => { onMounted(() => {
new Swiper('.feedback-swiper', { new Swiper(".feedback-swiper", {
modules: [Pagination, Autoplay], modules: [Pagination, Autoplay],
direction: 'horizontal', direction: "horizontal",
loop: true, loop: true,
autoplay: { autoplay: {
delay: 3000, delay: 3000,
disableOnInteraction: false, disableOnInteraction: false,
}, },
slidesPerView: 'auto', slidesPerView: "auto",
spaceBetween: 72, spaceBetween: 72,
pagination: { pagination: {
el: '.swiper-pagination', el: ".swiper-pagination",
clickable: false, clickable: false,
}, },
}) });
}) });
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>

View File

@ -1,6 +1,6 @@
<template> <template>
<section> <section>
<UiHeading tag="H2" size="300" class="text-three"> <UiHeading tag="h1" size="300" class="text-three">
Книги для тебя, если ты не знаешь... Книги для тебя, если ты не знаешь...
</UiHeading> </UiHeading>
<ul class="flex mt-20 flex-row items-center justify-between"> <ul class="flex mt-20 flex-row items-center justify-between">
@ -19,36 +19,36 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import UiHeading from '@/components/Typography/UiHeading.vue' import UiHeading from "@/components/Typography/UiHeading.vue";
import UiParagraph from '@/components/Typography/UiParagraph.vue' import UiParagraph from "@/components/Typography/UiParagraph.vue";
import { reactive } from 'vue' import { reactive } from "vue";
const questions = reactive({ const questions = reactive({
data: [ data: [
{ {
img: '/img/svg/ellipse1.svg', img: "/img/svg/ellipse1.svg",
text: 'Что делать, если у тебя ноги не "от ушей" и ты далеко не Мисс Мира?', text: 'Что делать, если у тебя ноги не "от ушей" и ты далеко не Мисс Мира?',
}, },
{ {
img: '/img/svg/ellipse2.svg', img: "/img/svg/ellipse2.svg",
text: 'Как начать легко общаться с противоположным полом и о чем надо помолчать?', text: "Как начать легко общаться с противоположным полом и о чем надо помолчать?",
}, },
{ {
img: '/img/svg/ellipse3.svg', img: "/img/svg/ellipse3.svg",
text: 'Как относиться к сексу и на сколько важна девичья невинность?', text: "Как относиться к сексу и на сколько важна девичья невинность?",
}, },
{ {
img: '/img/svg/ellipse4.svg', img: "/img/svg/ellipse4.svg",
text: 'Сколько нужно заниматься, чтобы обрести НОВУЮ себя?', text: "Сколько нужно заниматься, чтобы обрести НОВУЮ себя?",
}, },
{ {
img: '/img/svg/ellipse5.svg', img: "/img/svg/ellipse5.svg",
text: 'Как выработать стратегию долгосрочных отношений?', text: "Как выработать стратегию долгосрочных отношений?",
}, },
{ {
img: '/img/svg/ellipse6.svg', img: "/img/svg/ellipse6.svg",
text: 'Как добиться того, чтоб парень делал так, как ты хочешь, но думал, что он сам так решил?', text: "Как добиться того, чтоб парень делал так, как ты хочешь, но думал, что он сам так решил?",
}, },
], ],
}) });
</script> </script>

View File

@ -1,12 +1,14 @@
import UiParagraph from '@/components/Typography/UiParagraph.vue'; import UiParagraph from '@/components/Typography/UiParagraph.vue';
<template> <template>
<div <div
v-for="({ topContent, button, botContent }, index) in content.data" v-for="({ topContent, button, botContent, path }, index) in content.data"
:key="index" :key="index"
class="flex flex-col items-center max-w-96" class="flex flex-col items-center max-w-96"
> >
<UiParagraph size="300" class="mb-12 h-32">{{ topContent }} </UiParagraph> <UiParagraph size="300" class="mb-12 h-32">{{ topContent }} </UiParagraph>
<UiButton variants="primary" class="mb-3 w-full">{{ button }} </UiButton> <UiButton tag="RouterLink" :to="path" variants="primary" class="mb-3 w-full"
>{{ button }}
</UiButton>
<UiParagraph as="span" size="200"> {{ botContent }}</UiParagraph> <UiParagraph as="span" size="200"> {{ botContent }}</UiParagraph>
</div> </div>
</template> </template>
@ -23,12 +25,14 @@ const content = reactive({
'💡 Узнай, как думает мужчина, что его действительно цепляет, и что делает женщину незабываемой.', '💡 Узнай, как думает мужчина, что его действительно цепляет, и что делает женщину незабываемой.',
button: 'КУПИТЬ КНИГУ I', button: 'КУПИТЬ КНИГУ I',
botContent: 'PDF + EPUB сразу после оплаты', botContent: 'PDF + EPUB сразу после оплаты',
path: '/books/1',
}, },
{ {
topContent: topContent:
'💡 Продолжение для тех, кто готов перейти от флирта к глубокому контакту. Как строить притяжение, не теряя себя.', '💡 Продолжение для тех, кто готов перейти от флирта к глубокому контакту. Как строить притяжение, не теряя себя.',
button: 'КУПИТЬ КНИГУ II', button: 'КУПИТЬ КНИГУ II',
botContent: 'PDF + EPUB сразу после оплаты', botContent: 'PDF + EPUB сразу после оплаты',
path: '/books/2',
}, },
], ],
}) })

View File

@ -1,12 +1,7 @@
<template> <template>
<section> <section>
<div class="relative z-50"> <div class="relative z-50">
<img <img src="/assets/img/webp/hero-banner-content.webp" alt="Книги" />
src="/src/assets/img/webp/hero-banner-content.webp"
alt="Книги"
width="100%"
height="100%"
/>
</div> </div>
<div class="flex flex-row justify-between pl-16 pr-24"> <div class="flex flex-row justify-between pl-16 pr-24">
<BuyContent /> <BuyContent />
@ -14,5 +9,5 @@
</section> </section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import BuyContent from './_ui/buyContent.vue' import BuyContent from "./_ui/buyContent.vue";
</script> </script>

View File

@ -0,0 +1,37 @@
<template>
<section class="flex flex-row">
<div
class="lg:w-5/12 pl-5 pr-5 shadow-md bg-[url('/assets/img/png/shadow.png')]"
>
<UiHeading tag="h2" size="300" class="text-three mb-4 mt-5">
💔 Ты не одна.
<br />Я знаю, через что ты<br />
проходишь.
</UiHeading>
<UiParagraph size="250" class="xl:max-w-[392px] mb-5"
>Когда ты снова и снова отдаёшь сердце, а в ответ тишина или игра, это
ранит. Я знаю это чувство. Я был по ту сторону: манипулировал,
очаровывал, уходил.
<br /><br />Я бывший Казанова. И однажды я понял: больше так нельзя.
<br /><br />Эти книги не теория. Это ключ к пониманию, как устроена
мужская психология, чего на самом деле хочет мужчина, и как перестать
теряться в отношениях.
<br /><br />Я написал их для тебя чтобы ты могла быть счастливой, не
прогибаясь, не умоляя, не теряя себя. Если ты устала «играть», если
хочешь любви по-настоящему начни с первой страницы. В этих книгах нет
воды. Только правда.</UiParagraph
>
</div>
<div class="lg:w-7/12">
<NuxtImg src="/img/webp/meetingAlone.webp" alt="meeting" loading="lazy" />
</div>
</section>
</template>
<script setup lang="ts">
import UiHeading from "@/components/Typography/UiHeading.vue";
import UiParagraph from "@/components/Typography/UiParagraph.vue";
</script>

31
pages/index/index.vue Normal file
View File

@ -0,0 +1,31 @@
<template>
<div>
<section class="relative z-50">
<HeroBanner
class="pt-28 bg-[url('/assets/img/png/bg.png')] z-40 bg-no-repeat bg-[400px] rounded-[50px] relative after:absolute after:bg-[url('/assets/img/webp/header-flowers-4.webp')] after:top-0 after:w-[418px] after:right-0 after:h-[230px] after:z-20 after:content-[''] after:bg-contain after:bg-no-repeat after:bg-right"
/>
<ForYouSection class="mt-40 pl-16 pr-24" />
<YouNotAloneSection class="mt-40 pl-16 pr-24 mb-40" />
<FeedbackSection class="mt-40 mb-52" />
</section>
</div>
</template>
<script setup lang="ts">
import FeedbackSection from "./_ui/feedbackSection/feedbackSection.vue";
import ForYouSection from "./_ui/forYouSection/forYouSection.vue";
import HeroBanner from "./_ui/heroBanner/heroBanner.vue";
import YouNotAloneSection from "./_ui/youNotAloneSection/youNotAloneSection.vue";
useHead({
title: "Vino Galante",
meta: [
{ name: "description", content: "Онлайн магазин книг автора Vino Galante" },
],
link: [
{
rel: "canonical",
href: `https://ebook.miduway.space/`,
},
],
});
</script>

104
pages/privacy/index.vue Normal file
View File

@ -0,0 +1,104 @@
<template>
<section class="relative z-50">
<UiHeading size="300" tag="H1" class="mb-5">
Политика конфиденциальности
</UiHeading>
<UiParagraph size="300" class="mb-5"
>Настоящая политика конфиденциальности описывает, как мы собираем,
используем и защищаем личную информацию, которую пользователи
предоставляют при использовании нашего сайта.</UiParagraph
>
<UiHeading size="300" tag="h2">1. Сбор информации</UiHeading>
<UiParagraph size="300"
>При оформлении заказа на сайте вы предоставляете персональные данные,
которые включают:</UiParagraph
>
<ul class="list-disc pl-5 mb-3">
<li><UiParagraph size="300">Адрес электронной почты</UiParagraph></li>
<li><UiParagraph size="300">Имя (по желанию)</UiParagraph></li>
</ul>
<UiHeading size="300" tag="h2">2. Использование информации</UiHeading>
<UiParagraph size="300" class="mb-3"
>Ваши личные данные используются исключительно для:</UiParagraph
>
<ul class="list-disc pl-5">
<li>
<UiParagraph size="300"
>Обработки и выполнения вашего заказа.</UiParagraph
>
</li>
<li>
<UiParagraph> Связи с вами по вопросам вашего заказа. </UiParagraph>
</li>
<li>
<UiParagraph size="300" class="mb-3">
Уведомления о важных обновлениях и новостях сайта (только если вы явно
согласились на рассылку).</UiParagraph
>
</li>
</ul>
<UiHeading size="300" tag="h2">3. Защита личных данных</UiHeading>
<UiParagraph size="300" class="mb-3">
Мы предпринимаем необходимые меры для защиты ваших личных данных от
несанкционированного доступа, изменения, разглашения или уничтожения.
Доступ к личным данным имеют только сотрудники, непосредственно
участвующие в обработке заказов.
</UiParagraph>
<UiHeading size="300" tag="h2"
>4. Передача информации третьим лицам</UiHeading
>
<UiParagraph size="300" class="mb-3">
Мы не передаём ваши личные данные третьим лицам, за исключением случаев,
предусмотренных законодательством Российской Федерации.
</UiParagraph>
<UiHeading size="300" tag="h2">5. Cookies (Куки-файлы)</UiHeading>
<UiParagraph size="300" class="mb-3">
Мы используем файлы cookie, чтобы улучшить ваш пользовательский опыт. Вы
можете отключить файлы cookie в настройках вашего браузера, однако это
может ограничить доступ к некоторым функциям сайта.
</UiParagraph>
<UiHeading size="300" tag="h2"
>6. Изменения в политике конфиденциальности</UiHeading
>
<UiParagraph size="300" class="mb-3">
Мы можем периодически обновлять настоящую политику конфиденциальности. Об
изменениях мы уведомим вас, разместив новую версию политики на данной
странице.
</UiParagraph>
<UiHeading size="300" tag="h2">7. Ваше согласие</UiHeading>
<UiParagraph size="300"
>Используя наш сайт, вы соглашаетесь с условиями данной политики
конфиденциальности.</UiParagraph
>
<UiParagraph size="300">
Если у вас возникнут вопросы или пожелания относительно обработки ваших
персональных данных, пожалуйста, свяжитесь с нами по электронной почте:
<a href="mailto:vinogalante@yandex.ru">vinogalante@yandex.ru</a>.
</UiParagraph>
</section>
</template>
<script setup lang="ts">
import UiHeading from "@/components/Typography/UiHeading.vue";
import UiParagraph from "@/components/Typography/UiParagraph.vue";
useHead({
title: "Поликита конфиденциальности | Vino Galante",
meta: [
{ name: "description", content: "Онлайн магазин книг автора Vino Galante" },
],
link: [
{
rel: "canonical",
href: `https://ebook.miduway.space/privacy`,
},
],
});
</script>

View File

@ -0,0 +1,46 @@
[
{
"question": "1. Как купить книгу?",
"answer": "Чтобы купить книгу, просто перейдите на страницу соответствующей книги и нажмите кнопку «Купить». Вы будете перенаправлены в корзину, где можно подтвердить заказ и выбрать способ оплаты."
},
{
"question": "2. Какие способы оплаты доступны?",
"answer": "На сайте доступны самые удобные и безопасные способы оплаты: банковские карты (Visa, Mastercard, Мир), Система Быстрых Платежей (СБП) и сервис SberPay. Вы можете выбрать любой из них при оформлении заказа."
},
{
"question": "3. Можно ли скачать книгу сразу после покупки?",
"answer": "Да, после подтверждения покупки, ссылка на скачивание книги будет доступна сразу. Вы получите файл на указанный при заказе email, либо сможете скачать книгу непосредственно с сайта."
},
{
"question": "4. Что делать, если я не могу скачать книгу?",
"answer": "Если у вас возникли проблемы с скачиванием книги, пожалуйста, свяжитесь с нами через контактный email vinogalante@yandex.ru, и мы постараемся решить ваш вопрос как можно скорее."
},
{
"question": "5. Есть ли пробный отрывок книги?",
"answer": "Да, на каждой странице книги есть возможность ознакомиться с аннотацией и скачать отрывок или читать его прямо на сайте. Это поможет вам оценить стиль и содержание книги перед покупкой."
},
{
"question": "6. Как читать книгу на мобильном устройстве?",
"answer": "Книги можно читать на мобильных устройствах в любом формате (например, PDF). Просто скачайте файл и откройте его с помощью подходящего приложения, например, Adobe Reader для PDF."
},
{
"question": "7. Можно ли вернуть книгу, если она не понравилась?",
"answer": "К сожалению, электронные книги не подлежат возврату, поскольку они могут быть скачаны сразу после покупки. Мы рекомендуем ознакомиться с отрывком книги перед покупкой, чтобы убедиться, что она соответствует вашим ожиданиям."
},
{
"question": "8. Как связаться с автором?",
"answer": "Вы можете связаться со мной по электронной почте vinogalante@yandex.ru. Я всегда рад услышать отзывы о книгах и ответить на вопросы."
},
{
"question": "9. Как узнать, когда выйдет следующая книга?",
"answer": "На данный момент автор не планирует продолжать писать книги по данной теме. Сейчас автор сосредоточен на других проектах."
},
{
"question": "10. Есть ли скидки на книги?",
"answer": "Да, при оплате двух книг сразу предоставляется скидка 10%. Просто добавьте обе книги в корзину, и скидка будет автоматически применена при оформлении заказа."
},
{
"question": "11. Могу ли я получить книгу в другом формате?",
"answer": "На данный момент книги доступны в формате PDF, ePub. Если вам нужен другой формат, пожалуйста, свяжитесь с нами, и мы постараемся помочь."
}
]

43
pages/question/index.vue Normal file
View File

@ -0,0 +1,43 @@
<template>
<section class="relative z-50 ml-4">
<div v-for="({ question, answer }, index) in questions" :key="index" class="mb-16">
<button
type="button"
@click="toggleFAQ(index)"
class="flex gap-12 mb-5 items-baseline font-bold"
>
<UiParagraph as="span" size="600">
{{ question }}
</UiParagraph>
<img
src="/assets/icon/arrow.svg"
alt="question"
class="duration-500"
:class="activeIndex === index ? 'rotate-90' : 'rotate-0'"
/>
</button>
<Transition name="slide">
<div v-show="activeIndex === index">
<UiParagraph size="300">
{{ answer }}
</UiParagraph>
</div>
</Transition>
</div>
</section>
</template>
<script setup lang="ts">
import UiParagraph from '~/components/Typography/UiParagraph.vue'
import questions from './_data/question-answer.json'
const activeIndex = ref<null | number>(null)
const toggleFAQ = (index: number) => {
activeIndex.value = activeIndex.value === index ? null : index
}
useHead({
title: 'Вопрос - ответ | Vino Galante',
})
</script>

5
postcss.config.mjs Normal file
View File

@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/img/books/book1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

BIN
public/img/books/book2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
public/img/books/image2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,4 +1,4 @@
<svg width="24" height="33" viewBox="0 0 24 33" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="26" viewBox="0 0 24 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.19223 17.578H4.30762V15.3809H5.19223C5.42685 15.3809 5.65185 15.4966 5.81775 15.7026C5.98365 15.9086 6.07685 16.1881 6.07685 16.4794C6.07685 16.7708 5.98365 17.0502 5.81775 17.2562C5.65185 17.4622 5.42685 17.578 5.19223 17.578ZM11.3845 21.9722V15.3809H12.2692C12.5038 15.3809 12.7288 15.4966 12.8947 15.7026C13.0606 15.9086 13.1538 16.1881 13.1538 16.4794V20.8737C13.1538 21.165 13.0606 21.4444 12.8947 21.6504C12.7288 21.8565 12.5038 21.9722 12.2692 21.9722H11.3845Z" fill="#C01F64"/> <path d="M5.19223 13.3786H4.30762V11.7148H5.19223C5.42685 11.7148 5.65185 11.8025 5.81775 11.9585C5.98365 12.1145 6.07685 12.3261 6.07685 12.5467C6.07685 12.7674 5.98365 12.979 5.81775 13.135C5.65185 13.291 5.42685 13.3786 5.19223 13.3786ZM11.3845 16.7062V11.7148H12.2692C12.5038 11.7148 12.7288 11.8025 12.8947 11.9585C13.0606 12.1145 13.1538 12.3261 13.1538 12.5467V15.8743C13.1538 16.0949 13.0606 16.3065 12.8947 16.4625C12.7288 16.6185 12.5038 16.7062 12.2692 16.7062H11.3845Z" fill="#C01F64"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.769531 3.29567C0.769531 2.42161 1.04913 1.58334 1.54682 0.965281C2.04452 0.347222 2.71953 0 3.42338 0L17.9435 0L23.7695 7.2351V29.6611C23.7695 30.5351 23.4899 31.3734 22.9922 31.9915C22.4945 32.6095 21.8195 32.9567 21.1157 32.9567H3.42338C2.71953 32.9567 2.04452 32.6095 1.54682 31.9915C1.04913 31.3734 0.769531 30.5351 0.769531 29.6611V3.29567ZM5.19261 13.1827H2.53876V24.1683H4.30799V19.774H5.19261C5.89645 19.774 6.57147 19.4268 7.06916 18.8088C7.56685 18.1907 7.84645 17.3524 7.84645 16.4784C7.84645 15.6043 7.56685 14.766 7.06916 14.148C6.57147 13.5299 5.89645 13.1827 5.19261 13.1827ZM12.2695 13.1827H9.61568V24.1683H12.2695C12.9734 24.1683 13.6484 23.8211 14.1461 23.203C14.6438 22.5849 14.9234 21.7467 14.9234 20.8726V16.4784C14.9234 15.6043 14.6438 14.766 14.1461 14.148C13.6484 13.5299 12.9734 13.1827 12.2695 13.1827ZM16.6926 24.1683V13.1827H22.0003V15.3798H18.4618V17.5769H20.2311V19.774H18.4618V24.1683H16.6926Z" fill="#C01F64"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.769531 2.56306C0.769531 1.90116 1.04913 1.26638 1.54682 0.798349C2.04452 0.330319 2.71953 0.0673828 3.42338 0.0673828L17.9435 0.0673828L23.7695 5.54622V22.5285C23.7695 23.1903 23.4899 23.8251 22.9922 24.2932C22.4945 24.7612 21.8195 25.0241 21.1157 25.0241H3.42338C2.71953 25.0241 2.04452 24.7612 1.54682 24.2932C1.04913 23.8251 0.769531 23.1903 0.769531 22.5285V2.56306ZM5.19261 10.0501H2.53876V18.369H4.30799V15.0414H5.19261C5.89645 15.0414 6.57147 14.7785 7.06916 14.3105C7.56685 13.8424 7.84645 13.2076 7.84645 12.5458C7.84645 11.8839 7.56685 11.2491 7.06916 10.781C6.57147 10.313 5.89645 10.0501 5.19261 10.0501ZM12.2695 10.0501H9.61568V18.369H12.2695C12.9734 18.369 13.6484 18.1061 14.1461 17.638C14.6438 17.17 14.9234 16.5352 14.9234 15.8733V12.5458C14.9234 11.8839 14.6438 11.2491 14.1461 10.781C13.6484 10.313 12.9734 10.0501 12.2695 10.0501ZM16.6926 18.369V10.0501H22.0003V11.7139H18.4618V13.3776H20.2311V15.0414H18.4618V18.369H16.6926Z" fill="#C01F64"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,3 +1,3 @@
<svg width="24" height="31" viewBox="0 0 24 31" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.862 1.01361C10.4118 0.54615 9.85883 0.301357 9.29362 0.319278H3.26314C2.93561 0.31897 2.61125 0.406925 2.30861 0.578112C2.00597 0.749299 1.73099 1.00036 1.49939 1.31693C1.26779 1.63351 1.08412 2.00939 0.958888 2.42307C0.833653 2.83675 0.769307 3.28013 0.769532 3.72783V27.23C0.770437 28.1336 1.03345 28.9999 1.50089 29.6389C1.96834 30.2778 2.60207 30.6373 3.26314 30.6386H21.2759C21.937 30.6373 22.5707 30.2778 23.0382 29.6389C23.5056 28.9999 23.7686 28.1336 23.7695 27.23V8.20711C23.7686 7.30348 23.5056 6.43722 23.0382 5.79826C22.5707 5.1593 21.937 4.79979 21.2759 4.79856H12.119L11.7735 2.91193C11.6367 2.15634 11.3151 1.48504 10.862 1.01361ZM17.161 16.801C16.4806 16.801 15.828 16.4315 15.3469 15.7739C14.8658 15.1162 14.5955 14.2243 14.5955 13.2942C14.5955 12.3642 14.8658 11.4722 15.3469 10.8146C15.828 10.1569 16.4806 9.78748 17.161 9.78748C17.8414 9.78748 18.4939 10.1569 18.975 10.8146C19.4561 11.4722 19.7264 12.3642 19.7264 13.2942C19.7264 14.2243 19.4561 15.1162 18.975 15.7739C18.4939 16.4315 17.8414 16.801 17.161 16.801ZM9.81013 15.9804L19.1586 27.7163H3.26314C3.16906 27.7151 3.0791 27.6634 3.01258 27.5725C2.94605 27.4816 2.90829 27.3586 2.9074 27.23V21.3925L8.52399 15.9804C8.7056 15.778 8.93274 15.6674 9.16706 15.6674C9.40139 15.6674 9.62853 15.778 9.81013 15.9804Z" fill="#C01F64"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M10.862 1.09623C10.4118 0.726207 9.85883 0.532441 9.29362 0.546625H3.26314C2.93561 0.546382 2.61125 0.616003 2.30861 0.751506C2.00597 0.88701 1.73099 1.08574 1.49939 1.33632C1.26779 1.58691 1.08412 1.88443 0.958888 2.21189C0.833653 2.53934 0.769307 2.89029 0.769532 3.24467V21.8479C0.770437 22.5631 1.03345 23.2488 1.50089 23.7546C1.96834 24.2603 2.60207 24.5449 3.26314 24.5459H21.2759C21.937 24.5449 22.5707 24.2603 23.0382 23.7546C23.5056 23.2488 23.7686 22.5631 23.7695 21.8479V6.79025C23.7686 6.07498 23.5056 5.38929 23.0382 4.88352C22.5707 4.37775 21.937 4.09318 21.2759 4.0922H12.119L11.7735 2.59884C11.6367 2.00076 11.3151 1.46938 10.862 1.09623ZM17.161 13.5927C16.4806 13.5927 15.828 13.3003 15.3469 12.7797C14.8658 12.2592 14.5955 11.5531 14.5955 10.817C14.5955 10.0808 14.8658 9.37475 15.3469 8.8542C15.828 8.33364 16.4806 8.04119 17.161 8.04119C17.8414 8.04119 18.4939 8.33364 18.975 8.8542C19.4561 9.37475 19.7264 10.0808 19.7264 10.817C19.7264 11.5531 19.4561 12.2592 18.975 12.7797C18.4939 13.3003 17.8414 13.5927 17.161 13.5927ZM9.81013 12.9432L19.1586 22.2328H3.26314C3.16906 22.2318 3.0791 22.1909 3.01258 22.119C2.94605 22.047 2.90829 21.9496 2.9074 21.8479V17.2271L8.52399 12.9432C8.7056 12.783 8.93274 12.6955 9.16706 12.6955C9.40139 12.6955 9.62853 12.783 9.81013 12.9432Z" fill="#C01F64"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,12 +1,12 @@
<svg width="31" height="28" viewBox="0 0 31 28" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="23" viewBox="0 0 24 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.3585 3.16504H7.69186C6.1457 3.16504 3.8457 4.05678 3.8457 6.51325V22.1383C3.8457 24.5947 6.1457 25.4865 7.69186 25.4865H26.9226V23.2543H7.70724C7.11493 23.2409 6.40981 23.0378 6.40981 22.1383C6.40981 22.0255 6.42134 21.9251 6.44058 21.8336C6.58416 21.1907 7.18929 21.0333 7.70724 21.0222H26.9226V5.39718C26.9226 4.80518 26.6525 4.23743 26.1716 3.81882C25.6908 3.40021 25.0386 3.16504 24.3585 3.16504ZM24.3585 13.2097L21.7944 12.0936L19.2303 13.2097V5.39718H24.3585V13.2097Z" fill="url(#paint0_linear_868_364)"/> <path d="M21.3585 0.232422H4.69186C3.1457 0.232422 0.845703 1.12416 0.845703 3.58064V19.2056C0.845703 21.6621 3.1457 22.5539 4.69186 22.5539H23.9226V20.3217H4.70724C4.11493 20.3083 3.40981 20.1052 3.40981 19.2056C3.40981 19.0929 3.42134 18.9925 3.44058 18.9009C3.58416 18.2581 4.18929 18.1007 4.70724 18.0896H23.9226V2.46456C23.9226 1.87256 23.6525 1.30481 23.1716 0.886201C22.6908 0.467593 22.0386 0.232422 21.3585 0.232422ZM21.3585 10.2771L18.7944 9.16099L16.2303 10.2771V2.46456H21.3585V10.2771Z" fill="url(#paint0_linear_868_365)"/>
<path d="M24.3585 3.16504H7.69186C6.1457 3.16504 3.8457 4.05678 3.8457 6.51325V22.1383C3.8457 24.5947 6.1457 25.4865 7.69186 25.4865H26.9226V23.2543H7.70724C7.11493 23.2409 6.40981 23.0378 6.40981 22.1383C6.40981 22.0255 6.42134 21.9251 6.44058 21.8336C6.58416 21.1907 7.18929 21.0333 7.70724 21.0222H26.9226V5.39718C26.9226 4.80518 26.6525 4.23743 26.1716 3.81882C25.6908 3.40021 25.0386 3.16504 24.3585 3.16504ZM24.3585 13.2097L21.7944 12.0936L19.2303 13.2097V5.39718H24.3585V13.2097Z" fill="url(#paint1_radial_868_364)" fill-opacity="0.2"/> <path d="M21.3585 0.232422H4.69186C3.1457 0.232422 0.845703 1.12416 0.845703 3.58064V19.2056C0.845703 21.6621 3.1457 22.5539 4.69186 22.5539H23.9226V20.3217H4.70724C4.11493 20.3083 3.40981 20.1052 3.40981 19.2056C3.40981 19.0929 3.42134 18.9925 3.44058 18.9009C3.58416 18.2581 4.18929 18.1007 4.70724 18.0896H23.9226V2.46456C23.9226 1.87256 23.6525 1.30481 23.1716 0.886201C22.6908 0.467593 22.0386 0.232422 21.3585 0.232422ZM21.3585 10.2771L18.7944 9.16099L16.2303 10.2771V2.46456H21.3585V10.2771Z" fill="url(#paint1_radial_868_365)" fill-opacity="0.2"/>
<defs> <defs>
<linearGradient id="paint0_linear_868_364" x1="15.3842" y1="34.2754" x2="15.3842" y2="-25.1728" gradientUnits="userSpaceOnUse"> <linearGradient id="paint0_linear_868_365" x1="12.3842" y1="31.3428" x2="12.3842" y2="-28.1054" gradientUnits="userSpaceOnUse">
<stop stop-color="#E20C66"/> <stop stop-color="#E20C66"/>
<stop offset="1" stop-color="#E20C66" stop-opacity="0"/> <stop offset="1" stop-color="#E20C66" stop-opacity="0"/>
</linearGradient> </linearGradient>
<radialGradient id="paint1_radial_868_364" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(15.3842 14.3258) rotate(50.5894) scale(14.4454 118.907)"> <radialGradient id="paint1_radial_868_365" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12.3842 11.3931) rotate(50.5894) scale(14.4454 118.907)">
<stop stop-color="#EE70A4"/> <stop stop-color="#EE70A4"/>
<stop offset="1" stop-color="#FF33AD"/> <stop offset="1" stop-color="#FF33AD"/>
</radialGradient> </radialGradient>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,29 @@
import { readFile } from 'node:fs/promises'
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')
if (!slug) {
throw createError({
statusCode: 400,
statusMessage: 'Slug is required'
})
}
try {
// Читаем markdown файл из папки content/books
// slug теперь представляет номер главы (1, 2, 3, etc.)
const content = await readFile(`content/books/${slug}.md`, 'utf-8')
return {
content,
slug,
chapter: slug
}
} catch (error) {
throw createError({
statusCode: 404,
statusMessage: 'Глава не найдена'
})
}
})

3
server/tsconfig.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

View File

@ -1,10 +0,0 @@
<template>
<UiLayout>
<RouterView />
</UiLayout>
</template>
<script setup lang="ts">
import UiLayout from './layout/UiLayout.vue'
import { RouterView } from 'vue-router'
</script>

View File

@ -1,13 +0,0 @@
export const colorVariants = {
primary: ['bg-accent-50', 'text-primary', 'hover:bg-accent-100', 'active:bg-accent-150'],
secondary: [
'bg-transparent',
'text-primary',
'hover:bg-accent-50',
'border-accent-50',
'border',
'hover:bg-accent-100',
'active:bg-accent-150',
'cursor-pointer',
],
}

View File

@ -1,9 +0,0 @@
import { createRouter, createWebHistory } from 'vue-router'
import routes from './routes'
const router = createRouter({
history: createWebHistory(import.meta.env.VITE_URL),
routes,
})
export default router

View File

@ -1,43 +0,0 @@
import type { Component } from 'vue'
interface ChlidrenRoute {
path: string
name: string
component: () => Promise<Component>
props?: boolean
}
interface Route {
path: string
name: string
component: () => Promise<Component>
props?: boolean
children?: ChlidrenRoute[]
}
const routes: Route[] = [
{
path: '/',
name: 'home',
component: () => import('@/pages/mainPage/indexPage.vue'),
},
{
path: '/books/:slug',
name: 'book-detail',
component: () => import('@/pages/books/_slug/indexBookPage.vue'),
props: true,
children: [
{
path: ':titlesSlug',
name: 'book-titles',
component: () => import('@/pages/books/_slug/_titlesSlug/indexTitlesPage.vue'),
},
],
},
{
path: '/privacy',
name: 'privacy',
component: () => import('@/pages/privacy/privacyPage.vue'),
},
]
export default routes

View File

@ -1,3 +0,0 @@
export { default as footerNavigation } from '@/layout/UiFooter/_data/footer-navigation.data'
export { default as refLitres } from '@/layout/UiFooter/_data/ref-litres.data'

View File

@ -1 +0,0 @@
export { default as headerNavigation } from '@/layout/UiHeader/_data/header-navigation.data'

View File

@ -1,16 +0,0 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createHead } from '@vueuse/head'
import './style.css'
import App from './App.vue'
import router from '@/config/router/index'
const app = createApp(App)
const head = createHead()
app.use(head)
app.use(createPinia())
app.use(router)
app.mount('#app')

View File

@ -1,32 +0,0 @@
<template>
<section class="flex flex-row">
<div class="lg:w-5/12 pl-5 pr-5 shadow-md bg-[url('/src/assets/img/png/shadow.png')]">
<UiHeading tag="H2" size="300" class="text-three mb-4 mt-5">
💔 Ты не одна.
<br />Я знаю, через что ты<br />
проходишь.
</UiHeading>
<UiParagraph size="250" class="xl:max-w-[392px] mb-5"
>Когда ты снова и снова отдаёшь сердце, а в ответ тишина или игра, это ранит. Я знаю это
чувство. Я был по ту сторону: манипулировал, очаровывал, уходил.
<br /><br />Я бывший Казанова. И однажды я понял: больше так нельзя.
<br /><br />Эти книги не теория. Это ключ к пониманию, как устроена мужская психология,
чего на самом деле хочет мужчина, и как перестать теряться в отношениях.
<br /><br />Я написал их для тебя чтобы ты могла быть счастливой, не прогибаясь, не
умоляя, не теряя себя. Если ты устала «играть», если хочешь любви по-настоящему начни с
первой страницы. В этих книгах нет воды. Только правда.</UiParagraph
>
</div>
<div class="lg:w-7/12">
<img alt="meeting" width="100%" height="100%" src="/img/webp/meetingAlone.webp" />
</div>
</section>
</template>
<script setup lang="ts">
import UiHeading from '@/components/Typography/UiHeading.vue'
import UiParagraph from '@/components/Typography/UiParagraph.vue'
</script>

View File

@ -1,31 +0,0 @@
<template>
<div>
<section class="relative z-50">
<HeroBanner
class="pt-28 bg-[url('/src/assets/img/png/bg.png')] z-40 bg-no-repeat bg-[400px] rounded-[50px] relative after:absolute after:bg-[url('/src/assets/img/webp/header-flowers-4.webp')] after:top-0 after:w-[418px] after:right-0 after:h-[230px] after:z-20 after:content-[''] after:bg-contain after:bg-no-repeat after:bg-right"
/>
<ForYouSection class="mt-40 pl-16 pr-24" />
<YouNotAloneSection class="mt-40 pl-16 pr-24 mb-40" />
<FeedbackSection class="mt-40 mb-52" />
</section>
</div>
</template>
<script setup lang="ts">
import FeedbackSection from './_ui/feedbackSection/feedbackSection.vue'
import ForYouSection from './_ui/forYouSection/forYouSection.vue'
import HeroBanner from './_ui/heroBanner/heroBanner.vue'
import YouNotAloneSection from './_ui/youNotAloneSection/youNotAloneSection.vue'
import { useHead } from '@vueuse/head'
useHead({
title: 'Vino Galante',
meta: [{ name: 'description', content: 'Онлайн магазин книг автора Vino Galante' }],
link: [
{
rel: 'canonical',
href: `https://ebook.miduway.space/`,
},
],
})
</script>

View File

@ -1,87 +0,0 @@
<template>
<section>
<UiHeading size="300" tag="H1" class="mb-5"> Политика конфиденциальности </UiHeading>
<UiParagraph size="300"
>Настоящая политика конфиденциальности описывает, как мы собираем, используем и защищаем
личную информацию, которую пользователи предоставляют при использовании нашего
сайта.</UiParagraph
>
<UiHeading size="300" tag="H2">1. Сбор информации</UiHeading>
<UiParagraph size="300"
>При оформлении заказа на сайте вы предоставляете персональные данные, которые
включают:</UiParagraph
>
<ul class="list-disc pl-5">
<li><UiParagraph size="300">Адрес электронной почты</UiParagraph></li>
<li><UiParagraph size="300">Имя (по желанию)</UiParagraph></li>
</ul>
<UiHeading size="300" tag="H2">2. Использование информации</UiHeading>
<UiParagraph size="300">Ваши личные данные используются исключительно для:</UiParagraph>
<ul class="list-disc pl-5">
<li><UiParagraph size="300">Обработки и выполнения вашего заказа.</UiParagraph></li>
<li><UiParagraph> Связи с вами по вопросам вашего заказа. </UiParagraph></li>
<li>
<UiParagraph size="300">
Уведомления о важных обновлениях и новостях сайта (только если вы явно согласились на
рассылку).</UiParagraph
>
</li>
</ul>
<UiHeading size="300" tag="H2">3. Защита личных данных</UiHeading>
<UiParagraph size="300">
Мы предпринимаем необходимые меры для защиты ваших личных данных от несанкционированного
доступа, изменения, разглашения или уничтожения. Доступ к личным данным имеют только
сотрудники, непосредственно участвующие в обработке заказов.
</UiParagraph>
<UiHeading size="300" tag="H2">4. Передача информации третьим лицам</UiHeading>
<UiParagraph size="300">
Мы не передаём ваши личные данные третьим лицам, за исключением случаев, предусмотренных
законодательством Российской Федерации.
</UiParagraph>
<UiHeading size="300" tag="H2">5. Cookies (Куки-файлы)</UiHeading>
<UiParagraph size="300">
Мы используем файлы cookie, чтобы улучшить ваш пользовательский опыт. Вы можете отключить
файлы cookie в настройках вашего браузера, однако это может ограничить доступ к некоторым
функциям сайта.
</UiParagraph>
<UiHeading size="300" tag="H2">6. Изменения в политике конфиденциальности</UiHeading>
<UiParagraph size="300">
Мы можем периодически обновлять настоящую политику конфиденциальности. Об изменениях мы
уведомим вас, разместив новую версию политики на данной странице.
</UiParagraph>
<UiHeading size="300" tag="H2">7. Ваше согласие</UiHeading>
<UiParagraph size="300"
>Используя наш сайт, вы соглашаетесь с условиями данной политики
конфиденциальности.</UiParagraph
>
<UiParagraph size="300">
Если у вас возникнут вопросы или пожелания относительно обработки ваших персональных данных,
пожалуйста, свяжитесь с нами по электронной почте:
<a href="mailto:vinogalante@yandex.ru">vinogalante@yandex.ru</a>.
</UiParagraph>
</section>
</template>
<script setup lang="ts">
import UiHeading from '@/components/Typography/UiHeading.vue'
import UiParagraph from '@/components/Typography/UiParagraph.vue'
import { useHead } from '@vueuse/head'
useHead({
title: 'Поликита конфиденциальности | Vino Galante',
meta: [{ name: 'description', content: 'Онлайн магазин книг автора Vino Galante' }],
link: [
{
rel: 'canonical',
href: `https://ebook.miduway.space/privacy`,
},
],
})
</script>

1
src/vite-env.d.ts vendored
View File

@ -1 +0,0 @@
/// <reference types="vite/client" />

58
store/useSelectedBook.ts Normal file
View File

@ -0,0 +1,58 @@
import { defineStore } from 'pinia'
interface CartItem {
id: number
quantity: number
summary?: number
}
export const useSelectedBook = defineStore('selectedBook', {
state: () => ({
cartQuantities: [] as CartItem[],
}),
actions: {
increment(id: number) {
const item = this.cartQuantities.find((i) => i.id === id)
if (item && item.quantity < 1) {
item.quantity++
}
},
decrement(id: number) {
const item = this.cartQuantities.find((i) => i.id === id)
if (item) {
item.quantity--
}
},
reset(id: number) {
const idx = this.cartQuantities.findIndex((i) => i.id === id)
if (idx !== -1) {
this.cartQuantities.splice(idx, 1)
}
},
addToCart(id: number) {
if (!this.cartQuantities.find((i) => i.id === id)) {
this.cartQuantities.push({ id, quantity: 1 })
}
},
getQuantity(id: number) {
return this.cartQuantities.find((i) => i.id === id)?.quantity || 0
},
},
getters: {
getTotalPrice: (state) => (books: { id: number; price: number }[]) => {
const selected = state.cartQuantities.filter((i) => i.quantity > 0)
if (selected.length === 2) {
// Проверяем, что это две разные книги
const ids = selected.map((i) => i.id)
if (ids[0] !== ids[1]) {
return 936
}
}
// Обычная сумма
return selected.reduce((sum, item) => {
const book = books.find((b) => b.id === item.id)
return sum + (book ? book.price * item.quantity : 0)
}, 0)
},
},
})

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