Skip to content

Commit

Permalink
Merge branch 'staging' of https://github.com/kartverket/frisk-frontend
Browse files Browse the repository at this point in the history
…into staging
  • Loading branch information
chribjel committed Jan 17, 2025
2 parents f178595 + e06df4e commit 85461cd
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 24 deletions.
175 changes: 164 additions & 11 deletions frisk.config.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import type { HTMLInputTypeAttribute } from "react";
import { useEffect, useState, type HTMLInputTypeAttribute } from "react";
import {
getFunction,
getFunctions,
getMyMicrosoftTeams,
getTeam,
} from "@/services/backend";
import { getregelrettFrontendUrl } from "@/config";
import { object, string, array } from "zod";
import { Button } from "@kvib/react";
import { object, string, array, type z } from "zod";
import { Button, FormControl, FormLabel, Select } from "@kvib/react";
import type { useFunction } from "@/hooks/use-function";
import type { useMetadata } from "@/hooks/use-metadata";
import { msalInstance } from "@/services/msal";
import { InteractionRequiredAuthError } from "@azure/msal-browser";

export async function getConfig(): Promise<FriskConfig> {
const schemas = await getSchemasFromRegelrett();
Expand Down Expand Up @@ -137,6 +139,7 @@ export async function getConfig(): Promise<FriskConfig> {
columnName: "Funksjon",
addButtonName: "Legg til funksjon",
enableEntra: true,
//functionCardComponents: [createSchemaButton(schemas)],
functionCardComponents: [SchemaButton],
};
}
Expand Down Expand Up @@ -257,6 +260,7 @@ type Logo = {
type FunctionCardComponentProps = {
func: ReturnType<typeof useFunction>["func"];
metadata: ReturnType<typeof useMetadata>["metadata"];
addMetadata: ReturnType<typeof useMetadata>["addMetadata"];
};

function SchemaButton({ func, metadata }: FunctionCardComponentProps) {
Expand Down Expand Up @@ -298,16 +302,89 @@ function SchemaButton({ func, metadata }: FunctionCardComponentProps) {
);
}

const REGELRETT_BACKEND_URL =
import.meta.env.MODE === "development" ||
import.meta.env.MODE === "production"
? "https://regelrett-frontend-1024826672490.europe-north1.run.app/api"
: import.meta.env.MODE === "skip"
? "https://regelrett.atgcp1-prod.kartverket-intern.cloud/api"
: "http://localhost:8080";
export function OboFlowFeature({
func,
metadata,
addMetadata,
}: FunctionCardComponentProps) {
const [schemas, setSchemas] = useState<Array<{ id: string; name: string }>>(
[],
);
const [selectedSchema, setSelectedSchema] = useState("");

useEffect(() => {
getSchemasFromRegelrett().then((result) => setSchemas(result));
}, []);

const availableSchemas = schemas.filter(
(schema) => !metadata.data?.find((m) => m.key === schema.id),
);
return (
<form
onSubmit={async (e) => {
e.preventDefault();
if (!func.data) return;
const teamId = metadata.data?.find((obj) => obj.key === "team")?.value;
if (!teamId) return;
const schemaId = (
e.currentTarget.elements.namedItem("schema") as HTMLSelectElement
).value;
const response = await createRegelrettContext({
name: func.data.name,
teamId: teamId,
tableId: schemaId,
});
addMetadata.mutateAsync({
functionId: func.data.id,
key: schemaId,
value: response.id,
});
}}
>
<FormControl isRequired={true} style={{ width: "fit-content" }}>
<FormLabel style={{ fontSize: "14px" }}>
Opprett sikkerhetsskjema
</FormLabel>
<Select
name="schema"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setSelectedSchema(e.target.value)}
placeholder="Velg sikkerhetsskjema"
size={"sm"}
>
{availableSchemas.map((schema) => (
<option key={schema.id} value={schema.id}>
{schema.name}
</option>
))}
</Select>
</FormControl>
<Button
type="submit"
variant="primary"
colorScheme="blue"
size="sm"
width="fit-content"
my="16px"
onClick={(e) => e.stopPropagation()}
isDisabled={!selectedSchema}
>
Opprett skjema
</Button>
</form>
);
}

// const REGELRETT_BACKEND_URL =
// import.meta.env.MODE === "development" ||
// import.meta.env.MODE === "production"
// ? "https://regelrett-frontend-1024826672490.europe-north1.run.app/api"
// : import.meta.env.MODE === "skip"
// ? "https://regelrett.atgcp1-prod.kartverket-intern.cloud/api"
// : "http://localhost:8080";

async function getSchemasFromRegelrett() {
const response = await fetch(`${REGELRETT_BACKEND_URL}/schemas`);
const response = await fetch(`${getregelrettFrontendUrl()}/api/schemas`);
if (!response.ok) {
throw new Error(`Backend error: ${response.status} ${response.statusText}`);
}
Expand All @@ -320,3 +397,79 @@ const RegelrettSchema = object({
id: string(),
name: string(),
});

type RegelrettSchema = z.infer<typeof RegelrettSchema>;

async function getRegelrettTokens() {
const regelrettScope =
import.meta.env.MODE === "skip"
? "api://regelrett-backend/regelrett"
: "api://e9dc946b-6fef-44ab-82f1-c0ec2e402903/.default";

const accounts = msalInstance.getAllAccounts();
const account = accounts[0];
if (!account) {
throw new Error("No active account");
}
const tokenResponse = await msalInstance
.acquireTokenSilent({
scopes: [regelrettScope],
account: account,
})
.catch((error) => {
if (error instanceof InteractionRequiredAuthError) {
return msalInstance.acquireTokenRedirect({
scopes: [regelrettScope],
account: account,
});
}
});

if (!tokenResponse) {
throw new Error("No tokenResponse");
}
return tokenResponse;
}

async function fetchFromRegelrett(path: string, options: RequestInit = {}) {
const tokens = await getRegelrettTokens();
const response = await fetch(`${getregelrettFrontendUrl()}/api/${path}`, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${tokens.accessToken}`,
},
});
if (!response.ok) {
throw new Error(`Backend error: ${response.status} ${response.statusText}`);
}
return response;
}

export async function createRegelrettContext({
name,
teamId,
tableId,
}: { name: string; teamId: string; tableId: string }) {
const response = await fetchFromRegelrett("/contexts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
teamId,
tableId,
}),
});

const json = await response.json();
return RegelrettContext.parse(json);
}

const RegelrettContext = object({
id: string(),
name: string(),
tableId: string(),
teamId: string(),
});
3 changes: 2 additions & 1 deletion src/components/delete-function-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Props = {
export function DeleteFunctionModal({ onClose, isOpen, functionId }: Props) {
const { func, removeFunction } = useFunction(functionId);
const navigate = Route.useNavigate();
const { path, filters } = Route.useSearch();
const { path, flags, filters } = Route.useSearch();

return (
<Modal onClose={onClose} isOpen={isOpen} isCentered>
Expand Down Expand Up @@ -62,6 +62,7 @@ export function DeleteFunctionModal({ onClose, isOpen, functionId }: Props) {
: pathString,
) ?? ["1"],
filters,
flags: flags,
},
});
}}
Expand Down
20 changes: 14 additions & 6 deletions src/components/function-card-selected-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { EditAndSelectButtons } from "./edit-and-select-buttons";
import { MetadataView } from "./metadata/metadata-view";
import { useMetadata } from "@/hooks/use-metadata";
import { Route } from "@/routes";
import { OboFlowFeature } from "../../frisk.config";

export function FunctionCardSelectedView({
functionId,
}: { functionId: number }) {
const { func } = useFunction(functionId);
const { metadata } = useMetadata(functionId);
const { metadata, addMetadata } = useMetadata(functionId);
const { config } = Route.useLoaderData();
const { flags } = Route.useSearch();

return (
<Stack paddingLeft="10px" w="100%">
Expand All @@ -31,16 +33,22 @@ export function FunctionCardSelectedView({
{config.metadata?.map((meta) => (
<MetadataView key={meta.key} metadata={meta} functionId={functionId} />
))}

{config.functionCardComponents.map((Component) => {
return (
{flags?.includes("oboflow") ? (
<OboFlowFeature
func={func}
metadata={metadata}
addMetadata={addMetadata}
/>
) : (
config.functionCardComponents.map((Component) => (
<Component
key={Component.toString()}
func={func}
metadata={metadata}
addMetadata={addMetadata}
/>
);
})}
))
)}
</Stack>
);
}
3 changes: 3 additions & 0 deletions src/components/function-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export function FunctionCard({
],
filters: search.filters,
edit: search.edit,
flags: search.flags,
},
});
}}
Expand All @@ -86,7 +87,9 @@ export function FunctionCard({
variant="ghost"
aria-label="drag"
icon="drag_indicator"
isDisabled={!hasAccess}
/>

<Skeleton isLoaded={!func.isLoading} flex="1" minWidth={0}>
<Text
fontWeight="bold"
Expand Down
3 changes: 2 additions & 1 deletion src/components/search-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Route } from "@/routes";

export function SearchField() {
const navigate = Route.useNavigate();
const search = Route.useSearch();

return (
<Box maxW="400px" my="10px">
Expand All @@ -22,7 +23,7 @@ export function SearchField() {
}}
onChange={(newFunc: { value: string } | null) => {
if (newFunc) {
navigate({ search: { path: [newFunc.value] } });
navigate({ search: { ...search, path: [newFunc.value] } });
}
}}
placeholder="Søk på funksjonsnavnet her..."
Expand Down
13 changes: 10 additions & 3 deletions src/effects/create-and-redirect-effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import { useEffect } from "react";

export function CreateAndRedirectEffect() {
const navigate = Route.useNavigate();
const { path, functionId, newMetadataKey, newMetadataValue, redirect } =
Route.useSearch();
const {
path,
flags,
functionId,
newMetadataKey,
newMetadataValue,
redirect,
} = Route.useSearch();

const { func } = useFunction(functionId ?? 1);
const { addMetadata } = useMetadata(functionId ?? 1);
Expand All @@ -29,7 +35,7 @@ export function CreateAndRedirectEffect() {
if (redirect) {
window.location.href = redirect;
} else {
navigate({ search: { path: path } });
navigate({ search: { path: path, flags: flags } });
}
});
}
Expand All @@ -39,6 +45,7 @@ export function CreateAndRedirectEffect() {
newMetadataValue,
func.data,
path,
flags,
addMetadata,
navigate,
]);
Expand Down
7 changes: 5 additions & 2 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const functionSearchSchema = object({
filters: object({
metadata: array(object({ key: string(), value: unknown().optional() })),
}).optional(),

flags: array(string()).optional(),
});

export const Route = createFileRoute("/")({
Expand All @@ -39,7 +41,7 @@ export const Route = createFileRoute("/")({
});

function Index() {
const { path } = Route.useSearch();
const { path, flags } = Route.useSearch();
const navigate = Route.useNavigate();

const idArrays = path.map((pathArray) =>
Expand All @@ -59,11 +61,12 @@ function Index() {
search: {
path: updatedPathArray,
edit: undefined,
flags: flags,
},
});
}
});
}, [functions, path, navigate, idArrays]);
}, [functions, path, navigate, idArrays, flags]);

return (
<>
Expand Down

0 comments on commit 85461cd

Please sign in to comment.