Skip to content

Commit b76bd72

Browse files
vikaivVictoria Ivanova
andauthored
create new bug; upload attachments to bugs; delete bug attachments (#42)
* create new bug; upload attachments to bugs; delete bug attachments * fix focus problem * fix ts error; /context/preview method; move setBugsEvent to commo index store * clean up * fix existing bug editing * move clientIdMapStore logics from Report.tsx to store/index.ts --------- Co-authored-by: Victoria Ivanova <victoria.i@ati.su>
1 parent 48116e1 commit b76bd72

File tree

25 files changed

+1081
-236
lines changed

25 files changed

+1081
-236
lines changed

frontend/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!doctype html>
2-
<html lang="en">
2+
<html lang="en" class="scroll-smooth">
33
<head>
44
<meta charset="UTF-8" />
55
<link rel="icon" type="image/svg+xml" href="/vite.svg" />

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"prepare": "npx simple-git-hooks",
88
"start": "npm run start-backend && npm run dev",
9-
"start-backend": "docker compose -f ../docker-compose.yml --profile back up",
9+
"start-backend": "docker compose -f ../docker-compose.yml --profile back up -d",
1010
"dev": "vite",
1111
"build": "tsc && vite build",
1212
"lint": "eslint .",

frontend/src/api/attachments/index.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
1-
import { AttachmentTypes } from "@/const";
21
import { AttachmentResponse } from "./models";
32
import axios from "../axios";
43

5-
export const createBugAttachment = async (
6-
reportId: number,
7-
bugId: number,
8-
file: File,
9-
attachType: AttachmentTypes
10-
): Promise<AttachmentResponse> => {
11-
const formData = new FormData();
12-
formData.append("file", file);
13-
const { data } = await axios.post<AttachmentResponse>(
14-
`/v2/reports/${reportId}/bugs/${bugId}/attachments?attachType=${attachType}`,
15-
formData,
16-
{ headers: { "Content-Type": "multipart/form-data" } }
17-
);
18-
return data;
4+
type UploadAttachmentParameters = {
5+
reportId: number;
6+
bugId: number;
7+
attachType: number;
8+
file: File;
9+
};
10+
11+
export const uploadAttachment = async (params: UploadAttachmentParameters) => {
12+
try {
13+
const { reportId, bugId, file, attachType } = params;
14+
const formData = new FormData();
15+
formData.append("file", file);
16+
17+
const { data } = await axios.post(
18+
`/v2/reports/${reportId}/bugs/${bugId}/attachments?attachType=${attachType}`,
19+
formData,
20+
{
21+
headers: {
22+
"Content-Type": "multipart/form-data",
23+
},
24+
}
25+
);
26+
return data;
27+
} catch (error) {
28+
console.error("Ошибка при загрузке файла:", error);
29+
throw new Error("Не удалось загрузить файл");
30+
}
1931
};
2032

2133
export const deleteBugAttachment = async (

frontend/src/api/axios.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import {
44
convertObjectToCamel,
55
convertObjectToSnake,
66
} from "@/utils/convertCases";
7-
8-
const API_URL = window.env?.API_URL || import.meta.env.VITE_BASE_URL;
7+
import { API_URL } from "@/const";
98

109
const instance = axios.create({
1110
baseURL: API_URL,
@@ -43,8 +42,8 @@ instance.interceptors.response.use((response) => {
4342

4443
// Интерцептор запроса: преобразуем camelCase → snake_case
4544
instance.interceptors.request.use((config) => {
46-
if (config.headers["Content-Type"] !== "multipart/form-data") {
47-
config.data && (config.data = convertObjectToSnake(config.data));
45+
if (config.headers["Content-Type"] !== "multipart/form-data" && config.data) {
46+
config.data = convertObjectToSnake(config.data);
4847
}
4948

5049
if (signalRConnectionId) {

frontend/src/apiObsolete/axios.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import {
44
convertObjectToCamel,
55
convertObjectToSnake,
66
} from "@/utils/convertCases";
7-
8-
const API_URL = window.env?.API_URL || import.meta.env.VITE_BASE_URL;
7+
import { API_URL } from "@/const";
98

109
const instance = axios.create({
1110
baseURL: API_URL,

frontend/src/components/StatusSelect/StatusSelect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const StatusSelect = <T,>({
2424
<div className={`dropdown dropdown-end ${className}`}>
2525
<label
2626
tabIndex={0}
27-
className="flex items-center gap-2 justify-start cursor-pointer hover:bg-base-200 p-2 rounded"
27+
className="flex items-center gap-2 justify-start cursor-pointer hover:bg-base-200 p-1 rounded"
2828
>
2929
{currentOption?.indicator}
3030
</label>

frontend/src/const/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,10 @@ export const bugStatusMap: Record<number, StatusMeta> = {
5151
border: "border-success",
5252
},
5353
};
54+
55+
export const API_URL = window.env?.API_URL || import.meta.env.VITE_BASE_URL;
56+
57+
export enum BugResultTypes {
58+
RECEIVE = "receive",
59+
EXPECT = "expect",
60+
}

frontend/src/hooks/useWebSocketReportPage.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import {
55
LogLevel,
66
HubConnectionState,
77
} from "@microsoft/signalr";
8-
9-
const API_URL = window.env?.API_URL || import.meta.env.VITE_BASE_URL;
8+
import { API_URL } from "@/const";
109

1110
const useWebSocketReportPage = (
1211
reportId: number,

frontend/src/pages/Report/Report.tsx

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
import { useEffect } from "react";
2+
3+
import { formatDistanceToNow } from "date-fns";
4+
import { ru } from "date-fns/locale";
5+
import { useUnit } from "effector-react";
6+
import { useParams, useNavigate } from "react-router-dom";
7+
18
import { useReportPageSocket } from "@/hooks/useReportPageSocket";
29
import { useSocketEvent } from "@/hooks/useSocketEvent";
10+
import { $combinedBugsStore } from "@/store";
11+
import { createLocalBugEvent } from "@/store/localBugs";
312
import {
413
$initialReportStore,
514
$titleStore,
@@ -10,38 +19,16 @@ import {
1019
updateReportPathIdEvent,
1120
} from "@/store/report";
1221
import { SocketEvent } from "@/webSocketApi/models";
13-
import { formatDistanceToNow } from "date-fns";
14-
import { ru } from "date-fns/locale";
15-
import { useUnit, useStoreMap } from "effector-react";
16-
import { useEffect } from "react";
17-
import { useParams, useNavigate } from "react-router-dom";
18-
import Bug from "./components/Bug/Bug";
19-
import { $bugsData } from "@/store/bugs";
20-
import { BugEntity } from "@/types/bug";
2122

22-
function sortBugsByDate(a: BugEntity, b: BugEntity) {
23-
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
24-
}
23+
import Bug from "./components/Bug/Bug";
2524

2625
const ReportPage = () => {
2726
const navigate = useNavigate();
2827
const { reportId } = useParams();
2928
const initialReport = useUnit($initialReportStore);
3029
const title = useUnit($titleStore);
3130
const creatorUserName = useUnit($creatorUserNameStore);
32-
33-
const bugs = useStoreMap({
34-
store: $bugsData,
35-
keys: [reportId],
36-
fn: ({ bugs, reportBugs }, [currentReportId]) => {
37-
if (!currentReportId) return [];
38-
const bugIds = reportBugs[Number(currentReportId)] || [];
39-
return bugIds
40-
.map((id) => bugs[id])
41-
.filter(Boolean)
42-
.sort(sortBugsByDate);
43-
},
44-
});
31+
const allBugs = useUnit($combinedBugsStore);
4532

4633
useReportPageSocket();
4734
useSocketEvent(SocketEvent.ReportPatch, (patch) =>
@@ -51,7 +38,7 @@ const ReportPage = () => {
5138
// состояние страницы
5239
useEffect(() => {
5340
if (reportId) {
54-
updateReportPathIdEvent(parseInt(reportId));
41+
updateReportPathIdEvent(Number(reportId));
5542
} else {
5643
updateReportPathIdEvent(null);
5744
}
@@ -64,6 +51,10 @@ const ReportPage = () => {
6451
}
6552
}, [initialReport?.id, reportId, navigate]);
6653

54+
const handleAddBugClick = () => {
55+
createLocalBugEvent({ reportId: Number(reportId) });
56+
};
57+
6758
return (
6859
<>
6960
<input
@@ -85,9 +76,15 @@ const ReportPage = () => {
8576
пользователем <strong>{creatorUserName || "Загрузка..."}</strong>
8677
</div>
8778
<div className="flex flex-col gap-2">
88-
{bugs?.map((bug) => (
89-
<Bug key={bug.id} bug={bug} />
79+
{allBugs.map((bug) => (
80+
<Bug key={bug.clientId} bug={bug} />
9081
))}
82+
<button
83+
className="btn btn-outline btn-primary font-normal ml-auto"
84+
onClick={handleAddBugClick}
85+
>
86+
Добавить баг
87+
</button>
9188
</div>
9289
</>
9390
);

0 commit comments

Comments
 (0)