create header

This commit is contained in:
2025-05-12 17:23:05 +03:00
parent e74731b789
commit a5ffa7ae13
22 changed files with 576 additions and 24 deletions

41
frontend/.gitignore vendored Normal file
View File

@@ -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

36
frontend/README.md Normal file
View 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.

BIN
frontend/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

27
frontend/app/globals.css Normal file
View File

@@ -0,0 +1,27 @@
@import 'tailwindcss';
:root {
--background: #eeebeb;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--color-orange: #ff613a;
}
@media (prefers-color-scheme: light) {
:root {
--background: #eeebeb;
--foreground: #171717;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

37
frontend/app/layout.tsx Normal file
View File

@@ -0,0 +1,37 @@
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import './globals.css'
import Header from '@/components/Header'
const geistSans = Geist({
variable: '--font-geist-sans',
subsets: ['latin'],
})
const geistMono = Geist_Mono({
variable: '--font-geist-mono',
subsets: ['latin'],
})
export const metadata: Metadata = {
title: 'Отправка посылок в любую точку мира | TripWB',
description:
'Международная отправка посылок ✓ Отправка посылки в любую точку планеты ✓ Приемлемая цена отправки посылки ✓ Доставка в кратчайшие сроки ➡️ Обращайтесь к нам',
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
<body className="min-h-screen">
<main className="min-h-screen">
<Header />
{children}
</main>
</body>
</html>
)
}

7
frontend/app/page.tsx Normal file
View File

@@ -0,0 +1,7 @@
export default function Home() {
return (
<div>
<h1>Main page</h1>
</div>
)
}

View File

View File

@@ -0,0 +1,65 @@
import React from 'react'
import Image from 'next/image'
import Link from 'next/link'
import Burger from './ui/Burger'
import LangSwitcher from './LangSwitcher'
import Button from './ui/Button'
const Header = () => {
return (
<div className="flex justify-between items-center px-6 py-8 w-[95%] mx-auto">
<div className="flex items-center justify-center space-x-10">
<Image
src="/images/logo.png"
alt="logo"
width={50}
height={50}
priority
/>
{/* Desktop Burger */}
<div className="hidden md:block">
<Burger />
</div>
<Link
href="/"
className="text-base font-medium px-4 hover:underline hidden md:block"
>
Могу взять посылку
</Link>
</div>
<div className="flex items-center justify-center space-x-10">
<LangSwitcher />
<Button text="Разместить объявление" className="hidden md:block" />
{/* Auth Block - Desktop */}
<div className="hidden md:block text-base font-medium">
<Link href="/register" className="hover:underline">
Регистрация
</Link>
<span> / </span>
<Link href="/login" className="hover:underline">
Войти
</Link>
</div>
{/* Mobile Controls */}
<div className="flex items-center space-x-4 md:hidden">
{/* Mobile Burger */}
{/* Mobile Auth */}
<Link href="/login">
<Image
src="/images/userlogo.png"
alt="user"
width={24}
height={24}
/>
</Link>
<Burger />
</div>
</div>
</div>
)
}
export default Header

View File

@@ -0,0 +1,31 @@
'use client'
import React, { useState } from 'react'
const LangSwitcher = () => {
const [selectedLang, setSelectedLang] = useState('ru')
return (
<div className="flex items-center space-x-2">
<button
onClick={() => setSelectedLang('ru')}
className={`${
selectedLang === 'ru' ? 'text-orange' : 'text-gray-500'
} cursor-pointer`}
>
RU
</button>
<span className="text-gray-500">/</span>
<button
onClick={() => setSelectedLang('en')}
className={`${
selectedLang === 'en' ? 'text-orange' : 'text-gray-500'
} cursor-pointer`}
>
EN
</button>
</div>
)
}
export default LangSwitcher

View File

@@ -0,0 +1,82 @@
'use client'
import React, { useState } from 'react'
import Link from 'next/link'
import Button from './Button'
const Burger = () => {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<button
onClick={() => setIsOpen(!isOpen)}
className="relative z-50 w-8 h-8 flex flex-col justify-center items-center"
>
<div className="relative w-8 h-8">
<span
className={`absolute h-0.5 bg-orange transition-all duration-300 ease-in-out ${
isOpen ? 'w-8 rotate-45 top-4' : 'w-8 top-2'
}`}
></span>
<span
className={`absolute h-0.5 bg-orange transition-all duration-300 ease-in-out ${
isOpen ? 'w-0 opacity-0 top-4' : 'w-8 top-4'
}`}
></span>
<span
className={`absolute h-0.5 bg-orange transition-all duration-300 ease-in-out ${
isOpen ? 'w-8 -rotate-45 top-4' : 'w-8 top-6'
}`}
></span>
</div>
</button>
{/* меню */}
<div
className={`fixed left-0 w-full bg-[#eeebeb] shadow-lg transition-all duration-300 ease-in-out origin-top ${
isOpen
? 'top-[80px] h-[calc(100vh-80px)] opacity-100 scale-y-100'
: 'top-[80px] h-0 opacity-0 scale-y-0'
} ${isOpen ? 'pointer-events-auto' : 'pointer-events-none'}`}
>
<div
className={`flex flex-col items-center pt-12 space-y-8 transition-all duration-300 ${
isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-4'
}`}
>
<Link
href="/"
className="text-xl hover:text-orange transition-colors duration-200"
onClick={() => setIsOpen(false)}
>
Ссылка 1
</Link>
<Link
href="/search"
className="text-xl hover:text-orange transition-colors duration-200"
onClick={() => setIsOpen(false)}
>
Ссылка 2
</Link>
<Link
href="/about"
className="text-xl hover:text-orange transition-colors duration-200"
onClick={() => setIsOpen(false)}
>
Ссылка 3
</Link>
<Link
href="/"
className="text-xl hover:text-orange transition-colors duration-200"
onClick={() => setIsOpen(false)}
>
Могу взять посылку
</Link>
<Button text="Разместить объявление" />
</div>
</div>
</>
)
}
export default Burger

View File

@@ -0,0 +1,22 @@
import React from 'react'
interface ButtonProps {
onClick?: () => void
className?: string
text?: string
type?: 'button'
}
const Button = ({ onClick, className, text, type }: ButtonProps) => {
return (
<button
onClick={onClick}
className={`${className} text-base font-medium px-4 py-3 text-white bg-orange rounded-2xl cursor-pointer`}
type={type}
>
<span>{text}</span>
</button>
)
}
export default Button

View File

@@ -0,0 +1,16 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;

7
frontend/next.config.ts Normal file
View File

@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

View File

@@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

27
frontend/tsconfig.json Normal file
View File

@@ -0,0 +1,27 @@
{
"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": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}