Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 1x 15x 15x 15x 15x 15x 15x 15x 15x 7x 1x 1x 6x 6x 6x 5x 1x 1x 4x 4x 3x 4x 1x 1x 1x 6x 15x 12x 3x 1x | /**
* ReleaseNotesInfoBox Component
* Displays release notes from a static text file on login screen
* Only visible in development and staging environments
*/
import React, { useState, useEffect } from 'react';
import { Alert, AlertTitle, Box, Collapse, IconButton } from '@mui/material';
import { Close as CloseIcon } from '@mui/icons-material';
import { useTranslation } from 'react-i18next';
import { useEnvironment } from '@/contexts/useEnvironment';
export const ReleaseNotesInfoBox: React.FC = () => {
const { t } = useTranslation();
const environment = useEnvironment();
const [notes, setNotes] = useState<string>('');
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<boolean>(false);
const [dismissed, setDismissed] = useState<boolean>(false);
// Only show in development and staging
const shouldShow = environment === 'development' || environment === 'staging';
useEffect(() => {
if (!shouldShow) {
setLoading(false);
return;
}
const fetchReleaseNotes = async () => {
try {
const response = await fetch('/release-notes.txt');
if (!response.ok) {
// File doesn't exist or fetch failed - not an error, just no notes
setLoading(false);
return;
}
const text = await response.text();
// Only set notes if there's actual content (not empty or whitespace)
if (text.trim()) {
setNotes(text.trim());
}
setLoading(false);
} catch (err) {
console.warn('[ReleaseNotesInfoBox] Failed to fetch release notes:', err);
setError(true);
setLoading(false);
}
};
fetchReleaseNotes();
}, [shouldShow]);
// Don't render anything if:
// - Not in dev/staging environment
// - Still loading
// - Error occurred
// - No notes content
// - User dismissed the box
if (!shouldShow || loading || error || !notes || dismissed) {
return null;
}
return (
<Collapse in={!dismissed}>
<Box sx={{ mb: 2 }}>
<Alert
severity="info"
action={
<IconButton
aria-label={t('actions.close')}
color="inherit"
size="small"
onClick={() => setDismissed(true)}
>
<CloseIcon fontSize="inherit" />
</IconButton>
}
sx={{
'& .MuiAlert-message': {
width: '100%',
},
}}
>
<AlertTitle sx={{ fontWeight: 'bold' }}>{t('releaseNotes.title')}</AlertTitle>
<Box
component="pre"
sx={{
fontFamily: 'inherit',
fontSize: 'inherit',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
margin: 0,
marginTop: 1,
}}
>
{notes}
</Box>
</Alert>
</Box>
</Collapse>
);
};
|