mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 11:00:47 +02:00
update notif (#4937)
# Description of Changes <img width="1521" height="1041" alt="image" src="https://github.com/user-attachments/assets/2644bf70-0a9b-4c91-9046-02f555314608" /> <img width="1162" height="1598" alt="image" src="https://github.com/user-attachments/assets/36d693f7-6fdd-4f2b-9db1-39ac336d9055" /> <img width="1220" height="1625" alt="image" src="https://github.com/user-attachments/assets/4d4c19ea-0020-45fb-b15a-9f6ad377856c" /> <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Stack, Text, Badge, Button, Group, Loader, Center, Divider, Box, Collapse } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { updateService, UpdateSummary, FullUpdateInfo, MachineInfo } from '@app/services/updateService';
|
||||
import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex';
|
||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
|
||||
interface UpdateModalProps {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
currentVersion: string;
|
||||
updateSummary: UpdateSummary;
|
||||
machineInfo: MachineInfo;
|
||||
}
|
||||
|
||||
const UpdateModal: React.FC<UpdateModalProps> = ({
|
||||
opened,
|
||||
onClose,
|
||||
currentVersion,
|
||||
updateSummary,
|
||||
machineInfo,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [fullUpdateInfo, setFullUpdateInfo] = useState<FullUpdateInfo | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [expandedVersions, setExpandedVersions] = useState<Set<number>>(new Set([0]));
|
||||
|
||||
useEffect(() => {
|
||||
if (opened) {
|
||||
setLoading(true);
|
||||
setExpandedVersions(new Set([0]));
|
||||
updateService.getFullUpdateInfo(currentVersion, machineInfo).then((info) => {
|
||||
setFullUpdateInfo(info);
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}, [opened, currentVersion, machineInfo]);
|
||||
|
||||
const toggleVersion = (index: number) => {
|
||||
setExpandedVersions((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
if (newSet.has(index)) {
|
||||
newSet.delete(index);
|
||||
} else {
|
||||
newSet.add(index);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
|
||||
const getPriorityColor = (priority: string): string => {
|
||||
switch (priority?.toLowerCase()) {
|
||||
case 'urgent':
|
||||
return 'red';
|
||||
case 'normal':
|
||||
return 'blue';
|
||||
case 'minor':
|
||||
return 'cyan';
|
||||
case 'low':
|
||||
return 'gray';
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
};
|
||||
|
||||
const getPriorityLabel = (priority: string): string => {
|
||||
const key = priority?.toLowerCase();
|
||||
return t(`update.priority.${key}`, priority || 'Normal');
|
||||
};
|
||||
|
||||
const downloadUrl = updateService.getDownloadUrl(machineInfo);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={
|
||||
<Text fw={600} size="lg">
|
||||
{t('update.modalTitle', 'Update Available')}
|
||||
</Text>
|
||||
}
|
||||
centered
|
||||
size="xl"
|
||||
zIndex={Z_INDEX_OVER_CONFIG_MODAL}
|
||||
styles={{
|
||||
body: {
|
||||
maxHeight: '75vh',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack gap="lg" pt="md">
|
||||
{/* Version Summary Section */}
|
||||
<Box>
|
||||
<Group justify="space-between" align="flex-start" wrap="nowrap" mb="md">
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="xs" c="dimmed" tt="uppercase" fw={500}>
|
||||
{t('update.current', 'Current Version')}
|
||||
</Text>
|
||||
<Text fw={600} size="xl">
|
||||
{currentVersion}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack gap={4} style={{ flex: 1 }} ta="center">
|
||||
<Text size="xs" c="dimmed" tt="uppercase" fw={500}>
|
||||
{t('update.priorityLabel', 'Priority')}
|
||||
</Text>
|
||||
<Badge
|
||||
color={getPriorityColor(updateSummary.max_priority)}
|
||||
size="lg"
|
||||
variant="filled"
|
||||
style={{ alignSelf: 'center' }}
|
||||
>
|
||||
{getPriorityLabel(updateSummary.max_priority)}
|
||||
</Badge>
|
||||
</Stack>
|
||||
|
||||
<Stack gap={4} style={{ flex: 1 }} ta="right">
|
||||
<Text size="xs" c="dimmed" tt="uppercase" fw={500}>
|
||||
{t('update.latest', 'Latest Version')}
|
||||
</Text>
|
||||
<Text fw={600} size="xl" c="blue">
|
||||
{updateSummary.latest_version}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
{updateSummary.latest_stable_version && (
|
||||
<Box
|
||||
style={{
|
||||
background: 'var(--mantine-color-green-0)',
|
||||
padding: '10px 16px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--mantine-color-green-2)',
|
||||
}}
|
||||
>
|
||||
<Group gap="xs" justify="center">
|
||||
<Text size="sm" fw={500}>
|
||||
{t('update.latestStable', 'Latest Stable')}:
|
||||
</Text>
|
||||
<Text size="sm" fw={600} c="green">
|
||||
{updateSummary.latest_stable_version}
|
||||
</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Recommended action */}
|
||||
{updateSummary.recommended_action && (
|
||||
<Box
|
||||
style={{
|
||||
background: 'var(--mantine-color-blue-light)',
|
||||
padding: '12px 16px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--mantine-color-blue-outline)',
|
||||
}}
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap" align="flex-start">
|
||||
<InfoOutlinedIcon style={{ fontSize: 18, color: 'var(--mantine-color-blue-filled)', marginTop: 2 }} />
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Text size="xs" fw={600} mb={4} tt="uppercase">
|
||||
{t('update.recommendedAction', 'Recommended Action')}
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
{updateSummary.recommended_action}
|
||||
</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Breaking changes warning */}
|
||||
{updateSummary.any_breaking && (
|
||||
<Box
|
||||
style={{
|
||||
background: 'var(--mantine-color-orange-light)',
|
||||
padding: '12px 16px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--mantine-color-orange-outline)',
|
||||
}}
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap" align="flex-start">
|
||||
<WarningAmberIcon style={{ fontSize: 18, color: 'var(--mantine-color-orange-filled)', marginTop: 2 }} />
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Text size="xs" fw={600} mb={4} tt="uppercase">
|
||||
{t('update.breakingChangesDetected', 'Breaking Changes Detected')}
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
{t(
|
||||
'update.breakingChangesMessage',
|
||||
'Some versions contain breaking changes. Please review the migration guides below before updating.'
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Migration guides */}
|
||||
{updateSummary.migration_guides && updateSummary.migration_guides.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<Stack gap="xs">
|
||||
<Text fw={600} size="sm" tt="uppercase" c="dimmed">
|
||||
{t('update.migrationGuides', 'Migration Guides')}
|
||||
</Text>
|
||||
{updateSummary.migration_guides.map((guide, idx) => (
|
||||
<Box
|
||||
key={idx}
|
||||
style={{
|
||||
border: '1px solid var(--mantine-color-gray-3)',
|
||||
padding: '12px 16px',
|
||||
borderRadius: '8px',
|
||||
background: 'var(--mantine-color-gray-0)',
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between" align="center" wrap="nowrap">
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Text fw={600} size="sm">
|
||||
{t('update.version', 'Version')} {guide.version}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mt={4}>
|
||||
{guide.notes}
|
||||
</Text>
|
||||
</Box>
|
||||
<Button
|
||||
component="a"
|
||||
href={guide.url}
|
||||
target="_blank"
|
||||
variant="light"
|
||||
size="xs"
|
||||
rightSection={<OpenInNewIcon style={{ fontSize: 14 }} />}
|
||||
>
|
||||
{t('update.viewGuide', 'View Guide')}
|
||||
</Button>
|
||||
</Group>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Version details */}
|
||||
<Divider />
|
||||
{loading ? (
|
||||
<Center py="xl">
|
||||
<Stack align="center" gap="sm">
|
||||
<Loader size="md" />
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('update.loadingDetailedInfo', 'Loading detailed information...')}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Center>
|
||||
) : fullUpdateInfo && fullUpdateInfo.new_versions && fullUpdateInfo.new_versions.length > 0 ? (
|
||||
<Stack gap="xs">
|
||||
<Group justify="space-between" align="center">
|
||||
<Text fw={600} size="sm" tt="uppercase" c="dimmed">
|
||||
{t('update.availableUpdates', 'Available Updates')}
|
||||
</Text>
|
||||
<Badge variant="light" color="gray">
|
||||
{fullUpdateInfo.new_versions.length} {fullUpdateInfo.new_versions.length === 1 ? 'version' : 'versions'}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Stack gap="xs">
|
||||
{fullUpdateInfo.new_versions.map((version, index) => {
|
||||
const isExpanded = expandedVersions.has(index);
|
||||
return (
|
||||
<Box
|
||||
key={index}
|
||||
style={{
|
||||
border: '1px solid var(--mantine-color-gray-3)',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
justify="space-between"
|
||||
align="center"
|
||||
p="md"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
background: isExpanded ? 'var(--mantine-color-gray-0)' : 'transparent',
|
||||
transition: 'background 0.15s ease',
|
||||
}}
|
||||
onClick={() => toggleVersion(index)}
|
||||
>
|
||||
<Group gap="md" style={{ flex: 1 }}>
|
||||
<Box>
|
||||
<Text fw={600} size="sm" c="dimmed" mb={2}>
|
||||
{t('update.version', 'Version')}
|
||||
</Text>
|
||||
<Text fw={700} size="lg">
|
||||
{version.version}
|
||||
</Text>
|
||||
</Box>
|
||||
<Badge color={getPriorityColor(version.priority)} size="md">
|
||||
{getPriorityLabel(version.priority)}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v${version.version}`}
|
||||
target="_blank"
|
||||
variant="light"
|
||||
size="xs"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
rightSection={<OpenInNewIcon style={{ fontSize: 14 }} />}
|
||||
>
|
||||
{t('update.releaseNotes', 'Release Notes')}
|
||||
</Button>
|
||||
{isExpanded ? (
|
||||
<ExpandLessIcon style={{ fontSize: 20, color: 'var(--mantine-color-gray-6)' }} />
|
||||
) : (
|
||||
<ExpandMoreIcon style={{ fontSize: 20, color: 'var(--mantine-color-gray-6)' }} />
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<Collapse in={isExpanded}>
|
||||
<Box p="md" pt={0} style={{ borderTop: '1px solid var(--mantine-color-gray-2)' }}>
|
||||
<Stack gap="md" mt="md">
|
||||
<Box>
|
||||
<Text fw={600} size="sm" mb={6}>
|
||||
{version.announcement.title}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" style={{ lineHeight: 1.6 }}>
|
||||
{version.announcement.message}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{version.compatibility.breaking_changes && (
|
||||
<Box
|
||||
style={{
|
||||
background: 'var(--mantine-color-orange-light)',
|
||||
padding: '12px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid var(--mantine-color-orange-outline)',
|
||||
}}
|
||||
>
|
||||
<Group gap="xs" align="flex-start" wrap="nowrap" mb="xs">
|
||||
<WarningAmberIcon style={{ fontSize: 16, color: 'var(--mantine-color-orange-filled)', marginTop: 2 }} />
|
||||
<Text size="xs" fw={600} tt="uppercase">
|
||||
{t('update.breakingChanges', 'Breaking Changes')}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text size="sm" mb="xs">
|
||||
{version.compatibility.breaking_description ||
|
||||
t('update.breakingChangesDefault', 'This version contains breaking changes.')}
|
||||
</Text>
|
||||
{version.compatibility.migration_guide_url && (
|
||||
<Button
|
||||
component="a"
|
||||
href={version.compatibility.migration_guide_url}
|
||||
target="_blank"
|
||||
variant="light"
|
||||
color="orange"
|
||||
size="xs"
|
||||
rightSection={<OpenInNewIcon style={{ fontSize: 14 }} />}
|
||||
>
|
||||
{t('update.migrationGuide', 'Migration Guide')}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Stack>
|
||||
) : null}
|
||||
|
||||
{/* Action buttons */}
|
||||
<Divider />
|
||||
<Group justify="flex-end" gap="sm">
|
||||
<Button variant="default" onClick={onClose}>
|
||||
{t('update.close', 'Close')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="light"
|
||||
component="a"
|
||||
href="https://github.com/Stirling-Tools/Stirling-PDF/releases"
|
||||
target="_blank"
|
||||
rightSection={<OpenInNewIcon style={{ fontSize: 16 }} />}
|
||||
>
|
||||
{t('update.viewAllReleases', 'View All Releases')}
|
||||
</Button>
|
||||
{downloadUrl && (
|
||||
<Button
|
||||
component="a"
|
||||
href={downloadUrl}
|
||||
target="_blank"
|
||||
color="green"
|
||||
leftSection={<DownloadIcon style={{ fontSize: 16 }} />}
|
||||
>
|
||||
{t('update.downloadLatest', 'Download Latest')}
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateModal;
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Paper, Stack, Switch, Text, Tooltip, NumberInput, SegmentedControl, Code, Group, Anchor, ActionIcon } from '@mantine/core';
|
||||
import { Paper, Stack, Switch, Text, Tooltip, NumberInput, SegmentedControl, Code, Group, Anchor, ActionIcon, Button, Badge, Alert } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePreferences } from '@app/contexts/PreferencesContext';
|
||||
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
||||
import type { ToolPanelMode } from '@app/constants/toolPanel';
|
||||
import LocalIcon from '@app/components/shared/LocalIcon';
|
||||
import { updateService, UpdateSummary } from '@app/services/updateService';
|
||||
import UpdateModal from '@app/components/shared/UpdateModal';
|
||||
|
||||
const DEFAULT_AUTO_UNZIP_FILE_LIMIT = 4;
|
||||
const BANNER_DISMISSED_KEY = 'stirlingpdf_features_banner_dismissed';
|
||||
@@ -22,12 +24,44 @@ const GeneralSection: React.FC<GeneralSectionProps> = ({ hideTitle = false }) =>
|
||||
// Check localStorage on mount
|
||||
return localStorage.getItem(BANNER_DISMISSED_KEY) === 'true';
|
||||
});
|
||||
const [updateSummary, setUpdateSummary] = useState<UpdateSummary | null>(null);
|
||||
const [updateModalOpened, setUpdateModalOpened] = useState(false);
|
||||
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
||||
|
||||
// Sync local state with preference changes
|
||||
useEffect(() => {
|
||||
setFileLimitInput(preferences.autoUnzipFileLimit);
|
||||
}, [preferences.autoUnzipFileLimit]);
|
||||
|
||||
// Check for updates on mount
|
||||
useEffect(() => {
|
||||
if (config?.appVersion && config?.machineType) {
|
||||
checkForUpdate();
|
||||
}
|
||||
}, [config?.appVersion, config?.machineType]);
|
||||
|
||||
const checkForUpdate = async () => {
|
||||
if (!config?.appVersion || !config?.machineType) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCheckingUpdate(true);
|
||||
const machineInfo = {
|
||||
machineType: config.machineType,
|
||||
activeSecurity: config.activeSecurity ?? false,
|
||||
licenseType: config.license ?? 'NORMAL',
|
||||
};
|
||||
|
||||
const summary = await updateService.getUpdateSummary(config.appVersion, machineInfo);
|
||||
if (summary) {
|
||||
const isNewerVersion = updateService.compareVersions(summary.latest_version, config.appVersion) > 0;
|
||||
if (isNewerVersion) {
|
||||
setUpdateSummary(summary);
|
||||
}
|
||||
}
|
||||
setCheckingUpdate(false);
|
||||
};
|
||||
|
||||
// Check if login is disabled
|
||||
const loginDisabled = !config?.enableLogin;
|
||||
|
||||
@@ -170,6 +204,108 @@ const GeneralSection: React.FC<GeneralSectionProps> = ({ hideTitle = false }) =>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Update Check Section */}
|
||||
{config?.appVersion && (
|
||||
<Paper withBorder p="md" radius="md">
|
||||
<Stack gap="md">
|
||||
<div>
|
||||
<Group justify="space-between" align="center">
|
||||
<div>
|
||||
<Text fw={600} size="sm">
|
||||
{t('settings.general.updates.title', 'Software Updates')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mt={4}>
|
||||
{t('settings.general.updates.description', 'Check for updates and view version information')}
|
||||
</Text>
|
||||
</div>
|
||||
{updateSummary && (
|
||||
<Badge
|
||||
color={updateSummary.max_priority === 'urgent' ? 'red' : 'blue'}
|
||||
variant="filled"
|
||||
>
|
||||
{updateSummary.max_priority === 'urgent'
|
||||
? t('update.urgentUpdateAvailable', 'Urgent Update')
|
||||
: t('update.updateAvailable', 'Update Available')}
|
||||
</Badge>
|
||||
)}
|
||||
</Group>
|
||||
</div>
|
||||
|
||||
<Group justify="space-between" align="center">
|
||||
<div>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('settings.general.updates.currentVersion', 'Current Version')}:{' '}
|
||||
<Text component="span" fw={500}>
|
||||
{config.appVersion}
|
||||
</Text>
|
||||
</Text>
|
||||
{updateSummary && (
|
||||
<Text size="sm" c="dimmed" mt={4}>
|
||||
{t('settings.general.updates.latestVersion', 'Latest Version')}:{' '}
|
||||
<Text component="span" fw={500} c="blue">
|
||||
{updateSummary.latest_version}
|
||||
</Text>
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
<Group gap="sm">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="default"
|
||||
onClick={checkForUpdate}
|
||||
loading={checkingUpdate}
|
||||
leftSection={<LocalIcon icon="refresh-rounded" width="1rem" height="1rem" />}
|
||||
>
|
||||
{t('settings.general.updates.checkForUpdates', 'Check for Updates')}
|
||||
</Button>
|
||||
{updateSummary && (
|
||||
<Button
|
||||
size="sm"
|
||||
color={updateSummary.max_priority === 'urgent' ? 'red' : 'blue'}
|
||||
onClick={() => setUpdateModalOpened(true)}
|
||||
leftSection={<LocalIcon icon="system-update-rounded" width="1rem" height="1rem" />}
|
||||
>
|
||||
{t('settings.general.updates.viewDetails', 'View Details')}
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
{updateSummary?.any_breaking && (
|
||||
<Alert
|
||||
color="orange"
|
||||
title={t('update.breakingChangesDetected', 'Breaking Changes Detected')}
|
||||
styles={{
|
||||
title: { fontWeight: 600 }
|
||||
}}
|
||||
>
|
||||
<Text size="sm">
|
||||
{t(
|
||||
'update.breakingChangesMessage',
|
||||
'Some versions contain breaking changes. Please review the migration guides before updating.'
|
||||
)}
|
||||
</Text>
|
||||
</Alert>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
{/* Update Modal */}
|
||||
{updateSummary && config?.appVersion && config?.machineType && (
|
||||
<UpdateModal
|
||||
opened={updateModalOpened}
|
||||
onClose={() => setUpdateModalOpened(false)}
|
||||
currentVersion={config.appVersion}
|
||||
updateSummary={updateSummary}
|
||||
machineInfo={{
|
||||
machineType: config.machineType,
|
||||
activeSecurity: config.activeSecurity ?? false,
|
||||
licenseType: config.license ?? 'NORMAL',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user