80 lines
3.3 KiB
JavaScript
80 lines
3.3 KiB
JavaScript
import React, { useMemo } from 'react';
|
|
|
|
import { useApplications, useJobPostings, useStaff } from '@/lib/krowHooks';
|
|
import { AdminPage } from '@/components/admin/PageShell';
|
|
import { UiTreeRenderer } from '@/components/ui-tree/UiTreeRenderer';
|
|
import { useUiEditing } from '@/components/ui-tree/UiEditingProvider';
|
|
import { UiEditor } from '@/components/ui-editor/UiEditor';
|
|
import { composePage } from '@/lib/ui/composition';
|
|
import '@/components/ui-tree/nodeTypes';
|
|
import '@/pages/admin/analytics/nodes';
|
|
import {
|
|
buildEfficiency, buildFunnel, buildInsights, buildTrend, byDepartment, byPosition,
|
|
hiresWithFill, summarise,
|
|
} from '@/lib/hiringRecords';
|
|
|
|
/**
|
|
* Admin Analytics — how hiring is performing.
|
|
*
|
|
* This page and Hired History used to be the same component rendered twice with
|
|
* a different title, which is why they read as duplicates: identical sections,
|
|
* identical tabs, identical figures. They are not the same question.
|
|
*
|
|
* **Analytics asks "how is our hiring performing?"** — it is metrics, trends,
|
|
* comparison and what to do about them. Every section here is an aggregate: a
|
|
* rate, a distribution, a shape over time. No individual hire is named anywhere
|
|
* on this page, because a name is a record and records are Hired History's job.
|
|
*
|
|
* It is also structurally different, not just differently coloured. Analytics is
|
|
* one scrolling dashboard — the seven readings are meant to be taken together,
|
|
* and a comparison you have to remember across a tab switch is not a comparison.
|
|
* Hired History is a filtered record you search, so it keeps its controls and its
|
|
* table. Same design system, same blue, same cards; different jobs.
|
|
*
|
|
* Both read `lib/hiringRecords.js`, so a total here and the same total there
|
|
* cannot disagree.
|
|
*/
|
|
|
|
export default function AdminAnalytics() {
|
|
const { data: staff = [], isLoading } = useStaff();
|
|
const { data: applications = [] } = useApplications();
|
|
const { data: postings = [] } = useJobPostings();
|
|
|
|
const hires = useMemo(
|
|
() => hiresWithFill({ staff, applications, postings }),
|
|
[staff, applications, postings]
|
|
);
|
|
|
|
const summary = useMemo(() => summarise(hires), [hires]);
|
|
const funnel = useMemo(() => buildFunnel(applications), [applications]);
|
|
const trend = useMemo(() => buildTrend(hires), [hires]);
|
|
const departments = useMemo(() => byDepartment(hires), [hires]);
|
|
const positions = useMemo(() => byPosition(hires), [hires]);
|
|
const efficiency = useMemo(() => buildEfficiency(hires), [hires]);
|
|
const insights = useMemo(
|
|
() => buildInsights({ hires, departments, positions, funnel, efficiency }),
|
|
[hires, departments, positions, funnel, efficiency]
|
|
);
|
|
|
|
const context = useMemo(() => ({
|
|
summary, funnel, trend, departments, positions, efficiency, insights, hires, isLoading, applications,
|
|
}), [summary, funnel, trend, departments, positions, efficiency, insights, hires, isLoading, applications]);
|
|
|
|
return (
|
|
<AdminPage
|
|
title="Analytics"
|
|
subtitle="How hiring is performing — rates, trends, comparison and what to act on."
|
|
>
|
|
<UiEditor />
|
|
<AnalyticsComposition context={context} />
|
|
</AdminPage>
|
|
);
|
|
}
|
|
|
|
/** The composed page. */
|
|
function AnalyticsComposition({ context }) {
|
|
const editing = useUiEditing();
|
|
const tree = editing?.tree || composePage('analytics').tree;
|
|
return <UiTreeRenderer nodes={tree} context={context} />;
|
|
}
|