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
26 changes: 14 additions & 12 deletions src/hooks/useCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,10 @@ export const useCache = ({ loginRoute = '/login' }: CacheProps = {}) => {
/**
* Get all secrets
*/
// TODO: Previously this hook pre-populated individual secret caches with setQueryData,
// but this prevented useSecret from fetching fresh data (e.g., the value field).
// Consider re-adding cache pre-population if the list endpoint returns full secret data,
// or use a different query key pattern for partial vs full secret data.
const useSecrets = () => {
return useQuery({
queryKey: queryKeys.secrets.all(),
Expand All @@ -2210,16 +2214,7 @@ export const useCache = ({ loginRoute = '/login' }: CacheProps = {}) => {
});
if (resp.success && resp.secrets) {
const secrets = resp.secrets
.map((s: unknown) => {
const secret = toSecret(s);
if (secret) {
queryClient.setQueryData(
queryKeys.secrets.detail(secret.id),
secret,
);
}
return secret;
})
.map((s: unknown) => toSecret(s))
.filter(Boolean);
return secrets;
}
Expand Down Expand Up @@ -2604,7 +2599,12 @@ export const useCache = ({ loginRoute = '/login' }: CacheProps = {}) => {
*/
const useSecret = (
secretId: string,
options?: { enabled?: boolean; refetchOnMount?: boolean },
options?: {
enabled?: boolean;
refetchOnMount?: boolean | 'always';
staleTime?: number;
gcTime?: number;
},
) => {
return useQuery({
queryKey: queryKeys.secrets.detail(secretId),
Expand All @@ -2622,6 +2622,8 @@ export const useCache = ({ loginRoute = '/login' }: CacheProps = {}) => {
enabled: options?.enabled ?? !!secretId,
refetchOnMount:
options?.refetchOnMount ?? DEFAULT_QUERY_OPTIONS.refetchOnMount,
staleTime: options?.staleTime ?? DEFAULT_QUERY_OPTIONS.staleTime,
gcTime: options?.gcTime ?? DEFAULT_QUERY_OPTIONS.gcTime,
});
};

Expand Down Expand Up @@ -5459,7 +5461,7 @@ export const useCache = ({ loginRoute = '/login' }: CacheProps = {}) => {
}>({
url: `${configuration.iamRunUrl}/api/iam/v1/oauth2/authz/url/link?${queryString}`,
});
return resp.autorization_url;
return resp;
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useOAuth2AuthorizationLinkURL is documented/typed as returning an OAuth2 authorization URL (autorization_url: string), but the mutation currently returns the entire resp object. This makes the hook’s return type inconsistent with useOAuth2AuthorizationURL and will break callers expecting a string URL. Return resp.autorization_url (or update the response type and docstring if the endpoint intentionally returns a different shape).

Suggested change
return resp;
return resp.autorization_url;

Copilot uses AI. Check for mistakes.
},
});
};
Expand Down
17 changes: 14 additions & 3 deletions src/hooks/useNavigate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ export const useNavigate = () => {
if (isReactRouter && rrNavigate) {
// eslint-disable-next-line react-hooks/rules-of-hooks
return useCallback(
(to: string, options?: any) => {
// For React Router, just pass through directly without side effects
(to: string | number, options?: any) => {
// Scroll to top when navigating (except for history navigation)
if (typeof to === 'string') {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
}
// For React Router, pass through directly
return rrNavigate(to, options);
},
[rrNavigate],
Expand All @@ -58,11 +63,17 @@ export const useNavigate = () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const navigate = useCallback(
(
location: string,
location: string | number,
optionsOrEvent?: any,
resetPortals = true,
extraOptions?: any,
) => {
// Handle number for history navigation (e.g., -1 to go back)
if (typeof location === 'number') {
window.history.go(location);
return;
}

// Handle different call signatures for native navigation
let options: any = undefined;
let event: any = undefined;
Expand Down
227 changes: 198 additions & 29 deletions src/theme/DatalayerTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,230 @@ import { theme as primerTheme } from '@primer/react';
import cloneDeep from 'lodash/cloneDeep.js';
import merge from 'lodash/merge.js';

/**
* Datalayer Accessible Color System
* Based on Datalayer's brand manual - WCAG AA/AAA compliant
*/
export const datalayerColors = {
// Core Neutrals
black: '#000000', // Primary text - AAA on white
gray: '#59595C', // Secondary text - AA on white
white: '#FFFFFF', // Background

// Greens (Brand & Accessibility)
greenBrand: '#16A085', // Brand accent, icons, dividers, headings
greenAccent: '#1ABC9C', // Icons, charts, highlights on dark surfaces
greenText: '#117A65', // Accessible green for text & buttons (AA+ on white)
greenTint: '#E9F7F1', // Soft background for success / callouts
greenBright: '#2ECC71', // Highlights and glow on dark backgrounds
greenHover: '#0E6655', // Primary button hover
};

/**
* Datalayer Theme for Primer React.
*
* Uses accessible color palette from Datalayer's brand manual.
*/
const datalayerThemeDefs = {
colorSchemes: {
light: {
colors: {
// Canvas colors
canvas: {
default: datalayerColors.white,
// subtle: datalayerColors.greenTint,
},
// Foreground colors
fg: {
default: datalayerColors.black,
muted: datalayerColors.gray,
onEmphasis: datalayerColors.white,
},
// Accent colors (brand green)
accent: {
fg: datalayerColors.greenText,
emphasis: datalayerColors.greenBrand,
muted: datalayerColors.greenAccent,
// subtle: datalayerColors.greenTint,
},
// Success colors (green)
success: {
fg: datalayerColors.greenText,
emphasis: datalayerColors.greenBrand,
muted: datalayerColors.greenAccent,
// subtle: datalayerColors.greenTint,
},
// Button colors
btn: {
// text: 'var(--jp-ui-font-color1, rgba(0, 0, 0, 0.87))',
// bg: 'var(--jp-layout-color1, white)',
// border: 'var(--jp-border-color1, #bdbdbd)',
// hoverBg: 'var(--jp-layout-color2, #eee)',
// hoverBorder: 'var(--jp-border-color1, #bdbdbd)',
// activeBg: 'var(--jp-layout-color3, #bdbdbd)',
// activeBorder: 'var(--jp-border-color1, #bdbdbd)',
// selectedBg: 'var(--jp-layout-color0, white)',
// counterBg: 'var(--jp-layout-color4, #757575)',
text: datalayerColors.black,
bg: datalayerColors.white,
border: datalayerColors.gray,
hoverBg: datalayerColors.greenTint,
hoverBorder: datalayerColors.gray,
activeBg: datalayerColors.greenTint,
activeBorder: datalayerColors.gray,
selectedBg: datalayerColors.white,
counterBg: datalayerColors.gray,
primary: {
// text: 'var(--jp-ui-inverse-font-color1, rgba(255, 255, 255, 1))',
bg: 'var(--dla-color-green-dark)',
// border: 'var(--dla-color-green-light)',
hoverBg: 'var(--dla-color-grey)',
// hoverBorder: 'var(--dla-color-green-light)',
selectedBg: 'var(--dla-color-black)',
// disabledText: 'var(--jp-ui-inverse-font-color2, rgba(255, 255, 255, 0.7))',
// disabledBg: 'var(--jp-brand-color3, #c8e6c9)',
// disabledBorder: 'var(--jp-border-color1, #bdbdbd)',
// icon: 'var(--jp-ui-inverse-font-color2, rgba(255, 255, 255, 0.7))',
// counterBg: 'var(--jp-inverse-layout-color3, #616161)',
text: datalayerColors.white,
bg: datalayerColors.greenText,
border: datalayerColors.greenText,
hoverBg: datalayerColors.greenHover,
hoverBorder: datalayerColors.greenHover,
selectedBg: datalayerColors.greenHover,
disabledText: 'rgba(255, 255, 255, 0.7)',
disabledBg: datalayerColors.gray,
disabledBorder: datalayerColors.gray,
icon: datalayerColors.white,
counterBg: 'rgba(0, 0, 0, 0.2)',
},
outline: {
text: datalayerColors.greenText,
hoverText: datalayerColors.white,
hoverBg: datalayerColors.greenText,
hoverBorder: datalayerColors.greenText,
hoverCounterBg: 'rgba(255, 255, 255, 0.2)',
selectedText: datalayerColors.white,
selectedBg: datalayerColors.greenHover,
selectedBorder: datalayerColors.greenHover,
disabledText: datalayerColors.gray,
disabledBg: datalayerColors.greenTint,
disabledCounterBg: 'rgba(0, 0, 0, 0.05)',
counterBg: 'rgba(0, 0, 0, 0.05)',
counterFg: datalayerColors.greenText,
hoverCounterFg: datalayerColors.white,
disabledCounterFg: datalayerColors.gray,
},
danger: {
text: '#d32f2f',
hoverText: datalayerColors.white,
hoverBg: '#d32f2f',
hoverBorder: '#d32f2f',
hoverCounterBg: 'rgba(255, 255, 255, 0.2)',
selectedText: datalayerColors.white,
selectedBg: '#b71c1c',
selectedBorder: '#b71c1c',
disabledText: 'rgba(211, 47, 47, 0.5)',
disabledBg: datalayerColors.greenTint,
disabledCounterBg: 'rgba(211, 47, 47, 0.05)',
counterBg: 'rgba(211, 47, 47, 0.1)',
counterFg: '#d32f2f',
hoverCounterFg: datalayerColors.white,
disabledCounterFg: 'rgba(211, 47, 47, 0.5)',
icon: '#d32f2f',
},
},
},
shadows: {},
},
dark: {
colors: {},
colors: {
// Canvas colors
canvas: {
default: datalayerColors.black,
subtle: '#0d1117',
},
// Foreground colors
fg: {
default: datalayerColors.white,
muted: '#8b949e',
onEmphasis: datalayerColors.white,
},
// Accent colors (bright greens for dark mode)
accent: {
fg: datalayerColors.greenAccent,
emphasis: datalayerColors.greenBright,
muted: datalayerColors.greenBrand,
subtle: '#1f352d',
},
// Success colors
success: {
fg: datalayerColors.greenAccent,
emphasis: datalayerColors.greenBright,
muted: datalayerColors.greenBrand,
subtle: '#1f352d',
},
// Button colors for dark mode
btn: {
text: '#c9d1d9',
bg: '#21262d',
border: 'rgba(240, 246, 252, 0.1)',
hoverBg: '#30363d',
hoverBorder: '#8b949e',
activeBg: 'hsla(212, 12%, 18%, 1)',
activeBorder: '#6e7681',
selectedBg: '#161b22',
counterBg: '#30363d',
primary: {
text: datalayerColors.white,
bg: datalayerColors.greenAccent,
border: 'rgba(240, 246, 252, 0.1)',
hoverBg: datalayerColors.greenBright,
hoverBorder: 'rgba(240, 246, 252, 0.1)',
selectedBg: datalayerColors.greenBright,
disabledText: 'rgba(255, 255, 255, 0.5)',
disabledBg: 'rgba(22, 160, 133, 0.6)',
disabledBorder: 'rgba(240, 246, 252, 0.1)',
icon: datalayerColors.white,
counterBg: 'rgba(0, 0, 0, 0.2)',
},
outline: {
text: datalayerColors.greenAccent,
hoverText: datalayerColors.white,
hoverBg: datalayerColors.greenAccent,
hoverBorder: datalayerColors.greenAccent,
hoverCounterBg: 'rgba(255, 255, 255, 0.2)',
selectedText: datalayerColors.white,
selectedBg: datalayerColors.greenBright,
selectedBorder: datalayerColors.greenBright,
disabledText: 'rgba(26, 188, 156, 0.5)',
disabledBg: 'rgba(26, 188, 156, 0.1)',
disabledCounterBg: 'rgba(26, 188, 156, 0.05)',
counterBg: 'rgba(26, 188, 156, 0.1)',
counterFg: datalayerColors.greenAccent,
hoverCounterFg: datalayerColors.white,
disabledCounterFg: 'rgba(26, 188, 156, 0.5)',
},
danger: {
text: '#f85149',
hoverText: datalayerColors.white,
hoverBg: '#da3633',
hoverBorder: '#f85149',
hoverCounterBg: 'rgba(255, 255, 255, 0.2)',
selectedText: datalayerColors.white,
selectedBg: '#b62324',
selectedBorder: '#ff7b72',
disabledText: 'rgba(248, 81, 73, 0.5)',
disabledBg: 'rgba(248, 81, 73, 0.1)',
disabledCounterBg: 'rgba(248, 81, 73, 0.05)',
counterBg: 'rgba(248, 81, 73, 0.1)',
counterFg: '#f85149',
hoverCounterFg: datalayerColors.white,
disabledCounterFg: 'rgba(248, 81, 73, 0.5)',
icon: '#f85149',
},
},
},
shadows: {},
},
},
};

const { colorSchemes: primerSchemes, ...primerOthers } = cloneDeep(primerTheme);
const { colorSchemes: jupyterSchemes, ...datalayerOthers } = datalayerThemeDefs;
const { colorSchemes: datalayerSchemes, ...datalayerOthers } =
datalayerThemeDefs;

// Merge with the light theme to ensure all variables are defined (although the style may be ugly).
const datalayerTheme = merge(primerOthers, datalayerOthers, {
// Merge with the light theme to ensure all variables are defined.
export const datalayerTheme = merge(primerOthers, datalayerOthers, {
colorSchemes: { light: {}, dark: {} },
});
datalayerTheme.colorSchemes.light = {
colors: merge(primerSchemes.light.colors, jupyterSchemes.light.colors),
shadows: merge(primerSchemes.light.shadows, jupyterSchemes.light.shadows),
colors: merge(primerSchemes.light.colors, datalayerSchemes.light.colors),
shadows: merge(primerSchemes.light.shadows, datalayerSchemes.light.shadows),
};
datalayerTheme.colorSchemes.dark = {
colors: merge(primerSchemes.dark.colors, jupyterSchemes.dark.colors),
shadows: merge(primerSchemes.dark.shadows, jupyterSchemes.dark.shadows),
colors: merge(primerSchemes.dark.colors, datalayerSchemes.dark.colors),
shadows: merge(primerSchemes.dark.shadows, datalayerSchemes.dark.shadows),
};

export { datalayerTheme };
export default datalayerTheme;
Loading
Loading