feat(sprint-5.5): add NestJS API, BlockMetaBar, block components + fix Vercel build
- Add vercel.json to build only apps/web (fix Vercel build failure) - NestJS API: BlocksModule, BlocksController, BlocksService with Prisma 7 - PostgreSQL migration: Block model (path, version, isInPreview) - BlockMetaBar component: inline version edit, API fetch with offline fallback - New block components: CeoBlock, ContactFormsBlock, FooterBlock, NewsBlock, ReviewsBlock - PreviewClient: fetch isInPreview from API, block visibility toggle - Pages updated: hero, doctors, ceo, contact-forms, contact, news, reviews - docker-compose: PostgreSQL on port 5434 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,8 +24,11 @@
|
|||||||
"@nestjs/common": "^11.0.1",
|
"@nestjs/common": "^11.0.1",
|
||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.1",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@prisma/adapter-pg": "^7.5.0",
|
||||||
"@prisma/client": "^7.5.0",
|
"@prisma/client": "^7.5.0",
|
||||||
|
"@types/pg": "^8.20.0",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
|
"pg": "^8.20.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
@@ -52,9 +55,13 @@
|
|||||||
"ts-loader": "^9.5.2",
|
"ts-loader": "^9.5.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.20.0"
|
"typescript-eslint": "^8.20.0"
|
||||||
},
|
},
|
||||||
|
"prisma": {
|
||||||
|
"seed": "ts-node prisma/seed.ts"
|
||||||
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
"js",
|
"js",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default defineConfig({
|
|||||||
schema: "prisma/schema.prisma",
|
schema: "prisma/schema.prisma",
|
||||||
migrations: {
|
migrations: {
|
||||||
path: "prisma/migrations",
|
path: "prisma/migrations",
|
||||||
|
seed: "tsx prisma/seed.ts",
|
||||||
},
|
},
|
||||||
datasource: {
|
datasource: {
|
||||||
url: process.env["DATABASE_URL"],
|
url: process.env["DATABASE_URL"],
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "Role" AS ENUM ('viewer', 'editor');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "ComponentStatus" AS ENUM ('draft', 'review', 'approved');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "User" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"passwordHash" TEXT NOT NULL,
|
||||||
|
"role" "Role" NOT NULL DEFAULT 'viewer',
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ExperimentalComponent" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"baseComponent" TEXT NOT NULL,
|
||||||
|
"attributes" JSONB NOT NULL,
|
||||||
|
"status" "ComponentStatus" NOT NULL DEFAULT 'draft',
|
||||||
|
"authorId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "ExperimentalComponent_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Block" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"path" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"version" TEXT NOT NULL,
|
||||||
|
"isInPreview" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "Block_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Block_path_key" ON "Block"("path");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ExperimentalComponent" ADD CONSTRAINT "ExperimentalComponent_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client"
|
provider = "prisma-client-js"
|
||||||
output = "../generated/prisma"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
@@ -40,3 +39,13 @@ model ExperimentalComponent {
|
|||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Block {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
path String @unique
|
||||||
|
name String
|
||||||
|
version String
|
||||||
|
isInPreview Boolean @default(false)
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
|
const BLOCKS = [
|
||||||
|
{ path: '/components/navigation', name: 'Шапка / Навигация', version: 'v1.0', isInPreview: true },
|
||||||
|
{ path: '/blocks/hero', name: 'Hero-баннер', version: 'v1.1', isInPreview: true },
|
||||||
|
{ path: '/blocks/ceo', name: 'Вводный текст (CEO-блок)', version: 'v0.1', isInPreview: false },
|
||||||
|
{ path: '/blocks/doctors', name: 'Наши врачи', version: 'v1.1', isInPreview: true },
|
||||||
|
{ path: '/blocks/reviews', name: 'Отзывы', version: 'v0.1', isInPreview: false },
|
||||||
|
{ path: '/blocks/contact-forms', name: 'Формы записи', version: 'v0.1', isInPreview: false },
|
||||||
|
{ path: '/blocks/news', name: 'Новости', version: 'v0.1', isInPreview: false },
|
||||||
|
{ path: '/blocks/contact', name: 'Подвал / Контакт', version: 'v0.1', isInPreview: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
for (const block of BLOCKS) {
|
||||||
|
await prisma.block.upsert({
|
||||||
|
where: { path: block.path },
|
||||||
|
update: {},
|
||||||
|
create: block,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(`Seeded ${BLOCKS.length} blocks`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(() => prisma.$disconnect());
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
import { BlocksModule } from './blocks/blocks.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [BlocksModule],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Controller, Get, Patch, Query, Body } from '@nestjs/common';
|
||||||
|
import { BlocksService } from './blocks.service';
|
||||||
|
|
||||||
|
@Controller('blocks')
|
||||||
|
export class BlocksController {
|
||||||
|
constructor(private blocks: BlocksService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
findAll() {
|
||||||
|
return this.blocks.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('by-path')
|
||||||
|
findByPath(@Query('path') path: string) {
|
||||||
|
return this.blocks.findByPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch('by-path')
|
||||||
|
update(
|
||||||
|
@Query('path') path: string,
|
||||||
|
@Body() body: { version?: string; isInPreview?: boolean },
|
||||||
|
) {
|
||||||
|
return this.blocks.update(path, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { BlocksController } from './blocks.controller';
|
||||||
|
import { BlocksService } from './blocks.service';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [BlocksController],
|
||||||
|
providers: [BlocksService, PrismaService],
|
||||||
|
})
|
||||||
|
export class BlocksModule {}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BlocksService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return this.prisma.block.findMany({ orderBy: { createdAt: 'asc' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByPath(path: string) {
|
||||||
|
const existing = await this.prisma.block.findUnique({ where: { path } });
|
||||||
|
if (existing) return existing;
|
||||||
|
return this.prisma.block.create({
|
||||||
|
data: { path, name: path, version: 'v0.1', isInPreview: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(path: string, data: { version?: string; isInPreview?: boolean }) {
|
||||||
|
return this.prisma.block.update({ where: { path }, data });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
app.enableCors({ origin: 'http://localhost:3001' });
|
||||||
await app.listen(process.env.PORT ?? 3000);
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||||
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||||
|
constructor() {
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
|
super({ adapter });
|
||||||
|
}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
await this.$connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onModuleDestroy() {
|
||||||
|
await this.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
import { CeoBlock, CEO_QUESTIONS } from "@/components/blocks/CeoBlock";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Вводный текст (CEO-блок). Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Вводный текст (CEO-блок). Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -46,13 +48,6 @@ const LLM_CEO_TEXT = `
|
|||||||
✕ Не менять стиль вопросов на другой формат
|
✕ Не менять стиль вопросов на другой формат
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
const CEO_QUESTIONS = [
|
|
||||||
"У вас болит ухо, заложен нос, першит в горле, и вы не можете понять причину?",
|
|
||||||
"Вам срочно нужен платный ЛОР в Перми или, как ещё говорят, «ухогорлонос»?",
|
|
||||||
"Заболел ребёнок?",
|
|
||||||
"Срочно ищете частные ЛОР-клиники Перми для детей 0+ и взрослых с удобным режимом работы с 9:00 до 21:00 по будням?",
|
|
||||||
"Вам назначили проведение эндоскопической операции на ухе, горле или носе?",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function CeoPage() {
|
export default function CeoPage() {
|
||||||
return (
|
return (
|
||||||
@@ -68,6 +63,7 @@ export default function CeoPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Вводный текст (CEO-блок)
|
Вводный текст (CEO-блок)
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/ceo" defaultVersion="v1.0" defaultIsInPreview={false} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Блок после hero-баннера на perm.oclinica.ru/lor. Описание специализации клиники
|
Блок после hero-баннера на perm.oclinica.ru/lor. Описание специализации клиники
|
||||||
+ вопросы-стимулы для пациентов.
|
+ вопросы-стимулы для пациентов.
|
||||||
@@ -79,38 +75,8 @@ export default function CeoPage() {
|
|||||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||||
Живой пример
|
Живой пример
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div className="rounded-xl overflow-hidden" style={{ border: "1px solid var(--bb-border)" }}>
|
||||||
className="rounded-xl p-8"
|
<CeoBlock />
|
||||||
style={{
|
|
||||||
background: "#fff",
|
|
||||||
border: "1px solid var(--bb-border)",
|
|
||||||
fontFamily: "var(--font-web)",
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: 1.75,
|
|
||||||
color: "#374151",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Клиника ухо, нос специализируется на оториноларингологии – лечении взрослых и детей
|
|
||||||
с ЛОР заболеваниями. ЛОР клиника представлена на двух адресах:{" "}
|
|
||||||
<a href="#" style={{ color: "#0089c3" }}>ул. Цитная, 9</a>, <a href="#" style={{ color: "#0089c3" }}>ул. Г. Звезда, 31а</a>.{" "}
|
|
||||||
Это <a href="#" style={{ color: "#0089c3" }}>Клиника лечения кашля и аллергии</a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="mt-4 space-y-2">
|
|
||||||
{CEO_QUESTIONS.map((q) => (
|
|
||||||
<p key={q}>— {q}</p>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="mt-4">
|
|
||||||
Обращайтесь в ЛОР центр ухо, горло, нос в Перми, наши врачи оториноларингологи обязательно Вам помогут!
|
|
||||||
</p>
|
|
||||||
<p className="mt-2">
|
|
||||||
Клиника ЛОР болезней ухо, горло, нос – это наиболее современный центр оториноларингологии в Перми,
|
|
||||||
благодаря эндоскопическому оборудованию, высокому профессионализму оториноларингологов.
|
|
||||||
</p>
|
|
||||||
<p className="mt-4 font-medium">Будьте здоровы!</p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
import { ContactFormsBlock } from "@/components/blocks/ContactFormsBlock";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Формы записи. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Формы записи. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -62,104 +64,18 @@ export default function ContactFormsPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Формы записи
|
Формы записи
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/contact-forms" defaultVersion="v1.0" defaultIsInPreview={false} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Два блока форм с perm.oclinica.ru/lor — запись на приём и запрос стоимости операции.
|
Два блока форм с perm.oclinica.ru/lor — запись на приём и запрос стоимости операции.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Форма 1: Будьте здоровы */}
|
{/* Живой пример */}
|
||||||
<section className="space-y-3">
|
<section className="space-y-3">
|
||||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||||
Форма 1 — «Будьте здоровы!»
|
Живой пример
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<ContactFormsBlock />
|
||||||
Фон секции: #b8e6ed. Запись на приём к врачу.
|
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
className="rounded-xl py-10 px-8 flex flex-col items-center gap-6"
|
|
||||||
style={{ background: "#b8e6ed" }}
|
|
||||||
>
|
|
||||||
<div className="text-center">
|
|
||||||
<h2 className="text-2xl font-bold mb-1" style={{ color: "#111827" }}>
|
|
||||||
Будьте здоровы!
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm" style={{ color: "#374151" }}>
|
|
||||||
Запишитесь на приём к врачу!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="w-full max-w-sm space-y-3">
|
|
||||||
<input
|
|
||||||
className="bb-input"
|
|
||||||
type="text"
|
|
||||||
placeholder="Введите ваше имя"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="bb-input"
|
|
||||||
type="tel"
|
|
||||||
placeholder="Введите ваш телефон"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
<select className="bb-select" disabled>
|
|
||||||
<option>Выберите ЛОР врача</option>
|
|
||||||
</select>
|
|
||||||
<label className="flex items-start gap-2 text-xs cursor-pointer" style={{ color: "#374151" }}>
|
|
||||||
<input type="checkbox" className="bb-checkbox mt-0.5" readOnly />
|
|
||||||
<span>
|
|
||||||
Отправляя данные, я даю согласие на обработку персональных данных
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<button className="bb-btn bb-btn-md bb-btn-primary w-full">
|
|
||||||
Запишите меня!
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Форма 2: Узнайте стоимость */}
|
|
||||||
<section className="space-y-3">
|
|
||||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
|
||||||
Форма 2 — «Узнайте стоимость операции»
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
|
||||||
Фон секции: #ffffff. Запрос консультации по стоимости.
|
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
className="rounded-xl py-10 px-8 flex flex-col items-center gap-6"
|
|
||||||
style={{ background: "#fff", border: "1px solid var(--bb-border)" }}
|
|
||||||
>
|
|
||||||
<div className="text-center">
|
|
||||||
<h2 className="text-2xl font-bold mb-1" style={{ color: "#111827" }}>
|
|
||||||
Узнайте стоимость операции
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm" style={{ color: "#374151" }}>
|
|
||||||
Проконсультируйтесь с ассистентом хирурга
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="w-full max-w-sm space-y-3">
|
|
||||||
<input
|
|
||||||
className="bb-input"
|
|
||||||
type="text"
|
|
||||||
placeholder="Введите ваше имя"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="bb-input"
|
|
||||||
type="tel"
|
|
||||||
placeholder="Введите ваш телефон"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
<label className="flex items-start gap-2 text-xs cursor-pointer" style={{ color: "#374151" }}>
|
|
||||||
<input type="checkbox" className="bb-checkbox mt-0.5" readOnly />
|
|
||||||
<span>
|
|
||||||
Отправляя данные, я даю согласие на обработку персональных данных
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<button className="bb-btn bb-btn-md bb-btn-primary w-full">
|
|
||||||
Перезвоните мне
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Сравнение двух форм */}
|
{/* Сравнение двух форм */}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
import { FooterBlock } from "@/components/blocks/FooterBlock";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Подвал (Footer). Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Подвал (Footer). Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -82,6 +84,7 @@ export default function ContactFooterPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Подвал (Footer)
|
Подвал (Footer)
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/contact" defaultVersion="v1.0" defaultIsInPreview={false} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Подвал сайта с perm.oclinica.ru — 4 колонки ссылок, логотип, адрес, часы работы, соцсети.
|
Подвал сайта с perm.oclinica.ru — 4 колонки ссылок, логотип, адрес, часы работы, соцсети.
|
||||||
</p>
|
</p>
|
||||||
@@ -92,97 +95,7 @@ export default function ContactFooterPage() {
|
|||||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||||
Живой пример
|
Живой пример
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<FooterBlock />
|
||||||
className="rounded-xl overflow-hidden"
|
|
||||||
style={{ border: "1px solid var(--bb-border)" }}
|
|
||||||
>
|
|
||||||
{/* 4 колонки ссылок */}
|
|
||||||
<div
|
|
||||||
className="grid grid-cols-4 gap-6 p-8"
|
|
||||||
style={{ background: "#f8f9fa", borderBottom: "1px solid #e5e7eb" }}
|
|
||||||
>
|
|
||||||
{FOOTER_COLUMNS.map((col) => (
|
|
||||||
<div key={col.title}>
|
|
||||||
<p className="font-semibold text-sm mb-3" style={{ color: "#111827" }}>
|
|
||||||
{col.title}
|
|
||||||
</p>
|
|
||||||
<ul className="space-y-1.5">
|
|
||||||
{col.links.map((link) => (
|
|
||||||
<li key={link}>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="text-xs bb-nav-link"
|
|
||||||
style={{ color: "#53514e", textDecoration: "none" }}
|
|
||||||
>
|
|
||||||
{link}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Нижняя полоса */}
|
|
||||||
<div
|
|
||||||
className="flex items-start justify-between gap-6 px-8 py-5"
|
|
||||||
style={{ background: "#fff" }}
|
|
||||||
>
|
|
||||||
{/* Логотип */}
|
|
||||||
<div className="flex items-center gap-2 shrink-0">
|
|
||||||
<div
|
|
||||||
className="w-10 h-10 rounded-full flex items-center justify-center text-white font-bold"
|
|
||||||
style={{ background: "#0089c3" }}
|
|
||||||
>
|
|
||||||
✚
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
className="text-[10px] font-bold uppercase leading-tight"
|
|
||||||
style={{ color: "#53514e" }}
|
|
||||||
>
|
|
||||||
Клиника<br />ухо, горло, нос
|
|
||||||
</div>
|
|
||||||
<div className="text-[8px] leading-tight" style={{ color: "#9ca3af" }}>
|
|
||||||
им. проф. Е.Н.Оленевой
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Адрес и соцсети */}
|
|
||||||
<div className="text-center space-y-2">
|
|
||||||
<p className="text-xs" style={{ color: "#374151" }}>
|
|
||||||
Мы находимся по адресу: Пермь, ул. Г. Звезда, 31а
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center justify-center gap-2">
|
|
||||||
{["VK", "OK", "YT", "TG"].map((s) => (
|
|
||||||
<a
|
|
||||||
key={s}
|
|
||||||
href="#"
|
|
||||||
className="text-[11px] px-2 py-1 rounded"
|
|
||||||
style={{
|
|
||||||
background: "var(--bb-sidebar-bg)",
|
|
||||||
border: "1px solid var(--bb-border)",
|
|
||||||
color: "var(--bb-text-muted)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{s}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Часы работы */}
|
|
||||||
<div className="text-right text-xs space-y-1" style={{ color: "#374151" }}>
|
|
||||||
<p className="font-semibold text-xs mb-1" style={{ color: "#111827" }}>
|
|
||||||
Часы работы:
|
|
||||||
</p>
|
|
||||||
<p>Пн–пт: 9:00–21:00</p>
|
|
||||||
<p>Сб: 9:00–18:00</p>
|
|
||||||
<p>Вс: выходной</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Колонки */}
|
{/* Колонки */}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
import { DoctorsBlock, STATS, DOCTORS } from "@/components/blocks/DoctorsBlock";
|
import { DoctorsBlock, STATS, DOCTORS } from "@/components/blocks/DoctorsBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Блок «Наши врачи». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Блок «Наши врачи». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -58,6 +59,7 @@ export default function DoctorsBlockPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Блок «Наши врачи»
|
Блок «Наши врачи»
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/doctors" defaultVersion="v1.1" defaultIsInPreview={true} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Блок на странице perm.oclinica.ru/lor — заголовок, 3 стат-блока, сетка из 6 карточек врачей.
|
Блок на странице perm.oclinica.ru/lor — заголовок, 3 стат-блока, сетка из 6 карточек врачей.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
import { HeroBlock, HERO_CHECKS } from "@/components/blocks/HeroBlock";
|
import { HeroBlock, HERO_CHECKS } from "@/components/blocks/HeroBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Hero-баннер. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Hero-баннер. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -63,6 +64,7 @@ export default function HeroPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Hero-баннер
|
Hero-баннер
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/hero" defaultVersion="v1.1" defaultIsInPreview={true} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Главный баннер страницы раздела ЛОР — perm.oclinica.ru/lor. Двухколоночный блок, единый светло-кремовый фон{" "}
|
Главный баннер страницы раздела ЛОР — perm.oclinica.ru/lor. Двухколоночный блок, единый светло-кремовый фон{" "}
|
||||||
<strong>#f9f4e7</strong>.
|
<strong>#f9f4e7</strong>.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
import { NewsBlock } from "@/components/blocks/NewsBlock";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Блок «Новости». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Блок «Новости». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -79,6 +81,7 @@ export default function NewsBlockPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Блок «Новости»
|
Блок «Новости»
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/news" defaultVersion="v1.0" defaultIsInPreview={false} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Блок новостей с perm.oclinica.ru/lor — 4 карточки в ряд (дата + заголовок-ссылка),
|
Блок новостей с perm.oclinica.ru/lor — 4 карточки в ряд (дата + заголовок-ссылка),
|
||||||
кнопка «Все новости».
|
кнопка «Все новости».
|
||||||
@@ -90,42 +93,7 @@ export default function NewsBlockPage() {
|
|||||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||||
Живой пример
|
Живой пример
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<NewsBlock />
|
||||||
className="rounded-xl p-8 space-y-6"
|
|
||||||
style={{ background: "#fff", border: "1px solid var(--bb-border)" }}
|
|
||||||
>
|
|
||||||
<h2 className="text-xl font-bold" style={{ color: "#111827" }}>
|
|
||||||
Новости
|
|
||||||
</h2>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
||||||
{MOCK_NEWS.map((n) => (
|
|
||||||
<div
|
|
||||||
key={n.date}
|
|
||||||
className="bb-news-card rounded-lg p-3 cursor-pointer"
|
|
||||||
style={{
|
|
||||||
border: "1px solid var(--bb-border)",
|
|
||||||
transition: "background 0.15s, box-shadow 0.15s",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p className="text-xs mb-2" style={{ color: "#6b7280" }}>
|
|
||||||
{n.date}
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
href={n.href}
|
|
||||||
className="text-sm font-medium leading-snug block"
|
|
||||||
style={{ color: "#0089c3", textDecoration: "none" }}
|
|
||||||
>
|
|
||||||
{n.title}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<button className="bb-btn bb-btn-md bb-btn-outline">
|
|
||||||
Все новости
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Анатомия карточки */}
|
{/* Анатомия карточки */}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
import { ReviewsBlock } from "@/components/blocks/ReviewsBlock";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Блок «Отзывы». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Блок «Отзывы». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -64,6 +66,7 @@ export default function ReviewsBlockPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Блок «Отзывы о нас»
|
Блок «Отзывы о нас»
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/blocks/reviews" defaultVersion="v1.0" defaultIsInPreview={false} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Карусель отзывов с perm.oclinica.ru/lor — большая кавычка, текст, «Читать полностью», стрелки.
|
Карусель отзывов с perm.oclinica.ru/lor — большая кавычка, текст, «Читать полностью», стрелки.
|
||||||
</p>
|
</p>
|
||||||
@@ -74,76 +77,7 @@ export default function ReviewsBlockPage() {
|
|||||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||||
Живой пример
|
Живой пример
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<ReviewsBlock />
|
||||||
className="rounded-xl p-8 space-y-6"
|
|
||||||
style={{ background: "#fff", border: "1px solid var(--bb-border)" }}
|
|
||||||
>
|
|
||||||
{/* Заголовок */}
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-bold mb-2" style={{ color: "#111827" }}>
|
|
||||||
Отзывы о нас
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm" style={{ color: "#374151", lineHeight: 1.7 }}>
|
|
||||||
За 12 лет работы наши врачи оториноларингологи избавили от болезней ухо, горло, носа
|
|
||||||
более 50 000 пациентов. Но даже сейчас мы высоко ценим каждый положительный отзыв
|
|
||||||
и искренние слова благодарности.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Карусель (статичная) */}
|
|
||||||
<div className="relative flex items-center gap-4">
|
|
||||||
{/* Стрелка влево */}
|
|
||||||
<button
|
|
||||||
className="shrink-0 w-10 h-10 rounded-full flex items-center justify-center font-bold text-lg"
|
|
||||||
style={{
|
|
||||||
background: "var(--bb-sidebar-bg)",
|
|
||||||
border: "1px solid var(--bb-border)",
|
|
||||||
color: "var(--bb-text-muted)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
‹
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Карточка отзыва */}
|
|
||||||
<div
|
|
||||||
className="flex-1 rounded-xl p-6 relative"
|
|
||||||
style={{ background: "#eef4d1" }}
|
|
||||||
>
|
|
||||||
{/* Декоративная кавычка */}
|
|
||||||
<div
|
|
||||||
className="text-8xl leading-none mb-2 font-serif"
|
|
||||||
style={{ color: "#b8e6ed", lineHeight: 0.8 }}
|
|
||||||
>
|
|
||||||
«
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
className="text-sm italic leading-relaxed"
|
|
||||||
style={{ color: "#374151" }}
|
|
||||||
>
|
|
||||||
{MOCK_REVIEWS[0].text}
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="inline-block mt-3 text-sm"
|
|
||||||
style={{ color: "#0089c3" }}
|
|
||||||
>
|
|
||||||
Читать отзыв полностью
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Стрелка вправо */}
|
|
||||||
<button
|
|
||||||
className="shrink-0 w-10 h-10 rounded-full flex items-center justify-center font-bold text-lg"
|
|
||||||
style={{
|
|
||||||
background: "var(--bb-sidebar-bg)",
|
|
||||||
border: "1px solid var(--bb-border)",
|
|
||||||
color: "var(--bb-text-muted)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
›
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Несколько примеров */}
|
{/* Несколько примеров */}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import { Button } from "@/components/ui/Button";
|
import { Button } from "@/components/ui/Button";
|
||||||
import { CodeCopy } from "@/components/ui/CodeCopy";
|
import { CodeCopy } from "@/components/ui/CodeCopy";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Кнопки. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Кнопки. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -227,6 +228,7 @@ a.callback_url {
|
|||||||
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
|
||||||
Кнопки
|
Кнопки
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/components/buttons" defaultVersion="v2.0" defaultIsInPreview={false} />
|
||||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Кнопки скопированы с реального сайта{" "}
|
Кнопки скопированы с реального сайта{" "}
|
||||||
<span className="font-mono text-sm" style={{ color: "var(--bb-text)" }}>
|
<span className="font-mono text-sm" style={{ color: "var(--bb-text)" }}>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { CodeCopy } from "@/components/ui/CodeCopy";
|
import { CodeCopy } from "@/components/ui/CodeCopy";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Карточки. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Карточки. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -417,6 +418,7 @@ export default function CardsPage() {
|
|||||||
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
|
||||||
Карточки
|
Карточки
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/components/cards" defaultVersion="v1.0" defaultIsInPreview={false} />
|
||||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Карточки врача, новости, отзыва, цены и услуги — основные блоки контента сайта.
|
Карточки врача, новости, отзыва, цены и услуги — основные блоки контента сайта.
|
||||||
Бейджи, теги и алерты — вспомогательные элементы.
|
Бейджи, теги и алерты — вспомогательные элементы.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import { Toggle } from "@/components/ui/Toggle";
|
import { Toggle } from "@/components/ui/Toggle";
|
||||||
import { CodeCopy } from "@/components/ui/CodeCopy";
|
import { CodeCopy } from "@/components/ui/CodeCopy";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Форм-контролы. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Форм-контролы. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -264,6 +265,7 @@ input[type=email] {
|
|||||||
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
|
||||||
Форм-контролы
|
Форм-контролы
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/components/forms" defaultVersion="v2.0" defaultIsInPreview={false} />
|
||||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Элементы ввода данных: текстовые поля, выпадающие списки, флажки, переключатели.
|
Элементы ввода данных: текстовые поля, выпадающие списки, флажки, переключатели.
|
||||||
Применяются в формах записи, фильтрах и настройках.
|
Применяются в формах записи, фильтрах и настройках.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||||
import { NavigationBlock, NAV_ITEMS } from "@/components/blocks/NavigationBlock";
|
import { NavigationBlock, NAV_ITEMS } from "@/components/blocks/NavigationBlock";
|
||||||
|
import { BlockMetaBar } from "@/components/ui/BlockMetaBar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Шапка и навигация. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
title: "Шапка и навигация. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||||
@@ -57,6 +58,7 @@ export default function NavigationPage() {
|
|||||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||||
Шапка и навигация
|
Шапка и навигация
|
||||||
</h1>
|
</h1>
|
||||||
|
<BlockMetaBar path="/components/navigation" defaultVersion="v1.0" defaultIsInPreview={true} />
|
||||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Точное воспроизведение шапки с perm.oclinica.ru/lor. Три зоны: топ-бар, логотип, главное меню.
|
Точное воспроизведение шапки с perm.oclinica.ru/lor. Три зоны: топ-бар, логотип, главное меню.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import { useState, useEffect } from "react";
|
|||||||
import { NavigationBlock } from "@/components/blocks/NavigationBlock";
|
import { NavigationBlock } from "@/components/blocks/NavigationBlock";
|
||||||
import { HeroBlock } from "@/components/blocks/HeroBlock";
|
import { HeroBlock } from "@/components/blocks/HeroBlock";
|
||||||
import { DoctorsBlock } from "@/components/blocks/DoctorsBlock";
|
import { DoctorsBlock } from "@/components/blocks/DoctorsBlock";
|
||||||
|
import { CeoBlock } from "@/components/blocks/CeoBlock";
|
||||||
|
import { ReviewsBlock } from "@/components/blocks/ReviewsBlock";
|
||||||
|
import { NewsBlock } from "@/components/blocks/NewsBlock";
|
||||||
|
import { ContactFormsBlock } from "@/components/blocks/ContactFormsBlock";
|
||||||
|
import { FooterBlock } from "@/components/blocks/FooterBlock";
|
||||||
|
|
||||||
const STORAGE_KEY = "bb-preview-created";
|
const STORAGE_KEY = "bb-preview-created";
|
||||||
|
|
||||||
@@ -30,71 +35,89 @@ function BlockPlaceholder({ name, href }: { name: string; href: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BLOCKS: Array<{
|
interface BlockDef {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
href: string;
|
href: string;
|
||||||
ready: boolean;
|
path: string;
|
||||||
|
defaultReady: boolean;
|
||||||
component?: React.ReactNode;
|
component?: React.ReactNode;
|
||||||
}> = [
|
}
|
||||||
|
|
||||||
|
const BLOCKS: BlockDef[] = [
|
||||||
{
|
{
|
||||||
id: "navigation",
|
id: "navigation",
|
||||||
name: "Шапка / Навигация",
|
name: "Шапка / Навигация",
|
||||||
href: "/components/navigation",
|
href: "/components/navigation",
|
||||||
ready: true,
|
path: "/components/navigation",
|
||||||
|
defaultReady: true,
|
||||||
component: <NavigationBlock />,
|
component: <NavigationBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "hero",
|
id: "hero",
|
||||||
name: "Hero-баннер",
|
name: "Hero-баннер",
|
||||||
href: "/blocks/hero",
|
href: "/blocks/hero",
|
||||||
ready: true,
|
path: "/blocks/hero",
|
||||||
|
defaultReady: true,
|
||||||
component: <HeroBlock />,
|
component: <HeroBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ceo",
|
id: "ceo",
|
||||||
name: "Вводный текст (CEO-блок)",
|
name: "Вводный текст (CEO-блок)",
|
||||||
href: "/blocks/ceo",
|
href: "/blocks/ceo",
|
||||||
ready: false,
|
path: "/blocks/ceo",
|
||||||
|
defaultReady: false,
|
||||||
|
component: <CeoBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "doctors",
|
id: "doctors",
|
||||||
name: "Наши врачи",
|
name: "Наши врачи",
|
||||||
href: "/blocks/doctors",
|
href: "/blocks/doctors",
|
||||||
ready: true,
|
path: "/blocks/doctors",
|
||||||
|
defaultReady: true,
|
||||||
component: <DoctorsBlock />,
|
component: <DoctorsBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "reviews",
|
id: "reviews",
|
||||||
name: "Отзывы",
|
name: "Отзывы",
|
||||||
href: "/blocks/reviews",
|
href: "/blocks/reviews",
|
||||||
ready: false,
|
path: "/blocks/reviews",
|
||||||
|
defaultReady: false,
|
||||||
|
component: <ReviewsBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "contact-forms",
|
id: "contact-forms",
|
||||||
name: "Формы записи",
|
name: "Формы записи",
|
||||||
href: "/blocks/contact-forms",
|
href: "/blocks/contact-forms",
|
||||||
ready: false,
|
path: "/blocks/contact-forms",
|
||||||
|
defaultReady: false,
|
||||||
|
component: <ContactFormsBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "news",
|
id: "news",
|
||||||
name: "Новости",
|
name: "Новости",
|
||||||
href: "/blocks/news",
|
href: "/blocks/news",
|
||||||
ready: false,
|
path: "/blocks/news",
|
||||||
|
defaultReady: false,
|
||||||
|
component: <NewsBlock />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "footer",
|
id: "footer",
|
||||||
name: "Подвал / Контакт",
|
name: "Подвал / Контакт",
|
||||||
href: "/blocks/contact",
|
href: "/blocks/contact",
|
||||||
ready: false,
|
path: "/blocks/contact",
|
||||||
|
defaultReady: false,
|
||||||
|
component: <FooterBlock />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const READY_COUNT = BLOCKS.filter((b) => b.ready).length;
|
|
||||||
|
|
||||||
export function PreviewClient() {
|
export function PreviewClient() {
|
||||||
const [created, setCreated] = useState(false);
|
const [created, setCreated] = useState(false);
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
// Map of path → isInPreview from API; null = not loaded yet
|
||||||
|
const [apiMeta, setApiMeta] = useState<Record<string, boolean> | null>(null);
|
||||||
|
|
||||||
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
@@ -103,6 +126,27 @@ export function PreviewClient() {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!apiUrl) return;
|
||||||
|
fetch(`${apiUrl}/blocks`)
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((data: Array<{ path: string; isInPreview: boolean }>) => {
|
||||||
|
const map: Record<string, boolean> = {};
|
||||||
|
for (const b of data) map[b.path] = b.isInPreview;
|
||||||
|
setApiMeta(map);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// API offline — fall back to defaultReady
|
||||||
|
});
|
||||||
|
}, [apiUrl]);
|
||||||
|
|
||||||
|
function isReady(block: BlockDef): boolean {
|
||||||
|
if (apiMeta !== null && block.path in apiMeta) {
|
||||||
|
return apiMeta[block.path] && !!block.component;
|
||||||
|
}
|
||||||
|
return block.defaultReady && !!block.component;
|
||||||
|
}
|
||||||
|
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
localStorage.setItem(STORAGE_KEY, "true");
|
localStorage.setItem(STORAGE_KEY, "true");
|
||||||
setCreated(true);
|
setCreated(true);
|
||||||
@@ -113,9 +157,10 @@ export function PreviewClient() {
|
|||||||
setCreated(false);
|
setCreated(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid hydration mismatch — render nothing until mounted
|
|
||||||
if (!mounted) return null;
|
if (!mounted) return null;
|
||||||
|
|
||||||
|
const readyCount = BLOCKS.filter(isReady).length;
|
||||||
|
|
||||||
/* ── ПУСТОЕ СОСТОЯНИЕ ── */
|
/* ── ПУСТОЕ СОСТОЯНИЕ ── */
|
||||||
if (!created) {
|
if (!created) {
|
||||||
return (
|
return (
|
||||||
@@ -147,11 +192,11 @@ export function PreviewClient() {
|
|||||||
style={{ background: "#22c55e" }}
|
style={{ background: "#22c55e" }}
|
||||||
/>
|
/>
|
||||||
<span style={{ color: "var(--bb-text-muted)" }}>
|
<span style={{ color: "var(--bb-text-muted)" }}>
|
||||||
Готово блоков: {READY_COUNT} из {BLOCKS.length}
|
Готово блоков: {readyCount} из {BLOCKS.length}
|
||||||
</span>
|
</span>
|
||||||
<span style={{ color: "var(--bb-text-muted)" }}>·</span>
|
<span style={{ color: "var(--bb-text-muted)" }}>·</span>
|
||||||
<span style={{ color: "var(--bb-text-muted)" }}>
|
<span style={{ color: "var(--bb-text-muted)" }}>
|
||||||
{BLOCKS.length - READY_COUNT} плейсхолдеров
|
{BLOCKS.length - readyCount} плейсхолдеров
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -183,7 +228,10 @@ export function PreviewClient() {
|
|||||||
Просмотр текущей страницы
|
Просмотр текущей страницы
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs mt-0.5" style={{ color: "var(--bb-text-muted)" }}>
|
<p className="text-xs mt-0.5" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
perm.oclinica.ru/lor · {READY_COUNT}/{BLOCKS.length} блоков готово
|
perm.oclinica.ru/lor · {readyCount}/{BLOCKS.length} блоков готово
|
||||||
|
{apiMeta === null && apiUrl && (
|
||||||
|
<span style={{ color: "#f59e0b" }}> · API загружается...</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={handleRebuild} className="bb-btn bb-btn-sm bb-btn-outline">
|
<button onClick={handleRebuild} className="bb-btn bb-btn-sm bb-btn-outline">
|
||||||
@@ -194,7 +242,7 @@ export function PreviewClient() {
|
|||||||
{/* Собранная страница */}
|
{/* Собранная страница */}
|
||||||
<div className="max-w-5xl mx-auto px-8 py-8 space-y-12">
|
<div className="max-w-5xl mx-auto px-8 py-8 space-y-12">
|
||||||
{BLOCKS.map((block) =>
|
{BLOCKS.map((block) =>
|
||||||
block.ready && block.component ? (
|
isReady(block) ? (
|
||||||
<section key={block.id}>{block.component}</section>
|
<section key={block.id}>{block.component}</section>
|
||||||
) : (
|
) : (
|
||||||
<section key={block.id}>
|
<section key={block.id}>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
export const CEO_QUESTIONS = [
|
||||||
|
"У вас болит ухо, заложен нос, першит в горле, и вы не можете понять причину?",
|
||||||
|
"Вам срочно нужен платный ЛОР в Перми или, как ещё говорят, «ухогорлонос»?",
|
||||||
|
"Заболел ребёнок?",
|
||||||
|
"Срочно ищете частные ЛОР-клиники Перми для детей 0+ и взрослых с удобным режимом работы с 9:00 до 21:00 по будням?",
|
||||||
|
"Вам назначили проведение эндоскопической операции на ухе, горле или носе?",
|
||||||
|
];
|
||||||
|
|
||||||
|
export function CeoBlock() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: "#fff",
|
||||||
|
fontFamily: "var(--font-web, 'Fira Sans', sans-serif)",
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 1.75,
|
||||||
|
color: "#374151",
|
||||||
|
padding: "40px 48px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Клиника ухо, нос специализируется на оториноларингологии – лечении взрослых и детей
|
||||||
|
с ЛОР заболеваниями. ЛОР клиника представлена на двух адресах:{" "}
|
||||||
|
<a href="#" style={{ color: "#0089c3" }}>ул. Цитная, 9</a>,{" "}
|
||||||
|
<a href="#" style={{ color: "#0089c3" }}>ул. Г. Звезда, 31а</a>.{" "}
|
||||||
|
Это <a href="#" style={{ color: "#0089c3" }}>Клиника лечения кашля и аллергии</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={{ marginTop: 16 }}>
|
||||||
|
{CEO_QUESTIONS.map((q) => (
|
||||||
|
<p key={q} style={{ marginBottom: 8 }}>— {q}</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style={{ marginTop: 16 }}>
|
||||||
|
Обращайтесь в ЛОР центр ухо, горло, нос в Перми, наши врачи оториноларингологи обязательно Вам помогут!
|
||||||
|
</p>
|
||||||
|
<p style={{ marginTop: 8 }}>
|
||||||
|
Клиника ЛОР болезней ухо, горло, нос – это наиболее современный центр оториноларингологии в Перми,
|
||||||
|
благодаря эндоскопическому оборудованию, высокому профессионализму оториноларингологов.
|
||||||
|
</p>
|
||||||
|
<p style={{ marginTop: 16, fontWeight: 500 }}>Будьте здоровы!</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
export function ContactFormsBlock() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-8">
|
||||||
|
{/* Форма 1: Будьте здоровы */}
|
||||||
|
<div
|
||||||
|
className="rounded-xl py-10 px-8 flex flex-col items-center gap-6"
|
||||||
|
style={{ background: "#b8e6ed" }}
|
||||||
|
>
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-2xl font-bold mb-1" style={{ color: "#111827" }}>
|
||||||
|
Будьте здоровы!
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm" style={{ color: "#374151" }}>
|
||||||
|
Запишитесь на приём к врачу!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-full max-w-sm space-y-3">
|
||||||
|
<input
|
||||||
|
className="bb-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Введите ваше имя"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="bb-input"
|
||||||
|
type="tel"
|
||||||
|
placeholder="Введите ваш телефон"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<select className="bb-select" disabled>
|
||||||
|
<option>Выберите ЛОР врача</option>
|
||||||
|
</select>
|
||||||
|
<label className="flex items-start gap-2 text-xs cursor-pointer" style={{ color: "#374151" }}>
|
||||||
|
<input type="checkbox" className="bb-checkbox mt-0.5" readOnly />
|
||||||
|
<span>
|
||||||
|
Отправляя данные, я даю согласие на обработку персональных данных
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<button className="bb-btn bb-btn-md bb-btn-primary w-full">
|
||||||
|
Запишите меня!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Форма 2: Узнайте стоимость */}
|
||||||
|
<div
|
||||||
|
className="rounded-xl py-10 px-8 flex flex-col items-center gap-6"
|
||||||
|
style={{ background: "#fff", border: "1px solid var(--bb-border)" }}
|
||||||
|
>
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-2xl font-bold mb-1" style={{ color: "#111827" }}>
|
||||||
|
Узнайте стоимость операции
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm" style={{ color: "#374151" }}>
|
||||||
|
Проконсультируйтесь с ассистентом хирурга
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-full max-w-sm space-y-3">
|
||||||
|
<input
|
||||||
|
className="bb-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Введите ваше имя"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="bb-input"
|
||||||
|
type="tel"
|
||||||
|
placeholder="Введите ваш телефон"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<label className="flex items-start gap-2 text-xs cursor-pointer" style={{ color: "#374151" }}>
|
||||||
|
<input type="checkbox" className="bb-checkbox mt-0.5" readOnly />
|
||||||
|
<span>
|
||||||
|
Отправляя данные, я даю согласие на обработку персональных данных
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<button className="bb-btn bb-btn-md bb-btn-primary w-full">
|
||||||
|
Перезвоните мне
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
const FOOTER_COLUMNS = [
|
||||||
|
{
|
||||||
|
title: "О клинике",
|
||||||
|
links: ["Лицензия", "Миссия", "Врачи", "Вакансии", "История", "Образовательная деятельность", "При инфо"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Заболевания",
|
||||||
|
links: ["Ринит", "Отит", "Гайморит", "Тонзиллит", "Полипы носа", "Искривление перегородки"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Вопрос-ответ",
|
||||||
|
links: [
|
||||||
|
"Что нужно знать до операции на ухо",
|
||||||
|
"Что нужно знать до операции на нос",
|
||||||
|
"Отзывы до и после лечения у детей",
|
||||||
|
"Что нужно знать при лечении у детей",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Операции",
|
||||||
|
links: ["Септопластика", "Турбинопластика", "Тонзиллэктомия", "Аденотомия", "Тимпанопластика", "Мирингопластика"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function FooterBlock() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="rounded-xl overflow-hidden"
|
||||||
|
style={{ border: "1px solid var(--bb-border)" }}
|
||||||
|
>
|
||||||
|
{/* 4 колонки ссылок */}
|
||||||
|
<div
|
||||||
|
className="grid grid-cols-4 gap-6 p-8"
|
||||||
|
style={{ background: "#f8f9fa", borderBottom: "1px solid #e5e7eb" }}
|
||||||
|
>
|
||||||
|
{FOOTER_COLUMNS.map((col) => (
|
||||||
|
<div key={col.title}>
|
||||||
|
<p className="font-semibold text-sm mb-3" style={{ color: "#111827" }}>
|
||||||
|
{col.title}
|
||||||
|
</p>
|
||||||
|
<ul className="space-y-1.5">
|
||||||
|
{col.links.map((link) => (
|
||||||
|
<li key={link}>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-xs bb-nav-link"
|
||||||
|
style={{ color: "#53514e", textDecoration: "none" }}
|
||||||
|
>
|
||||||
|
{link}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Нижняя полоса */}
|
||||||
|
<div
|
||||||
|
className="flex items-start justify-between gap-6 px-8 py-5"
|
||||||
|
style={{ background: "#fff" }}
|
||||||
|
>
|
||||||
|
{/* Логотип */}
|
||||||
|
<div className="flex items-center gap-2 shrink-0">
|
||||||
|
<div
|
||||||
|
className="w-10 h-10 rounded-full flex items-center justify-center text-white font-bold"
|
||||||
|
style={{ background: "#0089c3" }}
|
||||||
|
>
|
||||||
|
✚
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className="text-[10px] font-bold uppercase leading-tight"
|
||||||
|
style={{ color: "#53514e" }}
|
||||||
|
>
|
||||||
|
Клиника<br />ухо, горло, нос
|
||||||
|
</div>
|
||||||
|
<div className="text-[8px] leading-tight" style={{ color: "#9ca3af" }}>
|
||||||
|
им. проф. Е.Н.Оленевой
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Адрес и соцсети */}
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<p className="text-xs" style={{ color: "#374151" }}>
|
||||||
|
Мы находимся по адресу: Пермь, ул. Г. Звезда, 31а
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
{["VK", "OK", "YT", "TG"].map((s) => (
|
||||||
|
<a
|
||||||
|
key={s}
|
||||||
|
href="#"
|
||||||
|
className="text-[11px] px-2 py-1 rounded"
|
||||||
|
style={{
|
||||||
|
background: "var(--bb-sidebar-bg)",
|
||||||
|
border: "1px solid var(--bb-border)",
|
||||||
|
color: "var(--bb-text-muted)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{s}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Часы работы */}
|
||||||
|
<div className="text-right text-xs space-y-1" style={{ color: "#374151" }}>
|
||||||
|
<p className="font-semibold text-xs mb-1" style={{ color: "#111827" }}>
|
||||||
|
Часы работы:
|
||||||
|
</p>
|
||||||
|
<p>Пн–пт: 9:00–21:00</p>
|
||||||
|
<p>Сб: 9:00–18:00</p>
|
||||||
|
<p>Вс: выходной</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
const MOCK_NEWS = [
|
||||||
|
{
|
||||||
|
date: "20.12.2025",
|
||||||
|
title: "Наша работа клиники и новогодние праздники",
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: "11.08.2025",
|
||||||
|
title: "СЕРВИС ОБНОВЛЕНИЕ: Обновление графика работы клиники",
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: "12.06.2025",
|
||||||
|
title: "СЕРВИС ОБНОВЛЕНИЕ: Временное изменение работы клиники 22.06.25 г.",
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: "11.06.2025",
|
||||||
|
title: "График работы клиники в ближайшие праздники",
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function NewsBlock() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="rounded-xl p-8 space-y-6"
|
||||||
|
style={{ background: "#fff", border: "1px solid var(--bb-border)" }}
|
||||||
|
>
|
||||||
|
<h2 className="text-xl font-bold" style={{ color: "#111827" }}>
|
||||||
|
Новости
|
||||||
|
</h2>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
|
{MOCK_NEWS.map((n) => (
|
||||||
|
<div
|
||||||
|
key={n.date}
|
||||||
|
className="bb-news-card rounded-lg p-3 cursor-pointer"
|
||||||
|
style={{
|
||||||
|
border: "1px solid var(--bb-border)",
|
||||||
|
transition: "background 0.15s, box-shadow 0.15s",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className="text-xs mb-2" style={{ color: "#6b7280" }}>
|
||||||
|
{n.date}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={n.href}
|
||||||
|
className="text-sm font-medium leading-snug block"
|
||||||
|
style={{ color: "#0089c3", textDecoration: "none" }}
|
||||||
|
>
|
||||||
|
{n.title}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<button className="bb-btn bb-btn-md bb-btn-outline">
|
||||||
|
Все новости
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
const MOCK_REVIEWS = [
|
||||||
|
{
|
||||||
|
text: "Спасибо за приём, мне всё понравилось, спасибо за приём, мне всё понравилось. Врач очень внимательный и профессиональный.",
|
||||||
|
author: "Пациент клиники",
|
||||||
|
doctor: "Тимофеева Наталья Александровна",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Очень довольна лечением! Прошла курс процедур, нос дышит отлично. Рекомендую клинику всем.",
|
||||||
|
author: "Наталья К.",
|
||||||
|
doctor: "Макарова Людмила Тимофеевна",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function ReviewsBlock() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="rounded-xl p-8 space-y-6"
|
||||||
|
style={{ background: "#fff", border: "1px solid var(--bb-border)" }}
|
||||||
|
>
|
||||||
|
{/* Заголовок */}
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-bold mb-2" style={{ color: "#111827" }}>
|
||||||
|
Отзывы о нас
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm" style={{ color: "#374151", lineHeight: 1.7 }}>
|
||||||
|
За 12 лет работы наши врачи оториноларингологи избавили от болезней ухо, горло, носа
|
||||||
|
более 50 000 пациентов. Но даже сейчас мы высоко ценим каждый положительный отзыв
|
||||||
|
и искренние слова благодарности.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Карусель */}
|
||||||
|
<div className="relative flex items-center gap-4">
|
||||||
|
{/* Стрелка влево */}
|
||||||
|
<button
|
||||||
|
className="shrink-0 w-10 h-10 rounded-full flex items-center justify-center font-bold text-lg"
|
||||||
|
style={{
|
||||||
|
background: "var(--bb-sidebar-bg)",
|
||||||
|
border: "1px solid var(--bb-border)",
|
||||||
|
color: "var(--bb-text-muted)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
‹
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Карточка отзыва */}
|
||||||
|
<div
|
||||||
|
className="flex-1 rounded-xl p-6"
|
||||||
|
style={{ background: "#eef4d1" }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="text-8xl leading-none mb-2 font-serif"
|
||||||
|
style={{ color: "#b8e6ed", lineHeight: 0.8 }}
|
||||||
|
>
|
||||||
|
«
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
className="text-sm italic leading-relaxed"
|
||||||
|
style={{ color: "#374151" }}
|
||||||
|
>
|
||||||
|
{MOCK_REVIEWS[0].text}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="inline-block mt-3 text-sm"
|
||||||
|
style={{ color: "#0089c3" }}
|
||||||
|
>
|
||||||
|
Читать отзыв полностью
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Стрелка вправо */}
|
||||||
|
<button
|
||||||
|
className="shrink-0 w-10 h-10 rounded-full flex items-center justify-center font-bold text-lg"
|
||||||
|
style={{
|
||||||
|
background: "var(--bb-sidebar-bg)",
|
||||||
|
border: "1px solid var(--bb-border)",
|
||||||
|
color: "var(--bb-text-muted)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
›
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
interface BlockMeta {
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
isInPreview: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BlockMetaBarProps {
|
||||||
|
path: string;
|
||||||
|
defaultVersion: string;
|
||||||
|
defaultIsInPreview: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: BlockMetaBarProps) {
|
||||||
|
const [meta, setMeta] = useState<BlockMeta | null>(null);
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [versionInput, setVersionInput] = useState(defaultVersion);
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [togglingPreview, setTogglingPreview] = useState(false);
|
||||||
|
const [apiDown, setApiDown] = useState(false);
|
||||||
|
|
||||||
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!apiUrl) { setApiDown(true); return; }
|
||||||
|
fetch(`${apiUrl}/blocks/by-path?path=${encodeURIComponent(path)}`)
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((data: BlockMeta) => {
|
||||||
|
setMeta(data);
|
||||||
|
setVersionInput(data.version);
|
||||||
|
})
|
||||||
|
.catch(() => setApiDown(true));
|
||||||
|
}, [apiUrl, path]);
|
||||||
|
|
||||||
|
async function patch(data: { version?: string; isInPreview?: boolean }) {
|
||||||
|
if (!apiUrl) return null;
|
||||||
|
const r = await fetch(`${apiUrl}/blocks/by-path?path=${encodeURIComponent(path)}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
return r.json() as Promise<BlockMeta>;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveVersion() {
|
||||||
|
if (!meta) return;
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
const updated = await patch({ version: versionInput });
|
||||||
|
if (updated) { setMeta(updated); setEditing(false); }
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function togglePreview() {
|
||||||
|
if (!meta) return;
|
||||||
|
setTogglingPreview(true);
|
||||||
|
try {
|
||||||
|
const updated = await patch({ isInPreview: !meta.isInPreview });
|
||||||
|
if (updated) setMeta(updated);
|
||||||
|
} finally {
|
||||||
|
setTogglingPreview(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = meta?.version ?? defaultVersion;
|
||||||
|
const isInPreview = meta?.isInPreview ?? defaultIsInPreview;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex items-center flex-wrap gap-3 px-4 py-2 rounded-lg text-xs mb-6"
|
||||||
|
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}
|
||||||
|
>
|
||||||
|
{/* Version badge */}
|
||||||
|
<span className="font-semibold" style={{ color: "var(--bb-text-muted)" }}>
|
||||||
|
Версия:
|
||||||
|
</span>
|
||||||
|
{editing ? (
|
||||||
|
<span className="flex items-center gap-1.5">
|
||||||
|
<input
|
||||||
|
className="px-2 py-0.5 rounded border text-xs font-mono w-20"
|
||||||
|
style={{ borderColor: "var(--bb-border)", color: "var(--bb-text)" }}
|
||||||
|
value={versionInput}
|
||||||
|
onChange={(e) => setVersionInput(e.target.value)}
|
||||||
|
onKeyDown={(e) => { if (e.key === 'Enter') saveVersion(); if (e.key === 'Escape') setEditing(false); }}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={saveVersion}
|
||||||
|
disabled={saving}
|
||||||
|
className="px-2 py-0.5 rounded text-xs font-medium"
|
||||||
|
style={{ background: "var(--brand-053m)", color: "#fff" }}
|
||||||
|
>
|
||||||
|
{saving ? '...' : 'Сохранить'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => { setEditing(false); setVersionInput(version); }}
|
||||||
|
className="px-2 py-0.5 rounded text-xs"
|
||||||
|
style={{ color: "var(--bb-text-muted)" }}
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => !apiDown && setEditing(true)}
|
||||||
|
title={apiDown ? 'API недоступен' : 'Изменить версию'}
|
||||||
|
className="font-mono px-2 py-0.5 rounded"
|
||||||
|
style={{
|
||||||
|
background: "var(--bb-sidebar-active-bg, #dff0fa)",
|
||||||
|
color: "var(--brand-053m)",
|
||||||
|
cursor: apiDown ? 'default' : 'pointer',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{version}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span style={{ color: "var(--bb-border)" }}>·</span>
|
||||||
|
|
||||||
|
{/* Preview toggle */}
|
||||||
|
{apiDown ? (
|
||||||
|
<span
|
||||||
|
className="flex items-center gap-1"
|
||||||
|
style={{ color: isInPreview ? "#16a34a" : "var(--bb-text-muted)" }}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="inline-block w-1.5 h-1.5 rounded-full"
|
||||||
|
style={{ background: isInPreview ? "#22c55e" : "#d1d5db" }}
|
||||||
|
/>
|
||||||
|
{isInPreview ? "В превью" : "Не в превью"}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={togglePreview}
|
||||||
|
disabled={togglingPreview || !meta}
|
||||||
|
title={isInPreview ? "Убрать из превью страницы" : "Включить в превью страницы"}
|
||||||
|
className="flex items-center gap-1.5 px-2.5 py-1 rounded font-medium transition-colors"
|
||||||
|
style={isInPreview ? {
|
||||||
|
background: "#dcfce7",
|
||||||
|
color: "#16a34a",
|
||||||
|
border: "1px solid #86efac",
|
||||||
|
} : {
|
||||||
|
background: "var(--bb-sidebar-bg)",
|
||||||
|
color: "var(--bb-text-muted)",
|
||||||
|
border: "1px solid var(--bb-border)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="inline-block w-1.5 h-1.5 rounded-full"
|
||||||
|
style={{ background: isInPreview ? "#22c55e" : "#d1d5db" }}
|
||||||
|
/>
|
||||||
|
{togglingPreview
|
||||||
|
? '...'
|
||||||
|
: isInPreview
|
||||||
|
? "В превью"
|
||||||
|
: "Добавить в превью"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{apiDown && (
|
||||||
|
<>
|
||||||
|
<span style={{ color: "var(--bb-border)" }}>·</span>
|
||||||
|
<span style={{ color: "#f59e0b" }}>API офлайн</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+1
-1
@@ -8,7 +8,7 @@ services:
|
|||||||
POSTGRES_PASSWORD: brandbook
|
POSTGRES_PASSWORD: brandbook
|
||||||
POSTGRES_DB: brandbook
|
POSTGRES_DB: brandbook
|
||||||
ports:
|
ports:
|
||||||
- "5433:5432"
|
- "5434:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
|||||||
Generated
+492
@@ -19,12 +19,21 @@ importers:
|
|||||||
'@nestjs/platform-express':
|
'@nestjs/platform-express':
|
||||||
specifier: ^11.0.1
|
specifier: ^11.0.1
|
||||||
version: 11.1.17(@nestjs/common@11.1.17(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)
|
version: 11.1.17(@nestjs/common@11.1.17(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)
|
||||||
|
'@prisma/adapter-pg':
|
||||||
|
specifier: ^7.5.0
|
||||||
|
version: 7.5.0
|
||||||
'@prisma/client':
|
'@prisma/client':
|
||||||
specifier: ^7.5.0
|
specifier: ^7.5.0
|
||||||
version: 7.5.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)
|
version: 7.5.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)
|
||||||
|
'@types/pg':
|
||||||
|
specifier: ^8.20.0
|
||||||
|
version: 8.20.0
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^17.3.1
|
specifier: ^17.3.1
|
||||||
version: 17.3.1
|
version: 17.3.1
|
||||||
|
pg:
|
||||||
|
specifier: ^8.20.0
|
||||||
|
version: 8.20.0
|
||||||
reflect-metadata:
|
reflect-metadata:
|
||||||
specifier: ^0.2.2
|
specifier: ^0.2.2
|
||||||
version: 0.2.2
|
version: 0.2.2
|
||||||
@@ -98,6 +107,9 @@ importers:
|
|||||||
tsconfig-paths:
|
tsconfig-paths:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
|
tsx:
|
||||||
|
specifier: ^4.21.0
|
||||||
|
version: 4.21.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.7.3
|
specifier: ^5.7.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
@@ -390,6 +402,162 @@ packages:
|
|||||||
'@emnapi/wasi-threads@1.2.0':
|
'@emnapi/wasi-threads@1.2.0':
|
||||||
resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==}
|
resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==}
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [aix]
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.27.4':
|
||||||
|
resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.27.4':
|
||||||
|
resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.27.4':
|
||||||
|
resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.27.4':
|
||||||
|
resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.27.4':
|
||||||
|
resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.27.4':
|
||||||
|
resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.27.4':
|
||||||
|
resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@eslint-community/eslint-utils@4.9.1':
|
'@eslint-community/eslint-utils@4.9.1':
|
||||||
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
|
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
@@ -1015,6 +1183,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
||||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||||
|
|
||||||
|
'@prisma/adapter-pg@7.5.0':
|
||||||
|
resolution: {integrity: sha512-EJx7OLULahcC3IjJgdx2qRDNCT+ToY2v66UkeETMCLhNOTgqVzRzYvOEphY7Zp0eHyzfkC33Edd/qqeadf9R4A==}
|
||||||
|
|
||||||
'@prisma/client-runtime-utils@7.5.0':
|
'@prisma/client-runtime-utils@7.5.0':
|
||||||
resolution: {integrity: sha512-KnJ2b4Si/pcWEtK68uM+h0h1oh80CZt2suhLTVuLaSKg4n58Q9jBF/A42Kw6Ma+aThy1yAhfDeTC0JvEmeZnFQ==}
|
resolution: {integrity: sha512-KnJ2b4Si/pcWEtK68uM+h0h1oh80CZt2suhLTVuLaSKg4n58Q9jBF/A42Kw6Ma+aThy1yAhfDeTC0JvEmeZnFQ==}
|
||||||
|
|
||||||
@@ -1042,6 +1213,9 @@ packages:
|
|||||||
'@prisma/dev@0.20.0':
|
'@prisma/dev@0.20.0':
|
||||||
resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==}
|
resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==}
|
||||||
|
|
||||||
|
'@prisma/driver-adapter-utils@7.5.0':
|
||||||
|
resolution: {integrity: sha512-B79N/amgV677mFesFDBAdrW0OIaqawap9E0sjgLBtzIz2R3hIMS1QB8mLZuUEiS4q5Y8Oh3I25Kw4SLxMypk9Q==}
|
||||||
|
|
||||||
'@prisma/engines-version@7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e':
|
'@prisma/engines-version@7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e':
|
||||||
resolution: {integrity: sha512-E+iRV/vbJLl8iGjVr6g/TEWokA+gjkV/doZkaQN1i/ULVdDwGnPJDfLUIFGS3BVwlG/m6L8T4x1x5isl8hGMxA==}
|
resolution: {integrity: sha512-E+iRV/vbJLl8iGjVr6g/TEWokA+gjkV/doZkaQN1i/ULVdDwGnPJDfLUIFGS3BVwlG/m6L8T4x1x5isl8hGMxA==}
|
||||||
|
|
||||||
@@ -1262,6 +1436,12 @@ packages:
|
|||||||
'@types/node@22.19.15':
|
'@types/node@22.19.15':
|
||||||
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
|
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
|
||||||
|
|
||||||
|
'@types/pg@8.11.11':
|
||||||
|
resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==}
|
||||||
|
|
||||||
|
'@types/pg@8.20.0':
|
||||||
|
resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==}
|
||||||
|
|
||||||
'@types/qs@6.15.0':
|
'@types/qs@6.15.0':
|
||||||
resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==}
|
resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==}
|
||||||
|
|
||||||
@@ -2141,6 +2321,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
|
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
esbuild@0.27.4:
|
||||||
|
resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
escalade@3.2.0:
|
escalade@3.2.0:
|
||||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3382,6 +3567,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
|
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
obuf@1.1.2:
|
||||||
|
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
|
||||||
|
|
||||||
ohash@2.0.11:
|
ohash@2.0.11:
|
||||||
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
|
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
|
||||||
|
|
||||||
@@ -3479,6 +3667,48 @@ packages:
|
|||||||
perfect-debounce@1.0.0:
|
perfect-debounce@1.0.0:
|
||||||
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||||
|
|
||||||
|
pg-cloudflare@1.3.0:
|
||||||
|
resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==}
|
||||||
|
|
||||||
|
pg-connection-string@2.12.0:
|
||||||
|
resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==}
|
||||||
|
|
||||||
|
pg-int8@1.0.1:
|
||||||
|
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
||||||
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
||||||
|
pg-numeric@1.0.2:
|
||||||
|
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
pg-pool@3.13.0:
|
||||||
|
resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==}
|
||||||
|
peerDependencies:
|
||||||
|
pg: '>=8.0'
|
||||||
|
|
||||||
|
pg-protocol@1.13.0:
|
||||||
|
resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==}
|
||||||
|
|
||||||
|
pg-types@2.2.0:
|
||||||
|
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
pg-types@4.1.0:
|
||||||
|
resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
pg@8.20.0:
|
||||||
|
resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==}
|
||||||
|
engines: {node: '>= 16.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
pg-native: '>=3.0.1'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
pg-native:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
pgpass@1.0.5:
|
||||||
|
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
|
||||||
|
|
||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
@@ -3521,6 +3751,41 @@ packages:
|
|||||||
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
|
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
postgres-array@2.0.0:
|
||||||
|
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
postgres-array@3.0.4:
|
||||||
|
resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
postgres-bytea@1.0.1:
|
||||||
|
resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
postgres-bytea@3.0.0:
|
||||||
|
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
postgres-date@1.0.7:
|
||||||
|
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
postgres-date@2.1.0:
|
||||||
|
resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
postgres-interval@1.2.0:
|
||||||
|
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
postgres-interval@3.0.0:
|
||||||
|
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
postgres-range@1.1.4:
|
||||||
|
resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==}
|
||||||
|
|
||||||
postgres@3.4.7:
|
postgres@3.4.7:
|
||||||
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
|
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -3816,6 +4081,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
|
split2@4.2.0:
|
||||||
|
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||||
|
engines: {node: '>= 10.x'}
|
||||||
|
|
||||||
sprintf-js@1.0.3:
|
sprintf-js@1.0.3:
|
||||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||||
|
|
||||||
@@ -4075,6 +4344,11 @@ packages:
|
|||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
tsx@4.21.0:
|
||||||
|
resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -4267,6 +4541,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
|
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
|
||||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
|
|
||||||
|
xtend@4.0.2:
|
||||||
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
|
engines: {node: '>=0.4'}
|
||||||
|
|
||||||
y18n@5.0.8:
|
y18n@5.0.8:
|
||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -4603,6 +4881,84 @@ snapshots:
|
|||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.27.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
|
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.4(jiti@2.6.1)
|
eslint: 9.39.4(jiti@2.6.1)
|
||||||
@@ -5282,6 +5638,15 @@ snapshots:
|
|||||||
|
|
||||||
'@pkgr/core@0.2.9': {}
|
'@pkgr/core@0.2.9': {}
|
||||||
|
|
||||||
|
'@prisma/adapter-pg@7.5.0':
|
||||||
|
dependencies:
|
||||||
|
'@prisma/driver-adapter-utils': 7.5.0
|
||||||
|
'@types/pg': 8.11.11
|
||||||
|
pg: 8.20.0
|
||||||
|
postgres-array: 3.0.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- pg-native
|
||||||
|
|
||||||
'@prisma/client-runtime-utils@7.5.0': {}
|
'@prisma/client-runtime-utils@7.5.0': {}
|
||||||
|
|
||||||
'@prisma/client@7.5.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)':
|
'@prisma/client@7.5.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)':
|
||||||
@@ -5326,6 +5691,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
'@prisma/driver-adapter-utils@7.5.0':
|
||||||
|
dependencies:
|
||||||
|
'@prisma/debug': 7.5.0
|
||||||
|
|
||||||
'@prisma/engines-version@7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e': {}
|
'@prisma/engines-version@7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e': {}
|
||||||
|
|
||||||
'@prisma/engines@7.5.0':
|
'@prisma/engines@7.5.0':
|
||||||
@@ -5554,6 +5923,18 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.21.0
|
undici-types: 6.21.0
|
||||||
|
|
||||||
|
'@types/pg@8.11.11':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.15
|
||||||
|
pg-protocol: 1.13.0
|
||||||
|
pg-types: 4.1.0
|
||||||
|
|
||||||
|
'@types/pg@8.20.0':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.15
|
||||||
|
pg-protocol: 1.13.0
|
||||||
|
pg-types: 2.2.0
|
||||||
|
|
||||||
'@types/qs@6.15.0': {}
|
'@types/qs@6.15.0': {}
|
||||||
|
|
||||||
'@types/range-parser@1.2.7': {}
|
'@types/range-parser@1.2.7': {}
|
||||||
@@ -6530,6 +6911,35 @@ snapshots:
|
|||||||
is-date-object: 1.1.0
|
is-date-object: 1.1.0
|
||||||
is-symbol: 1.1.1
|
is-symbol: 1.1.1
|
||||||
|
|
||||||
|
esbuild@0.27.4:
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/aix-ppc64': 0.27.4
|
||||||
|
'@esbuild/android-arm': 0.27.4
|
||||||
|
'@esbuild/android-arm64': 0.27.4
|
||||||
|
'@esbuild/android-x64': 0.27.4
|
||||||
|
'@esbuild/darwin-arm64': 0.27.4
|
||||||
|
'@esbuild/darwin-x64': 0.27.4
|
||||||
|
'@esbuild/freebsd-arm64': 0.27.4
|
||||||
|
'@esbuild/freebsd-x64': 0.27.4
|
||||||
|
'@esbuild/linux-arm': 0.27.4
|
||||||
|
'@esbuild/linux-arm64': 0.27.4
|
||||||
|
'@esbuild/linux-ia32': 0.27.4
|
||||||
|
'@esbuild/linux-loong64': 0.27.4
|
||||||
|
'@esbuild/linux-mips64el': 0.27.4
|
||||||
|
'@esbuild/linux-ppc64': 0.27.4
|
||||||
|
'@esbuild/linux-riscv64': 0.27.4
|
||||||
|
'@esbuild/linux-s390x': 0.27.4
|
||||||
|
'@esbuild/linux-x64': 0.27.4
|
||||||
|
'@esbuild/netbsd-arm64': 0.27.4
|
||||||
|
'@esbuild/netbsd-x64': 0.27.4
|
||||||
|
'@esbuild/openbsd-arm64': 0.27.4
|
||||||
|
'@esbuild/openbsd-x64': 0.27.4
|
||||||
|
'@esbuild/openharmony-arm64': 0.27.4
|
||||||
|
'@esbuild/sunos-x64': 0.27.4
|
||||||
|
'@esbuild/win32-arm64': 0.27.4
|
||||||
|
'@esbuild/win32-ia32': 0.27.4
|
||||||
|
'@esbuild/win32-x64': 0.27.4
|
||||||
|
|
||||||
escalade@3.2.0: {}
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
escape-html@1.0.3: {}
|
escape-html@1.0.3: {}
|
||||||
@@ -8033,6 +8443,8 @@ snapshots:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
|
obuf@1.1.2: {}
|
||||||
|
|
||||||
ohash@2.0.11: {}
|
ohash@2.0.11: {}
|
||||||
|
|
||||||
on-finished@2.4.1:
|
on-finished@2.4.1:
|
||||||
@@ -8133,6 +8545,53 @@ snapshots:
|
|||||||
|
|
||||||
perfect-debounce@1.0.0: {}
|
perfect-debounce@1.0.0: {}
|
||||||
|
|
||||||
|
pg-cloudflare@1.3.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
pg-connection-string@2.12.0: {}
|
||||||
|
|
||||||
|
pg-int8@1.0.1: {}
|
||||||
|
|
||||||
|
pg-numeric@1.0.2: {}
|
||||||
|
|
||||||
|
pg-pool@3.13.0(pg@8.20.0):
|
||||||
|
dependencies:
|
||||||
|
pg: 8.20.0
|
||||||
|
|
||||||
|
pg-protocol@1.13.0: {}
|
||||||
|
|
||||||
|
pg-types@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
pg-int8: 1.0.1
|
||||||
|
postgres-array: 2.0.0
|
||||||
|
postgres-bytea: 1.0.1
|
||||||
|
postgres-date: 1.0.7
|
||||||
|
postgres-interval: 1.2.0
|
||||||
|
|
||||||
|
pg-types@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
pg-int8: 1.0.1
|
||||||
|
pg-numeric: 1.0.2
|
||||||
|
postgres-array: 3.0.4
|
||||||
|
postgres-bytea: 3.0.0
|
||||||
|
postgres-date: 2.1.0
|
||||||
|
postgres-interval: 3.0.0
|
||||||
|
postgres-range: 1.1.4
|
||||||
|
|
||||||
|
pg@8.20.0:
|
||||||
|
dependencies:
|
||||||
|
pg-connection-string: 2.12.0
|
||||||
|
pg-pool: 3.13.0(pg@8.20.0)
|
||||||
|
pg-protocol: 1.13.0
|
||||||
|
pg-types: 2.2.0
|
||||||
|
pgpass: 1.0.5
|
||||||
|
optionalDependencies:
|
||||||
|
pg-cloudflare: 1.3.0
|
||||||
|
|
||||||
|
pgpass@1.0.5:
|
||||||
|
dependencies:
|
||||||
|
split2: 4.2.0
|
||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
@@ -8169,6 +8628,28 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
postgres-array@2.0.0: {}
|
||||||
|
|
||||||
|
postgres-array@3.0.4: {}
|
||||||
|
|
||||||
|
postgres-bytea@1.0.1: {}
|
||||||
|
|
||||||
|
postgres-bytea@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
obuf: 1.1.2
|
||||||
|
|
||||||
|
postgres-date@1.0.7: {}
|
||||||
|
|
||||||
|
postgres-date@2.1.0: {}
|
||||||
|
|
||||||
|
postgres-interval@1.2.0:
|
||||||
|
dependencies:
|
||||||
|
xtend: 4.0.2
|
||||||
|
|
||||||
|
postgres-interval@3.0.0: {}
|
||||||
|
|
||||||
|
postgres-range@1.1.4: {}
|
||||||
|
|
||||||
postgres@3.4.7: {}
|
postgres@3.4.7: {}
|
||||||
|
|
||||||
prelude-ls@1.2.1: {}
|
prelude-ls@1.2.1: {}
|
||||||
@@ -8532,6 +9013,8 @@ snapshots:
|
|||||||
|
|
||||||
source-map@0.7.6: {}
|
source-map@0.7.6: {}
|
||||||
|
|
||||||
|
split2@4.2.0: {}
|
||||||
|
|
||||||
sprintf-js@1.0.3: {}
|
sprintf-js@1.0.3: {}
|
||||||
|
|
||||||
sqlstring@2.3.3: {}
|
sqlstring@2.3.3: {}
|
||||||
@@ -8809,6 +9292,13 @@ snapshots:
|
|||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
tsx@4.21.0:
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.27.4
|
||||||
|
get-tsconfig: 4.13.6
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
@@ -9073,6 +9563,8 @@ snapshots:
|
|||||||
imurmurhash: 0.1.4
|
imurmurhash: 0.1.4
|
||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
|
|
||||||
|
xtend@4.0.2: {}
|
||||||
|
|
||||||
y18n@5.0.8: {}
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"buildCommand": "pnpm --filter web build",
|
||||||
|
"outputDirectory": "apps/web/.next",
|
||||||
|
"installCommand": "pnpm install"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user