Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/app/core/dashboard/characteristics/characteristics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import Box from '@mui/joy/Box';

import { DEFAULT_PATHNAME } from '@/constants';
import { Heatmap } from '@/components/charts/heatmap';
import { HeadField, PatentSearchArgs } from '@/types';
import { HeadField, PatentCharacteristic, PatentSearchArgs } from '@/types';

import { fetchPatentCharacteristics } from './actions';
import { PatentCharacteristicsControl } from './control';
import { PatentCharacteristicsControl, getClickUrl } from './control';

const CharacteristicsInner = async ({
pathname = DEFAULT_PATHNAME,
Expand All @@ -17,12 +17,11 @@ const CharacteristicsInner = async ({
const data = await fetchPatentCharacteristics(args);

return (
<Heatmap
clickBaseUrl={`${pathname}/patents?terms=`}
clickField="documents"
<Heatmap<PatentCharacteristic>
getClickUrl={getClickUrl}
data={data}
pathname={pathname}
tooltipFields={['documents']}
tooltipFields={['head', 'concept', 'documents']}
xField="head"
yField="concept"
/>
Expand Down
20 changes: 13 additions & 7 deletions src/app/core/dashboard/characteristics/control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import Typography from '@mui/joy/Typography';
import { Section } from '@/components/layout/section';
import { Select } from '@/components/input';
import { useNavigation } from '@/hooks/navigation';
import { HeadField, PatentSearchArgs } from '@/types';
import { HeadField, PatentCharacteristic, PatentSearchArgs } from '@/types';
import { DEFAULT_PATHNAME } from '@/constants';

export const PatentCharacteristicsControl = ({
headField,
terms,
children,
}: PatentSearchArgs & { children: ReactNode; headField: HeadField }) => {
const { params, navigate } = useNavigation();
const { setParam } = useNavigation();

return (
<>
Expand All @@ -31,11 +32,7 @@ export const PatentCharacteristicsControl = ({
label="Dimension"
onChange={(e: unknown, value: HeadField | null) => {
if (value) {
const newParams = new URLSearchParams(params);
newParams.set('headField', value);
navigate(`?${newParams.toString()}`, {
scroll: false,
});
setParam('headField', value);
}
}}
options={['priority_date', 'id']}
Expand All @@ -46,3 +43,12 @@ export const PatentCharacteristicsControl = ({
</>
);
};

export const getClickUrl = (
object: PatentCharacteristic,
pathname: string = DEFAULT_PATHNAME
) => {
const ids = object.documents?.join(';');
const term = `${object.concept} x ${object.head}`;
return `${pathname}/patents?ids=${ids}&terms=${term}`;
};
13 changes: 10 additions & 3 deletions src/app/core/dashboard/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ import { PatentCharacteristics } from './characteristics';
import { OverTime } from './over-time';
import { Summary } from './summary';

export type ContentArgs = PatentSearchArgs & { headField: HeadField };
export type ContentArgs = PatentSearchArgs & {
headField: HeadField;
tab: string;
};

export const Content = (args: ContentArgs) => {
export const Content = ({ tab, ...args }: ContentArgs) => {
try {
const tabs = [
{
id: 'assets',
label: 'Assets',
panel: (
<Suspense fallback={<Skeleton />}>
Expand All @@ -30,6 +34,7 @@ export const Content = (args: ContentArgs) => {
),
},
{
id: 'summary',
label: 'Summary',
panel: (
<Suspense fallback={<Skeleton />}>
Expand All @@ -38,6 +43,7 @@ export const Content = (args: ContentArgs) => {
),
},
{
id: 'over-time',
label: 'Over Time',
panel: (
<Suspense fallback={<Skeleton />}>
Expand All @@ -46,6 +52,7 @@ export const Content = (args: ContentArgs) => {
),
},
{
id: 'characteristics',
label: 'Characteristics',
panel: (
<Suspense fallback={<Skeleton />}>
Expand All @@ -56,7 +63,7 @@ export const Content = (args: ContentArgs) => {
];
return (
<Box sx={getStyles}>
<Tabs tabs={tabs} />
<Tabs openId={tab} tabs={tabs} />
</Box>
);
} catch (e) {
Expand Down
2 changes: 2 additions & 0 deletions src/app/core/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const Page = ({ searchParams }: { searchParams: Record<string, string> }) => {
? parseInt(searchParams.endYear, 10)
: undefined;
const headField = (searchParams.headField as HeadField) || 'priority_date';
const { tab } = searchParams;

return (
<>
Expand Down Expand Up @@ -57,6 +58,7 @@ const Page = ({ searchParams }: { searchParams: Record<string, string> }) => {
headField={headField}
queryType={queryType}
startYear={startYear}
tab={tab}
terms={terms || []}
/>
</Suspense>
Expand Down
5 changes: 4 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Metadata } from 'next';
import { Suspense } from 'react';
import { Mulish } from 'next/font/google';

import MuiXLicense from '@/components/data/mui-license';
Expand Down Expand Up @@ -26,7 +27,9 @@ const RootLayout = ({
>
<body className={mulish.className}>
<ThemeRegistry options={{ key: 'joy' }}>
<NavigationProvider>{children}</NavigationProvider>
<Suspense>
<NavigationProvider>{children}</NavigationProvider>
</Suspense>
<MuiXLicense />
</ThemeRegistry>
</body>
Expand Down
24 changes: 10 additions & 14 deletions src/components/charts/heatmap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { useRouter } from 'next/navigation';
import Typography from '@mui/joy/Typography';
import { Vega, VisualizationSpec } from 'react-vega';

import { PatentCharacteristics } from '@/types';

import { BaseChartProps } from './types';

type HeadmapSpecProps = {
Expand All @@ -17,10 +15,9 @@ type HeadmapSpecProps = {
yFieldTitle?: string;
};

type HeatmapProps = BaseChartProps & {
clickBaseUrl?: string; // TODO: both or neither with clickField
clickField?: string;
data: PatentCharacteristics; // TODO: make generic
type HeatmapProps<DT extends Record<string, unknown>> = BaseChartProps & {
data: DT[];
getClickUrl?: (obj: DT) => string;
} & HeadmapSpecProps;

const getSpec: (props: HeadmapSpecProps) => VisualizationSpec = ({
Expand Down Expand Up @@ -95,27 +92,26 @@ const getSpec: (props: HeadmapSpecProps) => VisualizationSpec = ({
/**
* Graph chart
*/
export const Heatmap = ({
export const Heatmap = <DT extends Record<string, unknown>>({
data,
clickBaseUrl,
clickField,
colorField,
getClickUrl,
tooltipFields = [],
title,
xField,
yField,
xFieldTitle = '',
yFieldTitle = '',
}: HeatmapProps): JSX.Element => {
}: HeatmapProps<DT>): JSX.Element => {
const router = useRouter();
const signalListeners = {
select: (_: unknown, value: unknown) => {
if (!clickField || typeof value !== 'object') {
if (!getClickUrl || typeof value !== 'object') {
return;
}
const obj = value as Record<string, unknown>;
const urlParams = (obj[clickField] as string[]).join(';');
router.push(`${clickBaseUrl}${urlParams}`);
const obj = value as DT;
const url = getClickUrl(obj);
router.push(url);
},
};

Expand Down
15 changes: 8 additions & 7 deletions src/components/composite/search/search-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { FetchAutocompletions } from './types';
*/
export const SearchBar = ({
endYear = 2024,
exemplarPatents,
// exemplarPatents,
fetchAutocompletions,
queryType,
startYear = 2014,
Expand All @@ -29,9 +29,9 @@ export const SearchBar = ({
const { navigate } = useNavigation();
const pathname = usePathname();
const [newTerms, setTerms] = useState<string[] | null>(terms);
const [newExemplarPatents, setExemplarPatents] = useState<string[] | null>(
exemplarPatents || null
);
// const [newExemplarPatents, setExemplarPatents] = useState<string[] | null>(
// exemplarPatents || null
// );
const [newQueryType, setQueryType] = useState<string | null>(
queryType || null
);
Expand Down Expand Up @@ -72,6 +72,7 @@ export const SearchBar = ({
minDistance={2}
max={2025}
size="lg"
sx={{ mr: 3 }}
valueLabelDisplay="on"
/>
</Grid>
Expand All @@ -90,7 +91,7 @@ export const SearchBar = ({
/>
</Grid>
</Grid>
<Grid container spacing={4} sx={{ mt: 1 }}>
{/* <Grid container spacing={4} sx={{ mt: 1 }}>
<Grid xs={12} sm={6}>
<Autocomplete<Option, true, false>
isMultiple
Expand All @@ -116,7 +117,7 @@ export const SearchBar = ({
variant="soft"
/>
</Grid>
</Grid>
</Grid> */}
</Section>
<Section variant="l2">
<Button
Expand All @@ -128,7 +129,7 @@ export const SearchBar = ({
const queryArgs = getQueryArgs({
endYear: newYearRange?.[1],
queryType: newQueryType,
exemplarPatents: newExemplarPatents,
exemplarPatents: [], // TODO
startYear: newYearRange?.[0],
terms: newTerms,
});
Expand Down
50 changes: 30 additions & 20 deletions src/components/layout/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import TabList from '@mui/joy/TabList';
import TabPanel from '@mui/joy/TabPanel';
import JoyTabs from '@mui/joy/Tabs';

import { getSelectableId } from '@/utils/string';
import { useNavigation } from '@/hooks/navigation';

type TabDef = {
id: string;
label: string;
panel: ReactNode;
};
Expand All @@ -17,23 +18,32 @@ type TabDef = {
* Tab component
* @param props.tabs
*/
export const Tabs = ({ tabs }: { tabs: TabDef[] }): JSX.Element => (
<JoyTabs>
<TabList>
{tabs.map(({ label }) => (
<Tab key={`${getSelectableId(label)}-tab`} variant="soft">
{label}
</Tab>
export const Tabs = ({
openId,
tabs,
}: {
openId?: string;
tabs: TabDef[];
}): JSX.Element => {
const { setParam } = useNavigation();
return (
<JoyTabs onChange={(e, id) => setParam('tab', `${id}`)} value={openId}>
<TabList>
{tabs.map(({ id, label }) => (
<Tab key={`${id}-tab`} value={id} variant="soft">
{label}
</Tab>
))}
</TabList>
{tabs.map(({ id, panel }) => (
<TabPanel
key={`${id}-panel`}
value={id}
sx={{ minHeight: '100vh' }}
>
{panel}
</TabPanel>
))}
</TabList>
{tabs.map(({ label, panel }, idx) => (
<TabPanel
key={`${getSelectableId(label)}-panel`}
value={idx}
sx={{ minHeight: '100vh' }}
>
{panel}
</TabPanel>
))}
</JoyTabs>
);
</JoyTabs>
);
};
17 changes: 12 additions & 5 deletions src/hooks/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type NavigationContextType = {
isPending: boolean;
navigate: (url: string, options?: NavigateOptions) => void;
params: ReadonlyURLSearchParams;
setParam: (key: string, value: string) => void;
};

const NavigationContext = createContext({
Expand All @@ -26,6 +27,9 @@ const NavigationContext = createContext({
console.warn(`No impl when attempting to route to ${url}`);
}) as NavigationContextType['navigate'],
params: new URLSearchParams() as ReadonlyURLSearchParams,
setParam: (key: string, value: string) => {
console.warn(`No impl when attempting to set param ${key} to ${value}`);
},
});

export const NavigationProvider = ({ children }: { children: ReactNode }) => {
Expand All @@ -38,11 +42,14 @@ export const NavigationProvider = ({ children }: { children: ReactNode }) => {
isPending,
navigate: (url: string, options?: NavigateOptions) => {
startTransition(() => {
if (options?.scroll === false) {
Router.replace(url, options);
} else {
Router.push(url, options);
}
Router.push(url, options);
});
},
setParam: (key: string, value: string) => {
const newParams = new URLSearchParams(params);
newParams.set(key, value);
Router.replace(`?${newParams.toString()}`, {
scroll: false, // TODO!
});
},
params,
Expand Down
16 changes: 8 additions & 8 deletions src/types/documents/patents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ const HeadFieldEnum = z.enum(['id', 'assignee', 'priority_date']);

export type HeadField = z.infer<typeof HeadFieldEnum>;

export const PatentCharacteristicsSchema = z.array(
z.object({
concept: z.string(),
count: z.number(),
head: z.union([z.string(), z.number()]),
documents: z.array(z.string()),
})
);
const PatentCharacteristicSchema = z.object({
concept: z.string(),
count: z.number(),
head: z.union([z.string(), z.number()]),
documents: z.array(z.string()),
});
export const PatentCharacteristicsSchema = z.array(PatentCharacteristicSchema);

export type PatentCharacteristic = z.infer<typeof PatentCharacteristicSchema>;
export type PatentCharacteristics = z.infer<typeof PatentCharacteristicsSchema>;