Skip to content

Commit 212d3d3

Browse files
authored
Merge branch 'main' into cowsed/qr_code_maintenance
2 parents 75b2ad1 + 7211009 commit 212d3d3

14 files changed

Lines changed: 248 additions & 93 deletions

File tree

client/src/pages/makerspace_page/equipment_pages/EquipmentInstanceCard.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Autocomplete, Button, Card, Chip, IconButton, Link, MenuItem, Select, Stack, TextField, Typography } from "@mui/material";
2-
import { DELETE_EQUIPMENT_INSTANCE, EquipmentInstance, InstanceStatus, UPDATE_INSTANCE, UPDATE_INSTANCE_CONTROLLER_ASSIGNMENT, UPDATE_INSTANCE_HOBBS_TIME } from "../../../queries/equipmentInstanceQueries";
2+
import { DELETE_EQUIPMENT_INSTANCE, EquipmentInstance, formatHobbsTime, InstanceStatus, UPDATE_INSTANCE, UPDATE_INSTANCE_CONTROLLER_ASSIGNMENT, UPDATE_INSTANCE_HOBBS_TIME } from "../../../queries/equipmentInstanceQueries";
33
import ActionButton from "../../../common/ActionButton";
44
import DriveFileRenameOutlineIcon from '@mui/icons-material/DriveFileRenameOutline';
55
import { useMutation, useQuery } from "@apollo/client/react";
@@ -100,19 +100,6 @@ export default function EquipmentInstanceCard(props: EquipmentInstanceCardProps)
100100
/>;
101101
}
102102

103-
function formatSeconds(allSeconds: number){
104-
const hours = Math.floor(allSeconds / 3600);
105-
if (hours > 0){
106-
return (allSeconds / 3600).toFixed(2) + " hrs"
107-
}
108-
const minutes = Math.floor((allSeconds % 3600) / 60);
109-
const seconds = allSeconds % 60;
110-
if (minutes > 0){
111-
return (minutes + seconds / 50).toFixed(2) + " min"
112-
} else {
113-
return Math.floor(allSeconds) + "sec"
114-
}
115-
}
116103

117104
function activeUserDisplay() {
118105
if (!currentAccessController) {
@@ -165,7 +152,7 @@ export default function EquipmentInstanceCard(props: EquipmentInstanceCardProps)
165152
<Stack alignItems={"center"} justifyContent={"space-between"}>
166153
{
167154
!allowEdit
168-
? formatSeconds(props.instance.hobbsTime)
155+
? formatHobbsTime(props.instance.hobbsTime)
169156
: hobbsTimeSetField()
170157
}
171158
</Stack>

client/src/pages/makerspace_page/equipment_pages/EquipmentInstanceRow.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Autocomplete, Button, Card, Chip, IconButton, Link, MenuItem, Select, Stack, TextField, Typography } from "@mui/material";
2-
import { DELETE_EQUIPMENT_INSTANCE, EquipmentInstance, InstanceStatus, UPDATE_INSTANCE, UPDATE_INSTANCE_CONTROLLER_ASSIGNMENT, UPDATE_INSTANCE_HOBBS_TIME } from "../../../queries/equipmentInstanceQueries";
2+
import { DELETE_EQUIPMENT_INSTANCE, EquipmentInstance, formatHobbsTime, InstanceStatus, UPDATE_INSTANCE, UPDATE_INSTANCE_CONTROLLER_ASSIGNMENT, UPDATE_INSTANCE_HOBBS_TIME } from "../../../queries/equipmentInstanceQueries";
33
import DriveFileRenameOutlineIcon from '@mui/icons-material/DriveFileRenameOutline';
44
import { useMutation, useQuery } from "@apollo/client/react";
55
import { useState } from "react";
@@ -99,20 +99,6 @@ export default function EquipmentInstanceRow(props: EquipmentInstanceRowProps) {
9999
/>;
100100
}
101101

102-
function formatSeconds(allSeconds: number){
103-
const hours = Math.floor(allSeconds / 3600);
104-
if (hours > 0){
105-
return (allSeconds / 3600).toFixed(2) + " hrs"
106-
}
107-
const minutes = Math.floor((allSeconds % 3600) / 60);
108-
const seconds = allSeconds % 60;
109-
if (minutes > 0){
110-
return (minutes + seconds / 50).toFixed(2) + " min"
111-
} else {
112-
return Math.floor(allSeconds) + "sec"
113-
}
114-
}
115-
116102
function activeUserDisplay() {
117103
if (!currentAccessController) {
118104
return <Typography>No User</Typography>;
@@ -162,7 +148,7 @@ export default function EquipmentInstanceRow(props: EquipmentInstanceRowProps) {
162148
<Stack alignItems={"center"} direction={"row"} width={"200px"}>
163149
{
164150
!allowEdit
165-
? formatSeconds(props.instance.hobbsTime)
151+
? formatHobbsTime(props.instance.hobbsTime)
166152
: hobbsTimeSetField()
167153
}
168154
</Stack>

client/src/pages/makerspace_page/maintenance_pages/MaintenancePage.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,16 @@ export default function MaintenancePage() {
6060
// const containsOperator = getGridStringOperators().filter((operator) => operator.value === "contains");
6161

6262
const columns: GridColDef[] = [
63-
{ field: "id", headerName: "ID", width: 10, filterable: false, resizable: false, hideable: false },
63+
{ field: "id", headerName: "ID", width: 25, filterable: false, resizable: false, hideable: false },
6464
{ field: "equipment", headerName: "Equipment", width: 350, sortable: false, filterable: false, resizable: false },
6565
{ field: "instance", headerName: "Instance", width: 300, sortable: false, filterable: false, resizable: false },
6666
{ field: "type", headerName: "Type", width: 140, sortable: false, filterable: false, resizable: false },
6767
{ field: "status", headerName: "Status", width: 140, filterable: false, resizable: false },
68-
{ field: "severity", headerName: "Severity", width: 140, filterable: false, resizable: false },
69-
{ field: "creator", headerName: "Creator", width: 140, sortable: false, filterable: false, resizable: false },
70-
{ field: "assigned", headerName: "Assigned", width: 140, sortable: false, filterable: false, resizable: false },
71-
{ field: "dateCreated", headerName: "Created", width: 180, filterable: false, resizable: false },
68+
{ field: "severity", headerName: "Severity", width: 120, filterable: false, resizable: false },
69+
{ field: "creator", headerName: "Creator", width: 120, sortable: false, filterable: false, resizable: false },
70+
{ field: "assigned", headerName: "Assigned", width: 120, sortable: false, filterable: false, resizable: false },
71+
{ field: "timeUnit", headerName: "Time Unit", width: 140, sortable: false, filterable: false, resizable: false },
72+
{ field: "dateCreated", headerName: "Create Date", width: 180, filterable: false, resizable: false },
7273
{
7374
field: "manage", headerName: "Manage", width: 140, filterable: false, sortable: false, renderCell: (params: GridRenderCellParams<any, MaintenanceTicket>) => (
7475
<MaintenanceTicketButtonCell ticket={params.value} />
@@ -96,6 +97,7 @@ export default function MaintenancePage() {
9697
severity: ticket.severity,
9798
creator: ticket.type === MaintenanceTicketType.REPORTED ? ticket.creator?.ritUsername ?? "" : "SERVER",
9899
assigned: ticket.assigned?.ritUsername ?? "UNASSIGNED",
100+
timeUnit: ticket.timeUnit,
99101
dateCreated: formatter.format(new Date(Number(ticket.dateCreated))),
100102
manage: ticket
101103
}

client/src/pages/makerspace_page/maintenance_pages/MaintenanceTicketModal.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Stack } from "@mui/system";
22
import PrettyModal from "../../../common/PrettyModal";
3-
import { ASSIGN_MAINTENANCE_TICKET, DELETE_MAINTENACE_TICKET, MaintenanceTicket, MaintenanceTicketSeverity, MaintenanceTicketStatus, MaintenanceTicketType, MODIFY_MAINTENANCE_TICKET_STATUS, UPDATE_MAINTENACE_TICKET } from "../../../queries/maintenanceTicketQueries";
3+
import { ASSIGN_MAINTENANCE_TICKET, DELETE_MAINTENACE_TICKET, MaintenanceTicket, MaintenanceTicketSeverity, MaintenanceTicketStatus, MaintenanceTicketTimeUnit, MaintenanceTicketType, MODIFY_MAINTENANCE_TICKET_STATUS, UPDATE_MAINTENACE_TICKET } from "../../../queries/maintenanceTicketQueries";
44
import { Autocomplete, Button, Chip, IconButton, TextField, Typography } from "@mui/material";
55
import { useState } from "react";
66
import CloseIcon from '@mui/icons-material/Close';
@@ -17,6 +17,7 @@ import { useParams } from "react-router-dom";
1717
import { GET_VALID_STAFF } from "../../../queries/makerspaceQueries";
1818
import { CurrentUser, useCurrentUser } from "../../../common/CurrentUserProvider";
1919
import { isManager } from "../../../common/PrivilegeUtils";
20+
import { formatHobbsTime } from "../../../queries/equipmentInstanceQueries";
2021

2122
interface TicketModalProps {
2223
open: boolean,
@@ -194,6 +195,19 @@ export default function MaintenanceTicketModal(props: TicketModalProps) {
194195
}
195196
}
196197

198+
function createTimeLabel(){
199+
const after = `Create After: ${props.ticket.intervalHours} hours`
200+
const difference = props.ticket.instance.hobbsTime - props.ticket.hobbsTimeAtCreate
201+
if (difference < 0){
202+
return `${after} (${formatHobbsTime(Math.abs(difference))} to go)`
203+
} else {
204+
return `${after} (${formatHobbsTime(difference)} ago)`
205+
}
206+
}
207+
208+
const shouldShowHobbsAtOpen= props.ticket.timeUnit == MaintenanceTicketTimeUnit.USAGE || props.ticket.status != MaintenanceTicketStatus.UPCOMING;
209+
const shouldShowHobbsAtClose= props.ticket.status == MaintenanceTicketStatus.CLOSED;
210+
197211
return (
198212
<PrettyModal open={props.open} onClose={handleClose} width={"600px"}>
199213
<Stack spacing={2}>
@@ -214,7 +228,10 @@ export default function MaintenanceTicketModal(props: TicketModalProps) {
214228
</Stack>
215229
<Stack direction={"row"} justifyContent={"space-between"}>
216230
<Typography variant="subtitle1">{`Ticket #${props.ticket.id}`}</Typography>
217-
<Typography variant="subtitle1">{`Created: ${formatter.format(Number(props.ticket.dateCreated))}`}</Typography>
231+
{ props.ticket.timeUnit=== MaintenanceTicketTimeUnit.CALENDAR ?
232+
<Typography variant="subtitle1">{`Create Date: ${formatter.format(Number(props.ticket.dateCreated))}`}</Typography>
233+
: <Typography variant="subtitle1">{createTimeLabel()}</Typography>
234+
}
218235
</Stack>
219236
<Stack direction={"row"} justifyContent={"space-between"} alignItems={"center"}>
220237
<Typography variant="body1">{`Reported by: ${props.ticket.type === MaintenanceTicketType.REPORTED ? props.ticket.creator?.ritUsername ?? "" : "SERVER"}`}</Typography>
@@ -317,6 +334,10 @@ export default function MaintenanceTicketModal(props: TicketModalProps) {
317334
</Stack>
318335
}
319336
</Stack>
337+
<Stack justifyContent={"space-between"} direction={"row"}>
338+
{shouldShowHobbsAtOpen ? <Typography variant="body1">Usage at Open: {formatHobbsTime(props.ticket.hobbsTimeAtCreate)}</Typography> : undefined}
339+
{shouldShowHobbsAtClose ? <Typography variant="body1">Usage at Close: {props.ticket.hobbsTimeAtClose ? formatHobbsTime(props.ticket.hobbsTimeAtClose) : "N/A"}</Typography> : undefined}
340+
</Stack>
320341
{
321342
editing
322343
? <TextField

client/src/pages/makerspace_page/maintenance_pages/NewIntervalTicketModal.tsx

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { FullMakerspace } from "../../../queries/makerspaceQueries";
77
import { useMemo, useState } from "react";
88
import { EquipmentInstance, GET_EQUIPMENT_INSTANCES } from "../../../queries/equipmentInstanceQueries";
99
import { useMutation, useQuery } from "@apollo/client/react";
10-
import { CREATE_INTERVAL_MAINTENANCE_TICKET, MaintenanceTicketSeverity } from "../../../queries/maintenanceTicketQueries";
10+
import { CREATE_INTERVAL_MAINTENANCE_TICKET, GET_MAINTENANCE_TICKETS, MaintenanceTicketSeverity, PAGINATED_MAINTENANCE_TICKETS } from "../../../queries/maintenanceTicketQueries";
1111
import { toast } from "react-toastify";
1212
import FileUploadButton from "../../../common/FileUploadButton";
1313
import styled from "styled-components";
@@ -32,7 +32,7 @@ export default function NewIntervalTicketModal(props: NewTicketModalProps) {
3232
const [imageUrl, setImageUrl] = useState<string>();
3333

3434
const equipmentInstancesResult = useQuery(GET_EQUIPMENT_INSTANCES, { variables: { equipmentID: equipment?.id ?? -1 } });
35-
const [createTicket] = useMutation(CREATE_INTERVAL_MAINTENANCE_TICKET, { refetchQueries: ["PaginatedMaintenanceTickets", "MaintenanceTickets"] });
35+
const [createTicket] = useMutation(CREATE_INTERVAL_MAINTENANCE_TICKET, { refetchQueries: [{query: PAGINATED_MAINTENANCE_TICKETS}, {query: GET_MAINTENANCE_TICKETS}] });
3636

3737
const EMPTY_ARRAY: EquipmentInstance[] = [];
3838
const instances: EquipmentInstance[] = equipmentInstancesResult.data?.equipmentInstances ?? EMPTY_ARRAY;
@@ -50,14 +50,20 @@ export default function NewIntervalTicketModal(props: NewTicketModalProps) {
5050
const makerspace_equipments = makerspace_equipments_2?.flat(1);
5151

5252
const [startDate, setStartDate] = useState(new Date());
53-
const [scale, setScale] = useState("days");
53+
const [timeUnit, setTimeUnit] = useState(""); // USAGE | CALENDAR
54+
const [scale, setScale] = useState("days"); // hours | days | weeks
5455
const [interval, setInterval] = useState("1");
5556

5657
function handleChangeScale(_event: React.MouseEvent<HTMLElement>, value: string) {
5758
if (value !== null && value !== scale) {
5859
setScale(value);
5960
}
6061
}
62+
function handleChangeTimeUnit(_event: React.MouseEvent<HTMLElement>, value: string) {
63+
if (value !== null && value !== timeUnit) {
64+
setTimeUnit(value);
65+
}
66+
}
6167

6268
function handleClose() {
6369
setEquipment(undefined);
@@ -67,25 +73,29 @@ export default function NewIntervalTicketModal(props: NewTicketModalProps) {
6773
setImageUrl(undefined);
6874
setStartDate(new Date());
6975
setScale("days");
76+
setTimeUnit("CALENDAR")
7077
setInterval("1");
7178

7279
props.onClose();
7380
}
7481

7582
async function handleCreateTicket() {
76-
if (!(equipment && reportedInstance && severity && !Number.isNaN(Number(interval)))) {
77-
toast.error("A required field is empty!");
83+
if (!(equipment && reportedInstance && severity && !Number.isNaN(Number(interval)) && (timeUnit == "USAGE" || timeUnit == "CALENDAR") ) ) {
84+
toast.error("A required field is empty or invalid!");
7885
return;
7986
}
8087
try {
88+
const intervalHours = scale === "days" ? Math.floor(Number(interval) * 24) : (scale == "weeks") ? Math.floor(Number(interval) * 168) : Math.floor(Number(interval))
89+
8190
await createTicket({
8291
variables: {
8392
severity: severity,
8493
instanceID: Number(reportedInstance.id),
8594
description: description,
8695
startDate: startDate.toISOString(),
8796
imageUrl: imageUrl,
88-
intervalHours: scale === "days" ? Number(interval) * 24 : Number(interval) * 168
97+
intervalHours: intervalHours,
98+
timeUnit: timeUnit,
8999
}
90100
})
91101
} catch (e) {
@@ -127,6 +137,7 @@ export default function NewIntervalTicketModal(props: NewTicketModalProps) {
127137
{
128138
equipment
129139
? <Autocomplete
140+
fullWidth
130141
key={instances.length === 1 ? "auto-selected" : "manual-select"}
131142
renderInput={
132143
(params) => (
@@ -159,21 +170,40 @@ export default function NewIntervalTicketModal(props: NewTicketModalProps) {
159170
disabled
160171
/>
161172
}
162-
<Autocomplete
163-
renderInput={
164-
(params) => (
165-
<TextField
166-
{...params}
167-
label="Severity"
168-
placeholder="Select Severity..."
169-
required
170-
/>
171-
)
172-
}
173-
options={[MaintenanceTicketSeverity.HIGH, MaintenanceTicketSeverity.MEDIUM, MaintenanceTicketSeverity.LOW]}
174-
value={severity}
175-
onChange={(event, newValue) => setSeverity(newValue ?? undefined)}
176-
/>
173+
<Stack direction={"row"} spacing={1}>
174+
<Autocomplete
175+
renderInput={
176+
(params) => (
177+
<TextField
178+
{...params}
179+
label="Severity"
180+
placeholder="Select Severity..."
181+
required
182+
/>
183+
)
184+
}
185+
options={[MaintenanceTicketSeverity.HIGH, MaintenanceTicketSeverity.MEDIUM, MaintenanceTicketSeverity.LOW]}
186+
value={severity}
187+
onChange={(event, newValue) => setSeverity(newValue ?? undefined)}
188+
sx={{flexGrow: 1}}
189+
/>
190+
191+
<ToggleButtonGroup
192+
exclusive
193+
value={timeUnit}
194+
onChange={handleChangeTimeUnit}
195+
>
196+
<ToggleButton value="USAGE">
197+
Usage
198+
</ToggleButton>
199+
<ToggleButton value="CALENDAR">
200+
Calendar
201+
</ToggleButton>
202+
</ToggleButtonGroup>
203+
204+
205+
</Stack>
206+
177207
<Stack direction={"row"} justifyContent={"space-between"}>
178208
<DatePicker
179209
value={startDate}
@@ -196,11 +226,15 @@ export default function NewIntervalTicketModal(props: NewTicketModalProps) {
196226
width: "120px"
197227
}}
198228
/>
229+
199230
<ToggleButtonGroup
200231
exclusive
201232
value={scale}
202233
onChange={handleChangeScale}
203234
>
235+
<ToggleButton value="hours">
236+
Hours
237+
</ToggleButton>
204238
<ToggleButton value="days">
205239
Days
206240
</ToggleButton>

client/src/queries/equipmentInstanceQueries.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ export interface EquipmentInstance {
2222
accessController: AccessController | undefined;
2323
hobbsTime: number
2424
}
25+
export function formatHobbsTime(allSeconds: number){
26+
const hours = Math.floor(allSeconds / 3600);
27+
if (hours > 0){
28+
return (allSeconds / 3600).toFixed(2) + " hrs"
29+
}
30+
const minutes = Math.floor((allSeconds % 3600) / 60);
31+
const seconds = allSeconds % 60;
32+
if (minutes > 0){
33+
return (minutes + seconds / 50).toFixed(2) + " min"
34+
} else {
35+
return Math.floor(allSeconds) + "sec"
36+
}
37+
}
38+
39+
2540

2641
export const GET_EQUIPMENT_INSTANCES = gql`
2742
query EquipmentInstances($equipmentID: ID!) {

0 commit comments

Comments
 (0)