diff --git a/typescript/frontend-marios2/src/Resources/routes.json b/typescript/frontend-marios2/src/Resources/routes.json
index e7f8541bd..6daa7f353 100644
--- a/typescript/frontend-marios2/src/Resources/routes.json
+++ b/typescript/frontend-marios2/src/Resources/routes.json
@@ -23,5 +23,6 @@
"mainstats": "mainstats",
"detailed_view": "detailed_view/",
"report": "report",
+ "installationTickets": "installationTickets",
"tickets": "/tickets/"
}
diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx
index 377f869bf..d13f4e139 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx
@@ -28,6 +28,7 @@ import Topology from '../Topology/Topology';
import BatteryView from '../BatteryView/BatteryView';
import Configuration from '../Configuration/Configuration';
import PvView from '../PvView/PvView';
+import InstallationTicketsTab from '../Tickets/InstallationTicketsTab';
interface singleInstallationProps {
current_installation?: I_Installation;
@@ -381,7 +382,8 @@ function Installation(props: singleInstallationProps) {
currentTab != 'information' &&
currentTab != 'history' &&
currentTab != 'manage' &&
- currentTab != 'log' && (
+ currentTab != 'log' &&
+ currentTab != 'installationTickets' && (
)}
+ {currentUser.userType == UserType.admin && (
+
+ }
+ />
+ )}
+
}
diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
index 81121533a..8db02e68a 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
@@ -32,7 +32,8 @@ function InstallationTabs(props: InstallationTabsProps) {
'information',
'configuration',
'history',
- 'pvview'
+ 'pvview',
+ 'installationTickets'
];
const [currentTab, setCurrentTab] = useState(undefined);
@@ -165,6 +166,10 @@ function InstallationTabs(props: InstallationTabsProps) {
{
value: 'pvview',
label:
+ },
+ {
+ value: 'installationTickets',
+ label:
}
]
: currentUser.userType == UserType.partner
@@ -294,6 +299,10 @@ function InstallationTabs(props: InstallationTabsProps) {
defaultMessage="History Of Actions"
/>
)
+ },
+ {
+ value: 'installationTickets',
+ label:
}
]
: currentUser.userType == UserType.partner
diff --git a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx
index c0f432645..b8a678512 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx
@@ -23,6 +23,7 @@ import SalidomoOverview from '../Overview/salidomoOverview';
import { UserType } from '../../../interfaces/UserTypes';
import HistoryOfActions from '../History/History';
import BuildIcon from '@mui/icons-material/Build';
+import InstallationTicketsTab from '../Tickets/InstallationTicketsTab';
import AccessContextProvider from '../../../contexts/AccessContextProvider';
import Access from '../ManageAccess/Access';
@@ -324,7 +325,8 @@ function SalidomoInstallation(props: singleInstallationProps) {
currentTab != 'information' &&
currentTab != 'manage' &&
currentTab != 'history' &&
- currentTab != 'log' && (
+ currentTab != 'log' &&
+ currentTab != 'installationTickets' && (
)}
+ {currentUser.userType == UserType.admin && (
+
+ }
+ />
+ )}
+
}
diff --git a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx
index e84691bac..1a0f619ac 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx
@@ -28,7 +28,8 @@ function SalidomoInstallationTabs(props: InstallationTabsProps) {
'manage',
'overview',
'log',
- 'history'
+ 'history',
+ 'installationTickets'
];
const [currentTab, setCurrentTab] = useState(undefined);
@@ -136,6 +137,10 @@ function SalidomoInstallationTabs(props: InstallationTabsProps) {
defaultMessage="History Of Actions"
/>
)
+ },
+ {
+ value: 'installationTickets',
+ label:
}
]
: [
@@ -217,6 +222,10 @@ function SalidomoInstallationTabs(props: InstallationTabsProps) {
defaultMessage="History Of Actions"
/>
)
+ },
+ {
+ value: 'installationTickets',
+ label:
}
]
: currentTab != 'list' &&
diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx
index 6c8089500..91c3bc9d3 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx
@@ -28,6 +28,7 @@ import SodistoreHomeConfiguration from './SodistoreHomeConfiguration';
import TopologySodistoreHome from '../Topology/TopologySodistoreHome';
import Overview from '../Overview/overview';
import WeeklyReport from './WeeklyReport';
+import InstallationTicketsTab from '../Tickets/InstallationTicketsTab';
interface singleInstallationProps {
current_installation?: I_Installation;
@@ -473,7 +474,8 @@ function SodioHomeInstallation(props: singleInstallationProps) {
currentTab != 'manage' &&
currentTab != 'history' &&
currentTab != 'log' &&
- currentTab != 'report' && (
+ currentTab != 'report' &&
+ currentTab != 'installationTickets' && (
)}
+ {currentUser.userType == UserType.admin && (
+
+ }
+ />
+ )}
+
}
diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx
index cc9a4197a..398959ea3 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx
@@ -31,7 +31,8 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
'log',
'history',
'configuration',
- 'report'
+ 'report',
+ 'installationTickets'
];
const [currentTab, setCurrentTab] = useState(undefined);
@@ -159,6 +160,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
defaultMessage="Report"
/>
)
+ },
+ {
+ value: 'installationTickets',
+ label:
}
]
: currentUser.userType == UserType.partner
@@ -310,6 +315,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
defaultMessage="Report"
/>
)
+ },
+ {
+ value: 'installationTickets',
+ label:
}
]
: inInstallationView && currentUser.userType == UserType.partner
diff --git a/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx b/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx
index 446f6c5d8..39f803775 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx
@@ -61,9 +61,10 @@ interface Props {
open: boolean;
onClose: () => void;
onCreated: () => void;
+ defaultInstallationId?: number;
}
-function CreateTicketModal({ open, onClose, onCreated }: Props) {
+function CreateTicketModal({ open, onClose, onCreated, defaultInstallationId }: Props) {
const [subject, setSubject] = useState('');
const [selectedProduct, setSelectedProduct] = useState('');
const [selectedDevice, setSelectedDevice] = useState('');
@@ -100,13 +101,19 @@ function CreateTicketModal({ open, onClose, onCreated }: Props) {
.then((res) => {
const data = res.data;
if (Array.isArray(data)) {
- setAllInstallations(
- data.map((item: any) => ({
- id: item.id,
- name: item.name,
- device: item.device
- }))
- );
+ const mapped = data.map((item: any) => ({
+ id: item.id,
+ name: item.name,
+ device: item.device
+ }));
+ setAllInstallations(mapped);
+ if (defaultInstallationId != null) {
+ const match = mapped.find((inst: Installation) => inst.id === defaultInstallationId);
+ if (match) {
+ setSelectedInstallation(match);
+ setSelectedDevice(match.device ?? '');
+ }
+ }
}
})
.catch(() => setAllInstallations([]))
@@ -114,7 +121,20 @@ function CreateTicketModal({ open, onClose, onCreated }: Props) {
}, [selectedProduct]);
useEffect(() => {
- setSelectedInstallation(null);
+ if (defaultInstallationId == null || !open) return;
+ axiosConfig
+ .get('/GetInstallationById', { params: { id: defaultInstallationId } })
+ .then((res) => {
+ const inst = res.data;
+ if (inst) {
+ setSelectedProduct(inst.product);
+ }
+ })
+ .catch(() => {});
+ }, [defaultInstallationId, open]);
+
+ useEffect(() => {
+ if (defaultInstallationId == null) setSelectedInstallation(null);
}, [selectedDevice]);
useEffect(() => {
diff --git a/typescript/frontend-marios2/src/content/dashboards/Tickets/InstallationTicketsTab.tsx b/typescript/frontend-marios2/src/content/dashboards/Tickets/InstallationTicketsTab.tsx
new file mode 100644
index 000000000..51de69fa7
--- /dev/null
+++ b/typescript/frontend-marios2/src/content/dashboards/Tickets/InstallationTicketsTab.tsx
@@ -0,0 +1,181 @@
+import React, { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ Alert,
+ Box,
+ Button,
+ Chip,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Typography
+} from '@mui/material';
+import AddIcon from '@mui/icons-material/Add';
+import { FormattedMessage, useIntl } from 'react-intl';
+import axiosConfig from 'src/Resources/axiosConfig';
+import { Ticket, TicketStatus } from 'src/interfaces/TicketTypes';
+import StatusChip from './StatusChip';
+import CreateTicketModal from './CreateTicketModal';
+
+interface Props {
+ installationId: number;
+}
+
+const statusCountKeys: {
+ status: number;
+ id: string;
+ defaultMessage: string;
+ color: string;
+}[] = [
+ { status: TicketStatus.Open, id: 'statusOpen', defaultMessage: 'Open', color: '#d32f2f' },
+ { status: TicketStatus.InProgress, id: 'statusInProgress', defaultMessage: 'In Progress', color: '#ed6c02' },
+ { status: TicketStatus.Escalated, id: 'statusEscalated', defaultMessage: 'Escalated', color: '#9c27b0' },
+ { status: TicketStatus.Resolved, id: 'statusResolved', defaultMessage: 'Resolved', color: '#2e7d32' },
+ { status: TicketStatus.Closed, id: 'statusClosed', defaultMessage: 'Closed', color: '#757575' }
+];
+
+function InstallationTicketsTab({ installationId }: Props) {
+ const navigate = useNavigate();
+ const intl = useIntl();
+ const [tickets, setTickets] = useState([]);
+ const [error, setError] = useState('');
+ const [loading, setLoading] = useState(true);
+ const [createOpen, setCreateOpen] = useState(false);
+
+ const fetchTickets = () => {
+ axiosConfig
+ .get('/GetTicketsForInstallation', { params: { installationId } })
+ .then((res) => {
+ setTickets(res.data);
+ setError('');
+ })
+ .catch(() => setError('Failed to load tickets.'))
+ .finally(() => setLoading(false));
+ };
+
+ useEffect(() => {
+ fetchTickets();
+ }, [installationId]);
+
+ const statusCounts = statusCountKeys.map((s) => ({
+ ...s,
+ count: tickets.filter((t) => t.status === s.status).length
+ }));
+
+ if (loading) return null;
+
+ return (
+
+
+
+
+
+ }
+ onClick={() => setCreateOpen(true)}
+ >
+
+
+
+
+
+ {statusCounts.map((s) => (
+ 0 ? s.color : '#e0e0e0',
+ color: s.count > 0 ? '#fff' : '#757575',
+ fontWeight: 600
+ }}
+ />
+ ))}
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {tickets.length === 0 && !error ? (
+
+
+
+ ) : (
+
+
+
+
+ #
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {tickets
+ .sort(
+ (a, b) =>
+ new Date(b.createdAt).getTime() -
+ new Date(a.createdAt).getTime()
+ )
+ .map((ticket) => (
+ navigate(`/tickets/${ticket.id}`)}
+ >
+ {ticket.id}
+ {ticket.subject}
+
+
+
+
+ {intl.formatMessage({
+ id: `priority${['Critical', 'High', 'Medium', 'Low'][ticket.priority]}`,
+ defaultMessage: ['Critical', 'High', 'Medium', 'Low'][
+ ticket.priority
+ ]
+ })}
+
+
+ {new Date(ticket.createdAt).toLocaleDateString()}
+
+
+ ))}
+
+
+
+ )}
+
+ setCreateOpen(false)}
+ onCreated={() => {
+ setCreateOpen(false);
+ fetchTickets();
+ }}
+ defaultInstallationId={installationId}
+ />
+
+ );
+}
+
+export default InstallationTicketsTab;