Browse Source
Результаты спринта: - Monorepo (Next.js 16 + NestJS 11 + PostgreSQL/Prisma) - Docker Compose (PostgreSQL 16, порт 5433) - Боковая навигация со всеми разделами - Страница /foundation/logo с реальным логотипом из PDF - Цветовые варианты, охранная зона, таблица размеров, недопустимые варианты Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>sprint/2
48 changed files with 14376 additions and 23 deletions
@ -0,0 +1,8 @@ |
|||||||
|
# База данных |
||||||
|
DATABASE_URL="postgresql://brandbook:brandbook@localhost:5433/brandbook" |
||||||
|
|
||||||
|
# API (NestJS) |
||||||
|
API_PORT=3001 |
||||||
|
|
||||||
|
# Web (Next.js) |
||||||
|
NEXT_PUBLIC_API_URL=http://localhost:3001 |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
node_modules |
||||||
|
# Keep environment variables out of version control |
||||||
|
.env |
||||||
|
|
||||||
|
/generated/prisma |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
{ |
||||||
|
"singleQuote": true, |
||||||
|
"trailingComma": "all" |
||||||
|
} |
||||||
@ -0,0 +1,98 @@ |
|||||||
|
<p align="center"> |
||||||
|
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a> |
||||||
|
</p> |
||||||
|
|
||||||
|
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 |
||||||
|
[circleci-url]: https://circleci.com/gh/nestjs/nest |
||||||
|
|
||||||
|
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p> |
||||||
|
<p align="center"> |
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a> |
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a> |
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a> |
||||||
|
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a> |
||||||
|
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a> |
||||||
|
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a> |
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a> |
||||||
|
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a> |
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a> |
||||||
|
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a> |
||||||
|
</p> |
||||||
|
<!--[](https://opencollective.com/nest#backer) |
||||||
|
[](https://opencollective.com/nest#sponsor)--> |
||||||
|
|
||||||
|
## Description |
||||||
|
|
||||||
|
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. |
||||||
|
|
||||||
|
## Project setup |
||||||
|
|
||||||
|
```bash |
||||||
|
$ pnpm install |
||||||
|
``` |
||||||
|
|
||||||
|
## Compile and run the project |
||||||
|
|
||||||
|
```bash |
||||||
|
# development |
||||||
|
$ pnpm run start |
||||||
|
|
||||||
|
# watch mode |
||||||
|
$ pnpm run start:dev |
||||||
|
|
||||||
|
# production mode |
||||||
|
$ pnpm run start:prod |
||||||
|
``` |
||||||
|
|
||||||
|
## Run tests |
||||||
|
|
||||||
|
```bash |
||||||
|
# unit tests |
||||||
|
$ pnpm run test |
||||||
|
|
||||||
|
# e2e tests |
||||||
|
$ pnpm run test:e2e |
||||||
|
|
||||||
|
# test coverage |
||||||
|
$ pnpm run test:cov |
||||||
|
``` |
||||||
|
|
||||||
|
## Deployment |
||||||
|
|
||||||
|
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. |
||||||
|
|
||||||
|
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: |
||||||
|
|
||||||
|
```bash |
||||||
|
$ pnpm install -g @nestjs/mau |
||||||
|
$ mau deploy |
||||||
|
``` |
||||||
|
|
||||||
|
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. |
||||||
|
|
||||||
|
## Resources |
||||||
|
|
||||||
|
Check out a few resources that may come in handy when working with NestJS: |
||||||
|
|
||||||
|
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. |
||||||
|
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). |
||||||
|
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). |
||||||
|
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. |
||||||
|
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). |
||||||
|
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). |
||||||
|
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). |
||||||
|
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). |
||||||
|
|
||||||
|
## Support |
||||||
|
|
||||||
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). |
||||||
|
|
||||||
|
## Stay in touch |
||||||
|
|
||||||
|
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) |
||||||
|
- Website - [https://nestjs.com](https://nestjs.com/) |
||||||
|
- Twitter - [@nestframework](https://twitter.com/nestframework) |
||||||
|
|
||||||
|
## License |
||||||
|
|
||||||
|
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
// @ts-check
|
||||||
|
import eslint from '@eslint/js'; |
||||||
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; |
||||||
|
import globals from 'globals'; |
||||||
|
import tseslint from 'typescript-eslint'; |
||||||
|
|
||||||
|
export default tseslint.config( |
||||||
|
{ |
||||||
|
ignores: ['eslint.config.mjs'], |
||||||
|
}, |
||||||
|
eslint.configs.recommended, |
||||||
|
...tseslint.configs.recommendedTypeChecked, |
||||||
|
eslintPluginPrettierRecommended, |
||||||
|
{ |
||||||
|
languageOptions: { |
||||||
|
globals: { |
||||||
|
...globals.node, |
||||||
|
...globals.jest, |
||||||
|
}, |
||||||
|
sourceType: 'commonjs', |
||||||
|
parserOptions: { |
||||||
|
projectService: true, |
||||||
|
tsconfigRootDir: import.meta.dirname, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
rules: { |
||||||
|
'@typescript-eslint/no-explicit-any': 'off', |
||||||
|
'@typescript-eslint/no-floating-promises': 'warn', |
||||||
|
'@typescript-eslint/no-unsafe-argument': 'warn', |
||||||
|
"prettier/prettier": ["error", { endOfLine: "auto" }], |
||||||
|
}, |
||||||
|
}, |
||||||
|
); |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://json.schemastore.org/nest-cli", |
||||||
|
"collection": "@nestjs/schematics", |
||||||
|
"sourceRoot": "src", |
||||||
|
"compilerOptions": { |
||||||
|
"deleteOutDir": true |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,75 @@ |
|||||||
|
{ |
||||||
|
"name": "api", |
||||||
|
"version": "0.0.1", |
||||||
|
"description": "", |
||||||
|
"author": "", |
||||||
|
"private": true, |
||||||
|
"license": "UNLICENSED", |
||||||
|
"scripts": { |
||||||
|
"build": "nest build", |
||||||
|
"dev": "nest start --watch", |
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", |
||||||
|
"start": "nest start", |
||||||
|
"start:dev": "nest start --watch", |
||||||
|
"start:debug": "nest start --debug --watch", |
||||||
|
"start:prod": "node dist/main", |
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", |
||||||
|
"test": "jest", |
||||||
|
"test:watch": "jest --watch", |
||||||
|
"test:cov": "jest --coverage", |
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", |
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@nestjs/common": "^11.0.1", |
||||||
|
"@nestjs/core": "^11.0.1", |
||||||
|
"@nestjs/platform-express": "^11.0.1", |
||||||
|
"@prisma/client": "^7.5.0", |
||||||
|
"dotenv": "^17.3.1", |
||||||
|
"reflect-metadata": "^0.2.2", |
||||||
|
"rxjs": "^7.8.1" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@eslint/eslintrc": "^3.2.0", |
||||||
|
"@eslint/js": "^9.18.0", |
||||||
|
"@nestjs/cli": "^11.0.0", |
||||||
|
"@nestjs/schematics": "^11.0.0", |
||||||
|
"@nestjs/testing": "^11.0.1", |
||||||
|
"@types/express": "^5.0.0", |
||||||
|
"@types/jest": "^30.0.0", |
||||||
|
"@types/node": "^22.10.7", |
||||||
|
"@types/supertest": "^6.0.2", |
||||||
|
"eslint": "^9.18.0", |
||||||
|
"eslint-config-prettier": "^10.0.1", |
||||||
|
"eslint-plugin-prettier": "^5.2.2", |
||||||
|
"globals": "^16.0.0", |
||||||
|
"jest": "^30.0.0", |
||||||
|
"prettier": "^3.4.2", |
||||||
|
"prisma": "^7.5.0", |
||||||
|
"source-map-support": "^0.5.21", |
||||||
|
"supertest": "^7.0.0", |
||||||
|
"ts-jest": "^29.2.5", |
||||||
|
"ts-loader": "^9.5.2", |
||||||
|
"ts-node": "^10.9.2", |
||||||
|
"tsconfig-paths": "^4.2.0", |
||||||
|
"typescript": "^5.7.3", |
||||||
|
"typescript-eslint": "^8.20.0" |
||||||
|
}, |
||||||
|
"jest": { |
||||||
|
"moduleFileExtensions": [ |
||||||
|
"js", |
||||||
|
"json", |
||||||
|
"ts" |
||||||
|
], |
||||||
|
"rootDir": "src", |
||||||
|
"testRegex": ".*\\.spec\\.ts$", |
||||||
|
"transform": { |
||||||
|
"^.+\\.(t|j)s$": "ts-jest" |
||||||
|
}, |
||||||
|
"collectCoverageFrom": [ |
||||||
|
"**/*.(t|j)s" |
||||||
|
], |
||||||
|
"coverageDirectory": "../coverage", |
||||||
|
"testEnvironment": "node" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
// This file was generated by Prisma, and assumes you have installed the following:
|
||||||
|
// npm install --save-dev prisma dotenv
|
||||||
|
import "dotenv/config"; |
||||||
|
import { defineConfig } from "prisma/config"; |
||||||
|
|
||||||
|
export default defineConfig({ |
||||||
|
schema: "prisma/schema.prisma", |
||||||
|
migrations: { |
||||||
|
path: "prisma/migrations", |
||||||
|
}, |
||||||
|
datasource: { |
||||||
|
url: process.env["DATABASE_URL"], |
||||||
|
}, |
||||||
|
}); |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
generator client { |
||||||
|
provider = "prisma-client" |
||||||
|
output = "../generated/prisma" |
||||||
|
} |
||||||
|
|
||||||
|
datasource db { |
||||||
|
provider = "postgresql" |
||||||
|
} |
||||||
|
|
||||||
|
enum Role { |
||||||
|
viewer |
||||||
|
editor |
||||||
|
} |
||||||
|
|
||||||
|
enum ComponentStatus { |
||||||
|
draft |
||||||
|
review |
||||||
|
approved |
||||||
|
} |
||||||
|
|
||||||
|
model User { |
||||||
|
id String @id @default(uuid()) |
||||||
|
email String @unique |
||||||
|
name String |
||||||
|
passwordHash String |
||||||
|
role Role @default(viewer) |
||||||
|
createdAt DateTime @default(now()) |
||||||
|
|
||||||
|
components ExperimentalComponent[] |
||||||
|
} |
||||||
|
|
||||||
|
model ExperimentalComponent { |
||||||
|
id String @id @default(uuid()) |
||||||
|
name String |
||||||
|
baseComponent String |
||||||
|
attributes Json |
||||||
|
status ComponentStatus @default(draft) |
||||||
|
author User @relation(fields: [authorId], references: [id]) |
||||||
|
authorId String |
||||||
|
createdAt DateTime @default(now()) |
||||||
|
updatedAt DateTime @updatedAt |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
import { Test, TestingModule } from '@nestjs/testing'; |
||||||
|
import { AppController } from './app.controller'; |
||||||
|
import { AppService } from './app.service'; |
||||||
|
|
||||||
|
describe('AppController', () => { |
||||||
|
let appController: AppController; |
||||||
|
|
||||||
|
beforeEach(async () => { |
||||||
|
const app: TestingModule = await Test.createTestingModule({ |
||||||
|
controllers: [AppController], |
||||||
|
providers: [AppService], |
||||||
|
}).compile(); |
||||||
|
|
||||||
|
appController = app.get<AppController>(AppController); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('root', () => { |
||||||
|
it('should return "Hello World!"', () => { |
||||||
|
expect(appController.getHello()).toBe('Hello World!'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
import { Controller, Get } from '@nestjs/common'; |
||||||
|
import { AppService } from './app.service'; |
||||||
|
|
||||||
|
@Controller() |
||||||
|
export class AppController { |
||||||
|
constructor(private readonly appService: AppService) {} |
||||||
|
|
||||||
|
@Get() |
||||||
|
getHello(): string { |
||||||
|
return this.appService.getHello(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
import { Module } from '@nestjs/common'; |
||||||
|
import { AppController } from './app.controller'; |
||||||
|
import { AppService } from './app.service'; |
||||||
|
|
||||||
|
@Module({ |
||||||
|
imports: [], |
||||||
|
controllers: [AppController], |
||||||
|
providers: [AppService], |
||||||
|
}) |
||||||
|
export class AppModule {} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
import { Injectable } from '@nestjs/common'; |
||||||
|
|
||||||
|
@Injectable() |
||||||
|
export class AppService { |
||||||
|
getHello(): string { |
||||||
|
return 'Hello World!'; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
import { NestFactory } from '@nestjs/core'; |
||||||
|
import { AppModule } from './app.module'; |
||||||
|
|
||||||
|
async function bootstrap() { |
||||||
|
const app = await NestFactory.create(AppModule); |
||||||
|
await app.listen(process.env.PORT ?? 3000); |
||||||
|
} |
||||||
|
bootstrap(); |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
import { Test, TestingModule } from '@nestjs/testing'; |
||||||
|
import { INestApplication } from '@nestjs/common'; |
||||||
|
import request from 'supertest'; |
||||||
|
import { App } from 'supertest/types'; |
||||||
|
import { AppModule } from './../src/app.module'; |
||||||
|
|
||||||
|
describe('AppController (e2e)', () => { |
||||||
|
let app: INestApplication<App>; |
||||||
|
|
||||||
|
beforeEach(async () => { |
||||||
|
const moduleFixture: TestingModule = await Test.createTestingModule({ |
||||||
|
imports: [AppModule], |
||||||
|
}).compile(); |
||||||
|
|
||||||
|
app = moduleFixture.createNestApplication(); |
||||||
|
await app.init(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('/ (GET)', () => { |
||||||
|
return request(app.getHttpServer()) |
||||||
|
.get('/') |
||||||
|
.expect(200) |
||||||
|
.expect('Hello World!'); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"moduleFileExtensions": ["js", "json", "ts"], |
||||||
|
"rootDir": ".", |
||||||
|
"testEnvironment": "node", |
||||||
|
"testRegex": ".e2e-spec.ts$", |
||||||
|
"transform": { |
||||||
|
"^.+\\.(t|j)s$": "ts-jest" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
{ |
||||||
|
"extends": "./tsconfig.json", |
||||||
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"] |
||||||
|
} |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"module": "nodenext", |
||||||
|
"moduleResolution": "nodenext", |
||||||
|
"resolvePackageJsonExports": true, |
||||||
|
"esModuleInterop": true, |
||||||
|
"isolatedModules": true, |
||||||
|
"declaration": true, |
||||||
|
"removeComments": true, |
||||||
|
"emitDecoratorMetadata": true, |
||||||
|
"experimentalDecorators": true, |
||||||
|
"allowSyntheticDefaultImports": true, |
||||||
|
"target": "ES2023", |
||||||
|
"sourceMap": true, |
||||||
|
"outDir": "./dist", |
||||||
|
"baseUrl": "./", |
||||||
|
"incremental": true, |
||||||
|
"skipLibCheck": true, |
||||||
|
"strictNullChecks": true, |
||||||
|
"forceConsistentCasingInFileNames": true, |
||||||
|
"noImplicitAny": false, |
||||||
|
"strictBindCallApply": false, |
||||||
|
"noFallthroughCasesInSwitch": false |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
||||||
|
|
||||||
|
# dependencies |
||||||
|
/node_modules |
||||||
|
/.pnp |
||||||
|
.pnp.* |
||||||
|
.yarn/* |
||||||
|
!.yarn/patches |
||||||
|
!.yarn/plugins |
||||||
|
!.yarn/releases |
||||||
|
!.yarn/versions |
||||||
|
|
||||||
|
# testing |
||||||
|
/coverage |
||||||
|
|
||||||
|
# next.js |
||||||
|
/.next/ |
||||||
|
/out/ |
||||||
|
|
||||||
|
# production |
||||||
|
/build |
||||||
|
|
||||||
|
# misc |
||||||
|
.DS_Store |
||||||
|
*.pem |
||||||
|
|
||||||
|
# debug |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
.pnpm-debug.log* |
||||||
|
|
||||||
|
# env files (can opt-in for committing if needed) |
||||||
|
.env* |
||||||
|
|
||||||
|
# vercel |
||||||
|
.vercel |
||||||
|
|
||||||
|
# typescript |
||||||
|
*.tsbuildinfo |
||||||
|
next-env.d.ts |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
<!-- BEGIN:nextjs-agent-rules --> |
||||||
|
# This is NOT the Next.js you know |
||||||
|
|
||||||
|
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. |
||||||
|
<!-- END:nextjs-agent-rules --> |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). |
||||||
|
|
||||||
|
## Getting Started |
||||||
|
|
||||||
|
First, run the development server: |
||||||
|
|
||||||
|
```bash |
||||||
|
npm run dev |
||||||
|
# or |
||||||
|
yarn dev |
||||||
|
# or |
||||||
|
pnpm dev |
||||||
|
# or |
||||||
|
bun dev |
||||||
|
``` |
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. |
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. |
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. |
||||||
|
|
||||||
|
## Learn More |
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources: |
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. |
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. |
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! |
||||||
|
|
||||||
|
## Deploy on Vercel |
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. |
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. |
||||||
|
After Width: | Height: | Size: 25 KiB |
@ -0,0 +1,44 @@ |
|||||||
|
@import "tailwindcss"; |
||||||
|
|
||||||
|
/* ─── Бренд-токены О!Клиника ─────────────────────────────────────────── */ |
||||||
|
/* Цвета уточняются в Sprint 2 по таблице Oracal */ |
||||||
|
:root { |
||||||
|
/* Фирменные цвета (приблизительно — уточнить в Sprint 2) */ |
||||||
|
--brand-053m: #7ecfca; /* Основной бирюзовый (Oracal 053M) */ |
||||||
|
--brand-073m: #5b7b87; /* Тёмный серо-голубой (Oracal 073M) */ |
||||||
|
--brand-066m: #5bb5ad; /* Средний бирюзовый (Oracal 066M) */ |
||||||
|
--brand-050m: #1b4c72; /* Тёмно-синий, наружная реклама (Oracal 050M) */ |
||||||
|
--brand-081m: #c4a882; /* Бежевый (Oracal 081M) */ |
||||||
|
--brand-080m: #5c2e0e; /* Тёмно-коричневый (Oracal 080M) */ |
||||||
|
--brand-white: #ffffff; |
||||||
|
|
||||||
|
/* UI-цвета брендбука */ |
||||||
|
--bb-sidebar-bg: #f8f9fa; |
||||||
|
--bb-sidebar-border: #e5e7eb; |
||||||
|
--bb-sidebar-text: #374151; |
||||||
|
--bb-sidebar-text-muted: #6b7280; |
||||||
|
--bb-sidebar-active-bg: #e0f5f4; |
||||||
|
--bb-sidebar-active-text: var(--brand-053m); |
||||||
|
--bb-sidebar-section: #9ca3af; |
||||||
|
--bb-content-bg: #ffffff; |
||||||
|
--bb-border: #e5e7eb; |
||||||
|
--bb-text: #111827; |
||||||
|
--bb-text-muted: #6b7280; |
||||||
|
|
||||||
|
/* Шрифты */ |
||||||
|
--font-brand: 'DINPro', 'DIN Pro', Arial, sans-serif; |
||||||
|
--font-web: 'Fira Sans', sans-serif; |
||||||
|
--font-mono: 'JetBrains Mono', 'Courier New', monospace; |
||||||
|
} |
||||||
|
|
||||||
|
@theme inline { |
||||||
|
--color-background: #ffffff; |
||||||
|
--color-foreground: var(--bb-text); |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: var(--font-web); |
||||||
|
background: var(--bb-content-bg); |
||||||
|
color: var(--bb-text); |
||||||
|
-webkit-font-smoothing: antialiased; |
||||||
|
} |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
import type { Metadata } from "next"; |
||||||
|
import { Fira_Sans } from "next/font/google"; |
||||||
|
import "./globals.css"; |
||||||
|
import { Sidebar } from "@/components/layout/Sidebar"; |
||||||
|
|
||||||
|
const firaSans = Fira_Sans({ |
||||||
|
subsets: ["latin", "cyrillic"], |
||||||
|
weight: ["300", "400", "500", "600"], |
||||||
|
variable: "--font-fira-sans", |
||||||
|
display: "swap", |
||||||
|
}); |
||||||
|
|
||||||
|
export const metadata: Metadata = { |
||||||
|
title: "Цифровой брендбук | Клиника ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||||
|
description: "Интерактивный брендбук — Living Styleguide oclinica.ru", |
||||||
|
}; |
||||||
|
|
||||||
|
export default function RootLayout({ |
||||||
|
children, |
||||||
|
}: { |
||||||
|
children: React.ReactNode; |
||||||
|
}) { |
||||||
|
return ( |
||||||
|
<html lang="ru" className={firaSans.variable}> |
||||||
|
<body className="flex h-screen overflow-hidden bg-white"> |
||||||
|
<Sidebar /> |
||||||
|
<main className="flex-1 overflow-y-auto"> |
||||||
|
{children} |
||||||
|
</main> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
import { redirect } from "next/navigation"; |
||||||
|
|
||||||
|
export default function RootPage() { |
||||||
|
redirect("/foundation/logo"); |
||||||
|
} |
||||||
@ -0,0 +1,178 @@ |
|||||||
|
"use client"; |
||||||
|
|
||||||
|
import Link from "next/link"; |
||||||
|
import { usePathname } from "next/navigation"; |
||||||
|
|
||||||
|
type NavItem = { |
||||||
|
label: string; |
||||||
|
href: string; |
||||||
|
soon?: boolean; |
||||||
|
}; |
||||||
|
|
||||||
|
type NavSection = { |
||||||
|
title: string; |
||||||
|
items: NavItem[]; |
||||||
|
}; |
||||||
|
|
||||||
|
const NAV: NavSection[] = [ |
||||||
|
{ |
||||||
|
title: "Фундамент", |
||||||
|
items: [ |
||||||
|
{ label: "Логотип", href: "/foundation/logo" }, |
||||||
|
{ label: "Цвета", href: "/foundation/colors", soon: true }, |
||||||
|
{ label: "Типографика", href: "/foundation/typography", soon: true }, |
||||||
|
{ label: "Иконография", href: "/foundation/icons", soon: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "Компоненты", |
||||||
|
items: [ |
||||||
|
{ label: "Кнопки", href: "/components/buttons", soon: true }, |
||||||
|
{ label: "Форм-контролы", href: "/components/forms", soon: true }, |
||||||
|
{ label: "Карточки", href: "/components/cards", soon: true }, |
||||||
|
{ label: "Бейджи и теги", href: "/components/badges", soon: true }, |
||||||
|
{ label: "Алерты", href: "/components/alerts", soon: true }, |
||||||
|
{ label: "Модальные окна", href: "/components/modals", soon: true }, |
||||||
|
{ label: "Таблицы", href: "/components/tables", soon: true }, |
||||||
|
{ label: "Навигация", href: "/components/navigation", soon: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "Блоки", |
||||||
|
items: [ |
||||||
|
{ label: "Hero", href: "/blocks/hero", soon: true }, |
||||||
|
{ label: "CEO-текст", href: "/blocks/ceo", soon: true }, |
||||||
|
{ label: "Наши врачи", href: "/blocks/doctors", soon: true }, |
||||||
|
{ label: "Отзывы", href: "/blocks/reviews", soon: true }, |
||||||
|
{ label: "Новости", href: "/blocks/news", soon: true }, |
||||||
|
{ label: "Формы контакта", href: "/blocks/contact-forms", soon: true }, |
||||||
|
{ label: "Контакт", href: "/blocks/contact", soon: true }, |
||||||
|
{ label: "Услуги", href: "/blocks/services", soon: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "Страницы", |
||||||
|
items: [ |
||||||
|
{ label: "Главная", href: "/pages/home", soon: true }, |
||||||
|
{ label: "Заболевание", href: "/pages/disease", soon: true }, |
||||||
|
{ label: "Все врачи", href: "/pages/doctors", soon: true }, |
||||||
|
{ label: "Врач", href: "/pages/doctor", soon: true }, |
||||||
|
{ label: "Цены", href: "/pages/prices", soon: true }, |
||||||
|
{ label: "Контакты", href: "/pages/contacts", soon: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "Оффлайн элементы", |
||||||
|
items: [ |
||||||
|
{ label: "Форма сотрудников", href: "/offline/uniform", soon: true }, |
||||||
|
{ label: "Бейджи", href: "/offline/badges", soon: true }, |
||||||
|
{ label: "Навигация", href: "/offline/navigation", soon: true }, |
||||||
|
{ label: "Транспорт", href: "/offline/transport", soon: true }, |
||||||
|
{ label: "Печать", href: "/offline/print", soon: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "Эксперименты", |
||||||
|
items: [ |
||||||
|
{ label: "Библиотека", href: "/experiments", soon: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
export function Sidebar() { |
||||||
|
const pathname = usePathname(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<aside |
||||||
|
className="w-64 shrink-0 h-screen overflow-y-auto border-r flex flex-col" |
||||||
|
style={{ |
||||||
|
background: "var(--bb-sidebar-bg)", |
||||||
|
borderColor: "var(--bb-sidebar-border)", |
||||||
|
}} |
||||||
|
> |
||||||
|
{/* Логотип брендбука */} |
||||||
|
<div |
||||||
|
className="px-5 py-4 border-b" |
||||||
|
style={{ borderColor: "var(--bb-sidebar-border)" }} |
||||||
|
> |
||||||
|
<p |
||||||
|
className="text-xs font-semibold uppercase tracking-widest mb-0.5" |
||||||
|
style={{ color: "var(--brand-053m)" }} |
||||||
|
> |
||||||
|
Брендбук |
||||||
|
</p> |
||||||
|
<p |
||||||
|
className="text-xs leading-tight" |
||||||
|
style={{ color: "var(--bb-sidebar-text-muted)" }} |
||||||
|
> |
||||||
|
Клиника ухо, горло, нос |
||||||
|
<br /> |
||||||
|
им. проф. Е.Н.Оленевой |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
{/* Навигация */} |
||||||
|
<nav className="flex-1 px-3 py-4 space-y-5"> |
||||||
|
{NAV.map((section) => ( |
||||||
|
<div key={section.title}> |
||||||
|
<p |
||||||
|
className="px-2 mb-1 text-[10px] font-semibold uppercase tracking-widest" |
||||||
|
style={{ color: "var(--bb-sidebar-section)" }} |
||||||
|
> |
||||||
|
{section.title} |
||||||
|
</p> |
||||||
|
<ul className="space-y-0.5"> |
||||||
|
{section.items.map((item) => { |
||||||
|
const isActive = pathname === item.href; |
||||||
|
return ( |
||||||
|
<li key={item.href}> |
||||||
|
<Link |
||||||
|
href={item.soon ? "#" : item.href} |
||||||
|
className="flex items-center justify-between px-2 py-1.5 rounded-md text-sm transition-colors" |
||||||
|
style={{ |
||||||
|
color: isActive |
||||||
|
? "var(--brand-073m)" |
||||||
|
: item.soon |
||||||
|
? "var(--bb-sidebar-text-muted)" |
||||||
|
: "var(--bb-sidebar-text)", |
||||||
|
background: isActive |
||||||
|
? "var(--bb-sidebar-active-bg)" |
||||||
|
: "transparent", |
||||||
|
fontWeight: isActive ? 500 : 400, |
||||||
|
cursor: item.soon ? "default" : "pointer", |
||||||
|
}} |
||||||
|
> |
||||||
|
{item.label} |
||||||
|
{item.soon && ( |
||||||
|
<span |
||||||
|
className="text-[10px] px-1.5 py-0.5 rounded" |
||||||
|
style={{ |
||||||
|
background: "#f3f4f6", |
||||||
|
color: "var(--bb-sidebar-section)", |
||||||
|
}} |
||||||
|
> |
||||||
|
скоро |
||||||
|
</span> |
||||||
|
)} |
||||||
|
</Link> |
||||||
|
</li> |
||||||
|
); |
||||||
|
})} |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
))} |
||||||
|
</nav> |
||||||
|
|
||||||
|
{/* Версия */} |
||||||
|
<div |
||||||
|
className="px-5 py-3 border-t text-xs" |
||||||
|
style={{ |
||||||
|
borderColor: "var(--bb-sidebar-border)", |
||||||
|
color: "var(--bb-sidebar-text-muted)", |
||||||
|
}} |
||||||
|
> |
||||||
|
Sprint 1 · v0.1.0 |
||||||
|
</div> |
||||||
|
</aside> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
import { defineConfig, globalIgnores } from "eslint/config"; |
||||||
|
import nextVitals from "eslint-config-next/core-web-vitals"; |
||||||
|
import nextTs from "eslint-config-next/typescript"; |
||||||
|
|
||||||
|
const eslintConfig = defineConfig([ |
||||||
|
...nextVitals, |
||||||
|
...nextTs, |
||||||
|
// Override default ignores of eslint-config-next.
|
||||||
|
globalIgnores([ |
||||||
|
// Default ignores of eslint-config-next:
|
||||||
|
".next/**", |
||||||
|
"out/**", |
||||||
|
"build/**", |
||||||
|
"next-env.d.ts", |
||||||
|
]), |
||||||
|
]); |
||||||
|
|
||||||
|
export default eslintConfig; |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
import type { NextConfig } from "next"; |
||||||
|
import path from "path"; |
||||||
|
|
||||||
|
const nextConfig: NextConfig = { |
||||||
|
turbopack: { |
||||||
|
root: path.resolve(__dirname, "../.."), |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export default nextConfig; |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
{ |
||||||
|
"name": "web", |
||||||
|
"version": "0.1.0", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"dev": "next dev", |
||||||
|
"build": "next build", |
||||||
|
"start": "next start", |
||||||
|
"lint": "eslint" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"next": "16.2.1", |
||||||
|
"react": "19.2.4", |
||||||
|
"react-dom": "19.2.4" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@tailwindcss/postcss": "^4", |
||||||
|
"@types/node": "^20", |
||||||
|
"@types/react": "^19", |
||||||
|
"@types/react-dom": "^19", |
||||||
|
"eslint": "^9", |
||||||
|
"eslint-config-next": "16.2.1", |
||||||
|
"tailwindcss": "^4", |
||||||
|
"typescript": "^5" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
ignoredBuiltDependencies: |
||||||
|
- sharp |
||||||
|
- unrs-resolver |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
const config = { |
||||||
|
plugins: { |
||||||
|
"@tailwindcss/postcss": {}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export default config; |
||||||
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,34 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"target": "ES2017", |
||||||
|
"lib": ["dom", "dom.iterable", "esnext"], |
||||||
|
"allowJs": true, |
||||||
|
"skipLibCheck": true, |
||||||
|
"strict": true, |
||||||
|
"noEmit": true, |
||||||
|
"esModuleInterop": true, |
||||||
|
"module": "esnext", |
||||||
|
"moduleResolution": "bundler", |
||||||
|
"resolveJsonModule": true, |
||||||
|
"isolatedModules": true, |
||||||
|
"jsx": "react-jsx", |
||||||
|
"incremental": true, |
||||||
|
"plugins": [ |
||||||
|
{ |
||||||
|
"name": "next" |
||||||
|
} |
||||||
|
], |
||||||
|
"paths": { |
||||||
|
"@/*": ["./*"] |
||||||
|
} |
||||||
|
}, |
||||||
|
"include": [ |
||||||
|
"next-env.d.ts", |
||||||
|
"**/*.ts", |
||||||
|
"**/*.tsx", |
||||||
|
".next/types/**/*.ts", |
||||||
|
".next/dev/types/**/*.ts", |
||||||
|
"**/*.mts" |
||||||
|
], |
||||||
|
"exclude": ["node_modules"] |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
services: |
||||||
|
postgres: |
||||||
|
image: postgres:16-alpine |
||||||
|
container_name: brandbook_postgres |
||||||
|
restart: unless-stopped |
||||||
|
environment: |
||||||
|
POSTGRES_USER: brandbook |
||||||
|
POSTGRES_PASSWORD: brandbook |
||||||
|
POSTGRES_DB: brandbook |
||||||
|
ports: |
||||||
|
- "5433:5432" |
||||||
|
volumes: |
||||||
|
- postgres_data:/var/lib/postgresql/data |
||||||
|
|
||||||
|
volumes: |
||||||
|
postgres_data: |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
{ |
||||||
|
"name": "oclinica-brandbook", |
||||||
|
"version": "0.0.1", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"dev": "pnpm --parallel -r dev", |
||||||
|
"build": "pnpm --parallel -r build", |
||||||
|
"lint": "pnpm --parallel -r lint" |
||||||
|
}, |
||||||
|
"engines": { |
||||||
|
"node": ">=20", |
||||||
|
"pnpm": ">=9" |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue