Feature/#276/qrcodes (#289)

* bugfix: wrong format when on 12h format

* update: website link in placeholder

---------

Co-authored-by: Fabian Posenau <fabian@fphome.de>
Co-authored-by: cv <34649812+cpvalente@users.noreply.github.com>
This commit is contained in:
Fabian Posenau
2023-02-19 20:52:42 +01:00
committed by GitHub
parent df351b8cba
commit 63d763cbaf
13 changed files with 81 additions and 74 deletions
@@ -1,15 +1,17 @@
export type EventDataType = { export type EventDataType = {
title: string; title: string;
url: string; publicUrl: string;
publicInfo: string; publicInfo: string;
backstageUrl: string;
backstageInfo: string; backstageInfo: string;
endMessage: string; endMessage: string;
} };
export const eventDataPlaceholder: EventDataType = { export const eventDataPlaceholder: EventDataType = {
title: '', title: '',
url: '', publicUrl: '',
publicInfo: '', publicInfo: '',
backstageUrl: '',
backstageInfo: '', backstageInfo: '',
endMessage: '', endMessage: '',
}; };
@@ -27,8 +27,9 @@ export default function SettingsModal() {
setFormData({ setFormData({
title: data.title, title: data.title,
url: data.url, publicUrl: data.publicUrl,
publicInfo: data.publicInfo, publicInfo: data.publicInfo,
backstageUrl: data.backstageUrl,
backstageInfo: data.backstageInfo, backstageInfo: data.backstageInfo,
endMessage: data.endMessage, endMessage: data.endMessage,
}); });
@@ -45,7 +46,7 @@ export default function SettingsModal() {
try { try {
await postEvent(formData); await postEvent(formData);
} catch (error) { } catch (error) {
emitError(`Error saving event settings: ${error}`) emitError(`Error saving event settings: ${error}`);
} finally { } finally {
await refetch(); await refetch();
setChanged(false); setChanged(false);
@@ -53,7 +54,7 @@ export default function SettingsModal() {
setSubmitting(false); setSubmitting(false);
}, },
[emitError, formData, refetch] [emitError, formData, refetch],
); );
/** /**
@@ -69,12 +70,15 @@ export default function SettingsModal() {
* @param {string} field - object parameter to update * @param {string} field - object parameter to update
* @param {string} value - new object parameter value * @param {string} value - new object parameter value
*/ */
const handleChange = useCallback((field, value) => { const handleChange = useCallback(
const temp = { ...formData }; (field, value) => {
temp[field] = value; const temp = { ...formData };
setFormData(temp); temp[field] = value;
setChanged(true); setFormData(temp);
},[formData]); setChanged(true);
},
[formData],
);
return ( return (
<ModalBody className={style.modalBody}> <ModalBody className={style.modalBody}>
@@ -99,19 +103,19 @@ export default function SettingsModal() {
</div> </div>
<div className={style.hSeparator}>Additional Screen Info</div> <div className={style.hSeparator}>Additional Screen Info</div>
<div className={style.spacedEntry}> <div className={style.spacedEntry}>
<FormLabel htmlFor='url'> <FormLabel htmlFor='publicUrl'>
Event URL Public URL
<span className={style.labelNote}> <span className={style.labelNote}>
<br /> <br />
Shown as a QR code in some views QR code to be shown on public screens
</span> </span>
</FormLabel> </FormLabel>
<Input <Input
{...inputProps} {...inputProps}
name='url' name='publicUrl'
placeholder='www.onsite.no' placeholder='www.getontime.no'
value={formData.url} value={formData.publicUrl}
onChange={(event) => handleChange('url', event.target.value)} onChange={(event) => handleChange('publicUrl', event.target.value)}
/> />
</div> </div>
<div className={style.spacedEntry}> <div className={style.spacedEntry}>
@@ -130,6 +134,22 @@ export default function SettingsModal() {
onChange={(event) => handleChange('publicInfo', event.target.value)} onChange={(event) => handleChange('publicInfo', event.target.value)}
/> />
</div> </div>
<div className={style.spacedEntry}>
<FormLabel htmlFor='backstageUrl'>
Backstage URL
<span className={style.labelNote}>
<br />
QR to be shown on backstage screens
</span>
</FormLabel>
<Input
{...inputProps}
name='backstageUrl'
placeholder='www.getontime.no'
value={formData.backstageUrl}
onChange={(event) => handleChange('backstageUrl', event.target.value)}
/>
</div>
<div className={style.spacedEntry}> <div className={style.spacedEntry}>
<FormLabel htmlFor='backstageInfo'> <FormLabel htmlFor='backstageInfo'>
Backstage Info Backstage Info
@@ -165,12 +185,7 @@ export default function SettingsModal() {
/> />
</div> </div>
</div> </div>
<SubmitContainer <SubmitContainer revert={revert} submitting={submitting} changed={changed} status={status} />
revert={revert}
submitting={submitting}
changed={changed}
status={status}
/>
</form> </form>
</ModalBody> </ModalBody>
); );
+2 -1
View File
@@ -39,7 +39,8 @@ export const makeTable = (headerData, tableData, userFields) => {
const data = [ const data = [
['Ontime · Schedule Template'], ['Ontime · Schedule Template'],
['Event Name', headerData?.title || ''], ['Event Name', headerData?.title || ''],
['Event URL', headerData?.url || ''], ['Public URL', headerData?.publicUrl || ''],
['Backstage URL', headerData?.backstageUrl || ''],
[], [],
]; ];
@@ -83,12 +83,7 @@ export default function Backstage(props) {
</div> </div>
</div> </div>
<ProgressBar <ProgressBar className='progress-container' now={time.current} complete={time.duration} hidden={!showProgress} />
className='progress-container'
now={time.current}
complete={time.duration}
hidden={!showProgress}
/>
<div className='now-container'> <div className='now-container'>
<AnimatePresence> <AnimatePresence>
@@ -146,30 +141,21 @@ export default function Backstage(props) {
</AnimatePresence> </AnimatePresence>
</div> </div>
<ScheduleProvider <ScheduleProvider events={filteredEvents} selectedEventId={selectedId} isBackstage>
events={filteredEvents}
selectedEventId={selectedId}
isBackstage
>
<ScheduleNav className='schedule-nav-container' /> <ScheduleNav className='schedule-nav-container' />
<Schedule className='schedule-container' /> <Schedule className='schedule-container' />
</ScheduleProvider> </ScheduleProvider>
<div <div className={showPublicMessage ? 'public-container' : 'public-container public-container--hidden'}>
className={showPublicMessage ? 'public-container' : 'public-container public-container--hidden'}>
<div className='label'>Public message</div> <div className='label'>Public message</div>
<div className='message'>{publ.text}</div> <div className='message'>{publ.text}</div>
</div> </div>
<div className='info'> <div className='info'>
<div className='qr'> <div className='qr'>
{general.url != null && general.url !== '' && ( {general.backstageUrl && <QRCode value={general.backstageUrl} size={qrSize} level='L' />}
<QRCode value={general.url} size={qrSize} level='L' />
)}
</div> </div>
{general.backstageInfo && ( {general.backstageInfo && <div className='info__message'>{general.backstageInfo}</div>}
<div className='info__message'>{general.backstageInfo}</div>
)}
</div> </div>
</div> </div>
); );
@@ -64,7 +64,6 @@ export default function Public(props) {
</div> </div>
<div className='now-container'> <div className='now-container'>
<AnimatePresence> <AnimatePresence>
{publicTitle.showNow && ( {publicTitle.showNow && (
<motion.div <motion.div
@@ -106,29 +105,19 @@ export default function Public(props) {
</AnimatePresence> </AnimatePresence>
</div> </div>
<ScheduleProvider <ScheduleProvider events={events} selectedEventId={publicSelectedId}>
events={events}
selectedEventId={publicSelectedId}
>
<ScheduleNav className='schedule-nav-container' /> <ScheduleNav className='schedule-nav-container' />
<Schedule className='schedule-container' /> <Schedule className='schedule-container' />
</ScheduleProvider> </ScheduleProvider>
<div <div className={showPublicMessage ? 'public-container' : 'public-container public-container--hidden'}>
className={showPublicMessage ? 'public-container' : 'public-container public-container--hidden'}>
<div className='label'>Public message</div> <div className='label'>Public message</div>
<div className='message'>{publ.text}</div> <div className='message'>{publ.text}</div>
</div> </div>
<div className='info'> <div className='info'>
<div className='qr'> <div className='qr'>{general.publicUrl && <QRCode value={general.publicUrl} size={qrSize} level='L' />}</div>
{general.url != null && general.url !== '' && ( {general.publicInfo && <div className='info__message'>{general.publicInfo}</div>}
<QRCode value={general.url} size={qrSize} level='L' />
)}
</div>
{general.backstageInfo && (
<div className='info__message'>{general.backstageInfo}</div>
)}
</div> </div>
</div> </div>
); );
@@ -46,7 +46,7 @@ export default function StudioClock(props) {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const showSeconds = searchParams.get('seconds'); const showSeconds = searchParams.get('seconds');
formatOptions.showSeconds = Boolean(showSeconds); formatOptions.showSeconds = Boolean(showSeconds);
formatOptions.format = `hh:mm${formatOptions.showSeconds ? 'mm' : ''}`; formatOptions.format = `hh:mm${formatOptions.showSeconds ? ':ss' : ''}`;
useEffect(() => { useEffect(() => {
document.title = 'ontime - Studio Clock'; document.title = 'ontime - Studio Clock';
@@ -16,8 +16,9 @@ export const postEvent = async (req, res) => {
try { try {
const newEvent = removeUndefined({ const newEvent = removeUndefined({
title: req.body?.title, title: req.body?.title,
url: req.body?.url, publicUrl: req.body?.publicUrl,
publicInfo: req.body?.publicInfo, publicInfo: req.body?.publicInfo,
backstageUrl: req.body?.backstageUrl,
backstageInfo: req.body?.backstageInfo, backstageInfo: req.body?.backstageInfo,
endMessage: req.body?.endMessage, endMessage: req.body?.endMessage,
}); });
@@ -2,8 +2,9 @@ import { body, validationResult } from 'express-validator';
export const eventSanitizer = [ export const eventSanitizer = [
body('title').optional().isString().trim(), body('title').optional().isString().trim(),
body('url').optional().isString().trim(), body('publicUrl').optional().isString().trim(),
body('publicInfo').optional().isString().trim(), body('publicInfo').optional().isString().trim(),
body('backstageUrl').optional().isString().trim(),
body('backstageInfo').optional().isString().trim(), body('backstageInfo').optional().isString().trim(),
body('endMessage').optional().isString().trim(), body('endMessage').optional().isString().trim(),
(req, res, next) => { (req, res, next) => {
+2 -1
View File
@@ -2,8 +2,9 @@ export const dbModel = {
rundown: [], rundown: [],
event: { event: {
title: '', title: '',
url: '', publicUrl: '',
publicInfo: '', publicInfo: '',
backstageUrl: '',
backstageInfo: '', backstageInfo: '',
endMessage: '', endMessage: '',
}, },
+15 -7
View File
@@ -27,7 +27,8 @@ export const JSON_MIME = 'application/json';
export const parseExcel = async (excelData) => { export const parseExcel = async (excelData) => {
const eventData = { const eventData = {
title: '', title: '',
url: '', publicUrl: '',
backstageUrl: '',
}; };
const customUserFields = {}; const customUserFields = {};
const rundown = []; const rundown = [];
@@ -55,8 +56,9 @@ export const parseExcel = async (excelData) => {
.filter((e) => e.length > 0) .filter((e) => e.length > 0)
.forEach((row) => { .forEach((row) => {
let eventTitleNext = false; let eventTitleNext = false;
let eventUrlNext = false; let publicUrlNext = false;
let publicInfoNext = false; let publicInfoNext = false;
let backstageUrlNext = false;
let backstageInfoNext = false; let backstageInfoNext = false;
let endMessageNext = false; let endMessageNext = false;
const event = {}; const event = {};
@@ -66,12 +68,15 @@ export const parseExcel = async (excelData) => {
if (eventTitleNext) { if (eventTitleNext) {
eventData.title = column; eventData.title = column;
eventTitleNext = false; eventTitleNext = false;
} else if (eventUrlNext) { } else if (publicUrlNext) {
eventData.url = column; eventData.publicUrl = column;
eventUrlNext = false; publicUrlNext = false;
} else if (publicInfoNext) { } else if (publicInfoNext) {
eventData.publicInfo = column; eventData.publicInfo = column;
publicInfoNext = false; publicInfoNext = false;
} else if (backstageUrlNext) {
eventData.publicUrl = column;
backstageUrlNext = false;
} else if (backstageInfoNext) { } else if (backstageInfoNext) {
eventData.backstageInfo = column; eventData.backstageInfo = column;
backstageInfoNext = false; backstageInfoNext = false;
@@ -125,12 +130,15 @@ export const parseExcel = async (excelData) => {
case 'event name': case 'event name':
eventTitleNext = true; eventTitleNext = true;
break; break;
case 'event url': case 'public url':
eventUrlNext = true; publicUrlNext = true;
break; break;
case 'public info': case 'public info':
publicInfoNext = true; publicInfoNext = true;
break; break;
case 'backstage url':
backstageUrlNext = true;
break;
case 'backstage info': case 'backstage info':
backstageInfoNext = true; backstageInfoNext = true;
break; break;
+2 -1
View File
@@ -71,8 +71,9 @@ export const parseEvent = (data, enforce) => {
newEvent = { newEvent = {
...dbModel.event, ...dbModel.event,
title: e.title || dbModel.event.title, title: e.title || dbModel.event.title,
url: e.url || dbModel.event.url, publicUrl: e.publicUrl || dbModel.event.publicUrl,
publicInfo: e.publicInfo || dbModel.event.publicInfo, publicInfo: e.publicInfo || dbModel.event.publicInfo,
backstageUrl: e.backstageUrl || dbModel.event.backstageUrl,
backstageInfo: e.backstageInfo || dbModel.event.backstageInfo, backstageInfo: e.backstageInfo || dbModel.event.backstageInfo,
endMessage: e.endMessage || dbModel.event.endMessage, endMessage: e.endMessage || dbModel.event.endMessage,
}; };
+2 -1
View File
@@ -109,8 +109,9 @@
], ],
"event": { "event": {
"title": "All about Carlos demo event", "title": "All about Carlos demo event",
"url": "www.carlosvalente.com", "publicUrl": "www.getontime.no",
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject", "publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
"backstageUrl": "www.getontime.no",
"backstageInfo": "WiFi: demobackstage\nPassword: ontimeproject", "backstageInfo": "WiFi: demobackstage\nPassword: ontimeproject",
"endMessage": "" "endMessage": ""
}, },
+2 -1
View File
@@ -221,8 +221,9 @@
], ],
"event": { "event": {
"title": "All about Carlos demo event", "title": "All about Carlos demo event",
"url": "www.carlosvalente.com", "publicUrl": "www.getontime.no",
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject", "publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
"backstageUrl": "www.getontime.no",
"backstageInfo": "WiFi: demobackstage\nPassword: ontimeproject", "backstageInfo": "WiFi: demobackstage\nPassword: ontimeproject",
"endMessage": "" "endMessage": ""
}, },