mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
applying loader on pages
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { IconChevronRight } from '@tabler/icons-react';
|
||||
import { useGetAssignedToMeQuery, useGetQueueQuery } from '@ema-platform/api';
|
||||
import { AdvancedTable, useServerTable } from '@ema-platform/ui';
|
||||
import { AdvancedTable, PageLoader, useServerTable } from '@ema-platform/ui';
|
||||
import { dashboardQueueColumns } from './columns';
|
||||
|
||||
/**
|
||||
@@ -28,11 +28,7 @@ export function DashboardPage() {
|
||||
const table = useServerTable();
|
||||
|
||||
if (queue.isLoading || mine.isLoading) {
|
||||
return (
|
||||
<Center h={300}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Loading Backoffice Dashboard…" height={400} />;
|
||||
}
|
||||
|
||||
const unclaimed = queue.data?.items ?? [];
|
||||
|
||||
@@ -54,6 +54,7 @@ import { QuestionAssigner } from '../components/QuestionAssigner';
|
||||
import { RecordResultModal } from '../../result/components/RecordResultModal';
|
||||
import { ExamCandidatesPanel } from '../components/ExamCandidatesPanel';
|
||||
import { ExamIncidentsPanel } from '../components/ExamIncidentsPanel';
|
||||
import { PageLoader } from '@ema-platform/ui';
|
||||
import type { ExamStatus, QuestionBrief } from '../types/exam';
|
||||
|
||||
const STATUS_COLOR: Record<string, string> = {
|
||||
@@ -129,11 +130,7 @@ export function ExamDetailPage() {
|
||||
}, [allQuestions, exam?.certificationId, exam?.form]);
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<Center py="xl">
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Loading Exam Details…" height={400} />;
|
||||
if (isError || !exam) {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
|
||||
@@ -31,7 +31,12 @@ import {
|
||||
useGetMySeaTimeQuery,
|
||||
} from '@ema-platform/api';
|
||||
import { useCurrentProfile, usePermissions } from '@ema-platform/auth';
|
||||
import { AdvancedTable, notify, useServerTable } from '@ema-platform/ui';
|
||||
import {
|
||||
AdvancedTable,
|
||||
PageLoader,
|
||||
notify,
|
||||
useServerTable,
|
||||
} from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
import { certificateColumns } from './columns';
|
||||
|
||||
@@ -107,11 +112,7 @@ export function CertificatesPage() {
|
||||
}
|
||||
|
||||
if (loadingProfile || loadingApplications) {
|
||||
return (
|
||||
<Group justify="center" py="xl">
|
||||
<Loader />
|
||||
</Group>
|
||||
);
|
||||
return <PageLoader label="Loading Certificates…" height={400} />;
|
||||
}
|
||||
|
||||
const pagedIssued = issuedTable.paginate(issued);
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
useGetMyApplicationsQuery,
|
||||
useGetMyLicensesQuery,
|
||||
} from '@ema-platform/api';
|
||||
import { AdvancedTable, useServerTable } from '@ema-platform/ui';
|
||||
import { AdvancedTable, PageLoader, useServerTable } from '@ema-platform/ui';
|
||||
import type { IssuedLicense, LicenseApplication } from '@ema-platform/api';
|
||||
import { LicenseCatalogue } from '../../../licensing/components/LicenseCatalogue';
|
||||
import { LicenseCard, useRenewLicense } from '../../../licensing/components/LicenseCard';
|
||||
@@ -108,11 +108,7 @@ export function DashboardPage() {
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Center h={400}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Loading Dashboard…" height={450} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
useGetMyLicensesQuery,
|
||||
} from '@ema-platform/api';
|
||||
import { useCurrentProfile } from '@ema-platform/auth';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { PageLoader, notify } from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
|
||||
const ENDORSEMENT_TYPE_KEYS = ['ENDORSEMENT_COC', 'ENDORSEMENT_GOC'];
|
||||
@@ -94,11 +94,7 @@ export function EndorsementPage() {
|
||||
}
|
||||
|
||||
if (loadingProfile || loadingApplications) {
|
||||
return (
|
||||
<Group justify="center" py="xl">
|
||||
<Loader />
|
||||
</Group>
|
||||
);
|
||||
return <PageLoader label="Loading Endorsements…" height={400} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { IconClipboardList } from '@tabler/icons-react';
|
||||
import { AdvancedTable, notify, useServerTable } from '@ema-platform/ui';
|
||||
import { AdvancedTable, PageLoader, notify, useServerTable } from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
import {
|
||||
useApiQuery,
|
||||
@@ -178,11 +178,7 @@ export function ExamsPage() {
|
||||
};
|
||||
|
||||
if (loadingOpen || loadingMine || loadingResults) {
|
||||
return (
|
||||
<Group justify="center" py="xl">
|
||||
<Loader />
|
||||
</Group>
|
||||
);
|
||||
return <PageLoader label="Loading Exam Schedule…" height={400} />;
|
||||
}
|
||||
|
||||
const pagedRegistrations = registrationTable.paginate(mine ?? []);
|
||||
|
||||
@@ -53,7 +53,7 @@ import {
|
||||
type ValidationIssue,
|
||||
type Vessel,
|
||||
} from '@ema-platform/api';
|
||||
import { getCountryCode, getCountryName, ModalFooter } from '@ema-platform/ui';
|
||||
import { getCountryCode, getCountryName, ModalFooter, PageLoader } from '@ema-platform/ui';
|
||||
import {
|
||||
LICENSE_PERMISSIONS,
|
||||
PORTAL_PERMISSIONS,
|
||||
@@ -212,11 +212,7 @@ export function LicenseApplicationPage() {
|
||||
);
|
||||
|
||||
if (loadingConfig || !config || !appId || !application) {
|
||||
return (
|
||||
<Center h={400}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Loading Application…" height={400} />;
|
||||
}
|
||||
|
||||
const readOnly = !['DRAFT', 'RESUBMIT_REQUIRED'].includes(application.status);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
import { Center, Loader } from '@mantine/core';
|
||||
import { useGetMyOperatorTypesQuery } from '@ema-platform/api';
|
||||
import { PageLoader } from '@ema-platform/ui';
|
||||
|
||||
/**
|
||||
* Sends an applicant who has not said what they operate as to the step that
|
||||
@@ -75,11 +75,7 @@ export function RequireOperations({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Center h={200}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Checking operations profile…" height={350} />;
|
||||
}
|
||||
|
||||
// A failed lookup must not lock anyone out of the portal — the server still
|
||||
@@ -93,11 +89,7 @@ export function RequireOperations({ children }: { children: React.ReactNode }) {
|
||||
// the same tick; deciding on the pre-save cache bounced the applicant
|
||||
// straight back to the screen they had just completed.
|
||||
if (isFetching) {
|
||||
return (
|
||||
<Center h={200}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Checking operations profile…" height={350} />;
|
||||
}
|
||||
return <Navigate to="/onboarding/operations" replace />;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
||||
import { Center, Loader } from '@mantine/core';
|
||||
import { Navigate, useLocation, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { PageLoader, notify } from '@ema-platform/ui';
|
||||
import {
|
||||
PROFILE_FIELD_SECTION,
|
||||
useCurrentProfile,
|
||||
@@ -75,11 +75,7 @@ export function RequireSeafarerProfile({ children }: { children: React.ReactNode
|
||||
if (!gated) return <>{children}</>;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Center h={200}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Checking seafarer profile…" height={350} />;
|
||||
}
|
||||
|
||||
// A failed lookup must not lock anyone out — the server still refuses the
|
||||
@@ -93,11 +89,7 @@ export function RequireSeafarerProfile({ children }: { children: React.ReactNode
|
||||
// headed back here in the same tick — deciding on the pre-save cache would
|
||||
// bounce them off the screen they just finished.
|
||||
if (isFetching) {
|
||||
return (
|
||||
<Center h={200}>
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
return <PageLoader label="Checking seafarer profile…" height={350} />;
|
||||
}
|
||||
|
||||
const target = PROFILE_FIELD_SECTION[gaps[0]];
|
||||
|
||||
@@ -71,7 +71,7 @@ export function AuthBootstrap({ children }: { children: ReactNode }) {
|
||||
|
||||
// Rendering the router before the session resolves would let the guards
|
||||
// redirect based on a state that is about to change.
|
||||
if (!ready) return <PageLoader />;
|
||||
if (!ready) return <PageLoader label="Authenticating Maritime Session…" height="100vh" />;
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -1,31 +1,47 @@
|
||||
import { Button, createTheme, Loader, type MantineLoaderComponent } from '@mantine/core';
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
createTheme,
|
||||
Input,
|
||||
Loader,
|
||||
Select,
|
||||
TextInput,
|
||||
type MantineLoaderComponent,
|
||||
} from '@mantine/core';
|
||||
import { MaritimeLoader } from '../components/MaritimeLoader';
|
||||
|
||||
/**
|
||||
* Registers the branded ship loader as the default Mantine `Loader` type, and
|
||||
* opts `Button`'s inline loading state back out to the stock oval — a 1.88:1
|
||||
* ship does not fit a button's loader slot.
|
||||
*
|
||||
* Merge into an app theme with `mergeThemeOverrides(appTheme, maritimeLoaderTheme)`
|
||||
* rather than importing this in `libs/shared`: `libs/ui` already depends on
|
||||
* `libs/shared`, so the reverse import would be circular.
|
||||
* Registers the branded Ethiopian Maritime Authority loader as the default Mantine `Loader` type,
|
||||
* sets prominent 80px size for page/section loaders, and opts inline control loaders
|
||||
* (buttons, inputs, select fields) back out to the compact oval spinner.
|
||||
*/
|
||||
export const maritimeLoaderTheme = createTheme({
|
||||
components: {
|
||||
Loader: Loader.extend({
|
||||
defaultProps: {
|
||||
// Mantine types custom loaders as ForwardRefExoticComponent; a plain
|
||||
// function component satisfies the runtime contract (it forwards a
|
||||
// ref as a normal prop under React 19) but not the structural type.
|
||||
loaders: {
|
||||
...Loader.defaultLoaders,
|
||||
maritime: MaritimeLoader as unknown as MantineLoaderComponent,
|
||||
},
|
||||
type: 'maritime',
|
||||
size: 80,
|
||||
},
|
||||
}),
|
||||
Button: Button.extend({
|
||||
defaultProps: { loaderProps: { type: 'oval' } },
|
||||
}),
|
||||
ActionIcon: ActionIcon.extend({
|
||||
defaultProps: { loaderProps: { type: 'oval' } },
|
||||
}),
|
||||
Input: Input.extend({
|
||||
defaultProps: { loaderProps: { type: 'oval' } },
|
||||
}),
|
||||
TextInput: TextInput.extend({
|
||||
defaultProps: { loaderProps: { type: 'oval' } },
|
||||
}),
|
||||
Select: Select.extend({
|
||||
defaultProps: { loaderProps: { type: 'oval' } },
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user