From 393207fe21a57a9436865b5b8c454ae9b329a555 Mon Sep 17 00:00:00 2001 From: Fedorov Vladimir Date: Wed, 7 Oct 2026 23:09:47 +0700 Subject: [PATCH] up --- dist/assets/UsersView-DFrrjAyr.css | 1 - dist/index.html | 8 +- proto/main.proto | 101 ++++ .../crabs/evening_detective_server/index.ts | 190 ++++++ src/components/HeaderMenu.vue | 9 + src/components/TasksPage.vue | 562 ++++++++++++++++++ src/components/UsersPage.vue | 7 + src/router/index.ts | 5 + src/utils/__tests__/taskDates.test.ts | 23 + src/utils/taskDates.ts | 9 + src/views/TasksView.vue | 7 + 11 files changed, 917 insertions(+), 5 deletions(-) delete mode 100644 dist/assets/UsersView-DFrrjAyr.css create mode 100644 src/components/TasksPage.vue create mode 100644 src/utils/__tests__/taskDates.test.ts create mode 100644 src/utils/taskDates.ts create mode 100644 src/views/TasksView.vue diff --git a/dist/assets/UsersView-DFrrjAyr.css b/dist/assets/UsersView-DFrrjAyr.css deleted file mode 100644 index 41c8968..0000000 --- a/dist/assets/UsersView-DFrrjAyr.css +++ /dev/null @@ -1 +0,0 @@ -.user-block[data-v-248d8ad2]{background-color:#222;border-radius:10px;margin:0 0 20px;padding:20px}.user-field[data-v-248d8ad2]{margin:10px}.roles-buttons-block[data-v-248d8ad2]{margin-top:20px} diff --git a/dist/index.html b/dist/index.html index 80348be..ee26b9a 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,10 +5,10 @@ Вечерний детектив - - - - + + + + diff --git a/proto/main.proto b/proto/main.proto index 9cf2a10..915aebc 100644 --- a/proto/main.proto +++ b/proto/main.proto @@ -41,6 +41,54 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { }; service EveningDetectiveServer { + rpc GetTasks(GetTasksReq) returns (GetTasksRsp) { + option (google.api.http) = { get: "/api/tasks" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Задачи"; + summary: "Получить общую доску задач"; + description: "Доступно разработчикам и администратору."; + }; + } + rpc GetTaskAssignees(GetTaskAssigneesReq) returns (GetTaskAssigneesRsp) { + option (google.api.http) = { get: "/api/tasks/assignees" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Задачи"; + summary: "Получить разработчиков для назначения"; + description: "Доступно разработчикам и администратору."; + }; + } + rpc CreateTask(CreateTaskReq) returns (TaskRsp) { + option (google.api.http) = { post: "/api/tasks" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Задачи"; + summary: "Создать задачу"; + description: "Доступно разработчикам и администратору."; + }; + } + rpc UpdateTask(UpdateTaskReq) returns (TaskRsp) { + option (google.api.http) = { put: "/api/tasks/{ID}" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Задачи"; + summary: "Редактировать задачу"; + description: "Доступно разработчикам и администратору."; + }; + } + rpc SetTaskStatus(SetTaskStatusReq) returns (TaskRsp) { + option (google.api.http) = { post: "/api/tasks/{ID}/status" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Задачи"; + summary: "Изменить статус задачи"; + description: "Доступно разработчикам и администратору."; + }; + } + rpc DeleteTask(DeleteTaskReq) returns (DeleteTaskRsp) { + option (google.api.http) = { post: "/api/tasks/{ID}/delete" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Задачи"; + summary: "Удалить задачу"; + description: "Доступно разработчикам и администратору."; + }; + } rpc GetParticipation(GetParticipationReq) returns (ParticipationRsp) { option (google.api.http) = { post: "/api/participation/get" body: "*" }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { @@ -2188,3 +2236,56 @@ message TicketPage { optional google.protobuf.Timestamp used_at = 7; string used_by = 8; } + +// Developer tasks use new, in_progress, review and done statuses. +message DeveloperTask { + int32 ID = 1; + string title = 2; + string description = 3; + string status = 4; + // Omitted assignee means the task is unassigned. + optional int32 assigneeID = 5; + string assigneeName = 6; + optional int32 authorID = 7; + // Calendar deadline in YYYY-MM-DD format; empty means no deadline. + string dueDate = 8; + google.protobuf.Timestamp createdAt = 9; + google.protobuf.Timestamp updatedAt = 10; + // Required for updates and deletion to detect concurrent edits. + int32 version = 11; +} +message TaskAssignee { + int32 ID = 1; + string name = 2; +} +message GetTasksReq {} +message GetTasksRsp { repeated DeveloperTask tasks = 1; } +message GetTaskAssigneesReq {} +message GetTaskAssigneesRsp { repeated TaskAssignee assignees = 1; } +message CreateTaskReq { + string title = 1; + string description = 2; + string status = 3; + optional int32 assigneeID = 4; + string dueDate = 5; +} +message UpdateTaskReq { + int32 ID = 1; + string title = 2; + string description = 3; + string status = 4; + optional int32 assigneeID = 5; + string dueDate = 6; + int32 version = 7; +} +message SetTaskStatusReq { + int32 ID = 1; + string status = 2; + int32 version = 3; +} +message DeleteTaskReq { + int32 ID = 1; + int32 version = 2; +} +message TaskRsp { DeveloperTask task = 1; } +message DeleteTaskRsp {} diff --git a/src/api/generated/crabs/evening_detective_server/index.ts b/src/api/generated/crabs/evening_detective_server/index.ts index bb93276..36c380a 100644 --- a/src/api/generated/crabs/evening_detective_server/index.ts +++ b/src/api/generated/crabs/evening_detective_server/index.ts @@ -1111,7 +1111,86 @@ export type AcceptTicketRsp = { ticket: TicketPage | undefined; }; +// Developer tasks use new, in_progress, review and done statuses. +export type DeveloperTask = { + ID: number | undefined; + title: string | undefined; + description: string | undefined; + status: string | undefined; + // Omitted assignee means the task is unassigned. + assigneeID?: number; + assigneeName: string | undefined; + authorID?: number; + // Calendar deadline in YYYY-MM-DD format; empty means no deadline. + dueDate: string | undefined; + createdAt: wellKnownTimestamp | undefined; + updatedAt: wellKnownTimestamp | undefined; + // Required for updates and deletion to detect concurrent edits. + version: number | undefined; +}; + +export type TaskAssignee = { + ID: number | undefined; + name: string | undefined; +}; + +export type GetTasksReq = { +}; + +export type GetTasksRsp = { + tasks: DeveloperTask[] | undefined; +}; + +export type GetTaskAssigneesReq = { +}; + +export type GetTaskAssigneesRsp = { + assignees: TaskAssignee[] | undefined; +}; + +export type CreateTaskReq = { + title: string | undefined; + description: string | undefined; + status: string | undefined; + assigneeID?: number; + dueDate: string | undefined; +}; + +export type UpdateTaskReq = { + ID: number | undefined; + title: string | undefined; + description: string | undefined; + status: string | undefined; + assigneeID?: number; + dueDate: string | undefined; + version: number | undefined; +}; + +export type SetTaskStatusReq = { + ID: number | undefined; + status: string | undefined; + version: number | undefined; +}; + +export type DeleteTaskReq = { + ID: number | undefined; + version: number | undefined; +}; + +export type TaskRsp = { + task: DeveloperTask | undefined; +}; + +export type DeleteTaskRsp = { +}; + export interface EveningDetectiveServer { + GetTasks(request: GetTasksReq): Promise; + GetTaskAssignees(request: GetTaskAssigneesReq): Promise; + CreateTask(request: CreateTaskReq): Promise; + UpdateTask(request: UpdateTaskReq): Promise; + SetTaskStatus(request: SetTaskStatusReq): Promise; + DeleteTask(request: DeleteTaskReq): Promise; GetParticipation(request: GetParticipationReq): Promise; AnswerParticipation(request: AnswerParticipationReq): Promise; Ping(request: PingReq): Promise; @@ -1234,6 +1313,117 @@ export function createEveningDetectiveServerClient( handler: RequestHandler ): EveningDetectiveServer { return { + GetTasks(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + const path = `api/tasks`; // eslint-disable-line quotes + const body = null; + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "GET", + body, + }, { + service: "EveningDetectiveServer", + method: "GetTasks", + }) as Promise; + }, + GetTaskAssignees(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + const path = `api/tasks/assignees`; // eslint-disable-line quotes + const body = null; + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "GET", + body, + }, { + service: "EveningDetectiveServer", + method: "GetTaskAssignees", + }) as Promise; + }, + CreateTask(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + const path = `api/tasks`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "CreateTask", + }) as Promise; + }, + UpdateTask(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.ID) { + throw new Error("missing required field request.ID"); + } + const path = `api/tasks/${request.ID}`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "PUT", + body, + }, { + service: "EveningDetectiveServer", + method: "UpdateTask", + }) as Promise; + }, + SetTaskStatus(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.ID) { + throw new Error("missing required field request.ID"); + } + const path = `api/tasks/${request.ID}/status`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "SetTaskStatus", + }) as Promise; + }, + DeleteTask(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.ID) { + throw new Error("missing required field request.ID"); + } + const path = `api/tasks/${request.ID}/delete`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "DeleteTask", + }) as Promise; + }, GetParticipation(request) { // eslint-disable-line @typescript-eslint/no-unused-vars const path = `api/participation/get`; // eslint-disable-line quotes const body = JSON.stringify(request); diff --git a/src/components/HeaderMenu.vue b/src/components/HeaderMenu.vue index ec6ff36..d8e1b1e 100644 --- a/src/components/HeaderMenu.vue +++ b/src/components/HeaderMenu.vue @@ -112,6 +112,15 @@ const openMenu = ref(false) > Сценарии +