diff --git a/src/App.tsx b/src/App.tsx
index 2dbd619..64495dc 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,6 +7,8 @@ import { Messages } from './pages/Messages';
import { Schedule } from './pages/Schedule';
import { Grades } from './pages/Grades';
import { Debts } from './pages/Debts';
+import { Docs } from './pages/Docs';
+import { Support } from './pages/Support';
import { useStore } from './store/useStore';
import { ThemeProvider } from './components/ThemeProvider';
import { Layout } from './components/Layout';
@@ -21,13 +23,6 @@ const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) =
return {children};
};
-const PlaceholderPage: React.FC<{ title: string }> = ({ title }) => (
-
-
{title}
-
Этот раздел появится в будущих обновлениях
-
-);
-
function App() {
return (
@@ -70,7 +65,7 @@ function App() {
path="/docs"
element={
-
+
}
/>
@@ -78,7 +73,7 @@ function App() {
path="/support"
element={
-
+
}
/>
diff --git a/src/api/mock.ts b/src/api/mock.ts
index bac270f..347e512 100644
--- a/src/api/mock.ts
+++ b/src/api/mock.ts
@@ -9,28 +9,41 @@ export const mockProfile: Profile = {
raw: {
'Дата рождения': '01.01.2004',
'Статус': 'Студент',
+ 'Курс': '2',
+ 'Форма обучения': 'Очная',
},
};
export const mockMessages: MessageListItem[] = [
- { id: '1', date: '10.04.2026', subject: 'Оплата обучения', senderOrRecipient: 'Бухгалтерия', hasFiles: true },
- { id: '2', date: '09.04.2026', subject: 'Пересдача экзамена', senderOrRecipient: 'Деканат ИСС', hasFiles: false },
- { id: '3', date: '08.04.2026', subject: 'Приглашение на конференцию', senderOrRecipient: 'Студсовет', hasFiles: false },
+ { id: '1', date: '10.04.2026', subject: 'Оплата обучения за 4 семестр', senderOrRecipient: 'Бухгалтерия', hasFiles: true },
+ { id: '2', date: '09.04.2026', subject: 'Пересдача экзамена по Физике', senderOrRecipient: 'Деканат ИСС', hasFiles: false },
+ { id: '3', date: '08.04.2026', subject: 'Приглашение на конференцию "Сети 2026"', senderOrRecipient: 'Студсовет', hasFiles: false },
+ { id: '4', date: '07.04.2026', subject: 'Заполнение анкеты первокурсника', senderOrRecipient: 'Отдел кадров', hasFiles: true },
+ { id: '5', date: '05.04.2026', subject: 'Изменение в расписании на 12 апреля', senderOrRecipient: 'Учебный отдел', hasFiles: false },
];
export const mockSchedule: Schedule = {
days: [
{
- date: '13.04.2026, Понедельник',
+ date: '13.04.2026, Понедельник (Числитель)',
lessons: [
{ time: '09:00 - 10:35', subject: 'Высшая математика', type: 'Лекция', teacher: 'Петров П.П.', room: '123/1' },
{ time: '10:45 - 12:20', subject: 'Физика', type: 'Практика', teacher: 'Сидоров С.С.', room: '456/2' },
+ { time: '13:00 - 14:35', subject: 'Информационные технологии', type: 'Лабораторная', teacher: 'Николаев Н.Н.', room: '302/1' },
]
},
{
date: '14.04.2026, Вторник',
lessons: [
{ time: '13:00 - 14:35', subject: 'Иностранный язык', type: 'Практика', teacher: 'Smith J.', room: '222/1' },
+ { time: '14:45 - 16:20', subject: 'Физкультура', type: 'Практика', teacher: 'Зайцев А.В.', room: 'Спортзал' },
+ ]
+ },
+ {
+ date: '15.04.2026, Среда',
+ lessons: [
+ { time: '09:00 - 10:35', subject: 'Экология', type: 'Лекция', teacher: 'Зеленина Е.С.', room: '501/2' },
+ { time: '10:45 - 12:20', subject: 'История России', type: 'Лекция', teacher: 'Александров А.А.', room: 'Актовый зал' },
]
}
]
@@ -38,12 +51,19 @@ export const mockSchedule: Schedule = {
export const mockGrades: Grades = {
entries: [
- { subject: 'Информатика', type: 'Экзамен', mark: 'Отлично', teacher: 'Николаев Н.Н.', semester: '1' },
+ { subject: 'Математический анализ', type: 'Экзамен', mark: 'Отлично', teacher: 'Петров П.П.', semester: '1' },
{ subject: 'История', type: 'Зачет', mark: 'Зачтено', teacher: 'Александров А.А.', semester: '1' },
- { subject: 'Математический анализ', type: 'Экзамен', mark: 'Хорошо', teacher: 'Петров П.П.', semester: '1' },
+ { subject: 'Информатика', type: 'Экзамен', mark: 'Отлично', teacher: 'Николаев Н.Н.', semester: '1' },
+ { subject: 'Физика (Механика)', type: 'Дифф. зачет', mark: 'Хорошо', teacher: 'Сидоров С.С.', semester: '1' },
+ { subject: 'Основы программирования', type: 'Экзамен', mark: 'Отлично', teacher: 'Николаев Н.Н.', semester: '2' },
+ { subject: 'Электротехника', type: 'Экзамен', mark: 'Удовл.', teacher: 'Токов И.А.', semester: '2' },
]
};
+export const mockDebts: GradeEntry[] = [
+ { subject: 'Философия', type: 'Зачет', mark: 'Не зачтено', teacher: 'Мудров Ф.Ф.', semester: '2' },
+];
+
export const mockApi = {
getProfile: async (): Promise => {
return new Promise((resolve) => {
@@ -64,10 +84,11 @@ export const mockApi = {
return new Promise((resolve) => {
setTimeout(() => resolve({
id,
- text: 'Текст сообщения от бэкенда. Пожалуйста, обратите внимание на сроки.',
+ text: 'Добрый день!\n\nИнформируем вас о необходимости произвести оплату за 4 семестр обучения до 15 апреля 2026 года. В случае возникновения вопросов, пожалуйста, свяжитесь с бухгалтерией по внутреннему номеру 102.\n\nС уважением, Администрация.',
history: [
- { date: '10.04.2026', author: 'Бухгалтерия', text: 'Первое сообщение в истории.' }
- ]
+ { date: '10.04.2026', author: 'Бухгалтерия', text: 'Уведомление об оплате' }
+ ],
+ files: ['receipt_form.pdf', 'payment_instruction.docx']
}), 500);
});
},
@@ -83,7 +104,7 @@ export const mockApi = {
},
getDebts: async (): Promise<{ entries: GradeEntry[] }> => {
return new Promise((resolve) => {
- setTimeout(() => resolve({ entries: [] }), 500);
+ setTimeout(() => resolve({ entries: mockDebts }), 500);
});
},
};
diff --git a/src/pages/Docs.tsx b/src/pages/Docs.tsx
new file mode 100644
index 0000000..4b7db94
--- /dev/null
+++ b/src/pages/Docs.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import { FileText, FileCheck, FileSignature, Download, ExternalLink } from 'lucide-react';
+
+const docs = [
+ { id: '1', title: 'Справка об обучении', description: 'Для предоставления по месту требования', icon: FileCheck },
+ { id: '2', title: 'Справка о доходах (стипендия)', description: 'Для получения социальных льгот', icon: FileText },
+ { id: '3', title: 'Заявление на мат. помощь', description: 'Подача заявления в профком', icon: FileSignature },
+ { id: '4', title: 'Заявление на смену группы', description: 'Перевод в другую учебную группу', icon: FileSignature },
+];
+
+export const Docs: React.FC = () => {
+ return (
+
+
+
+
+ {docs.map((doc) => (
+
+
+
+
{doc.title}
+
{doc.description}
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
Готовые документы
+
Здесь будут отображаться документы, которые вы заказывали ранее и они уже готовы.
+
+
+
+
+ );
+};
diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx
index 8bc3699..94a99a7 100644
--- a/src/pages/Messages.tsx
+++ b/src/pages/Messages.tsx
@@ -1,14 +1,18 @@
import React, { useEffect, useState } from 'react';
import { api } from '../api/client';
-import type { MessageListItem } from '../types/api';
-import { Mail, Send, Paperclip, ChevronRight, Inbox, MessageSquareText } from 'lucide-react';
+import type { MessageListItem, MessageDetails } from '../types/api';
+import { Mail, Send, Paperclip, ChevronRight, Inbox, MessageSquareText, ArrowLeft, Download, Reply } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Badge } from '../components/ui/badge';
+import { Button } from '../components/ui/button';
export const Messages: React.FC = () => {
const [messages, setMessages] = useState([]);
+ const [selectedId, setSelectedId] = useState(null);
+ const [details, setDetails] = useState(null);
const [type, setType] = useState<'in' | 'out'>('in');
const [loading, setLoading] = useState(true);
+ const [detailsLoading, setDetailsLoading] = useState(false);
useEffect(() => {
const fetchMessages = async () => {
@@ -25,6 +29,102 @@ export const Messages: React.FC = () => {
fetchMessages();
}, [type]);
+ const handleSelectMessage = async (id: string) => {
+ setSelectedId(id);
+ setDetailsLoading(true);
+ try {
+ const data = await api.getMessageDetails(id);
+ setDetails(data);
+ } catch (err) {
+ console.error(err);
+ } finally {
+ setDetailsLoading(false);
+ }
+ };
+
+ const handleBack = () => {
+ setSelectedId(null);
+ setDetails(null);
+ };
+
+ if (selectedId) {
+ return (
+
+
+
+ {detailsLoading ? (
+
+
+
Загрузка содержания...
+
+ ) : details ? (
+
+
+
+
+
{messages.find(m => m.id === selectedId)?.subject}
+
+ От: {messages.find(m => m.id === selectedId)?.senderOrRecipient} • {messages.find(m => m.id === selectedId)?.date}
+
+
+
+
+
+
+ {details.text}
+
+
+ {details.files && details.files.length > 0 && (
+
+
Вложения ({details.files.length})
+
+ {details.files.map((file, i) => (
+
+ ))}
+
+
+ )}
+
+
+ {details.history && details.history.length > 0 && (
+
+ История переписки
+ {details.history.map((h, i) => (
+
+
+ {h.author}
+ {h.date}
+
+
{h.text}
+
+ ))}
+
+ )}
+
+ ) : (
+
+ Не удалось загрузить сообщение
+
+ )}
+
+ );
+ }
+
return (
@@ -75,6 +175,7 @@ export const Messages: React.FC = () => {
messages.map((msg) => (
handleSelectMessage(msg.id)}
className="surface-card group hover:bg-card/60 cursor-pointer flex items-center gap-4 p-4"
>
diff --git a/src/pages/Support.tsx b/src/pages/Support.tsx
new file mode 100644
index 0000000..f93d553
--- /dev/null
+++ b/src/pages/Support.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import { LifeBuoy, Send, MessageCircle, HelpCircle } from 'lucide-react';
+import { TextField } from '../components/TextField';
+import { Button } from '../components/ui/button';
+
+export const Support: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+ Новое обращение
+
+ Опишите вашу проблему, и мы ответим вам в течение рабочего дня.
+
+
+
+
+
+
+
+
+
+ FAQ
+
+
+ {[
+ 'Как восстановить пароль?',
+ 'Где найти справку о доходах?',
+ 'Как сменить учебную группу?',
+ 'Проблемы с личным кабинетом'
+ ].map((q, i) => (
+
+ ))}
+
+
+
+
+
Нужна помощь по телефону?
+
Технический отдел:
+7 (812) 123-45-67
+
+
+
+
+ );
+};