Skip to content

Commit f4cc26f

Browse files
charred-70tiffany-tran07evanugarte
authored
created button for led sign message expiration (#1856)
* created button for expiration w/ no logic rn * fixed conflict * set expiration * frontend core-v4 no longer fails with expiration button * enabed led sign in config.json * added sign_message class * changed 'cancel' button to 'hide' * made expiration button disappear when pressed and set expiration to null when updated without expiration * polishing ui of expiration inputs * made the expiration button ui look good on pc and mobile :D - char & tiffany * added condition for expiration that is in the past * i was in room 2 wbu * made led sign page send expiration date in utc * and thats the internship * lint * remove src/Pages/LedSign/sign_message.py * undo button classname change * separate variable * why not await * npx mocha test/api/LedSign.js --------- Co-authored-by: Tyffoni <tyffoni@gmail.com> Co-authored-by: evan <evanuxd@gmail.com>
1 parent 3fb68d5 commit f4cc26f

File tree

6 files changed

+106
-18
lines changed

6 files changed

+106
-18
lines changed

api/config/config.example.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@
2929
"officeAccessCard": {
3030
"API_KEY": "NOTHING_REALLY"
3131
},
32+
"LED_SIGN": {
33+
"ENABLED": false
34+
},
3235
"secretKey": "super duper secret key"
3336
}

api/main_endpoints/routes/LedSign.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@ const logger = require('../../util/logger');
1313
const { updateSign, healthCheck, turnOffSign } = require('../util/LedSign.js');
1414
const AuditLogActions = require('../util/auditLogActions.js');
1515
const AuditLog = require('../models/AuditLog.js');
16+
const {
17+
LED_SIGN = {}
18+
} = require('../../config/config.json');
1619

17-
const runningInDevelopment = process.env.NODE_ENV !== 'production'
18-
&& process.env.NODE_ENV !== 'test';
20+
const runningInTest = process.env.NODE_ENV === 'test';
1921

2022

2123
router.get('/healthCheck', async (req, res) => {
2224
/*
2325
* How these work with Quasar:
2426
* https://github.com/SCE-Development/Quasar/wiki/How-do-Health-Checks-Work%3F
2527
*/
26-
if (runningInDevelopment) {
28+
if (!LED_SIGN.ENABLED && !runningInTest) {
29+
logger.warn('led sign is disabled, returning 200 by default');
2730
return res.sendStatus(OK);
2831
}
2932
const dataFromSign = await healthCheck();
@@ -39,11 +42,12 @@ router.post('/updateSignText', async (req, res) => {
3942
return res.sendStatus(UNAUTHORIZED);
4043
}
4144
const user = await decodeToken(req); // Store the user here
42-
if (!user) {
45+
if (!user || Object.keys(user) === 0) {
4346
logger.warn('/updateSignText was requested with an invalid token');
4447
return res.sendStatus(UNAUTHORIZED);
4548
}
46-
if (runningInDevelopment) {
49+
if (!LED_SIGN.ENABLED && !runningInTest) {
50+
logger.warn('led sign is disabled, returning 200 by default');
4751
return res.sendStatus(OK);
4852
}
4953
// need to make this its own api endpoint
@@ -60,7 +64,7 @@ router.post('/updateSignText', async (req, res) => {
6064
status = SERVER_ERROR;
6165
}
6266

63-
AuditLog.create({
67+
await AuditLog.create({
6468
userId: user._id,
6569
action: AuditLogActions.UPDATE_SIGN,
6670
details: {

api/main_endpoints/util/LedSign.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ let LED_SIGN_URL = process.env.LED_SIGN_URL
2929
async function updateSign(data) {
3030
return new Promise((resolve) => {
3131
axios
32-
.post(LED_SIGN_URL + '/api/update-sign', data)
32+
.post(LED_SIGN_URL + '/update-sign', data)
3333
.then(() => {
3434
resolve(true);
3535
}).catch((err) => {
@@ -46,7 +46,7 @@ async function updateSign(data) {
4646
async function turnOffSign() {
4747
return new Promise((resolve) => {
4848
axios
49-
.get(LED_SIGN_URL + '/api/turn-off')
49+
.get(LED_SIGN_URL + '/turn-off')
5050
.then(() => {
5151
resolve(true);
5252
}).catch((err) => {
@@ -77,7 +77,7 @@ async function turnOffSign() {
7777
async function healthCheck() {
7878
return new Promise((resolve) => {
7979
axios
80-
.get(LED_SIGN_URL + '/api/health-check')
80+
.get(LED_SIGN_URL + '/health-check')
8181
.then(({ data }) => {
8282
resolve(data);
8383
}).catch((err) => {

docker-compose.dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ services:
4848
- MAIN_ENDPOINT_URL=sce-main-endpoints-dev:8080
4949
- CLEEZY_URL=http://host.docker.internal:8000
5050
- PRINTER_URL=http://host.docker.internal:14000
51-
- LED_SIGN_URL=http://host.docker.internal:11000
51+
- LED_SIGN_URL=http://host.docker.internal:10000
5252
- DISCORD_REDIRECT_URI=http://localhost/api/user/callback
5353
- MAILER_API_URL=http://sce-cloud-api-dev:8082/cloudapi
5454
- DATABASE_HOST=sce-mongodb-dev

docker-compose.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ services:
1515
dockerfile: ./Dockerfile
1616
command:
1717
- --hosts
18-
- http://host.docker.internal:11000/api/health-check
18+
# led sign
19+
- http://host.docker.internal:10000/health-check
20+
# printer
1921
- http://host.docker.internal:14000/healthcheck/printer
22+
# status page
2023
- http://host.docker.internal:17000/hello
2124
extra_hosts:
2225
- "host.docker.internal:host-gateway"
@@ -44,7 +47,7 @@ services:
4447
environment:
4548
- CLEEZY_URL=http://cleezy-app.sce:8000
4649
- PRINTER_URL=http://host.docker.internal:14000
47-
- LED_SIGN_URL=http://host.docker.internal:11000
50+
- LED_SIGN_URL=http://host.docker.internal:10000
4851
- DISCORD_REDIRECT_URI=https://sce.sjsu.edu/api/user/callback
4952
- MAILER_API_URL=http://sce-cloud-api:8082/cloudapi
5053
- DATABASE_HOST=sce-mongodb

src/Pages/LedSign/LedSign.js

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
import React, { useState, useEffect } from 'react';
22
import { healthCheck, updateSignText } from '../../APIFunctions/LedSign';
33
import { useSCE } from '../../Components/context/SceContext';
4+
45
import './ledsign.css';
56

67
function LedSign() {
78
const { user } = useSCE();
89
const [signHealthy, setSignHealthy] = useState(false);
10+
const [showInput, setInput] = useState(false);
911
const [loading, setLoading] = useState(true);
1012
const [text, setText] = useState('');
1113
const [brightness, setBrightness] = useState(50);
1214
const [scrollSpeed, setScrollSpeed] = useState(5);
1315
const [backgroundColor, setBackgroundColor] = useState('#0000ff');
1416
const [textColor, setTextColor] = useState('#00ff00');
1517
const [borderColor, setBorderColor] = useState('#ff0000');
18+
const [expiration, setExpiration] = useState(null);
19+
const [existingExpirationFromSign, setExistingExpirationFromSign] = useState(null);
1620
const [awaitingSignResponse, setAwaitingSignResponse] = useState(false);
17-
const [awaitingStopSignResponse, setAwaitingStopSignResponse]
18-
= useState(false);
1921
const [requestSuccessful, setRequestSuccessful] = useState();
2022
const [stopRequestSuccesful, setStopRequestSuccesful] = useState();
21-
2223
const inputArray = [
2324
{
2425
title: 'Sign Text:',
@@ -67,7 +68,43 @@ function LedSign() {
6768
}
6869
];
6970

71+
function isExpired() {
72+
if (!expiration) {
73+
return false;
74+
}
75+
const currDate = new Date();
76+
const expireDateObject = new Date(expiration);
77+
return expireDateObject < currDate;
78+
}
79+
80+
function getFormattedTime(maybeISOString = null) {
81+
let date = new Date();
82+
if (maybeISOString) {
83+
date = new Date(maybeISOString);
84+
}
85+
86+
return date.toLocaleString('en-US', {
87+
year: 'numeric',
88+
month: '2-digit',
89+
day: '2-digit',
90+
hour: 'numeric',
91+
minute: '2-digit',
92+
hour12: true,
93+
timeZoneName: 'short',
94+
});
95+
}
96+
97+
async function handleExpiration() {
98+
setExpiration(null);
99+
setInput(!showInput);
100+
}
101+
70102
async function handleSend() {
103+
let expirationToUse = null;
104+
if (expiration) {
105+
expirationToUse = new Date(expiration).toISOString();
106+
}
107+
71108
setAwaitingSignResponse(true);
72109
let correctedScrollSpeed = 10 - scrollSpeed;
73110
const signResponse = await updateSignText(
@@ -78,6 +115,7 @@ function LedSign() {
78115
backgroundColor,
79116
textColor,
80117
borderColor,
118+
expiration: expirationToUse,
81119
email: user.email,
82120
firstName: user.firstName,
83121
},
@@ -88,7 +126,6 @@ function LedSign() {
88126
}
89127

90128
async function handleStop() {
91-
setAwaitingStopSignResponse(true);
92129
const signResponse = await updateSignText(
93130
{
94131
ledIsOff: true,
@@ -98,7 +135,6 @@ function LedSign() {
98135
user.token
99136
);
100137
setStopRequestSuccesful(!signResponse.error);
101-
setAwaitingStopSignResponse(false);
102138
}
103139

104140
function renderRequestStatus() {
@@ -115,20 +151,60 @@ function LedSign() {
115151
);
116152
}
117153
}
154+
155+
function maybeShowExpirationDate() {
156+
if (!existingExpirationFromSign) {
157+
return <></>;
158+
}
159+
const humanizedExpiration = new Date(existingExpirationFromSign)
160+
.toLocaleString('en-US', {
161+
timeZoneName: 'short' // e.g., "Pacific Standard Time"
162+
});
163+
return <p>The current sign message will expire on {humanizedExpiration}</p>;
164+
}
165+
166+
function getExpirationButtonOrInput() {
167+
if (showInput) {
168+
return <>
169+
<div className='w-2/3 lg:w-1/2 flex items-center justify-items-center flex-col items-center sm:flex-row'>
170+
<input className='m-1 mt-6 w-full rounded-md text-center flex-1 sm:pt-1 pl-4' type="datetime-local" id="endTime" name="endTime" onChange={e => setExpiration(e.target.value)} />
171+
<button className='btn w-full bg-gray-600 hover:bg-gray-500 text-white mr-4 sm:w-1/3 ml-5 mt-5 mb-3' onClick={e => setInput(!showInput)}>
172+
Cancel Expiration
173+
</button>
174+
</div>
175+
{
176+
isExpired() && <div className="w-2/3 lg:w-1/2 text-left break-words">
177+
<p className='text-red-600 dark:text-red-400'>
178+
Your selected expiration is considered behind the current time of {getFormattedTime()}.
179+
</p>
180+
<p className='text-red-600 dark:text-red-400'>
181+
Submitting a message with this expiration will not update the sign.
182+
</p>
183+
</div>
184+
}
185+
</>;
186+
}
187+
188+
return <button className='btn w-2/3 lg:w-1/2 bg-gray-500 hover:bg-gray-400 text-white mt-2' onClick={handleExpiration}>
189+
Set Expiration
190+
</button>;
191+
}
192+
118193
useEffect(() => {
119194
async function checkSignHealth() {
120195
setLoading(true);
121196
const status = await healthCheck(user.firstName);
122197
if (status && !status.error) {
123198
setSignHealthy(true);
124199
const { responseData } = status;
125-
if (responseData && responseData.text) {
200+
if (Object.keys(responseData).length > 0) {
126201
setText(responseData.text);
127202
setBrightness(responseData.brightness);
128203
setScrollSpeed(responseData.scrollSpeed);
129204
setBackgroundColor(responseData.backgroundColor);
130205
setTextColor(responseData.textColor);
131206
setBorderColor(responseData.borderColor);
207+
setExistingExpirationFromSign(responseData.expiration);
132208
}
133209
} else {
134210
setSignHealthy(false);
@@ -198,6 +274,8 @@ function LedSign() {
198274
></div>
199275
</div>
200276
</div>
277+
{maybeShowExpirationDate()}
278+
{getExpirationButtonOrInput()}
201279
{
202280
inputArray.map(({
203281
id,

0 commit comments

Comments
 (0)