add assignee and category filter to ticket system
This commit is contained in:
parent
c5678b0856
commit
1bda67fec6
|
|
@ -2591,19 +2591,38 @@ public class Controller : ControllerBase
|
||||||
.ToList()
|
.ToList()
|
||||||
.ToDictionary(i => i.Id);
|
.ToDictionary(i => i.Id);
|
||||||
|
|
||||||
|
var assigneeIds = tickets
|
||||||
|
.Where(t => t.AssigneeId.HasValue)
|
||||||
|
.Select(t => t.AssigneeId!.Value)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var assigneesById = assigneeIds.Count == 0
|
||||||
|
? new Dictionary<Int64, User>()
|
||||||
|
: Db.Users
|
||||||
|
.Where(u => assigneeIds.Contains(u.Id))
|
||||||
|
.ToList()
|
||||||
|
.ToDictionary(u => u.Id);
|
||||||
|
|
||||||
var summaries = tickets.Select(t =>
|
var summaries = tickets.Select(t =>
|
||||||
{
|
{
|
||||||
Installation? installation = null;
|
Installation? installation = null;
|
||||||
if (t.InstallationId.HasValue)
|
if (t.InstallationId.HasValue)
|
||||||
installationsById.TryGetValue(t.InstallationId.Value, out installation);
|
installationsById.TryGetValue(t.InstallationId.Value, out installation);
|
||||||
|
|
||||||
|
User? assignee = null;
|
||||||
|
if (t.AssigneeId.HasValue)
|
||||||
|
assigneesById.TryGetValue(t.AssigneeId.Value, out assignee);
|
||||||
|
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
t.Id, t.Subject, t.Status, t.Priority, t.Category, t.SubCategory,
|
t.Id, t.Subject, t.Status, t.Priority, t.Category, t.SubCategory,
|
||||||
t.InstallationId, t.CreatedAt, t.UpdatedAt,
|
t.InstallationId, t.CreatedAt, t.UpdatedAt,
|
||||||
t.CustomSubCategory, t.CustomCategory,
|
t.CustomSubCategory, t.CustomCategory,
|
||||||
|
t.AssigneeId,
|
||||||
installationName = installation?.Name ?? (t.InstallationId.HasValue ? $"#{t.InstallationId}" : "No installation"),
|
installationName = installation?.Name ?? (t.InstallationId.HasValue ? $"#{t.InstallationId}" : "No installation"),
|
||||||
distributionPartner = installation?.DistributionPartner ?? ""
|
distributionPartner = installation?.DistributionPartner ?? "",
|
||||||
|
assigneeName = assignee?.Name
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import {
|
||||||
TicketSummary,
|
TicketSummary,
|
||||||
TicketStatus,
|
TicketStatus,
|
||||||
TicketPriority,
|
TicketPriority,
|
||||||
|
AdminUser,
|
||||||
categoryKeys,
|
categoryKeys,
|
||||||
subCategoryKeys,
|
subCategoryKeys,
|
||||||
getCategoryDisplayLabel,
|
getCategoryDisplayLabel,
|
||||||
|
|
@ -53,9 +54,12 @@ function TicketList() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [tickets, setTickets] = useState<TicketSummary[]>([]);
|
const [tickets, setTickets] = useState<TicketSummary[]>([]);
|
||||||
|
const [adminUsers, setAdminUsers] = useState<AdminUser[]>([]);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [statusFilter, setStatusFilter] = useState<number[]>([]);
|
const [statusFilter, setStatusFilter] = useState<number[]>([]);
|
||||||
const [partnerFilter, setPartnerFilter] = useState<string>('');
|
const [partnerFilter, setPartnerFilter] = useState<string>('');
|
||||||
|
const [assigneeFilter, setAssigneeFilter] = useState<string>('');
|
||||||
|
const [categoryFilter, setCategoryFilter] = useState<string>('');
|
||||||
const [createOpen, setCreateOpen] = useState(false);
|
const [createOpen, setCreateOpen] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
|
@ -68,12 +72,26 @@ function TicketList() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTickets();
|
fetchTickets();
|
||||||
|
axiosConfig
|
||||||
|
.get('/GetAdminUsers')
|
||||||
|
.then((res) => setAdminUsers(res.data))
|
||||||
|
.catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const partnerOptions = Array.from(
|
const partnerOptions = Array.from(
|
||||||
new Set(tickets.map((t) => t.distributionPartner).filter((p) => p && p.trim() !== ''))
|
new Set(tickets.map((t) => t.distributionPartner).filter((p) => p && p.trim() !== ''))
|
||||||
).sort();
|
).sort();
|
||||||
|
|
||||||
|
const assigneeOptions = adminUsers
|
||||||
|
.filter((u) => {
|
||||||
|
const name = (u.name ?? '').toLowerCase();
|
||||||
|
return (
|
||||||
|
!name.includes('inesco energy master admin') &&
|
||||||
|
!name.includes('paal myhre')
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? ''));
|
||||||
|
|
||||||
const filtered = tickets
|
const filtered = tickets
|
||||||
.filter((t) => {
|
.filter((t) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
|
|
@ -82,7 +100,14 @@ function TicketList() {
|
||||||
t.installationName.toLowerCase().includes(search.toLowerCase());
|
t.installationName.toLowerCase().includes(search.toLowerCase());
|
||||||
const matchesStatus = statusFilter.length === 0 || statusFilter.includes(t.status);
|
const matchesStatus = statusFilter.length === 0 || statusFilter.includes(t.status);
|
||||||
const matchesPartner = partnerFilter === '' || t.distributionPartner === partnerFilter;
|
const matchesPartner = partnerFilter === '' || t.distributionPartner === partnerFilter;
|
||||||
return matchesSearch && matchesStatus && matchesPartner;
|
const matchesAssignee =
|
||||||
|
assigneeFilter === '' ||
|
||||||
|
(assigneeFilter === '__unassigned__'
|
||||||
|
? !t.assigneeId
|
||||||
|
: t.assigneeId === Number(assigneeFilter));
|
||||||
|
const matchesCategory =
|
||||||
|
categoryFilter === '' || t.category === Number(categoryFilter);
|
||||||
|
return matchesSearch && matchesStatus && matchesPartner && matchesAssignee && matchesCategory;
|
||||||
})
|
})
|
||||||
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||||
|
|
||||||
|
|
@ -176,6 +201,53 @@ function TicketList() {
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl size="small" sx={{ minWidth: 200 }}>
|
||||||
|
<InputLabel>
|
||||||
|
<FormattedMessage id="assignee" defaultMessage="Assignee" />
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={assigneeFilter}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'assignee',
|
||||||
|
defaultMessage: 'Assignee'
|
||||||
|
})}
|
||||||
|
onChange={(e) => setAssigneeFilter(e.target.value as string)}
|
||||||
|
>
|
||||||
|
<MenuItem value="">
|
||||||
|
<FormattedMessage id="allAssignees" defaultMessage="All Assignees" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value="__unassigned__">
|
||||||
|
<FormattedMessage id="unassigned" defaultMessage="Unassigned" />
|
||||||
|
</MenuItem>
|
||||||
|
{assigneeOptions.map((u) => (
|
||||||
|
<MenuItem key={u.id} value={String(u.id)}>
|
||||||
|
{u.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl size="small" sx={{ minWidth: 200 }}>
|
||||||
|
<InputLabel>
|
||||||
|
<FormattedMessage id="category" defaultMessage="Category" />
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={categoryFilter}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'category',
|
||||||
|
defaultMessage: 'Category'
|
||||||
|
})}
|
||||||
|
onChange={(e) => setCategoryFilter(e.target.value as string)}
|
||||||
|
>
|
||||||
|
<MenuItem value="">
|
||||||
|
<FormattedMessage id="allCategories" defaultMessage="All Categories" />
|
||||||
|
</MenuItem>
|
||||||
|
{Object.entries(categoryKeys).map(([val, msg]) => (
|
||||||
|
<MenuItem key={val} value={val}>
|
||||||
|
{intl.formatMessage(msg)}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,8 @@ export type TicketSummary = {
|
||||||
distributionPartner: string;
|
distributionPartner: string;
|
||||||
customSubCategory: string | null;
|
customSubCategory: string | null;
|
||||||
customCategory: string | null;
|
customCategory: string | null;
|
||||||
|
assigneeId: number | null;
|
||||||
|
assigneeName: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdminUser = {
|
export type AdminUser = {
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,8 @@
|
||||||
"category": "Kategorie",
|
"category": "Kategorie",
|
||||||
"allStatuses": "Alle Status",
|
"allStatuses": "Alle Status",
|
||||||
"allPartners": "Alle Partner",
|
"allPartners": "Alle Partner",
|
||||||
|
"allAssignees": "Alle Zuständigen",
|
||||||
|
"allCategories": "Alle Kategorien",
|
||||||
"createdAt": "Erstellt",
|
"createdAt": "Erstellt",
|
||||||
"noTickets": "Keine Tickets gefunden.",
|
"noTickets": "Keine Tickets gefunden.",
|
||||||
"backToTickets": "Zurück zu Tickets",
|
"backToTickets": "Zurück zu Tickets",
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,8 @@
|
||||||
"category": "Category",
|
"category": "Category",
|
||||||
"allStatuses": "All Statuses",
|
"allStatuses": "All Statuses",
|
||||||
"allPartners": "All Partners",
|
"allPartners": "All Partners",
|
||||||
|
"allAssignees": "All Assignees",
|
||||||
|
"allCategories": "All Categories",
|
||||||
"createdAt": "Created",
|
"createdAt": "Created",
|
||||||
"noTickets": "No tickets found.",
|
"noTickets": "No tickets found.",
|
||||||
"backToTickets": "Back to Tickets",
|
"backToTickets": "Back to Tickets",
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,8 @@
|
||||||
"category": "Catégorie",
|
"category": "Catégorie",
|
||||||
"allStatuses": "Tous les statuts",
|
"allStatuses": "Tous les statuts",
|
||||||
"allPartners": "Tous les partenaires",
|
"allPartners": "Tous les partenaires",
|
||||||
|
"allAssignees": "Tous les responsables",
|
||||||
|
"allCategories": "Toutes les catégories",
|
||||||
"createdAt": "Créé",
|
"createdAt": "Créé",
|
||||||
"noTickets": "Aucun ticket trouvé.",
|
"noTickets": "Aucun ticket trouvé.",
|
||||||
"backToTickets": "Retour aux tickets",
|
"backToTickets": "Retour aux tickets",
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,8 @@
|
||||||
"category": "Categoria",
|
"category": "Categoria",
|
||||||
"allStatuses": "Tutti gli stati",
|
"allStatuses": "Tutti gli stati",
|
||||||
"allPartners": "Tutti i partner",
|
"allPartners": "Tutti i partner",
|
||||||
|
"allAssignees": "Tutti gli assegnatari",
|
||||||
|
"allCategories": "Tutte le categorie",
|
||||||
"createdAt": "Creato",
|
"createdAt": "Creato",
|
||||||
"noTickets": "Nessun ticket trovato.",
|
"noTickets": "Nessun ticket trovato.",
|
||||||
"backToTickets": "Torna ai ticket",
|
"backToTickets": "Torna ai ticket",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue