tickets.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import {
  2. List,
  3. ShowButton,
  4. useTable,
  5. EditButton,
  6. TagField,
  7. } from "@refinedev/antd";
  8. import type { BaseRecord } from "@refinedev/core";
  9. import { Space, Table, Tag, Badge, Typography, Input, Select, Button } from "antd";
  10. import { SearchOutlined, FilterOutlined } from '@ant-design/icons';
  11. import { useState } from "react";
  12. const { Title } = Typography;
  13. export const SupportTickets = () => {
  14. const [searchText, setSearchText] = useState("");
  15. const [priorityFilter, setPriorityFilter] = useState("all");
  16. const [statusFilter, setStatusFilter] = useState("all");
  17. const { tableProps } = useTable({
  18. syncWithLocation: true,
  19. filters: {
  20. // Add server-side filters based on local state if API supports it
  21. }
  22. });
  23. // Simulate client-side filtering for demo purposes
  24. // In a real app, this would be done on server side
  25. const filteredData = tableProps?.dataSource?.filter((ticket: any) => {
  26. let matches = true;
  27. if (searchText) {
  28. const searchLower = searchText.toLowerCase();
  29. matches = matches && (
  30. ticket?.title?.toLowerCase().includes(searchLower) ||
  31. ticket?.description?.toLowerCase().includes(searchLower)
  32. );
  33. }
  34. if (priorityFilter !== "all") {
  35. matches = matches && ticket?.priority === priorityFilter;
  36. }
  37. if (statusFilter !== "all") {
  38. matches = matches && ticket?.status === statusFilter;
  39. }
  40. return matches;
  41. });
  42. const priorityColors = {
  43. low: 'blue',
  44. medium: 'orange',
  45. high: 'red',
  46. critical: 'purple'
  47. };
  48. const statusColors = {
  49. open: 'green',
  50. 'in-progress': 'blue',
  51. resolved: 'gray',
  52. closed: 'black'
  53. };
  54. return (
  55. <>
  56. <Title level={4}>Support Tickets</Title>
  57. <div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
  58. <Space>
  59. <Input
  60. placeholder="Search tickets"
  61. prefix={<SearchOutlined />}
  62. value={searchText}
  63. onChange={(e) => setSearchText(e.target.value)}
  64. style={{ width: 200 }}
  65. allowClear
  66. />
  67. <Select
  68. style={{ width: 120 }}
  69. defaultValue="all"
  70. onChange={(value) => setPriorityFilter(value)}
  71. placeholder="Priority"
  72. >
  73. <Select.Option value="all">All Priorities</Select.Option>
  74. <Select.Option value="low">Low</Select.Option>
  75. <Select.Option value="medium">Medium</Select.Option>
  76. <Select.Option value="high">High</Select.Option>
  77. <Select.Option value="critical">Critical</Select.Option>
  78. </Select>
  79. <Select
  80. style={{ width: 120 }}
  81. defaultValue="all"
  82. onChange={(value) => setStatusFilter(value)}
  83. placeholder="Status"
  84. >
  85. <Select.Option value="all">All Statuses</Select.Option>
  86. <Select.Option value="open">Open</Select.Option>
  87. <Select.Option value="in-progress">In Progress</Select.Option>
  88. <Select.Option value="resolved">Resolved</Select.Option>
  89. <Select.Option value="closed">Closed</Select.Option>
  90. </Select>
  91. </Space>
  92. <Button type="primary">Create Ticket</Button>
  93. </div>
  94. <List>
  95. <Table
  96. {...tableProps}
  97. dataSource={filteredData || tableProps.dataSource}
  98. rowKey="id"
  99. pagination={{ pageSize: 10 }}
  100. >
  101. <Table.Column dataIndex="id" title="Ticket ID" />
  102. <Table.Column dataIndex="title" title="Title" />
  103. <Table.Column
  104. dataIndex="clientName"
  105. title="Client"
  106. render={(value, record: any) => (
  107. <span>
  108. {value}
  109. {record.clientId && <div style={{ fontSize: '12px', color: '#888' }}>ID: {record.clientId}</div>}
  110. </span>
  111. )}
  112. />
  113. <Table.Column
  114. dataIndex="priority"
  115. title="Priority"
  116. render={(value) => (
  117. <Tag color={priorityColors[value as keyof typeof priorityColors] || 'default'}>
  118. {value?.toUpperCase()}
  119. </Tag>
  120. )}
  121. />
  122. <Table.Column
  123. dataIndex="status"
  124. title="Status"
  125. render={(value) => (
  126. <Badge
  127. color={statusColors[value as keyof typeof statusColors] || 'default'}
  128. text={value?.replace('-', ' ')}
  129. />
  130. )}
  131. />
  132. <Table.Column
  133. dataIndex="category"
  134. title="Category"
  135. render={(value) => (
  136. <TagField value={value} />
  137. )}
  138. />
  139. <Table.Column dataIndex="createdAt" title="Created" />
  140. <Table.Column
  141. title="Actions"
  142. dataIndex="actions"
  143. render={(_, record: BaseRecord) => (
  144. <Space>
  145. <ShowButton hideText size="small" recordItemId={record.id} />
  146. <EditButton hideText size="small" recordItemId={record.id} />
  147. <Button type="primary" size="small">
  148. Respond
  149. </Button>
  150. </Space>
  151. )}
  152. />
  153. </Table>
  154. </List>
  155. </>
  156. );
  157. };