Fix Russian generator pages: use individual pages instead of dynamic route

The dynamic [slug].astro imported all 10 generator components, causing Astro
to bundle all their scripts into every Russian page. Scripts then crashed
trying to find DOM elements that don't exist on the page.

Each Russian generator page now imports only its own component, matching
the English page structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
emil
2026-05-09 16:57:11 +03:00
co-authored by Claude Sonnet 4.6
parent 65e42e7d44
commit be6a0d712f
11 changed files with 400 additions and 227 deletions
-227
View File
@@ -1,227 +0,0 @@
---
import { generators } from '../../../data/generators';
import BaseLayout from '../../../layouts/BaseLayout.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import NumberGenerator from '../../../components/generators/NumberGenerator.astro';
import ColorGenerator from '../../../components/generators/ColorGenerator.astro';
import PasswordGenerator from '../../../components/generators/PasswordGenerator.astro';
import LotteryGenerator from '../../../components/generators/LotteryGenerator.astro';
import DiceGenerator from '../../../components/generators/DiceGenerator.astro';
import CardGenerator from '../../../components/generators/CardGenerator.astro';
import CoinGenerator from '../../../components/generators/CoinGenerator.astro';
import ListGenerator from '../../../components/generators/ListGenerator.astro';
import UuidGenerator from '../../../components/generators/UuidGenerator.astro';
import WheelSpinner from '../../../components/generators/WheelSpinner.astro';
export function getStaticPaths() {
return generators.map((g) => ({
params: { slug: g.slug },
props: { generator: g },
}));
}
const { generator } = Astro.props;
const { slug } = Astro.params;
const seoContent: Record<string, { howTo: string[]; whenTo: string[] }> = {
numbers: {
howTo: [
'Введите минимальное значение в поле «От».',
'Введите максимальное значение в поле «До».',
'Нажмите «Сгенерировать» — случайное число появится мгновенно.',
'Нажмите на число, чтобы скопировать его в буфер обмена.',
],
whenTo: [
'Выбор победителя в розыгрыше или конкурсе.',
'Определение случайного первого игрока в настольной игре.',
'Быстрое принятие решения между пронумерованными вариантами.',
'Генерация тестовых данных с числами в заданном диапазоне.',
],
},
colors: {
howTo: [
'Нажмите «Сгенерировать», чтобы получить случайный цвет.',
'Цвет отображается в форматах HEX, RGB и HSL одновременно.',
'Нажмите на иконку копирования рядом с нужным форматом.',
],
whenTo: [
'Поиск вдохновения для цветовой палитры.',
'Случайный выбор цвета для дизайна или иллюстрации.',
'Генерация тестовых данных с цветовыми значениями.',
],
},
password: {
howTo: [
'Задайте длину пароля с помощью ползунка.',
'Выберите типы символов: заглавные, строчные, цифры, спецсимволы.',
'Нажмите «Сгенерировать» — готовый пароль появится на экране.',
'Нажмите на пароль, чтобы скопировать его.',
],
whenTo: [
'Создание надёжного пароля для нового аккаунта.',
'Генерация секретного ключа или токена.',
'Регулярное обновление паролей для повышения безопасности.',
],
},
lottery: {
howTo: [
'Укажите диапазон чисел в полях «От» и «До».',
'Задайте количество чисел для розыгрыша.',
'Нажмите «Тянуть» — выпавшие числа появятся на экране.',
'Нажмите «Копировать», чтобы сохранить результат.',
],
whenTo: [
'Розыгрыш лотерейных номеров.',
'Жеребьёвка участников конкурса.',
'Случайный выбор нескольких уникальных чисел.',
],
},
dice: {
howTo: [
'Укажите количество кубиков (до 20).',
'Выберите количество граней: d4, d6, d8, d10, d12, d20 или d100.',
'Нажмите «Бросить» — результаты отобразятся мгновенно.',
'Нажмите «Копировать», чтобы сохранить результат.',
],
whenTo: [
'Броски в настольных ролевых играх (D&D и другие).',
'Генерация случайных чисел для настольных игр.',
'Проверка вероятностей и симуляции.',
],
},
cards: {
howTo: [
'Укажите количество карт для розыгрыша.',
'Включите «Разрешить повторения», если нужны дубли.',
'Нажмите «Тянуть» — карты появятся на экране.',
'Нажмите «Копировать», чтобы скопировать результат.',
],
whenTo: [
'Случайная раздача карт для карточных игр.',
'Выбор случайных карт для гаданий.',
'Генерация случайных комбинаций карт для тестирования.',
],
},
coin: {
howTo: [
'Выберите количество монет с помощью ползунка.',
'Нажмите «Подбросить» — орёл или решка.',
'При нескольких монетах отображается итоговый счёт.',
'Нажмите «Копировать», чтобы сохранить результат.',
],
whenTo: [
'Принятие решений по принципу «орёл или решка».',
'Определение очерёдности в игре.',
'Симуляция случайных событий с равной вероятностью.',
],
},
list: {
howTo: [
'Введите список элементов — по одному на строку.',
'Укажите, сколько элементов выбрать.',
'При необходимости разрешите повторения.',
'Нажмите «Выбрать» — победители выделятся на экране.',
],
whenTo: [
'Розыгрыш победителей конкурса.',
'Случайное распределение задач между участниками команды.',
'Выбор случайного варианта из списка.',
],
},
uuid: {
howTo: [
'Выберите тип: UUID v4, Hex или Base64.',
'Для Hex и Base64 задайте длину в байтах.',
'Укажите количество токенов.',
'Нажмите «Сгенерировать» и скопируйте результат.',
],
whenTo: [
'Генерация уникальных идентификаторов для баз данных.',
'Создание токенов для API и сессий.',
'Генерация случайных ключей шифрования.',
],
},
wheel: {
howTo: [
'Введите варианты в поле — по одному на строку (от 2 до 24).',
'Колесо обновляется автоматически.',
'Нажмите «Крутить» и дождитесь результата.',
'Победитель выделяется — скопируйте его кнопкой «Копировать».',
],
whenTo: [
'Случайный выбор победителя розыгрыша.',
'Принятие решений в команде.',
'Распределение ролей или задач между участниками.',
],
},
};
const seo = seoContent[slug] ?? { howTo: [], whenTo: [] };
---
<BaseLayout
title={generator.ruSeoTitle}
description={generator.ruSeoDescription}
lang="ru"
>
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a
href="/ru/"
class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded"
aria-label="Все генераторы"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6"/>
</svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span
class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]"
aria-hidden="true"
></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">
Генератор {generator.ruTitle.toLowerCase()}
</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main>
{slug === 'numbers' && <NumberGenerator />}
{slug === 'colors' && <ColorGenerator />}
{slug === 'password' && <PasswordGenerator />}
{slug === 'lottery' && <LotteryGenerator />}
{slug === 'dice' && <DiceGenerator />}
{slug === 'cards' && <CardGenerator />}
{slug === 'coin' && <CoinGenerator />}
{slug === 'list' && <ListGenerator />}
{slug === 'uuid' && <UuidGenerator />}
{slug === 'wheel' && <WheelSpinner />}
</main>
<div class="mt-12 flex justify-center">
<AdBanner size="tile" />
</div>
<SeoBlock howTo={seo.howTo} whenTo={seo.whenTo} lang="ru" />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import CardGenerator from '../../../components/generators/CardGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'cards')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Генератор карт</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><CardGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Укажите количество карт для розыгрыша.',
'Включите «Разрешить повторения», если нужны дубли.',
'Нажмите «Тянуть» — карты появятся на экране.',
'Нажмите «Копировать», чтобы скопировать результат.',
]} whenTo={[
'Случайная раздача карт для карточных игр.',
'Выбор случайных карт для гаданий.',
'Генерация случайных комбинаций карт для тестирования.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import CoinGenerator from '../../../components/generators/CoinGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'coin')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Подбросить монетку</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><CoinGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Выберите количество монет с помощью ползунка.',
'Нажмите «Подбросить» — орёл или решка.',
'При нескольких монетах отображается итоговый счёт.',
'Нажмите «Копировать», чтобы сохранить результат.',
]} whenTo={[
'Принятие решений по принципу «орёл или решка».',
'Определение очерёдности в игре.',
'Симуляция случайных событий с равной вероятностью.',
]} />
</div>
</BaseLayout>
+39
View File
@@ -0,0 +1,39 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import ColorGenerator from '../../../components/generators/ColorGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'colors')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Генератор цветов</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><ColorGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Нажмите «Сгенерировать», чтобы получить случайный цвет.',
'Цвет отображается в форматах HEX, RGB и HSL одновременно.',
'Нажмите на иконку копирования рядом с нужным форматом.',
]} whenTo={[
'Поиск вдохновения для цветовой палитры.',
'Случайный выбор цвета для дизайна или иллюстрации.',
'Генерация тестовых данных с цветовыми значениями.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import DiceGenerator from '../../../components/generators/DiceGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'dice')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Бросить кубик онлайн</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><DiceGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Укажите количество кубиков (до 20).',
'Выберите количество граней: d4, d6, d8, d10, d12, d20 или d100.',
'Нажмите «Бросить» — результаты отобразятся мгновенно.',
'Нажмите «Копировать», чтобы сохранить результат.',
]} whenTo={[
'Броски в настольных ролевых играх (D&D и другие).',
'Генерация случайных чисел для настольных игр.',
'Проверка вероятностей и симуляции.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import ListGenerator from '../../../components/generators/ListGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'list')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Случайный выбор из списка</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><ListGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Введите список элементов — по одному на строку.',
'Укажите, сколько элементов выбрать.',
'При необходимости разрешите повторения.',
'Нажмите «Выбрать» — результаты появятся на экране.',
]} whenTo={[
'Розыгрыш победителей конкурса.',
'Случайное распределение задач между участниками команды.',
'Выбор случайного варианта из списка.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import LotteryGenerator from '../../../components/generators/LotteryGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'lottery')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Генератор лотереи</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><LotteryGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Укажите диапазон чисел в полях «От» и «До».',
'Задайте количество чисел для розыгрыша.',
'Нажмите «Тянуть» — выпавшие числа появятся на экране.',
'Нажмите «Копировать», чтобы сохранить результат.',
]} whenTo={[
'Розыгрыш лотерейных номеров.',
'Жеребьёвка участников конкурса.',
'Случайный выбор нескольких уникальных чисел.',
]} />
</div>
</BaseLayout>
+41
View File
@@ -0,0 +1,41 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import NumberGenerator from '../../../components/generators/NumberGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'numbers')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Генератор чисел</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><NumberGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Введите минимальное значение в поле «От».',
'Введите максимальное значение в поле «До».',
'Нажмите «Сгенерировать» — случайное число появится мгновенно.',
'Нажмите на число, чтобы скопировать его в буфер обмена.',
]} whenTo={[
'Выбор победителя в розыгрыше или конкурсе.',
'Определение случайного первого игрока в настольной игре.',
'Быстрое принятие решения между пронумерованными вариантами.',
'Генерация тестовых данных с числами в заданном диапазоне.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import PasswordGenerator from '../../../components/generators/PasswordGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'password')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Генератор паролей</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><PasswordGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Задайте длину пароля с помощью ползунка.',
'Выберите типы символов: заглавные, строчные, цифры, спецсимволы.',
'Нажмите «Сгенерировать» — готовый пароль появится на экране.',
'Нажмите на пароль, чтобы скопировать его.',
]} whenTo={[
'Создание надёжного пароля для нового аккаунта.',
'Генерация секретного ключа или токена.',
'Регулярное обновление паролей для повышения безопасности.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import UuidGenerator from '../../../components/generators/UuidGenerator.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'uuid')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Генератор UUID и токенов</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><UuidGenerator /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Выберите тип: UUID v4, Hex или Base64.',
'Для Hex и Base64 задайте длину в байтах.',
'Укажите количество токенов.',
'Нажмите «Сгенерировать» и скопируйте результат.',
]} whenTo={[
'Генерация уникальных идентификаторов для баз данных.',
'Создание токенов для API и сессий.',
'Генерация случайных ключей шифрования.',
]} />
</div>
</BaseLayout>
+40
View File
@@ -0,0 +1,40 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import WheelSpinner from '../../../components/generators/WheelSpinner.astro';
import AdBanner from '../../../components/AdBanner.astro';
import SeoBlock from '../../../components/SeoBlock.astro';
import { generators } from '../../../data/generators';
const generator = generators.find((g) => g.slug === 'wheel')!;
---
<BaseLayout title={generator.ruSeoTitle} description={generator.ruSeoDescription} lang="ru">
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a href="/ru/" class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded" aria-label="Все генераторы">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
Все генераторы
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]" aria-hidden="true"></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">Колесо фортуны онлайн</h1>
<p class="mt-2 text-base text-zinc-400">{generator.ruDescription}</p>
</header>
<main><WheelSpinner /></main>
<div class="mt-12 flex justify-center"><AdBanner size="tile" /></div>
<SeoBlock lang="ru" howTo={[
'Введите варианты в поле — по одному на строку (от 2 до 24).',
'Колесо обновляется автоматически.',
'Нажмите «Крутить» и дождитесь результата.',
'Победитель выделяется — скопируйте его кнопкой «Копировать».',
]} whenTo={[
'Случайный выбор победителя розыгрыша.',
'Принятие решений в команде.',
'Распределение ролей или задач между участниками.',
]} />
</div>
</BaseLayout>