fix(ticket): close final integration gaps
This commit is contained in:
@@ -101,6 +101,10 @@ func BuildTicketProgressWithContext(item *models.TicketProgress, ctx *TicketDeta
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildTicketProgressList(list []models.TicketProgress) []response.TicketProgressResponse {
|
||||||
|
return BuildTicketProgressListWithContext(list, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func BuildTicketProgressListWithContext(list []models.TicketProgress, ctx *TicketDetailBuildContext) []response.TicketProgressResponse {
|
func BuildTicketProgressListWithContext(list []models.TicketProgress, ctx *TicketDetailBuildContext) []response.TicketProgressResponse {
|
||||||
if len(list) == 0 {
|
if len(list) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"cs-agent/internal/services"
|
"cs-agent/internal/services"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
|
"github.com/mlogclub/simple/sqls"
|
||||||
"github.com/mlogclub/simple/web"
|
"github.com/mlogclub/simple/web"
|
||||||
"github.com/mlogclub/simple/web/params"
|
"github.com/mlogclub/simple/web/params"
|
||||||
)
|
)
|
||||||
@@ -213,6 +214,29 @@ func (c *TicketController) PostChange_status() *web.JsonResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *TicketController) PostAdd_progress() *web.JsonResult {
|
func (c *TicketController) PostAdd_progress() *web.JsonResult {
|
||||||
|
return c.createProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TicketController) AnyProgress_list() *web.JsonResult {
|
||||||
|
if _, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionTicketView); err != nil {
|
||||||
|
return web.JsonError(err)
|
||||||
|
}
|
||||||
|
ticketID, _ := params.GetInt64(c.Ctx, "ticketId")
|
||||||
|
if ticketID <= 0 {
|
||||||
|
return web.JsonErrorMsg("工单不存在")
|
||||||
|
}
|
||||||
|
if services.TicketService.Get(ticketID) == nil {
|
||||||
|
return web.JsonErrorMsg("工单不存在")
|
||||||
|
}
|
||||||
|
progresses := services.TicketProgressService.Find(sqls.NewCnd().Eq("ticket_id", ticketID).Asc("id"))
|
||||||
|
return web.JsonData(builders.BuildTicketProgressList(progresses))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TicketController) PostProgress_create() *web.JsonResult {
|
||||||
|
return c.createProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TicketController) createProgress() *web.JsonResult {
|
||||||
operator, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionTicketProgress)
|
operator, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionTicketProgress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return web.JsonError(err)
|
return web.JsonError(err)
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ var RolePermissions = map[string][]Permission{
|
|||||||
PermissionRoleView,
|
PermissionRoleView,
|
||||||
PermissionPermissionView,
|
PermissionPermissionView,
|
||||||
PermissionConversationView,
|
PermissionConversationView,
|
||||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketProgress,
|
PermissionTicketView, PermissionTicketCreate, PermissionTicketAssign, PermissionTicketChangeStatus, PermissionTicketProgress,
|
||||||
PermissionNotificationView, PermissionNotificationUpdate,
|
PermissionNotificationView, PermissionNotificationUpdate,
|
||||||
PermissionQuickReplyView,
|
PermissionQuickReplyView,
|
||||||
PermissionTagView,
|
PermissionTagView,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func handleTicketAssignedInAppNotification(ctx context.Context, event events.Tic
|
|||||||
NotificationType: "ticket_assigned",
|
NotificationType: "ticket_assigned",
|
||||||
BizType: "ticket",
|
BizType: "ticket",
|
||||||
BizID: ticket.ID,
|
BizID: ticket.ID,
|
||||||
ActionURL: fmt.Sprintf("/dashboard/tickets/%d", ticket.ID),
|
ActionURL: fmt.Sprintf("/dashboard/tickets?ticketId=%d", ticket.ID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("create ticket assigned in-app notification failed", "error", err, "ticketId", event.TicketID, "toUserId", event.ToUserID)
|
slog.Error("create ticket assigned in-app notification failed", "error", err, "ticketId", event.TicketID, "toUserId", event.ToUserID)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func TestTicketAssignedInAppNotification(t *testing.T) {
|
|||||||
if got.NotificationType != "ticket_assigned" || got.BizType != "ticket" || got.BizID != ticket.ID {
|
if got.NotificationType != "ticket_assigned" || got.BizType != "ticket" || got.BizID != ticket.ID {
|
||||||
t.Fatalf("unexpected notification: %+v", got)
|
t.Fatalf("unexpected notification: %+v", got)
|
||||||
}
|
}
|
||||||
if got.ActionURL != "/dashboard/tickets/1" {
|
if got.ActionURL != "/dashboard/tickets?ticketId=1" {
|
||||||
t.Fatalf("unexpected action url: %q", got.ActionURL)
|
t.Fatalf("unexpected action url: %q", got.ActionURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { PlusIcon, RefreshCcwIcon, SearchXIcon } from "lucide-react"
|
import { PlusIcon, RefreshCcwIcon, SearchXIcon } from "lucide-react"
|
||||||
|
import { useSearchParams } from "next/navigation"
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
@@ -103,6 +104,7 @@ function assigneeLabel(ticket: TicketItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function TicketsPage() {
|
export default function TicketsPage() {
|
||||||
|
const searchParams = useSearchParams()
|
||||||
const [tickets, setTickets] = useState<TicketItem[]>([])
|
const [tickets, setTickets] = useState<TicketItem[]>([])
|
||||||
const [summary, setSummary] = useState<TicketSummary>(emptySummary)
|
const [summary, setSummary] = useState<TicketSummary>(emptySummary)
|
||||||
const [quickView, setQuickView] = useState<QuickViewKey>("all")
|
const [quickView, setQuickView] = useState<QuickViewKey>("all")
|
||||||
@@ -191,6 +193,14 @@ export default function TicketsPage() {
|
|||||||
void loadData()
|
void loadData()
|
||||||
}, [loadData])
|
}, [loadData])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ticketId = Number(searchParams.get("ticketId"))
|
||||||
|
if (ticketId > 0) {
|
||||||
|
setSelectedTicketId(ticketId)
|
||||||
|
setDetailOpen(true)
|
||||||
|
}
|
||||||
|
}, [searchParams])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let active = true
|
let active = true
|
||||||
Promise.all([fetchAgentProfilesAll(), fetchTagsAll()])
|
Promise.all([fetchAgentProfilesAll(), fetchTagsAll()])
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export function createTicketProgress(payload: {
|
|||||||
ticketId: number
|
ticketId: number
|
||||||
content: string
|
content: string
|
||||||
}) {
|
}) {
|
||||||
return request<TicketProgress>("/api/dashboard/ticket/add_progress", {
|
return request<TicketProgress>("/api/dashboard/ticket/progress/create", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user