Skip to content

Commit 3b0a030

Browse files
authored
Merge pull request #84 from moevm/clients_list_of_orders
Список заказов и список откликов пользователей
2 parents ebf4c59 + 393e7e3 commit 3b0a030

File tree

10 files changed

+601
-96
lines changed

10 files changed

+601
-96
lines changed

frontend/src/components/OrderDetails.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { UserOutlined } from '@ant-design/icons'
66
import { api } from '../integrations/api'
77
import { roleUtils } from '../utils/role'
88
import { formatCompletionTime } from '../utils/time'
9+
import { getUserIdFromToken } from '../integrations/auth'
910

1011
interface OrderDetailsType {
1112
order: {
@@ -28,6 +29,7 @@ interface OrderDetailsType {
2829
coverLetter: string
2930
createdAt: string
3031
}>
32+
clientEmail: string
3133
}
3234
isClient: boolean
3335
isFreelancer: boolean
@@ -196,6 +198,9 @@ const OrderDetails: React.FC = () => {
196198
</div>
197199
<div>
198200
<div style={{ fontWeight: 600 }}>{order.clientName}</div>
201+
<div style={{ color: '#888', fontSize: '0.9em' }}>
202+
{order.clientEmail}
203+
</div>
199204
<div style={{ color: '#faad14' }}>
200205
{formatRating(order.clientRating)}
201206
</div>
@@ -212,6 +217,11 @@ const OrderDetails: React.FC = () => {
212217
</div>
213218

214219
<Tag color={getStatusColor(order.status)}>{getStatusText(order.status)}</Tag>
220+
{data.isFreelancer && order.freelancerId === getUserIdFromToken() && (
221+
<Tag color="success" style={{ marginLeft: 8 }}>
222+
Вас выбрали исполнителем
223+
</Tag>
224+
)}
215225

216226
<h2 style={{ marginTop: 24 }}>{order.title}</h2>
217227

@@ -357,7 +367,7 @@ const OrderDetails: React.FC = () => {
357367
</div>
358368
)}
359369

360-
{!data.isClient && roleUtils.getRole() === 'freelancer' && order.status !== 'beginning' && (
370+
{!data.isClient && roleUtils.getRole() === 'freelancer' && order.status !== 'beginning' && !data.hasActiveResponse && (
361371
<div style={{
362372
marginTop: 32,
363373
padding: 16,
Lines changed: 122 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
11
import { Button, Card, Col, Dropdown, Row, Space, Typography } from 'antd';
2-
import { Link } from '@tanstack/react-router';
2+
import { useNavigate } from '@tanstack/react-router';
33
import { useState } from 'react';
44
import { useUserProfile } from '../hooks/useUserProfile';
5-
import { roleUtils } from '../utils/role';
6-
import type {UserRole} from '../utils/role';
5+
import { roleUtils } from '../utils/role';
6+
import type { UserRole } from '../utils/role';
77
import type { MenuProps } from 'antd';
88

99
const { Title, Text } = Typography;
1010

1111
export default function ProfilePage() {
12-
const {data, isLoading} = useUserProfile();
12+
const { data, isLoading } = useUserProfile();
1313
const [selectedRole] = useState<UserRole>(roleUtils.getRole());
14+
const navigate = useNavigate();
1415

1516
const handleRoleChange = (role: UserRole) => {
1617
roleUtils.setRole(role);
1718
window.location.reload();
1819
};
1920

21+
const handleMyOrdersClick = () => {
22+
navigate({ to: '/profile/orders' });
23+
};
24+
25+
const handleMyResponsesClick = () => {
26+
navigate({ to: '/profile/responses' });
27+
};
28+
29+
const handleCreateOrderClick = () => {
30+
navigate({ to: '/orders/create' });
31+
};
32+
33+
const handleOrdersClick = () => {
34+
navigate({ to: '/orders' });
35+
};
36+
2037
const profileMenuItems: MenuProps['items'] = [
2138
{
2239
key: '1',
@@ -65,99 +82,109 @@ export default function ProfilePage() {
6582
if (isLoading) return <div style={{textAlign: 'center', padding: 40}}>Загрузка...</div>;
6683
if (!data) return <div style={{textAlign: 'center', padding: 40}}>Профиль не найден</div>;
6784

68-
const {displayName, email, balance, client} = data;
85+
const { displayName, email, balance, client } = data;
6986

7087
return (
71-
<div style={{maxWidth: 700, margin: '32px auto', background: '#f7faff', borderRadius: 16, padding: 32}}>
72-
<Row justify="space-between" align="middle" style={{marginBottom: 24}}>
73-
<Col>
74-
<Dropdown menu={{items: profileMenuItems}} trigger={['click']}>
75-
<Button type="default" style={{fontWeight: 500}}>Настройки профиля ▼</Button>
76-
</Dropdown>
77-
</Col>
78-
<Col>
79-
<Dropdown menu={{items: balanceMenuItems}} trigger={['click']}>
80-
<Button type="default"
81-
style={{fontWeight: 500}}>Баланс: <b>{balance.toLocaleString() || '0'} руб.</b></Button>
82-
</Dropdown>
83-
</Col>
84-
<Col>
85-
<Space>
86-
<Button type="primary">Мои заказы</Button>
87-
{selectedRole === 'client' ?
88-
<Link to="/orders/create">
89-
<Button>Создать заказ</Button>
90-
</Link> :
91-
<Link to="/orders">
92-
<Button>Заказы</Button>
93-
</Link>
94-
}
95-
</Space>
96-
</Col>
97-
</Row>
98-
<Row gutter={32}>
99-
<Col flex="auto">
100-
<Card style={{borderRadius: 12, marginBottom: 16}}>
101-
<Row align="middle" gutter={16}>
102-
<Col>
103-
<div style={{
104-
width: 120,
105-
height: 120,
106-
borderRadius: '50%',
107-
background: '#fde3cf',
108-
display: 'flex',
109-
alignItems: 'center',
110-
justifyContent: 'center',
111-
fontSize: 48,
112-
fontWeight: 700,
113-
color: '#ff7a45',
114-
}}>
115-
{displayName[0] || 'И'}
116-
</div>
117-
</Col>
118-
<Col flex="auto">
119-
<Title level={4} style={{marginBottom: 0}}>{displayName || 'Имя'}</Title>
120-
<Text type="secondary">{email}</Text>
121-
<div style={{margin: '8px 0'}}>
122-
<Text>Город: —</Text>
123-
</div>
124-
<div>
125-
<Text>Пол: —</Text>
126-
</div>
127-
<div>
128-
<Text>Дата рождения: —</Text>
129-
</div>
130-
<div style={{marginTop: 12}}>
131-
<Button>Редактировать</Button>
132-
</div>
133-
</Col>
134-
</Row>
135-
</Card>
136-
<Card style={{borderRadius: 12}}>
137-
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}>
138-
<div>
139-
<Text strong>
140-
{selectedRole === 'client' ? 'Рейтинг заказчика' : 'Рейтинг исполнителя'}
141-
</Text>
142-
<div style={{margin: '8px 0'}}>
143-
<span style={{color: '#faad14', fontSize: 20}}></span>
144-
<Text style={{fontSize: 18, marginLeft: 8}}>
145-
{selectedRole === 'client'
146-
? client?.rating.toFixed(1) ?? '—'
147-
: data.freelancer?.rating.toFixed(1) ?? '—'}
148-
</Text>
149-
</div>
150-
<Text type="secondary">
151-
Завершённых заказов: {selectedRole === 'client'
152-
? client?.completedOrders ?? '—'
153-
: data.freelancer?.completedOrders ?? '—'}
154-
</Text>
155-
</div>
156-
<Button>Показать отзывы</Button>
88+
<div style={{maxWidth: 700, margin: '32px auto', background: '#f7faff', borderRadius: 16, padding: 32}}>
89+
<Row justify="space-between" align="middle" style={{marginBottom: 24}}>
90+
<Col>
91+
<Dropdown menu={{items: profileMenuItems}} trigger={['click']}>
92+
<Button type="default" style={{fontWeight: 500}}>Настройки профиля ▼</Button>
93+
</Dropdown>
94+
</Col>
95+
<Col>
96+
<Dropdown menu={{items: balanceMenuItems}} trigger={['click']}>
97+
<Button type="default"
98+
style={{fontWeight: 500}}>Баланс: <b>{balance.toLocaleString() || '0'} руб.</b></Button>
99+
</Dropdown>
100+
</Col>
101+
<Col>
102+
<Space>
103+
{selectedRole === 'client' ? (
104+
<>
105+
<Button type="primary" onClick={handleMyOrdersClick}>
106+
Мои заказы
107+
</Button>
108+
<Button type="primary" onClick={handleCreateOrderClick}>
109+
Создать заказ
110+
</Button>
111+
</>
112+
) : (
113+
<>
114+
<Button type="primary" onClick={handleMyResponsesClick}>
115+
Мои отклики
116+
</Button>
117+
<Button type="primary" onClick={handleOrdersClick}>
118+
Заказы
119+
</Button>
120+
</>
121+
)}
122+
</Space>
123+
</Col>
124+
</Row>
125+
126+
<Col flex="auto">
127+
<Card style={{borderRadius: 12, marginBottom: 16}}>
128+
<Row align="middle" gutter={16}>
129+
<Col>
130+
<div style={{
131+
width: 120,
132+
height: 120,
133+
borderRadius: '50%',
134+
background: '#fde3cf',
135+
display: 'flex',
136+
alignItems: 'center',
137+
justifyContent: 'center',
138+
fontSize: 48,
139+
fontWeight: 700,
140+
color: '#ff7a45',
141+
}}>
142+
{displayName[0] || 'И'}
143+
</div>
144+
</Col>
145+
<Col flex="auto">
146+
<Title level={4} style={{marginBottom: 0}}>{displayName || 'Имя'}</Title>
147+
<Text type="secondary">{email}</Text>
148+
<div style={{margin: '8px 0'}}>
149+
<Text>Город: —</Text>
150+
</div>
151+
<div>
152+
<Text>Пол: —</Text>
153+
</div>
154+
<div>
155+
<Text>Дата рождения: —</Text>
156+
</div>
157+
<div style={{marginTop: 12}}>
158+
<Button>Редактировать</Button>
159+
</div>
160+
</Col>
161+
</Row>
162+
</Card>
163+
164+
<Card style={{borderRadius: 12}}>
165+
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}>
166+
<div>
167+
<Text strong>
168+
{selectedRole === 'client' ? 'Рейтинг заказчика' : 'Рейтинг исполнителя'}
169+
</Text>
170+
<div style={{margin: '8px 0'}}>
171+
<span style={{color: '#faad14', fontSize: 20}}></span>
172+
<Text style={{fontSize: 18, marginLeft: 8}}>
173+
{selectedRole === 'client'
174+
? client?.rating.toFixed(1) ?? '—'
175+
: data.freelancer?.rating.toFixed(1) ?? '—'}
176+
</Text>
157177
</div>
158-
</Card>
159-
</Col>
160-
</Row>
161-
</div>
178+
<Text type="secondary">
179+
Завершённых заказов: {selectedRole === 'client'
180+
? client?.completedOrders ?? '—'
181+
: data.freelancer?.completedOrders ?? '—'}
182+
</Text>
183+
</div>
184+
<Button>Показать отзывы</Button>
185+
</div>
186+
</Card>
187+
</Col>
188+
</div>
162189
);
163190
}

frontend/src/hooks/useUserProfile.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getUserIdFromToken } from '../integrations/auth';
33
import { api } from '../integrations/api';
44

55
interface UserProfile {
6+
id: string;
67
displayName: string;
78
email: string;
89
balance: number;

frontend/src/main.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { createOrdersRoute } from './routes/orders.route'
1919
import { createOrderDetailsRoute } from './routes/order.details.route'
2020
import { createCreateOrderRoute } from './routes/create-order.route'
2121
import { createOrderEditRoute } from './routes/orders.edit.route.tsx'
22+
import { createUserOrdersRoute } from './routes/user-orders.route'
23+
import { createUserResponsesRoute } from './routes/user-responses.route'
2224
import AdminLayout from './routes/AdminLayout'
2325
import AdminUsers from './routes/AdminUsers'
2426
import { AdminImportRoute } from './routes/admin.import.route'
@@ -107,6 +109,8 @@ const routeTree = rootRoute.addChildren([
107109
createOrderDetailsRoute(rootRoute),
108110
createCreateOrderRoute(rootRoute),
109111
createOrderEditRoute(rootRoute),
112+
createUserOrdersRoute(rootRoute),
113+
createUserResponsesRoute(rootRoute),
110114
profileRoute,
111115
])
112116

0 commit comments

Comments
 (0)