diff --git a/frontend/app/animation/page.tsx b/frontend/app/animation/page.tsx new file mode 100644 index 0000000..15a88fa --- /dev/null +++ b/frontend/app/animation/page.tsx @@ -0,0 +1,13 @@ +import Loading from '../components/Loading'; + +export const metadata = { + title: 'Cluster Options' +}; + +export default async function AnimationPage() { + return ( +
+ +
+ ); +} diff --git a/frontend/app/components/ClusterSettings.jsx b/frontend/app/components/ClusterSettings.jsx index d4e1787..833db7b 100644 --- a/frontend/app/components/ClusterSettings.jsx +++ b/frontend/app/components/ClusterSettings.jsx @@ -7,6 +7,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { FaDatabase, FaUser } from 'react-icons/fa'; import { IoIosArrowForward } from 'react-icons/io'; +import Loading from '../components/Loading.jsx'; export default function ClusterSettingsPage() { const searchParams = useSearchParams(); @@ -350,11 +351,7 @@ export default function ClusterSettingsPage() { } if (loadingClusters) { - return ( -
-

Loading cluster...

-
- ); + return ; } return ( @@ -362,8 +359,8 @@ export default function ClusterSettingsPage() {
{/* Header */} -
-

{cluster.name}

+
+

{cluster.name}

Cluster settings and overview

diff --git a/frontend/app/components/Form.jsx b/frontend/app/components/Form.jsx index 16f853d..ded49db 100644 --- a/frontend/app/components/Form.jsx +++ b/frontend/app/components/Form.jsx @@ -5,6 +5,7 @@ import Cookies from 'js-cookie'; import { useRouter } from 'next/navigation'; import ReactMarkdown from 'react-markdown'; import { FaClipboardList, FaRegCopy, FaCheck, FaLightbulb } from 'react-icons/fa'; +import Loading from '../components/Loading'; function getTodayTimeMs(value) { const numericValue = Number(value); @@ -448,13 +449,7 @@ export default function Form() { } if (!clusterId || !nameID) { - return ( -
-
- Loading report... -
-
- ); + return ; } // DELETE METHOD @@ -545,7 +540,7 @@ export default function Form() {
{/* Header */} -
+

Process Documentation

@@ -891,7 +886,7 @@ export default function Form() { type="button" onClick={() => checkHpcAnswer(optionIndex)} disabled={Boolean(hpcQuestionResult)} - className={`rounded-xl border px-4 py-3 text-left text-sm font-medium transition ${ + className={`rounded-xl border cursor-pointer px-4 py-3 text-left text-sm font-medium transition ${ isSelected ? 'border-cyan-300 bg-cyan-400/20 text-white' : 'border-white/10 bg-slate-900/40 text-slate-200 hover:border-cyan-300/40 hover:bg-cyan-400/10' @@ -922,9 +917,7 @@ export default function Form() { : 'text-red-300' }`} > - {hpcQuestionResult.correct - ? 'Correct ✅' - : 'Not quite ❌'} + {hpcQuestionResult.correct ? 'Correct' : 'Not quite'}

diff --git a/frontend/app/components/Header.jsx b/frontend/app/components/Header.jsx index 87ac385..d821c07 100644 --- a/frontend/app/components/Header.jsx +++ b/frontend/app/components/Header.jsx @@ -124,7 +124,7 @@ export default function Header() { aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'} aria-expanded={mobileMenuOpen} onClick={() => setMobileMenuOpen((isOpen) => !isOpen)} - className="flex h-10 w-10 cursor-pointer items-center justify-center rounded-xl border border-white/10 bg-white/10 text-2xl text-slate-100 transition hover:border-blue-300/35 hover:bg-blue-500/15 lg:hidden" + className="flex h-10 w-10 cursor-pointer items-center justify-center rounded-xl text-2xl text-slate-100 transition hover:border-blue-300/35 hover:bg-blue-500/15 lg:hidden" > {mobileMenuOpen ?

+
+ +

Loading ...

+
+
+ ); +} diff --git a/frontend/app/components/Name.jsx b/frontend/app/components/Name.jsx index f7c81ec..5b7fc20 100644 --- a/frontend/app/components/Name.jsx +++ b/frontend/app/components/Name.jsx @@ -73,53 +73,6 @@ export default function Name() {
{/* Header */} - {/* -
- - - -
- - {people.find((p) => p.id === selectedId)?.name || - 'Select a person...'} - - - ▼ - - - - - {people.map((person) => ( - - {person.name} - - ))} - -
-
-
- - - */} - {!selectedTeamId ? (
diff --git a/frontend/app/components/Options.jsx b/frontend/app/components/Options.jsx index 08f4c5a..f34c11f 100644 --- a/frontend/app/components/Options.jsx +++ b/frontend/app/components/Options.jsx @@ -389,7 +389,7 @@ export default function Options() {
{/* Header */} -
+

Administration

diff --git a/frontend/app/components/Overview.jsx b/frontend/app/components/Overview.jsx index 0380030..0944ee1 100644 --- a/frontend/app/components/Overview.jsx +++ b/frontend/app/components/Overview.jsx @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'; import DatePicker from 'react-datepicker'; import { FaCalendarAlt } from 'react-icons/fa'; +import Loading from '../components/Loading'; function dateToInputValue(date) { if (!date) return ''; @@ -87,11 +88,7 @@ export default function Overview() { // Show loading if (!reportLoaded) { - return ( -

- Loading overview... -
- ); + return ; } // Page @@ -100,7 +97,7 @@ export default function Overview() {
{/* Header */} -
+

Reports Overview

diff --git a/frontend/app/components/PersonalSchedule.jsx b/frontend/app/components/PersonalSchedule.jsx index de4f58b..12e4ec1 100644 --- a/frontend/app/components/PersonalSchedule.jsx +++ b/frontend/app/components/PersonalSchedule.jsx @@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'; import Cookies from 'js-cookie'; import { FaFolderOpen, FaInbox, FaUser } from 'react-icons/fa'; import { IoIosArrowForward } from 'react-icons/io'; +import Loading from '../components/Loading'; const CHECK_START_BUFFER_MS = 20 * 60 * 1000; @@ -191,13 +192,7 @@ export default function PersonalSchedule() { }, [router, userId]); if (loading) { - return ( -

-
- Loading clusters... -
-
- ); + return ; } const handleClusterClick = (cluster) => { @@ -212,7 +207,7 @@ export default function PersonalSchedule() {
{/* Header */} -
+

{name}'s Clusters

diff --git a/frontend/app/components/PoolSettingsPage.jsx b/frontend/app/components/PoolSettingsPage.jsx index 86027eb..e9c03c7 100644 --- a/frontend/app/components/PoolSettingsPage.jsx +++ b/frontend/app/components/PoolSettingsPage.jsx @@ -3,8 +3,7 @@ import { useEffect, useState } from 'react'; import { FaServer } from 'react-icons/fa'; import { useSearchParams } from 'next/navigation'; - -import ReportSkeleton from './PoolSkeleton'; +import Loading from './Loading'; export default function PoolClustersPage() { const searchParams = useSearchParams(); @@ -120,7 +119,7 @@ export default function PoolClustersPage() { } if (loading) { - return ; + return ; } if (!pool) { @@ -139,8 +138,8 @@ export default function PoolClustersPage() {
-
-

{pool.name}

+
+

{pool.name}

Manage clusters assigned to this pool. diff --git a/frontend/app/components/Report.jsx b/frontend/app/components/Report.jsx index cd0e814..a7b284b 100644 --- a/frontend/app/components/Report.jsx +++ b/frontend/app/components/Report.jsx @@ -226,7 +226,7 @@ export default function Report() { {/* Desktop */}

- +
diff --git a/frontend/app/components/Results.jsx b/frontend/app/components/Results.jsx index 0bf6edb..f6d46a8 100644 --- a/frontend/app/components/Results.jsx +++ b/frontend/app/components/Results.jsx @@ -7,6 +7,7 @@ import { FaDatabase, FaUser } from 'react-icons/fa'; import { IoIosArrowForward } from 'react-icons/io'; import DatePicker from 'react-datepicker'; import { FaCalendarAlt } from 'react-icons/fa'; +import Loading from './Loading'; function dateToInputValue(date) { if (!date) return ''; @@ -194,11 +195,7 @@ export default function ResultsPage() { }, [clusters, selectedClusterId]); if (!clustersLoaded) { - return ( -
- Loading clusters... -
- ); + return ; } return ( @@ -206,7 +203,7 @@ export default function ResultsPage() {
{/* HEADER */} -
+

Report Explorer

Find reports by cluster or by week.

diff --git a/frontend/app/components/Schedule.jsx b/frontend/app/components/Schedule.jsx index 5310215..6fbf274 100644 --- a/frontend/app/components/Schedule.jsx +++ b/frontend/app/components/Schedule.jsx @@ -5,6 +5,7 @@ import { IoSwapHorizontal } from 'react-icons/io5'; import { IoIosArrowForward, IoMdDoneAll } from 'react-icons/io'; import { BsThreeDots } from 'react-icons/bs'; import { IoCloseSharp } from 'react-icons/io5'; +import Loading from './Loading'; function formatDateParam(date) { const year = date.getFullYear(); @@ -196,13 +197,7 @@ export default function Schedule() { }, [swapTarget]); if (loading || !schedule) { - return ( -
-
- Loading schedule... -
-
- ); + return ; } return ( @@ -211,7 +206,7 @@ export default function Schedule() { {/* Header */}
-

Weekly Schedule

+

Weekly Schedule

Week beginning {formattedWeekBeginning}

diff --git a/frontend/app/components/TeamsSettingsPage.jsx b/frontend/app/components/TeamsSettingsPage.jsx index 21a16b2..1fe9ef6 100644 --- a/frontend/app/components/TeamsSettingsPage.jsx +++ b/frontend/app/components/TeamsSettingsPage.jsx @@ -6,6 +6,7 @@ import Link from 'next/link'; import { Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/react'; import { FaLayerGroup, FaUser, FaUsers } from 'react-icons/fa'; import { IoIosSettings } from 'react-icons/io'; +import Loading from '../components/Loading'; function timeInputToInt(value) { if (!value) return null; @@ -154,7 +155,7 @@ export default function TeamSettingsPage() { if (!team) { return (
-
+

Team not found

No team exists with ID: {teamId}

@@ -375,11 +376,7 @@ export default function TeamSettingsPage() { }; if (loadingTeams) { - return ( -
-

Loading team...

-
- ); + return ; } if (!team) { @@ -394,8 +391,8 @@ export default function TeamSettingsPage() {
-
-

{team.name}

+
+

{team.name}

Manage users and pools assigned to this team. diff --git a/frontend/app/globals.css b/frontend/app/globals.css index c9c981a..c256eec 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -84,3 +84,44 @@ body { .results-datepicker .react-datepicker__triangle { display: none; } +@keyframes spinQuarterPause { + 0% { + transform: rotate(0deg); + } + + 10% { + transform: rotate(90deg); + } + + 25% { + transform: rotate(90deg); + } + + 35% { + transform: rotate(180deg); + } + + 50% { + transform: rotate(180deg); + } + + 60% { + transform: rotate(270deg); + } + + 75% { + transform: rotate(270deg); + } + + 85% { + transform: rotate(360deg); + } + + 100% { + transform: rotate(360deg); + } +} + +.animate-spin-quarter-pause { + animation: spinQuarterPause 6s ease-out infinite; +}

Outcome