Update project 4 frontend start
This commit is contained in:
3
frontend-client/.eslintrc.json
Normal file
3
frontend-client/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||||
|
}
|
||||||
36
frontend-client/.gitignore
vendored
Normal file
36
frontend-client/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
7
frontend-client/Dockerfile
Normal file
7
frontend-client/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["npm", "run", "dev"]
|
||||||
36
frontend-client/README.md
Normal file
36
frontend-client/README.md
Normal file
@@ -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.
|
||||||
13
frontend-client/next.config.mjs
Normal file
13
frontend-client/next.config.mjs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "images.unsplash.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
6167
frontend-client/package-lock.json
generated
Normal file
6167
frontend-client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
frontend-client/package.json
Normal file
29
frontend-client/package.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend-client",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.13.6",
|
||||||
|
"lucide-react": "^0.577.0",
|
||||||
|
"next": "14.2.35",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"zustand": "^5.0.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.2.35",
|
||||||
|
"postcss": "^8",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
frontend-client/postcss.config.mjs
Normal file
8
frontend-client/postcss.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
18
frontend-client/src/api/client.ts
Normal file
18
frontend-client/src/api/client.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { useAuthStore } from "@/store/authStore";
|
||||||
|
|
||||||
|
const apiClient = axios.create({
|
||||||
|
baseURL: process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8081/api",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
apiClient.interceptors.request.use((config) => {
|
||||||
|
// getState() — безопасен вне React-дерева (server actions, route handlers тоже работают)
|
||||||
|
const token = useAuthStore.getState().token;
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiClient;
|
||||||
BIN
frontend-client/src/app/favicon.ico
Normal file
BIN
frontend-client/src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
frontend-client/src/app/fonts/GeistMonoVF.woff
Normal file
BIN
frontend-client/src/app/fonts/GeistMonoVF.woff
Normal file
Binary file not shown.
BIN
frontend-client/src/app/fonts/GeistVF.woff
Normal file
BIN
frontend-client/src/app/fonts/GeistVF.woff
Normal file
Binary file not shown.
13
frontend-client/src/app/globals.css
Normal file
13
frontend-client/src/app/globals.css
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg: #121212;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #121212;
|
||||||
|
color: #ffffff;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
24
frontend-client/src/app/layout.tsx
Normal file
24
frontend-client/src/app/layout.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ["latin", "cyrillic"] });
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Fight Tickets",
|
||||||
|
description: "Бронирование билетов на спортивные события",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="ru">
|
||||||
|
<body className={`${inter.className} bg-[#121212] text-white`}>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
120
frontend-client/src/app/page.tsx
Normal file
120
frontend-client/src/app/page.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Search, CalendarDays, Ticket, User } from "lucide-react";
|
||||||
|
import EventCard, { type EventItem } from "@/components/EventCard";
|
||||||
|
|
||||||
|
const MOCK_EVENTS: EventItem[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Чемпионат по ММА",
|
||||||
|
date: "15 Апреля",
|
||||||
|
time: "19:00",
|
||||||
|
venue: "ВТБ Арена",
|
||||||
|
city: "Москва",
|
||||||
|
priceFrom: 2500,
|
||||||
|
imageSrc:
|
||||||
|
"https://images.unsplash.com/photo-1549719386-74dfcbf7dbed?w=400&q=80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'Турнир по Боксу: "Удар года"',
|
||||||
|
date: "",
|
||||||
|
time: "",
|
||||||
|
venue: "ВТБ Арена",
|
||||||
|
city: "Москва",
|
||||||
|
priceFrom: 2500,
|
||||||
|
imageSrc:
|
||||||
|
"https://images.unsplash.com/photo-1517649763962-0c623066013b?w=400&q=80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Турнир по Бои",
|
||||||
|
date: "15 Апреля",
|
||||||
|
time: "19:00",
|
||||||
|
venue: "ВТБ Арена",
|
||||||
|
city: "Москва",
|
||||||
|
priceFrom: 3000,
|
||||||
|
imageSrc:
|
||||||
|
"https://images.unsplash.com/photo-1484810934922-571fd7d1b41d?w=400&q=80",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const FILTERS = ["Все", "Ближайшие", "Популярные", "Скоро"] as const;
|
||||||
|
type Filter = (typeof FILTERS)[number];
|
||||||
|
|
||||||
|
const NAV_ITEMS = [
|
||||||
|
{ label: "События", icon: CalendarDays },
|
||||||
|
{ label: "Мои билеты", icon: Ticket },
|
||||||
|
{ label: "Профиль", icon: User },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
|
const [activeFilter, setActiveFilter] = useState<Filter>("Все");
|
||||||
|
const [activeNav, setActiveNav] = useState(0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center min-h-screen bg-[#121212]">
|
||||||
|
{/* Phone-width wrapper */}
|
||||||
|
<div className="relative w-full max-w-[390px] flex flex-col min-h-screen bg-[#121212]">
|
||||||
|
{/* ── Header ── */}
|
||||||
|
<div className="flex items-center justify-between px-5 pt-12 pb-4">
|
||||||
|
<h1 className="text-[32px] font-bold leading-tight tracking-tight text-white">
|
||||||
|
События
|
||||||
|
</h1>
|
||||||
|
<button
|
||||||
|
aria-label="Поиск"
|
||||||
|
className="p-2 rounded-full text-white hover:bg-[#2C2C2E] transition-colors"
|
||||||
|
>
|
||||||
|
<Search size={24} strokeWidth={2} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Filter tabs ── */}
|
||||||
|
<div className="flex gap-2 px-5 pb-4 overflow-x-auto scrollbar-hide">
|
||||||
|
{FILTERS.map((f) => (
|
||||||
|
<button
|
||||||
|
key={f}
|
||||||
|
onClick={() => setActiveFilter(f)}
|
||||||
|
className={`flex-shrink-0 px-4 py-2 rounded-full text-sm font-medium transition-colors ${
|
||||||
|
activeFilter === f
|
||||||
|
? "bg-white text-[#121212]"
|
||||||
|
: "bg-[#2C2C2E] text-[#8E8E93] hover:bg-[#3A3A3C]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{f}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Event list ── */}
|
||||||
|
<div className="flex flex-col gap-3 px-4 flex-1 pb-24">
|
||||||
|
{MOCK_EVENTS.map((event) => (
|
||||||
|
<EventCard key={event.id} event={event} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Bottom navigation ── */}
|
||||||
|
<nav className="fixed bottom-0 left-1/2 -translate-x-1/2 w-full max-w-[390px] bg-[#1C1C1E] border-t border-[#2C2C2E]">
|
||||||
|
<div className="flex">
|
||||||
|
{NAV_ITEMS.map(({ label, icon: Icon }, idx) => (
|
||||||
|
<button
|
||||||
|
key={label}
|
||||||
|
onClick={() => setActiveNav(idx)}
|
||||||
|
className={`flex-1 flex flex-col items-center gap-1 py-3 text-[10px] font-medium transition-colors ${
|
||||||
|
activeNav === idx ? "text-white" : "text-[#8E8E93]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
size={22}
|
||||||
|
strokeWidth={activeNav === idx ? 2.5 : 1.8}
|
||||||
|
/>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
71
frontend-client/src/components/EventCard.tsx
Normal file
71
frontend-client/src/components/EventCard.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import { MapPin, Calendar } from "lucide-react";
|
||||||
|
|
||||||
|
export interface EventItem {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
date: string;
|
||||||
|
time: string;
|
||||||
|
venue: string;
|
||||||
|
city: string;
|
||||||
|
priceFrom: number;
|
||||||
|
imageSrc: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventCardProps {
|
||||||
|
event: EventItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EventCard({ event }: EventCardProps) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-4 bg-[#1C1C1E] rounded-[16px] overflow-hidden p-3">
|
||||||
|
{/* Poster thumbnail */}
|
||||||
|
<div className="relative w-[110px] h-[110px] flex-shrink-0 rounded-xl overflow-hidden">
|
||||||
|
<Image
|
||||||
|
src={event.imageSrc}
|
||||||
|
alt={event.title}
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
sizes="110px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Info */}
|
||||||
|
<div className="flex flex-col justify-between flex-1 min-w-0 py-0.5">
|
||||||
|
<p className="font-bold text-[15px] leading-tight text-white line-clamp-2">
|
||||||
|
{event.title}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1 mt-1">
|
||||||
|
{event.date && (
|
||||||
|
<div className="flex items-center gap-1.5 text-[#8E8E93] text-xs">
|
||||||
|
<Calendar size={12} strokeWidth={2} />
|
||||||
|
<span>
|
||||||
|
{event.date}, {event.time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-1.5 text-[#8E8E93] text-xs">
|
||||||
|
<MapPin size={12} strokeWidth={2} />
|
||||||
|
<span className="truncate">
|
||||||
|
{event.venue}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-[#8E8E93] text-xs">{event.city}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Price + CTA */}
|
||||||
|
<div className="flex items-center justify-between mt-2">
|
||||||
|
<span className="text-white text-sm font-medium">
|
||||||
|
от {event.priceFrom.toLocaleString("ru-RU")} ₽
|
||||||
|
</span>
|
||||||
|
<button className="bg-accent hover:bg-accent-hover transition-colors text-white text-xs font-semibold px-4 py-2 rounded-full whitespace-nowrap">
|
||||||
|
Выбрать билет
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
frontend-client/src/store/authStore.ts
Normal file
26
frontend-client/src/store/authStore.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import { persist } from "zustand/middleware";
|
||||||
|
|
||||||
|
interface AuthUser {
|
||||||
|
id: number;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuthState {
|
||||||
|
token: string | null;
|
||||||
|
user: AuthUser | null;
|
||||||
|
setAuth: (token: string, user: AuthUser) => void;
|
||||||
|
clearAuth: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAuthStore = create<AuthState>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
token: null,
|
||||||
|
user: null,
|
||||||
|
setAuth: (token, user) => set({ token, user }),
|
||||||
|
clearAuth: () => set({ token: null, user: null }),
|
||||||
|
}),
|
||||||
|
{ name: "auth-storage" }
|
||||||
|
)
|
||||||
|
);
|
||||||
31
frontend-client/src/store/cartStore.ts
Normal file
31
frontend-client/src/store/cartStore.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface CartSeat {
|
||||||
|
seatId: number;
|
||||||
|
sector: string;
|
||||||
|
row: number;
|
||||||
|
number: number;
|
||||||
|
price: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CartState {
|
||||||
|
seats: CartSeat[];
|
||||||
|
addSeat: (seat: CartSeat) => void;
|
||||||
|
removeSeat: (seatId: number) => void;
|
||||||
|
clearCart: () => void;
|
||||||
|
totalPrice: () => number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCartStore = create<CartState>()((set, get) => ({
|
||||||
|
seats: [],
|
||||||
|
addSeat: (seat) =>
|
||||||
|
set((state) => ({
|
||||||
|
seats: state.seats.some((s) => s.seatId === seat.seatId)
|
||||||
|
? state.seats
|
||||||
|
: [...state.seats, seat],
|
||||||
|
})),
|
||||||
|
removeSeat: (seatId) =>
|
||||||
|
set((state) => ({ seats: state.seats.filter((s) => s.seatId !== seatId) })),
|
||||||
|
clearCart: () => set({ seats: [] }),
|
||||||
|
totalPrice: () => get().seats.reduce((sum, s) => sum + s.price, 0),
|
||||||
|
}));
|
||||||
25
frontend-client/tailwind.config.ts
Normal file
25
frontend-client/tailwind.config.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: [
|
||||||
|
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
surface: "#1C1C1E",
|
||||||
|
accent: "#E32636",
|
||||||
|
"accent-hover": "#C41E2A",
|
||||||
|
muted: "#8E8E93",
|
||||||
|
border: "#2C2C2E",
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
card: "16px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
26
frontend-client/tsconfig.json
Normal file
26
frontend-client/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -35,7 +35,20 @@ services:
|
|||||||
- postgres
|
- postgres
|
||||||
networks:
|
networks:
|
||||||
- ticket-network
|
- ticket-network
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ../frontend-client
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: frontend
|
||||||
|
ports:
|
||||||
|
- "3000:3000" # Пробиваем дыру напрямую для дебага
|
||||||
|
volumes:
|
||||||
|
- ../frontend-client:/app
|
||||||
|
- /app/node_modules # Изолируем зависимости контейнера от хоста
|
||||||
|
networks:
|
||||||
|
- ticket-network
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v3.6
|
image: traefik:v3.6
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
|
|||||||
Reference in New Issue
Block a user