3 Commits

Author SHA1 Message Date
98b9670661 Merge pull request 'production' (#6) from production into develop
All checks were successful
Deploy Application / deploy (push) Successful in 5s
Reviewed-on: #6
2025-06-15 15:22:25 +00:00
1353b0e65a Merge pull request 'feat books pages' (#5) from production into develop
All checks were successful
Deploy Application / deploy (push) Successful in 5s
Reviewed-on: #5
2025-06-14 14:16:07 +00:00
2e3de70f56 Merge pull request 'production' (#4) from production into develop
All checks were successful
Deploy Application / deploy (push) Successful in 5s
Reviewed-on: #4
2025-06-14 08:20:01 +00:00
77 changed files with 1750 additions and 15216 deletions

View File

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

9
.gitignore vendored
View File

@ -11,14 +11,6 @@ node_modules
dist
dist-ssr
*.local
.nuxt
.output
.data
.nuxt
.nitro
.cache
dist
# Editor directories and files
.vscode/*
@ -31,4 +23,5 @@ dist
*.sln
*.sw?
frontend.env

2
.husky/pre-commit Normal file
View File

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

7
.prettierrc.json Normal file
View File

@ -0,0 +1,7 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}

3
.vscode/extensions.json vendored Normal file
View File

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

View File

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

15
app.vue
View File

@ -1,15 +0,0 @@
<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,42 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--color-primary: rgba(255, 255, 255, 1);
--color-secondary: rgba(229, 30, 125, 1);
--color-three: rgba(245, 75, 126, 1);
--color-accent-50: #f54378;
--color-accent-100: #e51e7d;
--color-accent-150: #c4035f;
--color-black-50: #0c1018;
/* system colors */
--color-error: oklch(57.7% 0.245 27.325);
--color-warning: oklch(76.9% 0.188 70.08);
/* disabled */
--color-text-disabled: oklch(55.1% 0.027 264.364);
--color-bg-disabled: oklch(92.8% 0.006 264.531);
}
/* Кастомные классы шрифтов */
.lato-thin { font-family: 'Lato', sans-serif; font-weight: 100; font-style: normal; }
.lato-light { font-family: 'Lato', sans-serif; font-weight: 300; font-style: normal; }
.lato-regular { font-family: 'Lato', sans-serif; font-weight: 400; font-style: normal; }
.lato-bold { font-family: 'Lato', sans-serif; font-weight: 700; font-style: normal; }
.lato-black { font-family: 'Lato', sans-serif; font-weight: 900; font-style: normal; }
.lato-thin-italic { font-family: 'Lato', sans-serif; font-weight: 100; font-style: italic; }
.lato-light-italic { font-family: 'Lato', sans-serif; font-weight: 300; font-style: italic; }
.lato-regular-italic { font-family: 'Lato', sans-serif; font-weight: 400; font-style: italic; }
.lato-bold-italic { font-family: 'Lato', sans-serif; font-weight: 700; font-style: italic; }
.lato-black-italic { font-family: 'Lato', sans-serif; font-weight: 900; font-style: italic; }
.russo-one-regular {
font-family: 'Russo One', sans-serif;
font-weight: 700;
font-style: normal;
}

View File

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

View File

@ -1,12 +0,0 @@
export default [
"@nuxt/eslint",
"@nuxt/icon",
"@nuxt/image",
// '@nuxt/content',
// '@nuxt/fonts',
"@pinia/nuxt",
"@nuxtjs/tailwindcss",
"@pinia/nuxt",
// '@nuxtjs/robots',
// '@nuxtjs/sitemap',
];

View File

@ -1,12 +0,0 @@
// ./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' },
],
}

21
index.html Normal file
View File

@ -0,0 +1,21 @@
<!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

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

View File

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

View File

@ -1,25 +0,0 @@
import config from "./config";
import { fileURLToPath, URL } from "node:url";
// 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,
});

15740
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,43 @@
{
"name": "e-book",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview",
"prepare": "husky",
"lint": "node_modules/.bin/eslint . --fix",
"format": "node_modules/.bin/prettier --write ./"
"format": "node_modules/.bin/prettier --write ./src/"
},
"dependencies": {
"@nuxt/content": "^3.6.0",
"@nuxt/fonts": "^0.11.4",
"@nuxt/icon": "^1.13.0",
"@nuxt/image": "^1.10.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",
"husky": "^9.1.7",
"nuxt": "^3.17.5",
"nuxt-schema-org": "^5.0.5",
"@tailwindcss/vite": "^4.1.8",
"@vueuse/head": "^2.0.0",
"pinia": "^3.0.3",
"swiper": "^11.2.8",
"vue": "^3.5.16"
"tailwindcss": "^4.1.8",
"vue": "^3.5.13",
"vue-router": "4"
},
"devDependencies": {
"@nuxt/eslint": "^1.4.1",
"@nuxt/test-utils": "^3.11.3",
"@nuxtjs/tailwindcss": "^6.11.4",
"@types/node": "^22.0.0",
"@tsconfig/node22": "^22.0.1",
"@types/node": "^22.14.0",
"@vitejs/plugin-vue": "^5.2.3",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0",
"autoprefixer": "^10.4.18",
"eslint": "^9.29.0",
"eslint-plugin-vue": "^10.0.0",
"postcss": "^8.5.6",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.22.0",
"eslint-plugin-vue": "~10.0.0",
"husky": "^9.1.7",
"jiti": "^2.4.2",
"npm-run-all2": "^7.0.2",
"playwright": "^1.52.0",
"prettier": "3.5.3",
"tailwindcss": "^3.4.17",
"typescript": "~5.8.3",
"vite": "^6.3.5",
"vite-plugin-vue-devtools": "^7.7.2",
"vue-tsc": "^2.2.8"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@ -1,31 +0,0 @@
<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>

View File

@ -1,104 +0,0 @@
<template>
<section>
<UiHeading size="300" tag="H1" class="mb-5">
Политика конфиденциальности
</UiHeading>
<UiParagraph size="300" class="mb-3"
>Настоящая политика конфиденциальности описывает, как мы собираем,
используем и защищаем личную информацию, которую пользователи
предоставляют при использовании нашего сайта.</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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

10
src/App.vue Normal file
View File

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

View File

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 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

View File

@ -11,7 +11,7 @@
<script setup lang="ts">
import { toRefs, computed } from 'vue'
import { colorVariants } from './UiButton.params.js'
import { colorVariants } from './UiButton.params.ts'
const props = defineProps({
tag: {

View File

@ -0,0 +1,9 @@
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

@ -0,0 +1,43 @@
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

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
<template>
<div
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/webp/footer-flowers.webp)] before:bg-no-repeat before:bg-bottom before:bg-contain before:z-40"
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="
route.path === '/books/1' || route.path === '/books/2'
? 'bg-[linear-gradient(135deg,rgba(17,17,30,1)_10%,rgba(183,32,76,1)_170%)]'
@ -9,15 +9,15 @@
>
<div
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(/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(/src/assets/img/webp/gradient.webp)] after:bg-no-repeat after:bg-[right_top] after:bg-contain"
/>
<div
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(/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(/src/assets/img/webp/group-flowers.webp)] after:bg-no-repeat after:bg-[right_top] after:bg-cover"
/>
<div
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(/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(/src/assets/img/webp/hero-flowers.webp)] before:bg-no-repeat before:bg-left before:bg-contain"
/>
<UiHeader class="relative z-50" />
<UiMain class="!min-h-[1200px] text-primary relative">

16
src/main.ts Normal file
View File

@ -0,0 +1,16 @@
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,20 +1,12 @@
<template>
<div v-if="titles" class="relative z-50 min-h-screen text-white mb-[208px]">
<section class="flex flex-col relative z-40 mt-40 ml-18">
<UiHeading
tag="H1"
class="whitespace-pre-line [&]:font-normal mb-10 -ml-5"
size="500"
>
<UiHeading tag="H1" class="whitespace-pre-line [&]:font-normal mb-10 -ml-5" size="500">
{{ titles.title }}
</UiHeading>
<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 -->
<UiHeading tag="H2" size="300" class="text-three [&]:font-normal">
{{ section.title }}
@ -43,27 +35,14 @@
</UiHeading>
</div>
<!-- Regular subsection -->
<UiHeading
v-else
tag="H3"
size="300"
class="[&]:text-gray-200 [&]:font-normal"
>
<UiHeading v-else tag="H3" size="300" class="[&]:text-gray-200 [&]:font-normal">
{{ subsection.title }}
</UiHeading>
<!-- Items list -->
<ul
v-if="subsection.items"
class="ml-6 flex flex-col gap-2 list-decimal"
>
<li
v-for="(item, itemIndex) in subsection.items"
:key="itemIndex"
>
<UiParagraph
size="300"
class="[&]:text-gray-200 [&]:font-normal"
<ul v-if="subsection.items" class="ml-6 flex flex-col gap-2 list-decimal">
<li v-for="(item, itemIndex) in subsection.items" :key="itemIndex">
<UiParagraph size="300" class="[&]:text-gray-200 [&]:font-normal"
>&nbsp;{{ item }}</UiParagraph
>
</li>
@ -71,15 +50,8 @@
<!-- Nested subsections -->
<div v-if="subsection.subsections" class="flex flex-col gap-2">
<div
v-for="(nestedSub, nestedIndex) in subsection.subsections"
:key="nestedIndex"
>
<UiHeading
tag="H4"
size="300"
class="[&]:text-gray-200 [&]:font-normal"
>
<div v-for="(nestedSub, nestedIndex) in subsection.subsections" :key="nestedIndex">
<UiHeading tag="H4" size="300" class="[&]:text-gray-200 [&]:font-normal">
{{ nestedSub.title }}
</UiHeading>
</div>
@ -94,61 +66,63 @@
</template>
<script setup lang="ts">
import { ref, computed, watch } from "vue";
import { useRoute } from "vue-router";
import UiHeading from "@/components/Typography/UiHeading.vue";
import UiParagraph from "@/components/Typography/UiParagraph.vue";
// import { useHead } from '@vueuse/head'
import { ref, computed, watch } from 'vue'
import { useRoute } from 'vue-router'
import UiHeading from '@/components/Typography/UiHeading.vue'
import UiParagraph from '@/components/Typography/UiParagraph.vue'
import { useHead } from '@vueuse/head'
interface SubsectionTitle {
text: string;
img?: string;
text: string
img?: string
}
interface Subsection {
title: string | SubsectionTitle;
items?: string[];
title: string | SubsectionTitle
items?: string[]
subsections?: Array<{
title: string;
}>;
title: string
}>
}
interface Section {
title: string;
subsections?: Subsection[];
title: string
subsections?: Subsection[]
}
interface TitlesData {
title: string;
titleMeta: string;
sections: Section[];
title: string
titleMeta: string
sections: Section[]
}
const route = useRoute();
const route = useRoute()
const currentTitlesData = ref<TitlesData | null>(null);
const currentTitlesData = ref<TitlesData | null>(null)
const titles = computed(() => currentTitlesData.value);
const titles = computed(() => currentTitlesData.value)
const loadTitlesData = async (slug: string) => {
try {
const module = await import(`./_data/${slug}.json`);
currentTitlesData.value = module.default as TitlesData;
const module = await import(`./_data/${slug}.json`)
currentTitlesData.value = module.default as TitlesData
} catch (error) {
console.error(`Ошибка при загрузке содержания с slug '${slug}':`, error);
currentTitlesData.value = null;
console.error(`Ошибка при загрузке содержания с slug '${slug}':`, error)
currentTitlesData.value = null
}
}
};
watch(
() => route.params.titlesSlug,
async (newSlug) => {
console.log('Route params changed:', route.params)
console.log('New titlesSlug:', newSlug)
if (newSlug) {
await loadTitlesData(newSlug as string);
await loadTitlesData(newSlug as string)
}
},
{ immediate: true }
);
{ immediate: true },
)
watch(titles, (newTitles) => {
if (newTitles) {
@ -156,17 +130,17 @@ watch(titles, (newTitles) => {
title: `${newTitles.titleMeta} | Vino Galante`,
meta: [
{
name: "description",
content: "Содержание книги Vino Galante",
name: 'description',
content: 'Содержание книги Vino Galante',
},
],
link: [
{
rel: "canonical",
rel: 'canonical',
href: `https://ebook.miduway.space/books/${route.params.slug}/${route.params.titlesSlug}`,
},
],
});
})
}
});
})
</script>

View File

@ -4,28 +4,19 @@
<template v-if="!route.params.titlesSlug">
<!--верхний блок-->
<section
class="flex 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"
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"
>
<!--левый блок контента-->
<section class="relative top-[-20px] min-w-[570px]">
<div class="flex flex-col items-center">
<img
:src="book.img"
:alt="book.buttonText"
width="100%"
height="100%"
/>
<img :src="book.img" :alt="book.buttonText" width="100%" height="100%" />
</div>
</section>
<!--правый блок контента-->
<section>
<div class="w-11/12 h-full flex flex-col justify-start">
<div>
<UiHeading
tag="H1"
class="whitespace-pre-line [&]:font-bold"
size="300"
>
<UiHeading tag="H1" class="whitespace-pre-line [&]:font-bold" size="300">
{{ book.title }}
</UiHeading>
<UiParagraph class="mb-10" size="250">
@ -42,13 +33,11 @@
</section>
</section>
<!--средний блок-->
<section class="flex flex-row items-center ml-20 justify-between">
<section class="flex flex-row items-center ml-18 justify-between">
<!--левый-->
<div class="flex flex-col items-center min-h-[310px]">
<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" />
</div>
<div class="mr-10 flex items-center flex-col gap-3">
@ -64,9 +53,7 @@
<div class="min-h-[310px]">
<!--о книге-->
<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">
<img
src="/img/svg/books/book-pages.svg"
@ -105,77 +92,50 @@
<!--навигация по книге-->
<div class="mt-24">
<ul
class="flex flex-row mr-32 items-end justify-between lg:whitespace-nowrap"
>
<li class="flex flex-row items-center">
<NuxtLink
to="#"
class="flex flex-col items-center cursor-pointer"
>
<div class="w-[62px] h-[58px]">
<img
src="/img/svg/books/read.svg"
alt="Читай отрывок"
width="62"
height="53"
/>
</div>
<UiParagraph size="250" as="span">
Читай отрывок
</UiParagraph>
</NuxtLink>
<ul class="flex flex-row mr-32 items-end justify-between lg:whitespace-nowrap">
<li class="flex flex-row gap-3 items-center">
<RouterLink to="#" class="flex flex-col items-center gap-3 cursor-pointer">
<img src="/img/svg/books/read.svg" alt="Читай отрывок" width="50" height="50" />
<UiParagraph size="250" as="span"> Читай отрывок </UiParagraph>
</RouterLink>
</li>
<li class="flex flex-row gap-3 items-center">
<NuxtLink
to="#"
class="flex flex-col items-center gap-4 cursor-pointer"
>
<div class="w-[62px] h-[53px]">
<RouterLink to="#" class="flex flex-col items-center gap-3 cursor-pointer">
<img
src="/img/svg/books/download.svg"
alt="Скачай отрывок"
width="62"
height="53"
width="50"
height="50"
/>
</div>
<UiParagraph size="250" as="span">
Скачай отрывок
</UiParagraph>
</NuxtLink>
<UiParagraph size="250" as="span"> Скачай отрывок </UiParagraph>
</RouterLink>
</li>
<li class="flex flex-row gap-3 items-center">
<NuxtLink
:to="`/books/${route.params.slug}/${book.hrefTitles}`"
<RouterLink
:to="{
name: 'book-titles',
params: {
slug: route.params.slug,
titlesSlug: book.hrefTitles,
},
}"
class="flex flex-col items-center gap-3 cursor-pointer"
>
<div class="w-[62px] h-[53px]">
<img
src="/img/svg/books/down2.svg"
alt="Содержание"
width="62"
height="53"
/>
</div>
<img src="/img/svg/books/titles.svg" alt="Содержание" width="50" height="50" />
<UiParagraph size="250" as="span"> Содержание </UiParagraph>
</NuxtLink>
</RouterLink>
</li>
</ul>
</div>
</div>
</section>
<!--нижний блок-->
<section class="ml-20 mt-32">
<section class="ml-18 mt-32">
<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">
<li
class="flex flex-col-reverse justify-end w-32 gap-4 h-64 items-center transition-transform transform hover:scale-110"
class="flex flex-col-reverse justify-end w-38 gap-4 h-64 items-center transition-transform transform hover:scale-110"
v-for="({ svg, text }, index) in book.whoUKnows"
:key="index"
>
@ -188,76 +148,74 @@
</div>
<div class="flex justify-center text-center mt-36">
<UiParagraph>
Или купи на ЛитРес - <br /><a
class="text-three"
:href="book.href"
target="_blank"
Или купи на ЛитРес - <br /><a class="text-three" :href="book.href" target="_blank"
>Реферальная ссылка для поддержки автора</a
>
</UiParagraph>
</div>
</section>
</template>
<NuxtPage />
<RouterView />
</div>
</template>
<div v-else class="text-white text-center py-20">Книга не найдена.</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from "vue";
import { useRoute } from "#app";
import UiHeading from "@/components/Typography/UiHeading.vue";
import UiParagraph from "@/components/Typography/UiParagraph.vue";
import UiButton from "@/components/UiButton/UiButton.vue";
import { ref, computed, watch } from 'vue'
import { useRoute, RouterView } from 'vue-router'
import UiHeading from '@/components/Typography/UiHeading.vue'
import UiParagraph from '@/components/Typography/UiParagraph.vue'
import UiButton from '@/components/UiButton/UiButton.vue'
import { useHead } from '@vueuse/head'
interface BookDetail {
id: number;
title: string;
metaTitle: string;
subtitle: string;
subdesc: string;
description: string;
img: string;
price: string;
buttonText: string;
buttonFormat: string;
pages: string;
illust: string;
format: string;
id: number
title: string
metaTitle: string
subtitle: string
subdesc: string
description: string
img: string
price: string
buttonText: string
buttonFormat: string
pages: string
illust: string
format: string
whoUKnows: Array<{
text: string;
svg: string;
}>;
href: string;
hrefTitles: string;
text: string
svg: string
}>
href: string
hrefTitles: string
}
const route = useRoute();
const route = useRoute()
const currentBookData = ref<BookDetail | null>(null);
const currentBookData = ref<BookDetail | null>(null)
const book = computed(() => currentBookData.value);
const book = computed(() => currentBookData.value)
const loadBookData = async (slug: string) => {
try {
const module = await import(`./_data/${slug}.json`);
currentBookData.value = module.default as BookDetail;
const module = await import(`./_data/${slug}.json`)
currentBookData.value = module.default as BookDetail
} catch (error) {
console.error(`Ошибка при загрузке книги с slug '${slug}':`, error);
currentBookData.value = null;
console.error(`Ошибка при загрузке книги с slug '${slug}':`, error)
currentBookData.value = null
}
}
};
watch(
() => route.params.slug,
async (newSlug) => {
if (newSlug) {
await loadBookData(newSlug as string);
await loadBookData(newSlug as string)
}
},
{ immediate: true }
);
{ immediate: true },
)
watch(book, (newBook) => {
if (newBook) {
@ -265,17 +223,17 @@ watch(book, (newBook) => {
title: `${newBook.metaTitle} | Vino Galante`,
meta: [
{
name: "description",
content: "Онлайн магазин книг автора Vino Galante",
name: 'description',
content: 'Онлайн магазин книг автора Vino Galante',
},
],
link: [
{
rel: "canonical",
rel: 'canonical',
href: `https://ebook.miduway.space/books/${route.params.slug}`,
},
],
});
})
}
});
})
</script>

View File

@ -30,13 +30,13 @@
Это поможет другим и вдохновит на новые главы. <br />
Спасибо!
</UiParagraph>
<UiButton class="mt-10" variants="secondary">Написать отзыв</UiButton>
<UiButton @click="console.log(1)" class="mt-10" variants="secondary">Написать отзыв</UiButton>
</div>
</section>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { onMounted } from 'vue' // Добавляем onMounted
import Swiper from 'swiper'
import { Pagination, Autoplay } from 'swiper/modules'
import UiHeading from '@/components/Typography/UiHeading.vue'

View File

@ -2,7 +2,7 @@
<section>
<div class="relative z-50">
<img
src="/assets/img/webp/hero-banner-content.webp"
src="/src/assets/img/webp/hero-banner-content.webp"
alt="Книги"
width="100%"
height="100%"

View File

@ -1,6 +1,6 @@
<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')]">
<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 />
@ -21,13 +21,7 @@
>
</div>
<div class="lg:w-7/12">
<NuxtImg
src="/img/webp/meetingAlone.webp"
alt="meeting"
width="100%"
height="100%"
loading="lazy"
/>
<img alt="meeting" width="100%" height="100%" src="/img/webp/meetingAlone.webp" />
</div>
</section>
</template>

View File

@ -0,0 +1,31 @@
<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

@ -0,0 +1,87 @@
<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>

87
src/style.css Normal file
View File

@ -0,0 +1,87 @@
@import 'tailwindcss';
@theme {
--color-primary: rgba(255, 255, 255, 1);
--color-secondary: rgba(229, 30, 125, 1);
--color-three: rgba(245, 75, 126, 1);
--color-accent-50: #f54378;
--color-accent-100: #e51e7d;
--color-accent-150: #c4035f;
--color-black-50: #0c1018;
/*system colors*/
--color-error: oklch(57.7% 0.245 27.325);
--color-warning: oklch(76.9% 0.188 70.08);
/*disabled*/
--color-text-disabled: oklch(55.1% 0.027 264.364);
--color-bg-disabled: oklch(92.8% 0.006 264.531);
}
.lato-thin {
font-family: 'Lato', sans-serif;
font-weight: 100;
font-style: normal;
}
.lato-light {
font-family: 'Lato', sans-serif;
font-weight: 300;
font-style: normal;
}
.lato-regular {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-style: normal;
}
.lato-bold {
font-family: 'Lato', sans-serif;
font-weight: 700;
font-style: normal;
}
.lato-black {
font-family: 'Lato', sans-serif;
font-weight: 900;
font-style: normal;
}
.lato-thin-italic {
font-family: 'Lato', sans-serif;
font-weight: 100;
font-style: italic;
}
.lato-light-italic {
font-family: 'Lato', sans-serif;
font-weight: 300;
font-style: italic;
}
.lato-regular-italic {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-style: italic;
}
.lato-bold-italic {
font-family: 'Lato', sans-serif;
font-weight: 700;
font-style: italic;
}
.lato-black-italic {
font-family: 'Lato', sans-serif;
font-weight: 900;
font-style: italic;
}
.russo-one-regular {
font-family: 'Russo One', sans-serif;
font-weight: 700;
font-style: normal;
}

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

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

View File

@ -1,38 +0,0 @@
// tailwind.config.js
const { fontFamily } = require('tailwindcss/defaultTheme')
module.exports = {
content: [
'./components/**/*.{vue,js}',
'./layouts/**/*.vue',
'./pages/**/*.vue',
'./app.vue',
'./nuxt.config.{js,ts}',
],
theme: {
extend: {
colors: {
primary: 'var(--color-primary)',
secondary: 'var(--color-secondary)',
three: 'var(--color-three)',
accent: {
50: 'var(--color-accent-50)',
100: 'var(--color-accent-100)',
150: 'var(--color-accent-150)',
},
black: {
50: 'var(--color-black-50)',
},
error: 'var(--color-error)',
warning: 'var(--color-warning)',
'text-disabled': 'var(--color-text-disabled)',
'bg-disabled': 'var(--color-bg-disabled)',
},
fontFamily: {
lato: ['Lato', ...fontFamily.sans],
russo: ['"Russo One"', ...fontFamily.sans],
},
},
},
plugins: [],
}

12
tsconfig.app.json Normal file
View File

@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["./src/**/*", "./src/**/*.vue"],
"exclude": ["./src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@ -1,4 +1,11 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

19
tsconfig.node.json Normal file
View File

@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

24
vite.config.ts Normal file
View File

@ -0,0 +1,24 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'
import vueDevTools from 'vite-plugin-vue-devtools'
import path from 'path'
export default defineConfig({
root: path.resolve(__dirname, '.'),
plugins: [vue(), vueDevTools(), tailwindcss()],
build: {
outDir: './dist', // Output to Nginx served directory
emptyOutDir: true,
},
server: {
host: '0.0.0.0', // Allow Docker container access
port: 4002,
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})