diff --git a/.gitignore b/.gitignore index f3e8370ad3574b2a6ff4b07aec0bcc28f278ee56..b6fc8ae02d3cde0017b1e32d6b43a7e2b88d6482 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ # local environment /frontend/local.env +/.vscode # misc .DS_Store diff --git a/frontend/src/modules/dashboard/Dashboard.tsx b/frontend/src/modules/dashboard/Dashboard.tsx index 20a5b548739eafb483975890f3a8de621a09fb78..434686102b0d1f33821ee52e3743fb74dda106ce 100644 --- a/frontend/src/modules/dashboard/Dashboard.tsx +++ b/frontend/src/modules/dashboard/Dashboard.tsx @@ -10,20 +10,7 @@ import React, { useEffect } from 'react'; import { useApps } from 'src/services/apps'; import { useSysInfo } from 'src/services/sysInfo'; import { AppStatusEnum } from 'src/services/apps/types'; -import { Popover, Transition } from '@headlessui/react'; -import { - CheckCircleIcon, - NewspaperIcon, - SparklesIcon, - LightningBoltIcon, - ChevronDownIcon, - CalendarIcon, - BeakerIcon, - HomeIcon, - RefreshIcon, -} from '@heroicons/react/outline'; -// import systemInfo from './systemInfo.json'; -import { DashboardCard, DashboardUtility } from './components'; +import { DashboardCard, DashboardUtility, UpdateAlert, VersionInfo } from './components'; import { DASHBOARD_QUICK_ACCESS, HIDDEN_APPS, UTILITY_APPS } from './consts'; export const Dashboard: React.FC = () => { @@ -43,140 +30,19 @@ export const Dashboard: React.FC = () => { const appVersions = { ...sysInfo.sysInfo.appVersions }; - const updateTime: Date = new Date(sysInfo.sysInfo.lastUpdated); - const hoursSinceUpdate = Math.floor((new Date().getTime() - updateTime.getTime()) / 3600000); - - // manual toggles to test alert bar - // const hoursSinceUpdate = 21; - - // const lastRelease = sysInfo.sysInfo.lastRelease; - - const updateStatus = - sysInfo.sysInfo.lastRelease > sysInfo.sysInfo.version ? 'imminent' : hoursSinceUpdate <= 24 ? 'updated' : 'no'; - - // the update alert box has two states: when the latest version - // on the v2 branch is more recent than the current one, the yellow alert bar - // shows that an update will happen in the next 24 hours. - // After the update, a green alert bar shows with a success message - // and stays there for 24 hours - const updateAlert = (status: string) => - status === 'imminent' ? ( - <div className="update-alert w-full h-5 py-3 bg-yellow-100 flex items-center justify-center text-xs font-medium text-gray-500 gap-2"> - <LightningBoltIcon className="h-4 w-4 text-primary-800" /> - <p>Attention: your Stackspin instance will be updated in the next 24 hours. </p> - <a - className="hover:text-primary-500 underline" - href={sysInfo.sysInfo.releaseNotesUrl} - target="_blank" - rel="noreferrer noopener" - > - Explore new features <NewspaperIcon className="h-4 w-4 inline" /> - </a> - </div> - ) : status === 'updated' ? ( - <div className="update-alert w-full h-5 py-3 bg-primary-200 flex items-center justify-center text-xs font-medium text-primary-700 gap-2"> - <SparklesIcon className="h-4 w-4 text-primary-800" /> - <p>Your Stackspin just got an update!</p> - <a - className="hover:text-primary-500 underline" - href={sysInfo.sysInfo.releaseNotesUrl} - target="_blank" - rel="noreferrer noopener" - > - See what's new <NewspaperIcon className="h-4 w-4 inline" /> - </a> - </div> - ) : null; - - const branch = sysInfo.sysInfo.followingGit; - - // eslint-disable-next-line - // let branch = 'DF234FD'; - const knownMainBranch = 'v2'; - - const versionInfo = - branch === knownMainBranch ? ( - <> - <CheckCircleIcon className="h-4 w-4 text-primary-600" /> - <span>Stackspin v{sysInfo.sysInfo.version}</span> - </> - ) : branch !== knownMainBranch ? ( - <> - <span>Stackspin v{sysInfo.sysInfo.version}</span> - </> - ) : null; - - const updatesText = - branch === knownMainBranch ? ( - <> - <div className="px-4 py-2 flex items-center gap-1"> - <HomeIcon className="w-4 h-4 text-gray-500" /> - <span>Using main branch ({branch})</span> - </div> - <div className="px-4 py-2 flex items-center gap-1"> - <RefreshIcon className="w-4 h-4 text-gray-500" /> - <span>Automatic updates active</span> - </div> - </> - ) : branch !== knownMainBranch ? ( - <div className="px-4 py-2 flex items-center gap-1"> - <BeakerIcon className="w-4 h-4 text-gray-500" /> - <span>Custom branch ({branch})</span> - </div> - ) : null; - return ( <div className="relative"> - {updateAlert(updateStatus)} + <UpdateAlert sysInfo={sysInfo.sysInfo} /> <div className="max-w-7xl mx-auto py-4 px-3 sm:px-6 lg:px-8"> <div className="mt-6 pb-5 border-b border-gray-200 sm:flex sm:items-center sm:justify-between"> <h1 className="text-3xl leading-6 font-bold text-gray-900">Dashboard</h1> <div className="system-status text-xs font-medium text-gray-500 flex flex-col gap-2"> <div className="flex items-center gap-1"> - <Popover className="relative flex items-center"> - <Popover.Button className="flex text-sm px-2.5 py-1.5 group items-center gap-1 border border-transparent hover:shadow-sm focus:shadow-sm font-medium rounded-md text-gray-500 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"> - {versionInfo} - <ChevronDownIcon className="h-4 w-4 text-primary-800" /> - </Popover.Button> - <Transition - enter="transition ease-out duration-200" - enterFrom="transform opacity-0 scale-95" - enterTo="transform opacity-100 scale-100" - leave="transition ease-in duration-75" - leaveFrom="transform opacity-100 scale-100" - leaveTo="transform opacity-0 scale-95" - > - <Popover.Panel className="absolute z-10 origin-top-right right-0 top-5 mt-2 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"> - <div className="flex flex-col items-stretch p-4 w-60 divide-y divide-gray-100 text-xs text-gray-500"> - {updatesText} - <div className="px-4 py-2 flex items-center gap-1"> - <NewspaperIcon className="h-4 w-4 text-gray-500" /> - <a - className="hover:text-primary-500 underline" - href={sysInfo.sysInfo.releaseNotesUrl} - target="_blank" - rel="noreferrer noopener" - > - Changelog - </a> - </div> - <div className="px-4 py-2 flex items-center gap-1"> - <CalendarIcon className="h-4 w-4 text-gray-500" /> - <span>Last update:</span> - <span> - {/* {updateTime.getDate()}.{updateTime.getMonth()} {updateTime.getFullYear()} */} - {updateTime.toLocaleDateString('en-uk', { day: 'numeric', year: 'numeric', month: 'short' })} - </span> - </div> - </div> - </Popover.Panel> - </Transition> - </Popover> + <VersionInfo sysInfo={sysInfo.sysInfo} /> </div> </div> </div> </div> - <div className="max-w-7xl mx-auto py-4 px-3 sm:px-6 lg:px-8 h-full flex-grow"> <div className="grid grid-cols-1 md:grid-cols-2 md:gap-4 lg:grid-cols-4 mb-10"> {apps @@ -191,7 +57,6 @@ export const Dashboard: React.FC = () => { <div className="pb-4 border-b border-gray-200 sm:flex sm:items-center"> <h3 className="text-lg leading-6 font-medium text-gray-900">Utilities</h3> </div> - <dl className="mt-5 grid grid-cols-1 gap-2 sm:grid-cols-2"> {DASHBOARD_QUICK_ACCESS.map((item) => ( <DashboardUtility item={item} key={item.name} /> diff --git a/frontend/src/modules/dashboard/components/UpdateAlert/UpdateAlert.tsx b/frontend/src/modules/dashboard/components/UpdateAlert/UpdateAlert.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d2407196bd80d754a18c3cb108632b9c724c36ed --- /dev/null +++ b/frontend/src/modules/dashboard/components/UpdateAlert/UpdateAlert.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { LightningBoltIcon, NewspaperIcon, SparklesIcon } from '@heroicons/react/outline'; +import { SysInfoState } from 'src/services/sysInfo/redux/types'; + +export const UpdateAlert = (sysInfo: SysInfoState) => { + const updateTime: Date = new Date(sysInfo.sysInfo.lastUpdated); + const hoursSinceUpdate = Math.floor((new Date().getTime() - updateTime.getTime()) / 3600000); + const updateStatus: string = + sysInfo.sysInfo.lastRelease > sysInfo.sysInfo.version ? 'imminent' : hoursSinceUpdate <= 24 ? 'updated' : 'no'; + + const updateAlert = (status: string) => + status === 'imminent' ? ( + <div className="update-alert w-full h-5 py-3 bg-yellow-100 flex items-center justify-center text-xs font-medium text-gray-500 gap-2"> + <LightningBoltIcon className="h-4 w-4 text-primary-800" /> + <p>Attention: your Stackspin instance will be updated in the next 24 hours. </p> + <a + className="hover:text-primary-500 underline" + href={sysInfo.sysInfo.releaseNotesUrl} + target="_blank" + rel="noreferrer noopener" + > + Explore new features <NewspaperIcon className="h-4 w-4 inline" /> + </a> + </div> + ) : status === 'updated' ? ( + <div className="update-alert w-full h-5 py-3 bg-primary-200 flex items-center justify-center text-xs font-medium text-primary-700 gap-2"> + <SparklesIcon className="h-4 w-4 text-primary-800" /> + <p>Your Stackspin just got an update!</p> + <a + className="hover:text-primary-500 underline" + href={sysInfo.sysInfo.releaseNotesUrl} + target="_blank" + rel="noreferrer noopener" + > + See what's new <NewspaperIcon className="h-4 w-4 inline" /> + </a> + </div> + ) : null; + + return <>{updateAlert('imminent')}</>; +}; diff --git a/frontend/src/modules/dashboard/components/UpdateAlert/index.ts b/frontend/src/modules/dashboard/components/UpdateAlert/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a64428b088ac4da2792f2933045eae67d299392 --- /dev/null +++ b/frontend/src/modules/dashboard/components/UpdateAlert/index.ts @@ -0,0 +1 @@ +export { UpdateAlert } from './UpdateAlert'; diff --git a/frontend/src/modules/dashboard/components/VersionInfo/VersionInfo.tsx b/frontend/src/modules/dashboard/components/VersionInfo/VersionInfo.tsx new file mode 100644 index 0000000000000000000000000000000000000000..20e6246bf9fa1c5aef879b2b7d2a1e7dd9eb9a1d --- /dev/null +++ b/frontend/src/modules/dashboard/components/VersionInfo/VersionInfo.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { Popover, Transition } from '@headlessui/react'; +import { + CheckCircleIcon, + NewspaperIcon, + ChevronDownIcon, + CalendarIcon, + BeakerIcon, + HomeIcon, + RefreshIcon, +} from '@heroicons/react/outline'; +import { SysInfoState } from 'src/services/sysInfo/redux/types'; + +export const VersionInfo = (sysInfo: SysInfoState) => { + const branch = sysInfo.sysInfo.followingGit; + const updateTime: Date = new Date(sysInfo.sysInfo.lastUpdated); + + // eslint-disable-next-line + // let branch = 'DF234FD'; + const knownMainBranch = 'v2'; + + const versionInfo = + branch === knownMainBranch ? ( + <> + <CheckCircleIcon className="h-4 w-4 text-primary-600" /> + <span>Stackspin v{sysInfo.sysInfo.version}</span> + </> + ) : branch !== knownMainBranch ? ( + <> + <span>Stackspin v{sysInfo.sysInfo.version}</span> + </> + ) : null; + + const updatesText = + branch === knownMainBranch ? ( + <> + <div className="px-4 py-2 flex items-center gap-1"> + <HomeIcon className="w-4 h-4 text-gray-500" /> + <span>Using main branch ({branch})</span> + </div> + <div className="px-4 py-2 flex items-center gap-1"> + <RefreshIcon className="w-4 h-4 text-gray-500" /> + <span>Automatic updates active</span> + </div> + </> + ) : branch !== knownMainBranch ? ( + <div className="px-4 py-2 flex items-center gap-1"> + <BeakerIcon className="w-4 h-4 text-gray-500" /> + <span>Custom branch ({branch})</span> + </div> + ) : null; + + return ( + <Popover className="relative flex items-center"> + <Popover.Button className="flex text-sm px-2.5 py-1.5 group items-center gap-1 border border-transparent hover:shadow-sm focus:shadow-sm font-medium rounded-md text-gray-500 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"> + {versionInfo} + <ChevronDownIcon className="h-4 w-4 text-primary-800" /> + </Popover.Button> + <Transition + enter="transition ease-out duration-200" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <Popover.Panel className="absolute z-10 origin-top-right right-0 top-5 mt-2 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"> + <div className="flex flex-col items-stretch p-4 w-60 divide-y divide-gray-100 text-xs text-gray-500"> + {updatesText} + <div className="px-4 py-2 flex items-center gap-1"> + <NewspaperIcon className="h-4 w-4 text-gray-500" /> + <a + className="hover:text-primary-500 underline" + href={sysInfo.sysInfo.releaseNotesUrl} + target="_blank" + rel="noreferrer noopener" + > + Changelog + </a> + </div> + <div className="px-4 py-2 flex items-center gap-1"> + <CalendarIcon className="h-4 w-4 text-gray-500" /> + <span>Last update:</span> + <span> + {/* {updateTime.getDate()}.{updateTime.getMonth()} {updateTime.getFullYear()} */} + {updateTime.toLocaleDateString('en-uk', { day: 'numeric', year: 'numeric', month: 'short' })} + </span> + </div> + </div> + </Popover.Panel> + </Transition> + </Popover> + ); +}; diff --git a/frontend/src/modules/dashboard/components/VersionInfo/index.ts b/frontend/src/modules/dashboard/components/VersionInfo/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d573df80cdd98623869aadf0b1e303a3d0bba112 --- /dev/null +++ b/frontend/src/modules/dashboard/components/VersionInfo/index.ts @@ -0,0 +1 @@ +export { VersionInfo } from './VersionInfo'; diff --git a/frontend/src/modules/dashboard/components/index.ts b/frontend/src/modules/dashboard/components/index.ts index 2a1468292e7106bd4f50c42b37e8f42a27ed45d4..db323609b13ba45e746f672df221d368a67cb949 100644 --- a/frontend/src/modules/dashboard/components/index.ts +++ b/frontend/src/modules/dashboard/components/index.ts @@ -1,2 +1,4 @@ export { DashboardCard } from './DashboardCard'; export { DashboardUtility } from './DashboardUtility'; +export { UpdateAlert } from './UpdateAlert'; +export { VersionInfo } from './VersionInfo'; diff --git a/frontend/src/modules/dashboard/systemInfo.json b/frontend/src/modules/dashboard/systemInfo.json deleted file mode 100644 index 70b1928d0523cbc17a4e9d3e440f38038f5bcfd2..0000000000000000000000000000000000000000 --- a/frontend/src/modules/dashboard/systemInfo.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "appVersions": { - "dashboard": "0.6.5", - "nextcloud": "25.0.4", - "velero": "1.9.4", - "wekan": "5.93", - "wordpress": "6.0.1", - "zulip": "6.0", - "hedgedoc": "4.2" - }, - "lastRelease": "2.4", - "lastUpdated": "2023-03-16T11:41:24Z", - "releaseNotesUrl": "https://open.greenhost.net/stackspin/stackspin/-/blob/main/CHANGELOG.md#anchor-24", - "version": "2.4" -}