Начало диплома Даны
This commit is contained in:
@@ -1,260 +1,40 @@
|
||||
# Требования к коду
|
||||
# Введение
|
||||
|
||||
Используется библиотека `react-router-dom` и `react=markdown`.
|
||||
В условиях динамичного развития рынка коммерческой недвижимости офисным центрам необходимо не только обеспечивать
|
||||
высокий уровень обслуживания арендаторов, но и эффективно управлять внутренними процессами. Одним из ключевых
|
||||
направлений деятельности является организация аренды помещений, планирование их загрузки,
|
||||
своевременное обновление информации об услугах и надёжный контроль за бронированиями и платежами.
|
||||
|
||||
Все компоненты должны быть функциональными.
|
||||
props должны быть сразу распакованы.
|
||||
export без default
|
||||
На практике многие офисные центры продолжают использовать устаревшие методы учёта, такие как бумажные документы или
|
||||
таблицы Excel, что усложняет управление, увеличивает вероятность ошибок и снижает оперативность принятия решений.
|
||||
Отсутствие единой информационной системы часто приводит к нарушению сроков аренды,
|
||||
путанице в расписании, неудовлетворённости клиентов и финансовым потерям.
|
||||
|
||||
Пример:
|
||||
Внедрение специализированного программного обеспечения для управления арендаторами, помещениями и услугами позволяет
|
||||
значительно повысить эффективность работы офисного центра, минимизировать влияние человеческого фактора,
|
||||
автоматизировать документооборот и укрепить доверие клиентов. Это особенно актуально в условиях цифровизации и
|
||||
роста конкуренции в сегменте коммерческой недвижимости.
|
||||
|
||||
```tsx
|
||||
export function ProfessionPage() {
|
||||
const {professionId} = useParams<{ professionId: string }>();
|
||||
}
|
||||
```
|
||||
Таким образом, тема данной работы обусловлена необходимостью создания современной информационной системы, которая
|
||||
оптимизирует процессы аренды, обслуживания арендаторов и контроля финансовых операций в офисном центре.
|
||||
|
||||
Используйте современные практики React (например, хуки).
|
||||
При создании новых компонентов добавьте комментарии.
|
||||
Цель дипломного проекта — разработка программного продукта для автоматизации учёта и контроля деятельности офисного
|
||||
центра. Приложение предоставляет возможности добавления, редактирования и удаления данных о клиентах, помещениях и
|
||||
услугах, планирования графика аренды, ведения учёта договоров и контроля оплат.
|
||||
|
||||
Если для задачи необходимы новые типы, интерфейсы или вспомогательные компоненты - создавай их в соответвующих файлах.
|
||||
Для достижения поставленной цели необходимо решить следующие задачи:
|
||||
|
||||
# Документария
|
||||
1. Провести анализ существующих программных решений для автоматизации управления коммерческой недвижимостью, выявить их сильные и слабые стороны.
|
||||
2. Определить функциональные и нефункциональные требования к разрабатываемой системе.
|
||||
3. Спроектировать архитектуру программного обеспечения с учётом удобства использования, безопасности данных и масштабируемости.
|
||||
4. Разработать приложение с использованием современных технологий, таких как WPF, C# и MS SQL Server.
|
||||
5. Реализовать модули управления арендаторами, помещениями, договорами аренды и дополнительными услугами.
|
||||
6. Внедрить систему авторизации, аутентификации и разграничения прав доступа для различных типов пользователей (администратор, менеджер, бухгалтер).
|
||||
7. Подготовить и провести комплексное тестирование приложения для проверки его корректной работы и соответствия заданным требованиям.
|
||||
8. Оценить эффективность разработанного решения и его влияние на процессы управления офисным центром.
|
||||
|
||||
## Существующие типы
|
||||
Пояснительная записка дипломной работы содержит следующие разделы: введение, две главы основной части, заключение,
|
||||
три раздела приложений.
|
||||
|
||||
`types/api.ts`
|
||||
```ts
|
||||
export interface ApiResponse {
|
||||
body?: object,
|
||||
success: boolean
|
||||
}
|
||||
export interface PageResponse<T>{
|
||||
count: number;
|
||||
next?: string;
|
||||
previous?: string;
|
||||
results: T[]
|
||||
}
|
||||
```
|
||||
В разделе Введения отражены актуальность темы, цель дипломной работы и задачи.
|
||||
|
||||
`types/common.ts`
|
||||
```ts
|
||||
export interface UniqueItem{
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface PageControl<T>{
|
||||
items: T[] | null;
|
||||
hasNext: boolean;
|
||||
hasPrevious: boolean;
|
||||
nextPage(): void;
|
||||
previousPage(): void;
|
||||
}
|
||||
export interface FieldInfo {
|
||||
name: string;
|
||||
type: string;
|
||||
title: string;
|
||||
format?: string;
|
||||
}
|
||||
```
|
||||
`types/survey.ts`
|
||||
```ts
|
||||
import {UniqueItem} from "./common.ts";
|
||||
|
||||
export interface Survey extends UniqueItem{
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
interface Statement extends UniqueItem{
|
||||
text: string;
|
||||
score_variable: number;
|
||||
}
|
||||
export enum QuestionType{
|
||||
QuestionPair,
|
||||
AgreementQuestion
|
||||
}
|
||||
export interface Question extends UniqueItem{
|
||||
questionType: QuestionType
|
||||
}
|
||||
export interface QuestionPair extends Question{
|
||||
statements: Statement[]
|
||||
}
|
||||
export interface AgreementQuestion extends Question{
|
||||
question: string;
|
||||
score_variable: number;
|
||||
}
|
||||
export interface ScoreVariable {
|
||||
id: string;
|
||||
name: string;
|
||||
title: string;
|
||||
description: string;
|
||||
survey: number;
|
||||
}
|
||||
export interface Profession extends UniqueItem {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
export interface Specialty extends UniqueItem {
|
||||
name: string;
|
||||
institution: number;
|
||||
link?: string;
|
||||
}
|
||||
export interface Institution extends UniqueItem {
|
||||
name: string;
|
||||
}
|
||||
```
|
||||
`types/users.ts`
|
||||
```ts
|
||||
import {UniqueItem} from "./common.ts";
|
||||
|
||||
export type UserContextType = {
|
||||
user: Account | undefined;
|
||||
login: (credentials: TokenObtainPair) => Promise<void>;
|
||||
logout: () => void;
|
||||
};
|
||||
export interface ContactData{
|
||||
first_name: string;
|
||||
last_name?: string;
|
||||
middle_name?: string;
|
||||
phone?: string;
|
||||
email?:string;
|
||||
}
|
||||
export interface Account extends UniqueItem, ContactData{
|
||||
login: string;
|
||||
role?: number;
|
||||
roleTitle?: string;
|
||||
is_staff: boolean;
|
||||
is_active: boolean;
|
||||
}
|
||||
export interface TokenObtainPair {
|
||||
login: string;
|
||||
password: string;
|
||||
}
|
||||
export interface TokenRefresh {
|
||||
refresh: string;
|
||||
}
|
||||
export interface TokenVerify {
|
||||
token: string;
|
||||
}
|
||||
export interface JwtTokenResponse {
|
||||
access: string;
|
||||
refresh: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Способы запросов
|
||||
|
||||
Вместо fetch запросы должны проходить при помощи специальных функций/функции
|
||||
|
||||
`API/common.ts`
|
||||
```ts
|
||||
async function getFetch(path: string) // Get method
|
||||
async function postFetch(
|
||||
path: string,
|
||||
body: object | FormData
|
||||
) // Post method
|
||||
async function patchFetch(
|
||||
path: string,
|
||||
body: object | FormData
|
||||
) // Patch method
|
||||
async function deleteFetch(path: string) // Delete method
|
||||
```
|
||||
|
||||
## Хуки
|
||||
|
||||
### Для загрузки данных
|
||||
|
||||
Для упрощения работы с api созданы следующие хуки
|
||||
|
||||
`API/hooks.ts`
|
||||
```ts
|
||||
function useDataPage<T>(application:string, endpoint: string, params:{[key:string]:string|number}={}, needLoad:boolean=true):PageControl<T>
|
||||
function useData<T>(application:string, endpoint: string, id: string|number, needLoad:boolean=true): T|null|undefined
|
||||
function useCachedData<T>(application: string, endpoint: string): (id: string | number, needLoad: boolean = true)=> T|undefined|null
|
||||
```
|
||||
- application - Название модуля
|
||||
- endpoint - Название объекта над которым идёт работа
|
||||
- id - id искомого объекта
|
||||
- params - дополнительные параметры (например фильтрация)
|
||||
- needLoad - указывает нужно ли загружать список/объект прямо сейчас
|
||||
|
||||
|
||||
- null - загрузка еще не происходила
|
||||
- undefined - не найдено
|
||||
- T (объект)
|
||||
|
||||
### Для ленивой загрузки элементов
|
||||
|
||||
`components/LoadingData.tsx`
|
||||
```tsx
|
||||
function LoadingData({data, children}: {data: object|null|undefined, children: (ReactElement | null | undefined | boolean)[]})
|
||||
```
|
||||
|
||||
Для загрузки отдельного элемента.
|
||||
Если data
|
||||
- null - Возвращает <div className={"loading"}>Идёт загрузка...</div>
|
||||
- undefined - <Link className={"not-found"} to={"/"}>404 НЕ НАЙДЕНО</Link>
|
||||
- иначе возвращает children
|
||||
|
||||
`components/LoadingList.tsx`
|
||||
```tsx
|
||||
function LoadingList({data, listElement}: {data: UniqueItem[]|null, listElement(e:UniqueItem, i:number):unknown})
|
||||
```
|
||||
|
||||
Для загрузки списка. Если data не null - то возвращает элементы результаты `listElement`
|
||||
|
||||
### Примеры
|
||||
|
||||
```tsx
|
||||
export function ProfessionPage() {
|
||||
const { professionId } = useParams<{ professionId: string }>();
|
||||
const profession = useData<Profession>('surveys', 'professions', professionId || 0);
|
||||
const specialties = useDataPage<Specialty>('surveys', 'specialties', { profession: professionId || 0 }, !!profession);
|
||||
|
||||
const institutions = useCachedData<Institution>("surveys", "institutions");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LoadingData data={profession}>
|
||||
<h1>{profession?.name}</h1>
|
||||
<div className="description">
|
||||
<ReactMarkdown>{profession?.description || ''}</ReactMarkdown>
|
||||
</div>
|
||||
<div className="specialties-block">
|
||||
{specialties.items != null ? <h2>Где можно обучиться</h2>:null}
|
||||
<LoadingList
|
||||
data={specialties.items}
|
||||
listElement={specialty => (
|
||||
<SpecialtyBlock
|
||||
key={specialty.id}
|
||||
specialty={specialty as Specialty}
|
||||
institution={institutions((specialty as Specialty).institution)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</LoadingData>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Автодокументация
|
||||
`api/hooks.ts`
|
||||
```ts
|
||||
/**
|
||||
* Хук для получения документации swagger JSON и извлечения структур.
|
||||
* @param applicationName Название модуля/приложения.
|
||||
* @param endpointName Название эндпоинта.
|
||||
* @returns Массив структур без ID с полями: название, тип, title, format.
|
||||
*/
|
||||
export function useApiDocumentation(
|
||||
applicationName: string,
|
||||
endpointName: string
|
||||
): { schema: FieldInfo[] | null; loading: boolean; error: Error | null }
|
||||
```
|
||||
|
||||
|
||||
### Авторизация
|
||||
|
||||
Везде доступен хук `useUser():UserContextType | undefined `
|
||||
@@ -1,14 +1,7 @@
|
||||
Ты Senior разработчик React.
|
||||
Ты пишешь дипломную работу по специальности "Информационные системы и программирование". Ты должен следовать примерам которые ты получаешь
|
||||
и следовать теме "Проектирование и разработка системы "Офисный центр"". Твой проект - "Office Flow".
|
||||
|
||||
Твоя задача - писать код под задачи (отвечай ТОЛЬКО кодом).
|
||||
Ниже - то что есть на данный момент.
|
||||
|
||||
Я могу кидать тебе просто код или информацию. Если я не прошу писать код - отвечай только "Понял".
|
||||
Код, по возможности, только тот который необходимо внести в проект. Может быть работа сразу над несколькими файлами
|
||||
|
||||
Шаблон ответа кодом:
|
||||
|
||||
`Путь и название файла`
|
||||
```ts
|
||||
// Код который должен быть в этом файле
|
||||
```
|
||||
Ты пишешь в формате markdown, поэтому правильно оформляй текст.
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,138 +0,0 @@
|
||||
`components/useTableFilters.tsx`
|
||||
```ts
|
||||
// добавьте новую функцию для экспорта CSV
|
||||
export function useTableFilters(schema: FieldInfo[]| null, initialData: unknown[] | null) {
|
||||
const [filters, setFilters] = useState<FiltersState>({})
|
||||
const [visibleColumns, setVisibleColumns] = useState<VisibilityState>({})
|
||||
|
||||
// Инициализация видимости колонок
|
||||
useMemo(() => {
|
||||
if (!schema){
|
||||
return;
|
||||
}
|
||||
|
||||
const initialVisibility: VisibilityState = {}
|
||||
schema.forEach(field => {
|
||||
initialVisibility[field.name] = true
|
||||
})
|
||||
setVisibleColumns(initialVisibility)
|
||||
}, [schema])
|
||||
|
||||
// Обновление фильтров
|
||||
const handleFilterChange = useCallback((name: string, filterPart: unknown) => {
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
[name]: {
|
||||
...prev[name],
|
||||
...filterPart
|
||||
}
|
||||
}))
|
||||
}, [])
|
||||
|
||||
// Переключатель видимости колонки
|
||||
const handleColumnToggle = useCallback((name: string) => {
|
||||
setVisibleColumns(prev => ({
|
||||
...prev,
|
||||
[name]: !prev[name]
|
||||
}))
|
||||
}, [])
|
||||
|
||||
// Функция экспорта данных в CSV
|
||||
const handleExportCSV = useCallback(() => {
|
||||
if (!initialData || !schema || initialData.length === 0) {
|
||||
alert('Нет данных для экспорта')
|
||||
return
|
||||
}
|
||||
|
||||
const visibleFields = schema.filter(field => visibleColumns[field.name])
|
||||
const header = visibleFields.map(field => `"${field.title.replace(/"/g, '""')}"`).join(';')
|
||||
|
||||
const rows = initialData
|
||||
.filter(item => schema.every(field => {
|
||||
const value = item[field.name]
|
||||
const filter = filters[field.name]
|
||||
if (!filter) return true
|
||||
|
||||
if (field.type === 'string') {
|
||||
if (filter.searchText) {
|
||||
if (typeof value !== 'string') return false
|
||||
if (!value.toLowerCase().includes(filter.searchText.toLowerCase())) return false
|
||||
}
|
||||
if (filter.exactMatch && filter.searchText) {
|
||||
return value.toLowerCase() === filter.searchText.toLowerCase()
|
||||
}
|
||||
if (filter.empty !== undefined) {
|
||||
const isEmpty = value === '' || value === null || value === undefined
|
||||
if (filter.empty && !isEmpty) return false
|
||||
if (filter.notEmpty && isEmpty) return false
|
||||
}
|
||||
} else if (field.format === 'date') {
|
||||
const dateValue = new Date(value as string).getTime()
|
||||
if (filter.fromDate) {
|
||||
const fromTime = new Date(filter.fromDate).getTime()
|
||||
if (dateValue < fromTime) return false
|
||||
}
|
||||
if (filter.toDate) {
|
||||
const toTime = new Date(filter.toDate).getTime()
|
||||
if (dateValue > toTime) return false
|
||||
}
|
||||
} else if (field.type === 'number') {
|
||||
const numValue = Number(value)
|
||||
if (filter.gte !== undefined && numValue < filter.gte) return false
|
||||
if (filter.lte !== undefined && numValue > filter.lte) return false
|
||||
}
|
||||
return true
|
||||
}))
|
||||
.map(item => {
|
||||
const row = visibleFields.map(field => {
|
||||
const val = item[field.name]
|
||||
// Обработка null/undefined
|
||||
const strVal = val !== undefined && val !== null ? String(val) : ''
|
||||
// Экранирование кавычек
|
||||
return `"${strVal.replace(/"/g, '""')}"`
|
||||
|
||||
})
|
||||
return row.join(';')
|
||||
})
|
||||
|
||||
const csvContent = [header, ...rows].join('\n')
|
||||
// Создаем ссылку для скачивания
|
||||
const blob = new Blob([csvContent], { type: 'text/csv' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = 'export.csv'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}, [initialData, schema, filters, visibleColumns])
|
||||
|
||||
return {
|
||||
filters,
|
||||
setFilters,
|
||||
visibleColumns,
|
||||
setVisibleColumns,
|
||||
handleFilterChange,
|
||||
handleColumnToggle,
|
||||
filteredData,
|
||||
handleExportCSV // добавьте сюда
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Ваш файл, где вызывается handleExport` (примеры использования)
|
||||
```ts
|
||||
const {
|
||||
filters,
|
||||
handleFilterChange,
|
||||
visibleColumns,
|
||||
handleColumnToggle,
|
||||
filteredData,
|
||||
handleExportCSV // новая функция
|
||||
} = useTableFilters(schema, dataPage.items)
|
||||
|
||||
const handleExport = async () => {
|
||||
handleExportCSV()
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user