Skip to content

Commit

Permalink
feat(timers): Introduce custom labels for timers
Browse files Browse the repository at this point in the history
  • Loading branch information
Hypfer committed Apr 23, 2023
1 parent a5f920e commit eadff02
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 29 deletions.
5 changes: 5 additions & 0 deletions backend/lib/doc/Configuration.openapi.json
Expand Up @@ -393,6 +393,11 @@
"enabled": {
"type": "boolean"
},
"label": {
"type": "string",
"pattern": "^.{0,24}$",
"description": "An optional user-defined label for the timer"
},
"dow": {
"type": "array",
"description": "Day of Week\nSunday = 0, Monday = 1, ... Saturday = 6",
Expand Down
7 changes: 7 additions & 0 deletions backend/lib/entities/core/ValetudoTimer.js
Expand Up @@ -9,6 +9,7 @@ class ValetudoTimer extends SerializableEntity {
* @param {object} options
* @param {string} [options.id] uuidv4
* @param {boolean} options.enabled
* @param {string} [options.label]
*
* @param {Array<number>} options.dow Sunday = 0 because js
* @param {number} options.hour 0-23
Expand All @@ -31,6 +32,12 @@ class ValetudoTimer extends SerializableEntity {

this.id = options.id ?? uuid.v4();
this.enabled = options.enabled;

if (typeof options.label === "string" && options.label.length > 0) {
this.label = options.label.substring(0, 24);
}


this.dow = [...options.dow].sort((a,b) => {
return a - b;
});
Expand Down
2 changes: 2 additions & 0 deletions backend/lib/webserver/TimerRouter.js
Expand Up @@ -90,6 +90,7 @@ class TimerRouter {
} else {
const storedTimers = this.config.get("timers");
const newTimer = new ValetudoTimer({
label: req.body.label,
enabled: req.body.enabled === true,
dow: req.body.dow,
hour: req.body.hour,
Expand Down Expand Up @@ -128,6 +129,7 @@ class TimerRouter {
} else {
const newTimer = new ValetudoTimer({
id: req.params.id,
label: req.body.label,
enabled: req.body.enabled === true,
dow: req.body.dow,
hour: req.body.hour,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api/types.ts
Expand Up @@ -193,6 +193,7 @@ export enum ValetudoTimerPreActionType {
export interface Timer {
id: string;
enabled: boolean;
label?: string;
dow: Array<number>;
hour: number;
minute: number;
Expand Down
77 changes: 58 additions & 19 deletions frontend/src/valetudo/timers/TimerCard.tsx
Expand Up @@ -80,21 +80,35 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({
}, [timer]);

const weekdayLabels = React.useMemo(() => {
return weekdays.map((day, i) => {
const enabled = timerInLocalTime.dow.includes(day.dow);
return (
<Grid
container
direction="row"
sx={{
justifyContent: "space-between"
}}
>
{weekdays.map((day, i) => {
const enabled = timerInLocalTime.dow.includes(day.dow);

return (
<Typography
key={day.label}
variant={"body2"}
color={enabled ? "textPrimary" : "textSecondary"}
component={"span"}
sx={i < weekdays.length - 1 ? { marginRight: 1 } : {}}
>
{day.label.toUpperCase().slice(0, 3)}
</Typography>
);
});
return (
<Grid
item
key={day.label}
>
<Typography
variant={"body2"}
color={enabled ? "textPrimary" : "textSecondary"}
component={"span"}
sx={i < weekdays.length - 1 ? { marginRight: 1 } : {}}
>
{day.label.toUpperCase().slice(0, 3)}
</Typography>
</Grid>
);
})}
</Grid>
);
}, [timerInLocalTime]);

const timeLabel = React.useMemo(() => {
Expand Down Expand Up @@ -126,6 +140,29 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({
return <Typography variant={"subtitle1"}>{label}</Typography>;
}, [timer]);

const preActionLabel = React.useMemo(() => {
if (timer?.pre_actions?.length) {
return <Typography variant={"subtitle1"}>
{`${timer.pre_actions.length} Pre-Action${timer.pre_actions.length > 1 ? "s" : ""}`}
</Typography>;
} else {
return null;
}
}, [timer]);

const timerLabel = React.useMemo(() => {
return timer?.label || "Timer";
}, [timer]);

const dialogTimerText = React.useMemo(() => {
if (timer?.label) {
return `"${timer.label}"`;
} else {
return "this timer";
}

}, [timer]);

return (
<Card
key={timer.id}
Expand All @@ -134,9 +171,9 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({
<CardContent>
<Grid
container
spacing={4}
alignItems="center"
justifyContent="space-between"
sx={{minWidth: "16rem"}}
>
<Grid item>
<FormControlLabel
Expand All @@ -151,10 +188,10 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({
<Typography
variant="h6"
gutterBottom
sx={{ marginBottom: 0 }}
sx={{ marginBottom: 0, userSelect: "none" }}
title={timer.id}
>
Timer
{timerLabel}
</Typography>
}
/>
Expand Down Expand Up @@ -196,6 +233,8 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({

{actionLabel}

{preActionLabel}

<Dialog
open={deleteDialogOpen}
onClose={() => {
Expand All @@ -205,7 +244,7 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({
<DialogTitle>Delete timer?</DialogTitle>
<DialogContent>
<DialogContentText>
Do you really want to delete this timer?
Do you really want to delete {dialogTimerText}?
</DialogContentText>
</DialogContent>
<DialogActions>
Expand Down Expand Up @@ -250,7 +289,7 @@ const TimerCard: FunctionComponent<TimerCardProps> = ({
<DialogTitle>Execute timer?</DialogTitle>
<DialogContent>
<DialogContentText>
Do you really want to execute this timer right now?
Do you really want to execute {dialogTimerText} right now?
</DialogContentText>
</DialogContent>
<DialogActions>
Expand Down
52 changes: 42 additions & 10 deletions frontend/src/valetudo/timers/TimerEditDialog.tsx
Expand Up @@ -9,6 +9,7 @@ import {
Divider,
FormControl,
FormControlLabel,
Grid,
InputLabel,
MenuItem,
Select,
Expand Down Expand Up @@ -196,20 +197,51 @@ const TimerEditDialog: FunctionComponent<TimerDialogProps> = ({
</DialogTitle>
<DialogContent>
<Divider textAlign="left" sx={{mb: 1}}>General</Divider>
<FormControlLabel
sx={{padding: "0.5rem"}}
control={
<Checkbox
checked={editTimer.enabled}
onChange={(e) => {
<Grid
container
direction="column"
>
<Grid
item
sx={{paddingLeft: "0.5rem"}}
>
<FormControlLabel
control={
<Checkbox
checked={editTimer.enabled}
onChange={(e) => {
const newTimer = deepCopy(editTimer);
newTimer.enabled = e.target.checked;
setEditTimer(newTimer);
}}
/>
}
label="Enabled"
/>
</Grid>

<Grid
item
sx={{paddingLeft: "0.5rem", marginTop: "0.5rem"}}
>
<TextField
label="Custom Label"
value={editTimer.label}
variant="standard"
onChange={e => {
const newTimer = deepCopy(editTimer);
newTimer.enabled = e.target.checked;
newTimer.label = e.target.value.substring(0, 24);

if (newTimer.label.length === 0) {
newTimer.label = undefined;
}

setEditTimer(newTimer);
}}
/>
}
label="Enabled"
/>
</Grid>
</Grid>


<Divider textAlign="left" sx={{mt: 1, mb: 1.5}}>Schedule</Divider>

Expand Down

0 comments on commit eadff02

Please sign in to comment.