feat: padroniza tickets recentes nos dashboards
This commit is contained in:
parent
4655c7570a
commit
4079f67fcb
7 changed files with 184 additions and 35 deletions
|
|
@ -160,10 +160,9 @@ function normalizeWidgetConfig(type: WidgetType, config: unknown) {
|
||||||
title: "Tickets recentes",
|
title: "Tickets recentes",
|
||||||
dataSource: { metricKey: "tickets.awaiting_table", params: { limit: 20 } },
|
dataSource: { metricKey: "tickets.awaiting_table", params: { limit: 20 } },
|
||||||
columns: [
|
columns: [
|
||||||
{ field: "reference", label: "Ref." },
|
{ field: "reference", label: "Referência" },
|
||||||
{ field: "subject", label: "Assunto" },
|
{ field: "subject", label: "Assunto" },
|
||||||
{ field: "status", label: "Status" },
|
{ field: "status", label: "Status" },
|
||||||
{ field: "priority", label: "Prioridade" },
|
|
||||||
{ field: "updatedAt", label: "Atualizado em" },
|
{ field: "updatedAt", label: "Atualizado em" },
|
||||||
],
|
],
|
||||||
options: { downloadCSV: true },
|
options: { downloadCSV: true },
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,9 @@ import { RichTextEditor, sanitizeEditorHtml } from "@/components/ui/rich-text-ed
|
||||||
import { Spinner } from "@/components/ui/spinner"
|
import { Spinner } from "@/components/ui/spinner"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import {
|
import { PriorityIcon, priorityBadgeClass, priorityItemClass, priorityTriggerClass } from "@/components/tickets/priority-select"
|
||||||
PriorityIcon,
|
|
||||||
priorityBadgeClass,
|
|
||||||
priorityItemClass,
|
|
||||||
priorityStyles,
|
|
||||||
priorityTriggerClass,
|
|
||||||
} from "@/components/tickets/priority-select"
|
|
||||||
import { CategorySelectFields } from "@/components/tickets/category-select"
|
import { CategorySelectFields } from "@/components/tickets/category-select"
|
||||||
|
import { priorityStyles } from "@/lib/ticket-priority-style"
|
||||||
|
|
||||||
type CustomerOption = {
|
type CustomerOption = {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ import sanitizeHtml from "sanitize-html"
|
||||||
import { api } from "@/convex/_generated/api"
|
import { api } from "@/convex/_generated/api"
|
||||||
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
||||||
import { useAuth } from "@/lib/auth-client"
|
import { useAuth } from "@/lib/auth-client"
|
||||||
|
import { getTicketPriorityLabel } from "@/lib/ticket-priority-style"
|
||||||
|
import { getTicketStatusChipClass, getTicketStatusLabel } from "@/lib/ticket-status-style"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import type { TicketQueueSummary } from "@/lib/schemas/ticket"
|
import type { TicketQueueSummary } from "@/lib/schemas/ticket"
|
||||||
import {
|
import {
|
||||||
|
|
@ -69,6 +71,9 @@ const CHART_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var
|
||||||
const DEFAULT_CHART_HEIGHT = 320
|
const DEFAULT_CHART_HEIGHT = 320
|
||||||
// Em modo apresentação o card já define a altura disponível; evitar valores fixos previne cortes.
|
// Em modo apresentação o card já define a altura disponível; evitar valores fixos previne cortes.
|
||||||
const PRESENTATION_CHART_HEIGHT = 0
|
const PRESENTATION_CHART_HEIGHT = 0
|
||||||
|
const TIMESTAMP_THRESHOLD = 1_000_000_000
|
||||||
|
const STATUS_CHIP_BASE_CLASS = "inline-flex items-center gap-2 rounded-full px-4 py-2 text-sm font-semibold transition"
|
||||||
|
const KNOWN_TICKET_STATUSES = new Set(["PENDING", "AWAITING_ATTENDANCE", "PAUSED", "RESOLVED"])
|
||||||
|
|
||||||
export type DashboardFilters = {
|
export type DashboardFilters = {
|
||||||
range?: "7d" | "30d" | "90d" | "custom"
|
range?: "7d" | "30d" | "90d" | "custom"
|
||||||
|
|
@ -100,6 +105,8 @@ export type WidgetConfig = {
|
||||||
content?: string
|
content?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TableColumnConfig = { field: string; label: string }
|
||||||
|
|
||||||
export type DashboardWidgetRecord = {
|
export type DashboardWidgetRecord = {
|
||||||
id: Id<"dashboardWidgets">
|
id: Id<"dashboardWidgets">
|
||||||
widgetKey: string
|
widgetKey: string
|
||||||
|
|
@ -955,13 +962,16 @@ function renderTable({
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
mode: WidgetRendererProps["mode"]
|
mode: WidgetRendererProps["mode"]
|
||||||
}) {
|
}) {
|
||||||
const columns = Array.isArray(config.columns) && config.columns.length > 0
|
const columns: TableColumnConfig[] = Array.isArray(config.columns) && config.columns.length > 0
|
||||||
? config.columns
|
? (config.columns as TableColumnConfig[])
|
||||||
: [
|
: [
|
||||||
{ field: "subject", label: "Assunto" },
|
{ field: "subject", label: "Assunto" },
|
||||||
{ field: "status", label: "Status" },
|
{ field: "status", label: "Status" },
|
||||||
{ field: "updatedAt", label: "Atualizado em" },
|
{ field: "updatedAt", label: "Atualizado em" },
|
||||||
]
|
]
|
||||||
|
const columnsToRender = isRecentTicketsTable(config)
|
||||||
|
? columns.filter((column) => !isPriorityColumn(column))
|
||||||
|
: columns
|
||||||
const rows = Array.isArray(metric.data) ? (metric.data as Array<Record<string, unknown>>) : []
|
const rows = Array.isArray(metric.data) ? (metric.data as Array<Record<string, unknown>>) : []
|
||||||
const isPresentation = mode === "tv" || mode === "print"
|
const isPresentation = mode === "tv" || mode === "print"
|
||||||
const containerClass = cn(
|
const containerClass = cn(
|
||||||
|
|
@ -982,7 +992,7 @@ function renderTable({
|
||||||
<Table className="min-w-full table-fixed">
|
<Table className="min-w-full table-fixed">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{columns.map((column) => (
|
{columnsToRender.map((column) => (
|
||||||
<TableHead key={column.field} className="whitespace-nowrap">
|
<TableHead key={column.field} className="whitespace-nowrap">
|
||||||
{column.label}
|
{column.label}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
|
|
@ -992,11 +1002,11 @@ function renderTable({
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{rows.map((row, index) => (
|
{rows.map((row, index) => (
|
||||||
<TableRow key={index} className="border-b border-border/60 transition hover:bg-muted/40">
|
<TableRow key={index} className="border-b border-border/60 transition hover:bg-muted/40">
|
||||||
{columns.map((column) => {
|
{columnsToRender.map((column) => {
|
||||||
const cellValue = row[column.field as keyof typeof row]
|
const cellValue = row[column.field as keyof typeof row]
|
||||||
return (
|
return (
|
||||||
<TableCell key={column.field} className="whitespace-normal break-words">
|
<TableCell key={column.field} className="whitespace-normal break-words">
|
||||||
{renderTableCellValue(cellValue)}
|
{renderTableCellValue(cellValue, column)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
@ -1036,22 +1046,38 @@ function renderQueueSummary({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTableCellValue(value: unknown) {
|
function renderTableCellValue(value: unknown, column?: TableColumnConfig) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return "—"
|
||||||
|
}
|
||||||
|
const enriched = renderTicketTableCell(value, column)
|
||||||
|
if (enriched) {
|
||||||
|
return enriched
|
||||||
|
}
|
||||||
|
if (isStatusColumn(column)) {
|
||||||
|
return getTicketStatusLabel(value as string)
|
||||||
|
}
|
||||||
|
if (isPriorityColumn(column)) {
|
||||||
|
return getTicketPriorityLabel(value as string)
|
||||||
|
}
|
||||||
|
if (value instanceof Date) {
|
||||||
|
const formatted = tryFormatDate(value)
|
||||||
|
if (formatted) return formatted
|
||||||
|
}
|
||||||
if (typeof value === "number") {
|
if (typeof value === "number") {
|
||||||
|
if (isLikelyTimestampNumber(value, column)) {
|
||||||
|
const formatted = tryFormatDate(value)
|
||||||
|
if (formatted) return formatted
|
||||||
|
}
|
||||||
return numberFormatter.format(value)
|
return numberFormatter.format(value)
|
||||||
}
|
}
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
if (/^\d{4}-\d{2}-\d{2}T/.test(value) || /^\d+$/.test(value)) {
|
if (isLikelyTimestampString(value, column)) {
|
||||||
const date = new Date(value)
|
const formatted = tryFormatDate(value)
|
||||||
if (!Number.isNaN(date.getTime())) {
|
if (formatted) return formatted
|
||||||
return format(date, "dd/MM/yyyy HH:mm", { locale: ptBR })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
if (value === null || value === undefined) {
|
|
||||||
return "—"
|
|
||||||
}
|
|
||||||
if (typeof value === "boolean") {
|
if (typeof value === "boolean") {
|
||||||
return value ? "Sim" : "Não"
|
return value ? "Sim" : "Não"
|
||||||
}
|
}
|
||||||
|
|
@ -1068,6 +1094,102 @@ function renderTableCellValue(value: unknown) {
|
||||||
return String(value)
|
return String(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderTicketTableCell(value: unknown, column?: TableColumnConfig) {
|
||||||
|
if (!column) return null
|
||||||
|
if (isReferenceColumn(column)) {
|
||||||
|
const reference = typeof value === "number" || typeof value === "string" ? String(value) : null
|
||||||
|
return (
|
||||||
|
<Badge className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-semibold text-neutral-700">
|
||||||
|
{reference ? `#${reference}` : "—"}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (isStatusColumn(column) && isTicketStatusValue(value)) {
|
||||||
|
return (
|
||||||
|
<span className={cn(STATUS_CHIP_BASE_CLASS, getTicketStatusChipClass(String(value)))}>
|
||||||
|
{getTicketStatusLabel(String(value))}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFormatDate(value: number | string | Date): string | null {
|
||||||
|
const date = value instanceof Date ? value : new Date(value)
|
||||||
|
if (Number.isNaN(date.getTime())) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return format(date, "dd/MM/yyyy HH:mm", { locale: ptBR })
|
||||||
|
}
|
||||||
|
|
||||||
|
function isReferenceColumn(column?: TableColumnConfig) {
|
||||||
|
if (!column) return false
|
||||||
|
const field = column.field.toLowerCase()
|
||||||
|
const label = column.label.toLowerCase()
|
||||||
|
return field === "reference" || field === "ref" || label.includes("ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecentTicketsTable(config: WidgetConfig) {
|
||||||
|
return config?.dataSource?.metricKey === "tickets.awaiting_table"
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStatusColumn(column?: TableColumnConfig) {
|
||||||
|
if (!column) return false
|
||||||
|
const field = column.field.toLowerCase()
|
||||||
|
const label = column.label.toLowerCase()
|
||||||
|
return field === "status" || label.includes("status") || label.includes("situação")
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPriorityColumn(column?: TableColumnConfig) {
|
||||||
|
if (!column) return false
|
||||||
|
const field = column.field.toLowerCase()
|
||||||
|
const label = column.label.toLowerCase()
|
||||||
|
return field === "priority" || label.includes("prioridade")
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTicketStatusValue(value: unknown): value is string {
|
||||||
|
if (typeof value !== "string") return false
|
||||||
|
return KNOWN_TICKET_STATUSES.has(value.toUpperCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasDateHint(column?: TableColumnConfig) {
|
||||||
|
if (!column) return false
|
||||||
|
const field = column.field.toLowerCase()
|
||||||
|
const label = column.label.toLowerCase()
|
||||||
|
return (
|
||||||
|
field.endsWith("at") ||
|
||||||
|
field.includes("date") ||
|
||||||
|
field.includes("time") ||
|
||||||
|
field.includes("due") ||
|
||||||
|
label.includes("data") ||
|
||||||
|
label.includes("hora") ||
|
||||||
|
label.includes("atualiz") ||
|
||||||
|
label.includes("criad") ||
|
||||||
|
label.includes("resolvido") ||
|
||||||
|
label.includes("prazo")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLikelyTimestampNumber(value: number, column?: TableColumnConfig) {
|
||||||
|
if (!Number.isFinite(value) || value <= 0) return false
|
||||||
|
return value >= TIMESTAMP_THRESHOLD || hasDateHint(column)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLikelyTimestampString(value: string, column?: TableColumnConfig) {
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}T/.test(value) || /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (/^\d+$/.test(value)) {
|
||||||
|
if (value.length >= 10) return true
|
||||||
|
return hasDateHint(column)
|
||||||
|
}
|
||||||
|
if (hasDateHint(column)) {
|
||||||
|
const parsed = Date.parse(value)
|
||||||
|
return Number.isFinite(parsed)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function renderText({
|
function renderText({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { toast } from "sonner"
|
||||||
import { Spinner } from "@/components/ui/spinner"
|
import { Spinner } from "@/components/ui/spinner"
|
||||||
import { Dropzone } from "@/components/ui/dropzone"
|
import { Dropzone } from "@/components/ui/dropzone"
|
||||||
import { RichTextEditor, sanitizeEditorHtml } from "@/components/ui/rich-text-editor"
|
import { RichTextEditor, sanitizeEditorHtml } from "@/components/ui/rich-text-editor"
|
||||||
import { PriorityIcon, priorityStyles } from "@/components/tickets/priority-select"
|
import { PriorityIcon } from "@/components/tickets/priority-select"
|
||||||
import { CategorySelectFields } from "@/components/tickets/category-select"
|
import { CategorySelectFields } from "@/components/tickets/category-select"
|
||||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
|
@ -30,6 +30,7 @@ import { Calendar } from "@/components/ui/calendar"
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||||
import { useDefaultQueues } from "@/hooks/use-default-queues"
|
import { useDefaultQueues } from "@/hooks/use-default-queues"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { priorityStyles } from "@/lib/ticket-priority-style"
|
||||||
import { normalizeCustomFieldInputs } from "@/lib/ticket-form-helpers"
|
import { normalizeCustomFieldInputs } from "@/lib/ticket-form-helpers"
|
||||||
import type { TicketFormDefinition, TicketFormFieldDefinition } from "@/lib/ticket-form-types"
|
import type { TicketFormDefinition, TicketFormFieldDefinition } from "@/lib/ticket-form-types"
|
||||||
import { Calendar as CalendarIcon } from "lucide-react"
|
import { Calendar as CalendarIcon } from "lucide-react"
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import { type TicketPriority } from "@/lib/schemas/ticket"
|
import { type TicketPriority } from "@/lib/schemas/ticket"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { PriorityIcon, priorityStyles } from "@/components/tickets/priority-select"
|
import { PriorityIcon } from "@/components/tickets/priority-select"
|
||||||
|
import { getTicketPriorityMeta } from "@/lib/ticket-priority-style"
|
||||||
|
|
||||||
const baseClass = "inline-flex h-7 items-center gap-2 rounded-full px-3 text-xs font-semibold"
|
const baseClass = "inline-flex h-7 items-center gap-2 rounded-full px-3 text-xs font-semibold"
|
||||||
|
|
||||||
export function TicketPriorityPill({ priority, className }: { priority: TicketPriority; className?: string }) {
|
export function TicketPriorityPill({ priority, className }: { priority: TicketPriority; className?: string }) {
|
||||||
const styles = priorityStyles[priority]
|
const styles = getTicketPriorityMeta(priority)
|
||||||
return (
|
return (
|
||||||
<Badge className={cn(baseClass, styles?.badgeClass, className)}>
|
<Badge className={cn(baseClass, styles.badgeClass, className)}>
|
||||||
<PriorityIcon value={priority} />
|
<PriorityIcon value={priority} />
|
||||||
{styles?.label ?? priority}
|
{styles.label}
|
||||||
</Badge>
|
</Badge>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,13 @@ import { api } from "@/convex/_generated/api"
|
||||||
import type { Id } from "@/convex/_generated/dataModel"
|
import type { Id } from "@/convex/_generated/dataModel"
|
||||||
import type { TicketPriority } from "@/lib/schemas/ticket"
|
import type { TicketPriority } from "@/lib/schemas/ticket"
|
||||||
import { useAuth } from "@/lib/auth-client"
|
import { useAuth } from "@/lib/auth-client"
|
||||||
|
import { priorityStyles } from "@/lib/ticket-priority-style"
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import { ArrowDown, ArrowRight, ArrowUp, ChevronsUp, ChevronDown } from "lucide-react"
|
import { ArrowDown, ArrowRight, ArrowUp, ChevronsUp, ChevronDown } from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
export const priorityStyles: Record<TicketPriority, { label: string; badgeClass: string }> = {
|
|
||||||
LOW: { label: "Baixa", badgeClass: "bg-slate-100 text-slate-700" },
|
|
||||||
MEDIUM: { label: "Média", badgeClass: "bg-[#dff1fb] text-[#0a4760]" },
|
|
||||||
HIGH: { label: "Alta", badgeClass: "bg-[#fde8d1] text-[#7d3b05]" },
|
|
||||||
URGENT: { label: "Urgente", badgeClass: "bg-[#fbd9dd] text-[#8b0f1c]" },
|
|
||||||
}
|
|
||||||
|
|
||||||
export const priorityTriggerClass =
|
export const priorityTriggerClass =
|
||||||
"h-8 w-[160px] rounded-full border border-slate-300 bg-white px-3 text-left text-sm font-medium text-neutral-800 shadow-sm focus:ring-0 data-[state=open]:border-[#00d6eb]"
|
"h-8 w-[160px] rounded-full border border-slate-300 bg-white px-3 text-left text-sm font-medium text-neutral-800 shadow-sm focus:ring-0 data-[state=open]:border-[#00d6eb]"
|
||||||
export const priorityItemClass = "flex items-center gap-2 rounded-md px-2 py-2 text-sm text-neutral-800 transition data-[state=checked]:bg-[#00e8ff]/15 data-[state=checked]:text-neutral-900 focus:bg-[#00e8ff]/10"
|
export const priorityItemClass = "flex items-center gap-2 rounded-md px-2 py-2 text-sm text-neutral-800 transition data-[state=checked]:bg-[#00e8ff]/15 data-[state=checked]:text-neutral-900 focus:bg-[#00e8ff]/10"
|
||||||
|
|
@ -97,3 +91,5 @@ export function PrioritySelect({
|
||||||
</Select>
|
</Select>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { priorityStyles }
|
||||||
|
|
|
||||||
35
src/lib/ticket-priority-style.ts
Normal file
35
src/lib/ticket-priority-style.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import type { TicketPriority } from "@/lib/schemas/ticket"
|
||||||
|
|
||||||
|
export type TicketPriorityStyle = {
|
||||||
|
label: string
|
||||||
|
badgeClass: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const FALLBACK_PRIORITY: TicketPriority = "MEDIUM"
|
||||||
|
|
||||||
|
export const priorityStyles: Record<TicketPriority, TicketPriorityStyle> = {
|
||||||
|
LOW: { label: "Baixa", badgeClass: "bg-slate-100 text-slate-700" },
|
||||||
|
MEDIUM: { label: "Média", badgeClass: "bg-[#dff1fb] text-[#0a4760]" },
|
||||||
|
HIGH: { label: "Alta", badgeClass: "bg-[#fde8d1] text-[#7d3b05]" },
|
||||||
|
URGENT: { label: "Urgente", badgeClass: "bg-[#fbd9dd] text-[#8b0f1c]" },
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePriority(priority: TicketPriority | string | null | undefined): TicketPriorityStyle {
|
||||||
|
if (!priority) {
|
||||||
|
return priorityStyles[FALLBACK_PRIORITY]
|
||||||
|
}
|
||||||
|
const normalized = priority.toString().trim().toUpperCase() as TicketPriority
|
||||||
|
return priorityStyles[normalized] ?? priorityStyles[FALLBACK_PRIORITY]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTicketPriorityMeta(priority: TicketPriority | string | null | undefined): TicketPriorityStyle {
|
||||||
|
return resolvePriority(priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTicketPriorityLabel(priority: TicketPriority | string | null | undefined): string {
|
||||||
|
return resolvePriority(priority).label
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTicketPriorityBadgeClass(priority: TicketPriority | string | null | undefined): string {
|
||||||
|
return resolvePriority(priority).badgeClass
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue