Skip to content

Commit

Permalink
[KAIZEN-0] Legge til unleash
Browse files Browse the repository at this point in the history
  • Loading branch information
Jesperpaulsen committed Nov 17, 2023
1 parent 98ceec2 commit 95c8fd2
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 47 deletions.
111 changes: 98 additions & 13 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"@navikt/navspa": "^1.1.1",
"@nutgaard/maybe-ts": "^1.4.0",
"@tanstack/react-query": "^4.12.0",
"babel-polyfill": "^6.26.0",
"classnames": "^2.2.6",
"lodash.throttle": "^4.1.1",
Expand Down Expand Up @@ -85,7 +86,7 @@
"prettier": "^1.18.2",
"react-app-polyfill": "^1.0.6",
"react-scripts": "^3.4.1",
"typescript": "3.5.3",
"typescript": "^4.8.4",
"ws": "^7.4.6",
"yet-another-fetch-mock": "^4.0.0"
}
Expand Down
31 changes: 24 additions & 7 deletions src/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useOnMount } from './hooks/use-on-mount';
import { useOnChanged } from './hooks/use-on-changed';
import { getContextvalueValue, isContextvalueControlled, RESET_VALUE } from './redux/utils';
import { DecoratorHotkeysProvider } from './components/hurtigtaster/hurtigtaster';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

function Application(props: ApplicationProps) {
const dispatch = useDispatch<Dispatch<SagaActions>>();
Expand Down Expand Up @@ -51,6 +52,20 @@ function Application(props: ApplicationProps) {
}
);

const minutes = 60 * 1000;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10 * minutes,
cacheTime: 10 * minutes,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false
}
}
});

const isInitialized = useSelector((state: State) => state.appdata.initialized);

const apen: WrappedState<boolean> = useWrappedState(false);
Expand All @@ -61,13 +76,15 @@ function Application(props: ApplicationProps) {

return (
<DecoratorHotkeysProvider>
<header className="dekorator" ref={ref}>
<Banner apen={apen} appname={props.appname} hotkeys={props.hotkeys} />
{isInitialized && <Lenker apen={apen} proxyConfig={props.useProxy || false} />}
<Feilmelding />
{isInitialized && <NyEnhetContextModal />}
{isInitialized && <NyBrukerContextModal />}
</header>
<QueryClientProvider client={queryClient}>
<header className="dekorator" ref={ref}>
<Banner apen={apen} appname={props.appname} hotkeys={props.hotkeys} />
{isInitialized && <Lenker apen={apen} proxyConfig={props.useProxy || false} />}
<Feilmelding />
{isInitialized && <NyEnhetContextModal />}
{isInitialized && <NyBrukerContextModal />}
</header>
</QueryClientProvider>
</DecoratorHotkeysProvider>
);
}
Expand Down
14 changes: 13 additions & 1 deletion src/components/lenker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {Hotkey, ProxyConfig} from "../domain";
import {lagModiacontextholderUrl} from "../redux/api";
import {useDecoratorHotkeys} from "./hurtigtaster/hurtigtaster";
import './lenker.css';
import { useFeatureToggle } from '../featureToggle/useFeatureToggle';
import { FeatureToggles } from '../featureToggle/FeatureToggles';

function Lenke(props: { href: string; children: string; target?: string; }) {
/* eslint-disable jsx-a11y/anchor-has-content */
Expand Down Expand Up @@ -104,6 +106,14 @@ interface Props {
proxyConfig: ProxyConfig;
}

const useArbeidssokerRegistreringUrl = (fnr: string, enhet: string) => {
const { isOn } = useFeatureToggle(FeatureToggles.NY_ARBEIDSSOKER_REGISTRERING_URL)
if (isOn) {
return `https://arbeidssokerregistrering-for-veileder${finnNaisInternNavMiljoStreng()}/`
}
return arbeidssokerregistreringURL(fnr, enhet)
}

function Lenker(props: Props) {
const fnr = useFnrContextvalueState().withDefault('');
const enhet = useEnhetContextvalueState().withDefault('');
Expand All @@ -116,6 +126,8 @@ function Lenker(props: Props) {
lagHotkeys(fnr, aktorId).forEach(register);
}, [register, fnr, aktorId])

const arbeidssokerregistreringURL = useArbeidssokerRegistreringUrl(fnr, enhet)

if (!props.apen.value) {
return null;
}
Expand Down Expand Up @@ -166,7 +178,7 @@ function Lenker(props: Props) {
<Lenke href={`https://veilarbpersonflate${finnNaisInternNavMiljoStreng()}/${fnr ? fnr : ''}?enhet=${enhet}`}>
Aktivitetsplan
</Lenke>
<Lenke href={arbeidssokerregistreringURL(fnr, enhet)}>
<Lenke href={arbeidssokerregistreringURL}>
Registrer arbeidssøker
</Lenke>
<Lenke href={`https://tiltaksgjennomforing${finnNaisInternNavMiljoStreng()}/tiltaksgjennomforing`}>
Expand Down
6 changes: 3 additions & 3 deletions src/components/visibleIf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import * as React from 'react';

type Props<T> = T & { visible: boolean };

function visibleIf<PROPS>(component: React.ComponentType<PROPS>): React.FC<Props<PROPS>> {
function visibleIf<PROPS>(Component: React.FC<PROPS>): React.FC<Props<PROPS>> {
return (props: Props<PROPS>) => {
const { visible, ...rest } = props;
const { visible } = props;
if (!visible) {
return null;
}

return React.createElement(component, rest as any);
return <Component {...props} />;
};
}

Expand Down
3 changes: 3 additions & 0 deletions src/featureToggle/FeatureToggles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum FeatureToggles {
NY_ARBEIDSSOKER_REGISTRERING_URL = 'modiacontextholder.ny-arbeidssoker-registrering-url'
}
82 changes: 82 additions & 0 deletions src/featureToggle/featureToggleResource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as React from 'react';
import { getJson, lagModiacontextholderUrl } from '../redux/api';
import { FeatureToggles } from './FeatureToggles';
import { useQuery, UseQueryResult } from '@tanstack/react-query';
export type FeatureTogglesResponse = {
[key in FeatureToggles]: boolean;
};

const url = () => {
const queryParams = Object.values(FeatureToggles)
.map((it) => `id=${it}`)
.join('&');

return `${lagModiacontextholderUrl()}/api/featuretoggle/?${queryParams}`;
};

interface FetchError {
error: string;
}

type ReactElement = React.ReactElement | null;

export interface Config<T> {
ifPending: ReactElement | (() => ReactElement);
ifError: ReactElement | ((error: any) => ReactElement);
ifData: (data: T) => ReactElement;
}

export type RendererOrConfig<T> =
| ((data: T) => ReactElement)
| (Partial<Config<T>> & Pick<Config<T>, 'ifData'>);
export type DefaultConfig = Omit<Config<any>, 'ifData'>;

export function applyDefaults<T>(
defaults: DefaultConfig,
renderer: RendererOrConfig<T>
): Config<T> {
if (typeof renderer === 'function') {
return { ...defaults, ifData: renderer };
} else {
return { ...defaults, ...renderer };
}
}

export function useRest<TData = unknown, TError = unknown>(
response: UseQueryResult<TData, TError>,
config: Config<TData>
): ReactElement {
if (response.isLoading) {
if (typeof config.ifPending === 'function') {
return config.ifPending();
} else {
return config.ifPending;
}
} else if (response.isError) {
if (typeof config.ifError === 'function') {
return config.ifError(response.error);
} else {
return config.ifError;
}
} else {
return config.ifData(response.data!);
}
}

const defaults: DefaultConfig = {
ifPending: <div>Loading...</div>,
ifError: <div>Klarte ikke laste inn feature toggles</div>
};

const resource = {
queryKey: ['featureToggles'],
useFetch(): UseQueryResult<FeatureTogglesResponse, FetchError> {
return useQuery({ queryKey: this.queryKey, queryFn: () => getJson(url()) });
},
useRenderer(renderer: RendererOrConfig<FeatureTogglesResponse>) {
const response = this.useFetch();
return useRest(response, applyDefaults(defaults, renderer));
}
};

export default resource;
14 changes: 14 additions & 0 deletions src/featureToggle/useFeatureToggle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FeatureToggles } from './FeatureToggles';
import featureToggleResource from './featureToggleResource';

export const useFeatureToggle = (toggleId: FeatureToggles) => {
const toggles = featureToggleResource.useFetch();
if (toggles.isLoading) {
return { pending: true };
} else if (toggles.isError) {
return { pending: true };
} else if (toggles.data) {
return { pending: false, isOn: toggles.data[toggleId] };
}
return { pending: false, isOn: false };
};
13 changes: 10 additions & 3 deletions src/redux/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export async function getJson<T>(info: RequestInfo, init?: RequestInit): Promise
const data: T = await response.json();
return { data, error: undefined };
} catch (error) {
return { data: undefined, error };
return handleError(error);
}
}

Expand All @@ -115,7 +115,7 @@ async function postJson<T>(url: string, body: T, options?: RequestInit): Promise
}
return { data: body, error: undefined };
} catch (error) {
return { data: undefined, error };
return handleError(error);
}
}

Expand All @@ -131,7 +131,7 @@ async function deleteJson(url: string, options?: RequestInit): Promise<FetchResp
}
return { data: undefined, error: undefined };
} catch (error) {
return { data: undefined, error };
return handleError(error);
}
}

Expand Down Expand Up @@ -209,3 +209,10 @@ export function getVeilederflatehendelserUrl(ident: string) {
let subdomain = hentMiljoFraUrl().envclass === 'dev' ? '.dev' : '';
return `wss://veilederflatehendelser${finnMiljoStreng()}${subdomain}.adeo.no/modiaeventdistribution/ws/${ident}`;
}

function handleError(error: unknown) {
if (typeof error === 'string') {
return { data: undefined, error };
}
return { data: undefined, error: 'Det skjedde en uventet feil' };
}
4 changes: 2 additions & 2 deletions src/redux/enhet-update-sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function* updateEnhetValue(onsketEnhet: MaybeCls<string>) {
});
}

export function* updateWSRequestedEnhet(onsketEnhet: MaybeCls<string>) {
export function* updateWSRequestedEnhet(onsketEnhet: MaybeCls<string>): any {
const data: EnhetContextvalueState = yield selectFromInitializedState((state) => state.enhet);
if (isEnabled(data) && !data.ignoreWsEvents) {
const enhet = data.value.withDefault('');
Expand Down Expand Up @@ -71,7 +71,7 @@ export function* updateWSRequestedEnhet(onsketEnhet: MaybeCls<string>) {
}
}

export function* updateEnhet(action: EnhetChanged) {
export function* updateEnhet(action: EnhetChanged): any {
const props = yield selectFromInitializedState((state) => state.enhet);
if (isEnabled(props)) {
yield* forkApiWithErrorhandling(
Expand Down
4 changes: 2 additions & 2 deletions src/redux/fnr-update-sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function* updateFnrState(updated: Partial<FnrContextvalueState>) {
}
}

export function* updateWSRequestedFnr(onsketFnr: MaybeCls<string>) {
export function* updateWSRequestedFnr(onsketFnr: MaybeCls<string>): any {
const data: FnrContextvalueState = yield selectFromInitializedState((state) => state.fnr);
if (isEnabled(data) && !data.ignoreWsEvents) {
const fnr = data.value.withDefault('');
Expand Down Expand Up @@ -111,7 +111,7 @@ export function* updateWSRequestedFnr(onsketFnr: MaybeCls<string>) {
}
}

export function* updateFnr(action: FnrSubmit | FnrReset) {
export function* updateFnr(action: FnrSubmit | FnrReset): any {
const props = yield selectFromInitializedState((state) => state.fnr);
if (isEnabled(props)) {
if (action.type === SagaActionTypes.FNRRESET) {
Expand Down
4 changes: 2 additions & 2 deletions src/redux/init-sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ function* initDekoratorData(props: ApplicationProps) {

export function* initSaga(): IterableIterator<any> {
log.time('initSaga');
const action: { data: ApplicationProps } = yield take(SagaActionTypes.INIT);
const props = action.data;
const action = yield take(SagaActionTypes.INIT);
const props = (action as any).data;

log.time('init');
yield call(initDekoratorData, props);
Expand Down
4 changes: 2 additions & 2 deletions src/redux/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function* callApiWithErrorhandling<Fn extends (...args: any[]) => any>(
error: PredefiniertFeilmelding,
fn: Fn,
...args: Parameters<Fn>
) {
): any {
const result = yield call(fn, ...args);
if (hasError(result)) {
yield put(leggTilFeilmelding(error));
Expand All @@ -42,7 +42,7 @@ export function* spawnConditionally<Fn extends (...args: any[]) => any>(

export function* selectFromInitializedState<T, Fn extends (state: InitializedState) => T>(
selector: Fn
) {
): any {
const state = yield select((state: State) => state.appdata);
if (isInitialized(state)) {
return selector(state);
Expand Down
2 changes: 1 addition & 1 deletion src/redux/wsSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function* wsChange(event: WsChangeEvent) {
}
}

export function* wsListener() {
export function* wsListener(): any {
const saksbehandler: MaybeCls<Saksbehandler> = yield selectFromInitializedState(
(state) => state.data.saksbehandler
);
Expand Down
25 changes: 15 additions & 10 deletions src/utils/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,35 @@ const LogLevel: { [key: string]: LogLevel } = {
class Logging {
level: LogLevel = LogLevel.INFO;
trace(message?: any, ...optionalParams: any[]) {
this.log(LogLevel.TRACE, message, ...optionalParams);
this.log(LogLevel.TRACE, console.trace, message, ...optionalParams);
}
debug(message?: any, ...optionalParams: any[]) {
this.log(LogLevel.DEBUG, message, ...optionalParams);
this.log(LogLevel.DEBUG, console.debug, message, ...optionalParams);
}
info(message?: any, ...optionalParams: any[]) {
this.log(LogLevel.INFO, message, ...optionalParams);
this.log(LogLevel.INFO, console.info, message, ...optionalParams);
}
warn(message?: any, ...optionalParams: any[]) {
this.log(LogLevel.WARN, message, ...optionalParams);
this.log(LogLevel.WARN, console.warn, message, ...optionalParams);
}
error(message?: any, ...optionalParams: any[]) {
this.log(LogLevel.ERROR, message, ...optionalParams);
this.log(LogLevel.ERROR, console.error, message, ...optionalParams);
}
time(name: string) {
this.log(LogLevel.TIME, name);
this.log(LogLevel.TIME, console.time, name);
}
timeEnd(name: string) {
this.log(LogLevel.TIME_END, name);
this.log(LogLevel.TIME_END, console.timeEnd, name);
}

private log(method: LogLevel, message?: any, ...optionalParams: any[]): void {
if (method.level >= this.level.level) {
console[method.console](message, ...optionalParams);
private log(
level: LogLevel,
method: (...message: any) => void,
message?: any,
...optionalParams: any[]
): void {
if (level.level >= this.level.level) {
method(message, ...optionalParams);
}
}
}
Expand Down

0 comments on commit 95c8fd2

Please sign in to comment.