Initial version

This commit is contained in:
awe
2025-09-23 14:53:35 +03:00
parent b4aa400164
commit b61e3f93d4
27 changed files with 7528 additions and 0 deletions

57
CLAUDE.md Normal file
View File

@ -0,0 +1,57 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a website project for the radiophotonics department (ru.radiophotonics), currently in planning phase. The project is designed to create a flexible, easily administrable website for academic/research purposes.
## Planned Architecture
Based on Plan.md, this will be a full-stack web application with:
- **Frontend**: React + Next.js
- **Backend**: Go (REST API)
- **Database**: PostgreSQL
- **File Storage**: MinIO or S3-compatible storage
- **Authentication**: JWT with role-based access (Editor with login, Observer without login)
- **Admin Panel**: Web interface for content management
## Main Sections (Planned)
1. **Main Page** - General lab information and mission
2. **Publications and News** - Publication lists, PDF attachments, news publishing
3. **RIDs (Intellectual Property Results)** - Patents, developments, programs catalog
4. **Projects** - Project listings with related RIDs, publications, news, partners
5. **Partners** - Collaboration information and project links
6. **Staff** - Employee directory with photos, positions, contacts, publications
7. **Contacts** - Address, maps (Google/Yandex), contact form
8. **For Students** - Educational materials, handbooks (PDFs), schedules, instructions
## Technical Stack (Recommended)
### Backend (Go)
- Gin or Echo for API framework
- GORM for database ORM
- JWT for authentication
### Frontend (Next.js)
- TailwindCSS or shadcn/ui for styling
- Axios/React Query for API communication
### Editor Features
- WYSIWYG editor (tiptap or CKEditor)
- Drag-and-drop file upload
- Content management panel
## Development Phases
1. **Design Phase**: Database schema, API design
2. **MVP**: Basic backend CRUD + frontend with main sections + auth
3. **Admin Panel**: Content editing interface, file uploads
4. **UI Improvements**: Search, filters, image optimization, user roles
5. **Deployment**: Docker deployment, SSL setup
## Repository Status
This repository is in the initial planning stage with only Plan.md containing the project specification. No build tools, package managers, or source code have been implemented yet.

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.

View File

@ -0,0 +1,25 @@
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"),
{
ignores: [
"node_modules/**",
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
],
},
];
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;

6090
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
frontend/package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.5.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.5.3",
"@eslint/eslintrc": "^3"
}
}

View File

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

1
frontend/public/file.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
frontend/public/next.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@ -0,0 +1,226 @@
'use client';
import { useState } from 'react';
export default function ContactsPage() {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: ''
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// В реальном приложении здесь была бы отправка формы
alert('Сообщение отправлено! (это демо-версия)');
setFormData({ name: '', email: '', subject: '', message: '' });
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Контакты</h1>
<p className="text-lg text-gray-600">
Свяжитесь с нами для получения дополнительной информации о наших исследованиях и возможностях сотрудничества
</p>
</div>
<div className="grid lg:grid-cols-2 gap-12">
{/* Contact Information */}
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Контактная информация</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-3">Адрес</h3>
<div className="flex items-start space-x-3">
<span className="text-blue-600 mt-1">📍</span>
<div>
<p className="text-gray-700">119991, Москва</p>
<p className="text-gray-700">ул. Научная, д. 1</p>
<p className="text-gray-700">Отдел радиофотоники</p>
</div>
</div>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-3">Телефон</h3>
<div className="flex items-center space-x-3">
<span className="text-blue-600">📞</span>
<a href="tel:+74951234567" className="text-gray-700 hover:text-blue-600 transition-colors">
+7 (495) 123-45-67
</a>
</div>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-3">Email</h3>
<div className="flex items-center space-x-3">
<span className="text-blue-600">📧</span>
<a href="mailto:info@radiophotonics.ru" className="text-gray-700 hover:text-blue-600 transition-colors">
info@radiophotonics.ru
</a>
</div>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-3">Время работы</h3>
<div className="flex items-start space-x-3">
<span className="text-blue-600 mt-1">🕐</span>
<div>
<p className="text-gray-700">Понедельник - Пятница: 9:00 - 18:00</p>
<p className="text-gray-700">Суббота - Воскресенье: выходной</p>
</div>
</div>
</div>
</div>
{/* Map placeholder */}
<div className="mt-8">
<h3 className="text-lg font-semibold text-gray-900 mb-3">Местоположение</h3>
<div className="bg-gray-200 rounded-lg h-64 flex items-center justify-center">
<div className="text-center">
<span className="text-4xl text-gray-400 mb-2 block">🗺</span>
<p className="text-gray-600">Интерактивная карта</p>
<p className="text-sm text-gray-500">(будет добавлена позже)</p>
</div>
</div>
</div>
</div>
{/* Contact Form */}
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Обратная связь</h2>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-2">
Имя *
</label>
<input
type="text"
id="name"
name="name"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={formData.name}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
Email *
</label>
<input
type="email"
id="email"
name="email"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={formData.email}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="subject" className="block text-sm font-medium text-gray-700 mb-2">
Тема обращения *
</label>
<select
id="subject"
name="subject"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={formData.subject}
onChange={handleChange}
>
<option value="">Выберите тему</option>
<option value="collaboration">Сотрудничество</option>
<option value="research">Исследования</option>
<option value="publications">Публикации</option>
<option value="education">Образование</option>
<option value="other">Другое</option>
</select>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-2">
Сообщение *
</label>
<textarea
id="message"
name="message"
required
rows={6}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-vertical"
value={formData.message}
onChange={handleChange}
placeholder="Опишите ваш вопрос или предложение..."
/>
</div>
<button
type="submit"
className="w-full bg-blue-600 text-white px-6 py-3 rounded-md hover:bg-blue-700 transition-colors font-medium"
>
Отправить сообщение
</button>
</form>
<div className="mt-6 p-4 bg-blue-50 rounded-lg">
<p className="text-sm text-blue-800">
<strong>Примечание:</strong> Мы отвечаем на все обращения в течение 1-2 рабочих дней.
Для срочных вопросов используйте телефон.
</p>
</div>
</div>
</div>
{/* Additional Information */}
<div className="mt-16 bg-gray-50 rounded-lg p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-6 text-center">Направления сотрудничества</h2>
<div className="grid md:grid-cols-3 gap-6">
<div className="text-center">
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl">🔬</span>
</div>
<h3 className="text-lg font-semibold mb-2">Научные исследования</h3>
<p className="text-gray-600 text-sm">
Совместные исследования в области радиофотоники и оптических технологий
</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl">🤝</span>
</div>
<h3 className="text-lg font-semibold mb-2">Промышленное партнерство</h3>
<p className="text-gray-600 text-sm">
Внедрение разработок и технологий в промышленные решения
</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl">🎓</span>
</div>
<h3 className="text-lg font-semibold mb-2">Образование</h3>
<p className="text-gray-600 text-sm">
Образовательные программы, стажировки и подготовка кадров
</p>
</div>
</div>
</div>
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,26 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -0,0 +1,32 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const inter = Inter({
subsets: ["latin", "cyrillic"],
});
export const metadata: Metadata = {
title: "Отдел Радиофотоники",
description: "Научно-исследовательский центр в области радиофотоники и оптических технологий",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ru">
<body className={`${inter.className} antialiased min-h-screen flex flex-col`}>
<Header />
<main className="flex-1">
{children}
</main>
<Footer />
</body>
</html>
);
}

148
frontend/src/app/page.tsx Normal file
View File

@ -0,0 +1,148 @@
import Link from 'next/link';
import { mockNews, mockPublications, mockProjects } from '@/lib/mockData';
export default function Home() {
const latestNews = mockNews.slice(0, 2);
const recentPublications = mockPublications.slice(0, 3);
const activeProjects = mockProjects.filter(p => p.status === 'active').slice(0, 2);
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Hero Section */}
<section className="text-center py-16 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-lg mb-12">
<h1 className="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
Отдел Радиофотоники
</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto mb-8">
Ведущий научно-исследовательский центр в области радиофотоники и оптических технологий.
Мы занимаемся разработкой передовых решений для телекоммуникаций, сенсорики и обработки сигналов.
</p>
<div className="space-x-4">
<Link
href="/publications"
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors inline-block"
>
Наши публикации
</Link>
<Link
href="/projects"
className="bg-white text-blue-600 border border-blue-600 px-6 py-3 rounded-lg hover:bg-blue-50 transition-colors inline-block"
>
Проекты
</Link>
</div>
</section>
{/* Latest News */}
<section className="mb-12">
<div className="flex justify-between items-center mb-6">
<h2 className="text-3xl font-bold text-gray-900">Последние новости</h2>
<Link href="/news" className="text-blue-600 hover:text-blue-800">
Все новости
</Link>
</div>
<div className="grid md:grid-cols-2 gap-6">
{latestNews.map(news => (
<div key={news.id} className="bg-white border rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow">
<h3 className="text-xl font-semibold mb-3 text-gray-900">{news.title}</h3>
<p className="text-gray-600 mb-4 line-clamp-3">{news.content}</p>
<div className="flex justify-between items-center text-sm text-gray-500">
<span>{news.author}</span>
<span>{new Date(news.date).toLocaleDateString('ru-RU')}</span>
</div>
</div>
))}
</div>
</section>
{/* Recent Publications */}
<section className="mb-12">
<div className="flex justify-between items-center mb-6">
<h2 className="text-3xl font-bold text-gray-900">Последние публикации</h2>
<Link href="/publications" className="text-blue-600 hover:text-blue-800">
Все публикации
</Link>
</div>
<div className="grid gap-4">
{recentPublications.map(pub => (
<div key={pub.id} className="bg-white border rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow">
<h3 className="text-lg font-semibold mb-2 text-gray-900">{pub.title}</h3>
<p className="text-gray-600 mb-2">
{pub.authors.join(', ')} {pub.journal} {pub.year}
</p>
<p className="text-gray-700 line-clamp-2">{pub.abstract}</p>
<div className="flex flex-wrap gap-2 mt-3">
{pub.tags.map(tag => (
<span key={tag} className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">
{tag}
</span>
))}
</div>
</div>
))}
</div>
</section>
{/* Active Projects */}
<section className="mb-12">
<div className="flex justify-between items-center mb-6">
<h2 className="text-3xl font-bold text-gray-900">Активные проекты</h2>
<Link href="/projects" className="text-blue-600 hover:text-blue-800">
Все проекты
</Link>
</div>
<div className="grid md:grid-cols-2 gap-6">
{activeProjects.map(project => (
<div key={project.id} className="bg-white border rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center justify-between mb-3">
<h3 className="text-xl font-semibold text-gray-900">{project.title}</h3>
<span className="bg-green-100 text-green-800 text-xs px-2 py-1 rounded">
Активный
</span>
</div>
<p className="text-gray-700 mb-4 line-clamp-3">{project.description}</p>
<div className="text-sm text-gray-500">
<p>Период: {new Date(project.startDate).getFullYear()} - {project.endDate ? new Date(project.endDate).getFullYear() : 'н.в.'}</p>
{project.funding && <p>Финансирование: {project.funding}</p>}
</div>
</div>
))}
</div>
</section>
{/* Research Areas */}
<section className="bg-gray-50 rounded-lg p-8">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">Направления исследований</h2>
<div className="grid md:grid-cols-3 gap-6">
<div className="text-center">
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl">🔬</span>
</div>
<h3 className="text-xl font-semibold mb-3">Радиофотоника</h3>
<p className="text-gray-600">
Исследования в области взаимодействия оптических и микроволновых сигналов
</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl">📡</span>
</div>
<h3 className="text-xl font-semibold mb-3">Оптические телекоммуникации</h3>
<p className="text-gray-600">
Разработка систем высокоскоростной передачи данных по оптическим каналам
</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl"></span>
</div>
<h3 className="text-xl font-semibold mb-3">Квантовые технологии</h3>
<p className="text-gray-600">
Исследование квантовых эффектов в оптических системах и квантовых коммуникаций
</p>
</div>
</div>
</section>
</div>
);
}

View File

@ -0,0 +1,140 @@
import { mockProjects, mockStaff, mockPublications } from '@/lib/mockData';
import { Project } from '@/types';
export default function ProjectsPage() {
const activeProjects = mockProjects.filter(p => p.status === 'active');
const completedProjects = mockProjects.filter(p => p.status === 'completed');
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Проекты</h1>
<p className="text-lg text-gray-600">
Исследовательские проекты и разработки отдела радиофотоники
</p>
</div>
{/* Active Projects */}
{activeProjects.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-gray-900 mb-6">Активные проекты</h2>
<div className="grid gap-6">
{activeProjects.map(project => (
<ProjectCard key={project.id} project={project} />
))}
</div>
</section>
)}
{/* Completed Projects */}
{completedProjects.length > 0 && (
<section>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Завершенные проекты</h2>
<div className="grid gap-6">
{completedProjects.map(project => (
<ProjectCard key={project.id} project={project} />
))}
</div>
</section>
)}
</div>
);
}
function ProjectCard({ project }: { project: Project }) {
const teamMembers = mockStaff.filter(staff => project.team.includes(staff.id));
const projectPublications = mockPublications.filter(pub =>
project.publications.includes(pub.id)
);
const statusColors = {
active: 'bg-green-100 text-green-800',
completed: 'bg-blue-100 text-blue-800',
planned: 'bg-yellow-100 text-yellow-800'
};
const statusLabels = {
active: 'Активный',
completed: 'Завершен',
planned: 'Планируется'
};
return (
<div className="bg-white border rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-2xl font-semibold text-gray-900">{project.title}</h3>
<span className={`text-xs px-2 py-1 rounded ${statusColors[project.status]}`}>
{statusLabels[project.status]}
</span>
</div>
<div className="text-sm text-gray-600 mb-4">
<span className="font-medium">Период:</span> {new Date(project.startDate).getFullYear()} - {project.endDate ? new Date(project.endDate).getFullYear() : 'н.в.'}
{project.funding && (
<span className="ml-4">
<span className="font-medium">Финансирование:</span> {project.funding}
</span>
)}
</div>
</div>
</div>
<div className="mb-6">
<p className="text-gray-700 leading-relaxed">{project.description}</p>
</div>
{/* Team */}
{teamMembers.length > 0 && (
<div className="mb-4">
<h4 className="text-sm font-semibold text-gray-900 mb-2">Команда проекта:</h4>
<div className="flex flex-wrap gap-2">
{teamMembers.map(member => (
<div key={member.id} className="bg-gray-100 rounded-lg px-3 py-1">
<p className="text-sm font-medium text-gray-900">{member.name}</p>
<p className="text-xs text-gray-600">{member.position}</p>
</div>
))}
</div>
</div>
)}
{/* Partners */}
{project.partners && project.partners.length > 0 && (
<div className="mb-4">
<h4 className="text-sm font-semibold text-gray-900 mb-2">Партнеры:</h4>
<div className="flex flex-wrap gap-2">
{project.partners.map(partner => (
<span
key={partner}
className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded"
>
{partner}
</span>
))}
</div>
</div>
)}
{/* Publications */}
{projectPublications.length > 0 && (
<div>
<h4 className="text-sm font-semibold text-gray-900 mb-2">
Публикации проекта ({projectPublications.length}):
</h4>
<div className="space-y-2">
{projectPublications.map(pub => (
<div key={pub.id} className="border-l-4 border-blue-200 pl-3">
<p className="text-sm font-medium text-gray-900">{pub.title}</p>
<p className="text-xs text-gray-600">
{pub.authors.join(', ')} {pub.journal} {pub.year}
</p>
</div>
))}
</div>
</div>
)}
</div>
);
}

View File

@ -0,0 +1,202 @@
'use client';
import { useState } from 'react';
import { mockPublications } from '@/lib/mockData';
import { Publication } from '@/types';
export default function PublicationsPage() {
const [searchTerm, setSearchTerm] = useState('');
const [selectedType, setSelectedType] = useState<string>('all');
const [selectedYear, setSelectedYear] = useState<string>('all');
const filteredPublications = mockPublications.filter(publication => {
const matchesSearch =
publication.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
publication.authors.some(author => author.toLowerCase().includes(searchTerm.toLowerCase())) ||
publication.journal.toLowerCase().includes(searchTerm.toLowerCase()) ||
publication.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesType = selectedType === 'all' || publication.type === selectedType;
const matchesYear = selectedYear === 'all' || publication.year.toString() === selectedYear;
return matchesSearch && matchesType && matchesYear;
});
const uniqueYears = Array.from(new Set(mockPublications.map(p => p.year))).sort((a, b) => b - a);
const uniqueTypes = Array.from(new Set(mockPublications.map(p => p.type)));
const typeLabels: Record<string, string> = {
'article': 'Статья',
'conference': 'Конференция',
'book': 'Книга',
'patent': 'Патент'
};
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Публикации</h1>
<p className="text-lg text-gray-600">
Научные работы и публикации сотрудников отдела радиофотоники
</p>
</div>
{/* Search and Filters */}
<div className="bg-white border rounded-lg p-6 mb-8 shadow-sm">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="md:col-span-2">
<label htmlFor="search" className="block text-sm font-medium text-gray-700 mb-2">
Поиск
</label>
<input
type="text"
id="search"
placeholder="Поиск по названию, авторам, журналу или тегам..."
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div>
<label htmlFor="type" className="block text-sm font-medium text-gray-700 mb-2">
Тип публикации
</label>
<select
id="type"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={selectedType}
onChange={(e) => setSelectedType(e.target.value)}
>
<option value="all">Все типы</option>
{uniqueTypes.map(type => (
<option key={type} value={type}>{typeLabels[type] || type}</option>
))}
</select>
</div>
<div>
<label htmlFor="year" className="block text-sm font-medium text-gray-700 mb-2">
Год
</label>
<select
id="year"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
>
<option value="all">Все годы</option>
{uniqueYears.map(year => (
<option key={year} value={year.toString()}>{year}</option>
))}
</select>
</div>
</div>
<div className="mt-4 flex items-center justify-between">
<p className="text-sm text-gray-600">
Найдено публикаций: {filteredPublications.length} из {mockPublications.length}
</p>
{(searchTerm || selectedType !== 'all' || selectedYear !== 'all') && (
<button
onClick={() => {
setSearchTerm('');
setSelectedType('all');
setSelectedYear('all');
}}
className="text-sm text-blue-600 hover:text-blue-800"
>
Сбросить фильтры
</button>
)}
</div>
</div>
{/* Publications List */}
<div className="space-y-6">
{filteredPublications.length === 0 ? (
<div className="text-center py-12">
<p className="text-gray-500 text-lg">Публикации не найдены</p>
<p className="text-gray-400 text-sm mt-2">Попробуйте изменить параметры поиска</p>
</div>
) : (
filteredPublications.map(publication => (
<PublicationCard key={publication.id} publication={publication} />
))
)}
</div>
</div>
);
}
function PublicationCard({ publication }: { publication: Publication }) {
const typeLabels: Record<string, string> = {
'article': 'Статья',
'conference': 'Конференция',
'book': 'Книга',
'patent': 'Патент'
};
return (
<div className="bg-white border rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h2 className="text-xl font-semibold text-gray-900">{publication.title}</h2>
<span className="bg-gray-100 text-gray-700 text-xs px-2 py-1 rounded">
{typeLabels[publication.type] || publication.type}
</span>
</div>
<p className="text-gray-600 mb-2">
<span className="font-medium">Авторы:</span> {publication.authors.join(', ')}
</p>
<p className="text-gray-600 mb-2">
<span className="font-medium">Журнал:</span> {publication.journal} {publication.year}
</p>
{publication.doi && (
<p className="text-gray-600 mb-2">
<span className="font-medium">DOI:</span>
<a href={`https://doi.org/${publication.doi}`} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800 ml-1">
{publication.doi}
</a>
</p>
)}
</div>
{publication.pdfUrl && (
<div className="ml-4">
<a
href={publication.pdfUrl}
target="_blank"
rel="noopener noreferrer"
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition-colors text-sm"
>
PDF
</a>
</div>
)}
</div>
<div className="mb-4">
<p className="text-gray-700 leading-relaxed">{publication.abstract}</p>
</div>
<div className="flex flex-wrap gap-2">
{publication.tags.map(tag => (
<span
key={tag}
className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded hover:bg-blue-200 cursor-pointer transition-colors"
onClick={() => {
// В будущем можно добавить фильтрацию по тегам
}}
>
{tag}
</span>
))}
</div>
</div>
);
}

View File

@ -0,0 +1,110 @@
import { mockStaff, mockPublications } from '@/lib/mockData';
import { Staff } from '@/types';
import Image from 'next/image';
export default function StaffPage() {
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Сотрудники</h1>
<p className="text-lg text-gray-600">
Наша команда опытных исследователей и специалистов в области радиофотоники
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{mockStaff.map(staff => (
<StaffCard key={staff.id} staff={staff} />
))}
</div>
</div>
);
}
function StaffCard({ staff }: { staff: Staff }) {
const staffPublications = mockPublications.filter(pub =>
staff.publications.includes(pub.id)
);
return (
<div className="bg-white border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow">
{/* Photo */}
<div className="relative h-64 bg-gray-200">
<div className="absolute inset-0 bg-gradient-to-t from-gray-900/20 to-transparent" />
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-32 h-32 bg-gray-300 rounded-full flex items-center justify-center">
<span className="text-3xl text-gray-600">👤</span>
</div>
</div>
</div>
{/* Content */}
<div className="p-6">
<div className="mb-4">
<h3 className="text-xl font-semibold text-gray-900 mb-1">{staff.name}</h3>
<p className="text-blue-600 font-medium">{staff.position}</p>
<p className="text-sm text-gray-500">{staff.degree}</p>
</div>
<div className="mb-4">
<p className="text-gray-700 text-sm leading-relaxed">{staff.bio}</p>
</div>
{/* Contact Info */}
<div className="mb-4 space-y-2">
<div className="flex items-center text-sm text-gray-600">
<span className="w-4 h-4 mr-2">📧</span>
<a href={`mailto:${staff.email}`} className="hover:text-blue-600 transition-colors">
{staff.email}
</a>
</div>
{staff.phone && (
<div className="flex items-center text-sm text-gray-600">
<span className="w-4 h-4 mr-2">📞</span>
<a href={`tel:${staff.phone}`} className="hover:text-blue-600 transition-colors">
{staff.phone}
</a>
</div>
)}
</div>
{/* Research Interests */}
<div className="mb-4">
<h4 className="text-sm font-semibold text-gray-900 mb-2">Области исследований:</h4>
<div className="flex flex-wrap gap-1">
{staff.researchInterests.map(interest => (
<span
key={interest}
className="bg-gray-100 text-gray-700 text-xs px-2 py-1 rounded"
>
{interest}
</span>
))}
</div>
</div>
{/* Publications */}
{staffPublications.length > 0 && (
<div>
<h4 className="text-sm font-semibold text-gray-900 mb-2">
Публикации ({staffPublications.length}):
</h4>
<div className="space-y-2">
{staffPublications.slice(0, 2).map(pub => (
<div key={pub.id} className="text-xs text-gray-600">
<p className="font-medium text-gray-800 line-clamp-1">{pub.title}</p>
<p className="text-gray-500">{pub.journal}, {pub.year}</p>
</div>
))}
{staffPublications.length > 2 && (
<p className="text-xs text-blue-600">
и еще {staffPublications.length - 2} публикаций...
</p>
)}
</div>
</div>
)}
</div>
</div>
);
}

View File

@ -0,0 +1,35 @@
export default function Footer() {
return (
<footer className="bg-gray-800 text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 className="text-lg font-semibold mb-4">Отдел Радиофотоники</h3>
<p className="text-gray-300">
Ведущий научно-исследовательский центр в области радиофотоники и оптических технологий.
</p>
</div>
<div>
<h3 className="text-lg font-semibold mb-4">Контакты</h3>
<div className="text-gray-300 space-y-2">
<p>📧 info@radiophotonics.ru</p>
<p>📞 +7 (495) 123-45-67</p>
<p>📍 Москва, ул. Научная, 1</p>
</div>
</div>
<div>
<h3 className="text-lg font-semibold mb-4">Полезные ссылки</h3>
<div className="text-gray-300 space-y-2">
<p>🔗 Научные базы данных</p>
<p>🔗 Партнерские организации</p>
<p>🔗 Образовательные программы</p>
</div>
</div>
</div>
<div className="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400">
<p>&copy; 2024 Отдел Радиофотоники. Все права защищены.</p>
</div>
</div>
</footer>
)
}

View File

@ -0,0 +1,41 @@
import Link from 'next/link'
export default function Header() {
return (
<header className="bg-white shadow-sm border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<div className="flex items-center">
<Link href="/" className="text-xl font-bold text-gray-900">
Отдел Радиофотоники
</Link>
</div>
<nav className="hidden md:flex space-x-8">
<Link href="/" className="text-gray-700 hover:text-blue-600 transition-colors">
Главная
</Link>
<Link href="/publications" className="text-gray-700 hover:text-blue-600 transition-colors">
Публикации
</Link>
<Link href="/staff" className="text-gray-700 hover:text-blue-600 transition-colors">
Сотрудники
</Link>
<Link href="/projects" className="text-gray-700 hover:text-blue-600 transition-colors">
Проекты
</Link>
<Link href="/contacts" className="text-gray-700 hover:text-blue-600 transition-colors">
Контакты
</Link>
</nav>
<div className="md:hidden">
<button className="text-gray-700 hover:text-blue-600">
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
</header>
)
}

View File

@ -0,0 +1,172 @@
import { Publication, Staff, Project, News, RID, Partner } from '@/types';
export const mockPublications: Publication[] = [
{
id: '1',
title: 'Когерентная обработка сигналов в радиофотонных системах',
authors: ['Иванов И.И.', 'Петров П.П.', 'Сидоров С.С.'],
journal: 'Квантовая электроника',
year: 2024,
abstract: 'В работе исследуются методы когерентной обработки сигналов в современных радиофотонных системах. Предложен новый алгоритм фазовой синхронизации, обеспечивающий высокую точность измерений.',
doi: '10.1070/QEL17XXX',
pdfUrl: '/uploads/publications/coherent-processing-2024.pdf',
tags: ['радиофотоника', 'когерентная обработка', 'фазовая синхронизация'],
type: 'article'
},
{
id: '2',
title: 'Микроволновые фотонные фильтры на основе кремниевых волноводов',
authors: ['Васильев В.В.', 'Козлов К.К.'],
journal: 'Оптика и спектроскопия',
year: 2024,
abstract: 'Разработаны и исследованы микроволновые фотонные фильтры на базе кремниевых волноводных структур. Достигнута высокая селективность и низкие оптические потери.',
doi: '10.1134/S0030400X24XXX',
pdfUrl: '/uploads/publications/photonic-filters-2024.pdf',
tags: ['фотонные фильтры', 'кремниевые волноводы', 'микроволновая фотоника'],
type: 'article'
},
{
id: '3',
title: 'Оптические генераторы миллиметрового диапазона для 5G/6G систем',
authors: ['Александров А.А.', 'Николаев Н.Н.', 'Федоров Ф.Ф.'],
journal: 'IEEE Photonics Technology Letters',
year: 2023,
abstract: 'Представлены результаты разработки оптических генераторов миллиметрового диапазона для применения в системах мобильной связи следующего поколения.',
doi: '10.1109/LPT.2023.XXXX',
pdfUrl: '/uploads/publications/mmwave-generators-2023.pdf',
tags: ['миллиметровые волны', '5G', '6G', 'оптические генераторы'],
type: 'article'
}
];
export const mockStaff: Staff[] = [
{
id: '1',
name: 'Иванов Иван Иванович',
position: 'Заведующий отделом',
email: 'ivanov@radiophotonics.ru',
phone: '+7 (495) 123-45-67',
photo: '/uploads/staff/ivanov.jpg',
bio: 'Доктор физико-математических наук, профессор. Специалист в области радиофотоники и оптических телекоммуникаций. Автор более 150 научных работ.',
publications: ['1', '2'],
researchInterests: ['радиофотоника', 'оптические телекоммуникации', 'когерентная оптика'],
degree: 'д.ф.-м.н.'
},
{
id: '2',
name: 'Петров Петр Петрович',
position: 'Ведущий научный сотрудник',
email: 'petrov@radiophotonics.ru',
phone: '+7 (495) 123-45-68',
photo: '/uploads/staff/petrov.jpg',
bio: 'Кандидат физико-математических наук. Специализируется на микроволновой фотонике и интегральной оптике.',
publications: ['1', '3'],
researchInterests: ['микроволновая фотоника', 'интегральная оптика', 'оптоэлектроника'],
degree: 'к.ф.-м.н.'
},
{
id: '3',
name: 'Сидорова Анна Сергеевна',
position: 'Старший научный сотрудник',
email: 'sidorova@radiophotonics.ru',
phone: '+7 (495) 123-45-69',
photo: '/uploads/staff/sidorova.jpg',
bio: 'Кандидат технических наук. Работает в области фотонных сенсоров и оптических измерительных систем.',
publications: ['2'],
researchInterests: ['фотонные сенсоры', 'оптические измерения', 'волоконная оптика'],
degree: 'к.т.н.'
}
];
export const mockProjects: Project[] = [
{
id: '1',
title: 'Разработка радиофотонных систем для 6G сетей',
description: 'Исследование и создание передовых радиофотонных технологий для беспроводных сетей шестого поколения, обеспечивающих высокую скорость передачи данных и низкую задержку.',
status: 'active',
startDate: '2023-01-01',
endDate: '2025-12-31',
team: ['1', '2', '3'],
publications: ['3'],
partners: ['Huawei', 'Nokia'],
funding: 'РФФИ, грант №23-07-00123',
image: '/uploads/projects/6g-networks.jpg'
},
{
id: '2',
title: 'Квантовые коммуникации на основе фотонных кристаллов',
description: 'Разработка новых подходов к квантовым коммуникациям с использованием фотонных кристаллических структур для повышения безопасности передачи информации.',
status: 'active',
startDate: '2022-06-01',
endDate: '2024-12-31',
team: ['1', '3'],
publications: ['1', '2'],
funding: 'РНФ, грант №22-12-00456',
image: '/uploads/projects/quantum-comm.jpg'
}
];
export const mockNews: News[] = [
{
id: '1',
title: 'Отдел радиофотоники получил грант РНФ на исследование квантовых коммуникаций',
content: 'Коллектив отдела успешно выиграл конкурс Российского научного фонда и получил финансирование на трехлетний проект по разработке квантовых коммуникационных систем.',
date: '2024-01-15',
author: 'Иванов И.И.',
image: '/uploads/news/grant-rnf.jpg',
tags: ['гранты', 'квантовые коммуникации', 'РНФ']
},
{
id: '2',
title: 'Публикация в высокорейтинговом журнале IEEE',
content: 'Статья сотрудников отдела о микроволновых фотонных фильтрах опубликована в престижном международном журнале IEEE Photonics Technology Letters.',
date: '2024-02-10',
author: 'Петров П.П.',
image: '/uploads/news/ieee-publication.jpg',
tags: ['публикации', 'IEEE', 'фотонные фильтры']
}
];
export const mockRIDs: RID[] = [
{
id: '1',
title: 'Способ формирования радиофотонного сигнала с низким фазовым шумом',
type: 'patent',
registrationNumber: 'RU 2745123 C1',
registrationDate: '2021-03-22',
authors: ['Иванов И.И.', 'Петров П.П.'],
description: 'Изобретение относится к области радиофотоники и может быть использовано для создания высокостабильных генераторов.',
status: 'registered'
},
{
id: '2',
title: 'Программа моделирования фотонных интегральных схем',
type: 'software',
registrationNumber: '2023612345',
registrationDate: '2023-04-15',
authors: ['Сидорова А.С.', 'Васильев В.В.'],
description: 'Программное обеспечение для проектирования и моделирования фотонных интегральных схем.',
status: 'registered'
}
];
export const mockPartners: Partner[] = [
{
id: '1',
name: 'Huawei Technologies',
logo: '/uploads/partners/huawei-logo.png',
website: 'https://www.huawei.com',
description: 'Совместные исследования в области 5G/6G технологий и радиофотоники.',
collaborationType: ['исследования', 'разработка'],
projects: ['1']
},
{
id: '2',
name: 'ООО "ОптЛинк"',
logo: '/uploads/partners/optlink-logo.png',
website: 'https://optlink.ru',
description: 'Российская компания, специализирующаяся на производстве оптоэлектронных компонентов.',
collaborationType: ['производство', 'внедрение'],
projects: ['2']
}
];

View File

@ -0,0 +1,70 @@
export interface Publication {
id: string;
title: string;
authors: string[];
journal: string;
year: number;
abstract: string;
doi?: string;
pdfUrl?: string;
tags: string[];
type: 'article' | 'conference' | 'book' | 'patent';
}
export interface Staff {
id: string;
name: string;
position: string;
email: string;
phone?: string;
photo: string;
bio: string;
publications: string[];
researchInterests: string[];
degree: string;
}
export interface Project {
id: string;
title: string;
description: string;
status: 'active' | 'completed' | 'planned';
startDate: string;
endDate?: string;
team: string[];
publications: string[];
partners?: string[];
funding?: string;
image?: string;
}
export interface News {
id: string;
title: string;
content: string;
date: string;
author: string;
image?: string;
tags: string[];
}
export interface RID {
id: string;
title: string;
type: 'patent' | 'software' | 'database' | 'method';
registrationNumber?: string;
registrationDate?: string;
authors: string[];
description: string;
status: 'registered' | 'pending' | 'application';
}
export interface Partner {
id: string;
name: string;
logo: string;
website?: string;
description: string;
collaborationType: string[];
projects: string[];
}

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": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

6
package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "ru.radiophotonics",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}