add second layer of result validation
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import type { Sticky, Cluster } from '../types/types';
|
||||
import type { Sticky, ClusterResponse } from '../types/types';
|
||||
|
||||
const API_BASE = `${import.meta.env.VITE_API_BASE}/v1/notes`;
|
||||
|
||||
@@ -11,7 +11,7 @@ const fetchStickies = async (): Promise<Sticky[]> => {
|
||||
return response.json();
|
||||
};
|
||||
|
||||
const fetchClusters = async (): Promise<Cluster[]> => {
|
||||
const fetchClusters = async (): Promise<ClusterResponse> => {
|
||||
const response = await fetch(`${API_BASE}/cluster`, {
|
||||
method: 'POST',
|
||||
});
|
||||
@@ -29,7 +29,7 @@ export const useGetStickies = () => {
|
||||
};
|
||||
|
||||
export const useClusterStickies = () => {
|
||||
return useMutation<Cluster[]>({
|
||||
return useMutation<ClusterResponse>({
|
||||
mutationFn: fetchClusters,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4,12 +4,22 @@ import Sticky from './sticky';
|
||||
import Button from './button';
|
||||
import '../styles/stickies.css';
|
||||
|
||||
const scoreLabel = (score: number): string => {
|
||||
if (score >= 0.7) return 'Strong';
|
||||
if (score >= 0.4) return 'Moderate';
|
||||
if (score >= 0.1) return 'Weak';
|
||||
return 'Poor';
|
||||
};
|
||||
|
||||
const Stickies = () => {
|
||||
const { data: stickies, isLoading, error } = useGetStickies();
|
||||
const { mutate: cluster, data: clusters, isPending } = useClusterStickies();
|
||||
const { mutate: cluster, data: clusterResponse, isPending } = useClusterStickies();
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const clusters = clusterResponse?.clusters;
|
||||
const score = clusterResponse?.score;
|
||||
|
||||
const handleCluster = () => {
|
||||
cluster();
|
||||
};
|
||||
@@ -30,6 +40,11 @@ const Stickies = () => {
|
||||
<Button onClick={handleCluster} isLoading={isPending} label="Group Stickies By Topic" />
|
||||
{clusters ? (
|
||||
<div className="clusters-container">
|
||||
{score != null && (
|
||||
<div className="cohesion-score">
|
||||
Cluster cohesion: <strong>{score.toFixed(2)}</strong> — {scoreLabel(score)}
|
||||
</div>
|
||||
)}
|
||||
{clusters.map((group) => (
|
||||
<div key={group.label} className="cluster-group">
|
||||
<h3 className="cluster-label">{group.label}</h3>
|
||||
|
||||
@@ -29,3 +29,14 @@
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cohesion-score {
|
||||
text-align: center;
|
||||
font-size: 0.95em;
|
||||
color: #e0e0e0;
|
||||
padding: 8px 16px;
|
||||
background: rgba(109, 214, 244, 0.1);
|
||||
border-radius: 6px;
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ const MOCK_STICKIES = [
|
||||
{ id: 'note_002', text: 'Export takes too long', x: 798, y: 211, author: 'user_2', color: 'green' },
|
||||
];
|
||||
|
||||
const MOCK_CLUSTERS = [
|
||||
{ label: 'Auth Issues', noteIds: ['note_001'] },
|
||||
{ label: 'Export Issues', noteIds: ['note_002'] },
|
||||
];
|
||||
const MOCK_CLUSTER_RESPONSE = {
|
||||
clusters: [
|
||||
{ label: 'Auth Issues', noteIds: ['note_001'] },
|
||||
{ label: 'Export Issues', noteIds: ['note_002'] },
|
||||
],
|
||||
score: 0.74,
|
||||
};
|
||||
|
||||
let fetchMock: ReturnType<typeof vi.fn>;
|
||||
|
||||
@@ -73,7 +76,7 @@ describe('Stickies', () => {
|
||||
// First call = GET notes, second call = POST cluster
|
||||
fetchMock
|
||||
.mockReturnValueOnce(mockFetchOk(MOCK_STICKIES))
|
||||
.mockReturnValueOnce(mockFetchOk(MOCK_CLUSTERS));
|
||||
.mockReturnValueOnce(mockFetchOk(MOCK_CLUSTER_RESPONSE));
|
||||
|
||||
const { Wrapper } = createTestWrapper();
|
||||
render(<Stickies />, { wrapper: Wrapper });
|
||||
@@ -90,7 +93,7 @@ describe('Stickies', () => {
|
||||
it('should still render sticky note text inside clusters', async () => {
|
||||
fetchMock
|
||||
.mockReturnValueOnce(mockFetchOk(MOCK_STICKIES))
|
||||
.mockReturnValueOnce(mockFetchOk(MOCK_CLUSTERS));
|
||||
.mockReturnValueOnce(mockFetchOk(MOCK_CLUSTER_RESPONSE));
|
||||
|
||||
const { Wrapper } = createTestWrapper();
|
||||
render(<Stickies />, { wrapper: Wrapper });
|
||||
|
||||
5
frontend/src/types/types.d.ts
vendored
5
frontend/src/types/types.d.ts
vendored
@@ -11,3 +11,8 @@ export type Cluster = {
|
||||
label: string;
|
||||
noteIds: string[];
|
||||
};
|
||||
|
||||
export type ClusterResponse = {
|
||||
clusters: Cluster[];
|
||||
score: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user