Skip to content

Commit

Permalink
FKS-990-frontend-legg-til-mulighet-for-a-forvalte-application-category (
Browse files Browse the repository at this point in the history
#150)

* Add simple settings page and form for application category

* Change url, add menuitem and fix basepath error

* Get all application categories in resours admin form

* Rename url to NO and export paths

* Give error if name is duplicate and simplify typed response from fetch

* Fix names of constants

* Edit text

* Edit text 2
  • Loading branch information
IngMyr authored Oct 18, 2024
1 parent b65193f commit a0534d1
Show file tree
Hide file tree
Showing 15 changed files with 691 additions and 19 deletions.
6 changes: 6 additions & 0 deletions app/components/app-bar/AppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ export function AppBar(props: { me: IMeInfo, basePath?: string }) {
Kontrolladministrasjon
</LinkPanel.Title>
</LinkPanel>
{/* <LinkPanel border={false} href={`${props.basePath}/settings`}>
<LinkPanel.Title>
<TasklistIcon title="a11y-title" fontSize="1.5rem"/>
Innstillinger
</LinkPanel.Title>
</LinkPanel>*/}
</Box>

</HGrid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import React, {SetStateAction} from "react";
import {IApplicationResource} from "~/components/resource-admin/types";
import {Checkbox, CheckboxGroup, Radio, RadioGroup, Select, TextField, VStack} from "@navikt/ds-react";
import {IKodeverkApplicationCategory} from "~/data/types";

interface ResourceDataProps {
newApplicationResource: IApplicationResource
setNewApplicationResource: React.Dispatch<SetStateAction<IApplicationResource>>
applicationCategories: IKodeverkApplicationCategory[]
}

export default function ApplicationResourceData({
newApplicationResource,
setNewApplicationResource,
}: ResourceDataProps) {

export default function ApplicationResourceData(
{
newApplicationResource,
setNewApplicationResource,
applicationCategories
}: ResourceDataProps
) {
const doesValueContainNumbersOnly = (value: string) => {
return /^\d*$/.test(value)
}
Expand Down Expand Up @@ -129,6 +133,9 @@ export default function ApplicationResourceData({
};
});
}}>
{applicationCategories?.map((category) => {
return <Checkbox key={category.id} value={category.name}>{category.name}</Checkbox>
})}
<Checkbox value={"Pedagogisk programvare"}>Pedagogisk programvare</Checkbox>
</CheckboxGroup>
</li>
Expand Down
108 changes: 108 additions & 0 deletions app/components/resource/settings/ApplicationCategoryTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {Box, Button, Dropdown, HStack, Link, SortState, Table, VStack} from "@navikt/ds-react";
import {MenuElipsisHorizontalCircleIcon, PlusCircleIcon} from "@navikt/aksel-icons";
import React, {useMemo, useState} from "react";
import {IKodeverkApplicationCategory} from "~/data/types";
import {Outlet, useNavigate} from "@remix-run/react";
import {
getApplicationCategoryDeleteUrl,
getApplicationCategoryEditUrl,
SETTINGS_APPLICATION_CATEGORY_CREATE
} from "~/data/constants";

type ApplicationCategoryTableProps = {
applicationCategories: IKodeverkApplicationCategory[]
basePath?: string
}

export const ApplicationCategoryTable = ({applicationCategories, basePath}: ApplicationCategoryTableProps) => {
const navigate = useNavigate()
const [sort, setSort] = useState<SortState | undefined>();

const handleSortByName = () => {
setSort(
sort?.direction === "descending" ? undefined : {
orderBy: "name",
direction: sort?.direction === "ascending" ? "descending" : "ascending"
}
);
};

const sortedApplicationCategories = useMemo(() => {
return applicationCategories.sort((a, b) => {
if (sort?.direction === "ascending") {
return a.name.localeCompare(b.name);
}
if (sort?.direction === "descending") {
return b.name.localeCompare(a.name);
}
return 0;
})
}, [applicationCategories, sort]);

return (
<VStack>
<Outlet/>
<Table
sort={sort}
onSortChange={handleSortByName}
>
<Table.Header>
<Table.Row>
<Table.ColumnHeader scope="col" sortable sortKey="name">Navn</Table.ColumnHeader>
<Table.ColumnHeader scope="col">Beskrivelse</Table.ColumnHeader>
<Table.ColumnHeader scope="col" align="right">
Handlinger
</Table.ColumnHeader>
</Table.Row>
</Table.Header>

<Table.Body>
{sortedApplicationCategories.map(({id, name, description}) => (
<Table.Row key={id}>
<Table.HeaderCell scope="row" className="nowrap">{name}</Table.HeaderCell>
<Table.DataCell textSize={"small"}>{description}</Table.DataCell>
<Table.DataCell align="right">
<Dropdown>
<HStack justify="end">
<Button
as={Dropdown.Toggle}
icon={<MenuElipsisHorizontalCircleIcon title="Meny"/>}
size="small"
variant="tertiary"
/>
</HStack>
<Dropdown.Menu>
<Dropdown.Menu.GroupedList>
<Dropdown.Menu.GroupedList.Item
onClick={() => navigate(getApplicationCategoryEditUrl(id))}
>
Rediger
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item
onClick={() => navigate(getApplicationCategoryDeleteUrl(id))}

>
Slett
</Dropdown.Menu.GroupedList.Item>
</Dropdown.Menu.GroupedList>
</Dropdown.Menu>
</Dropdown>
</Table.DataCell>
</Table.Row>
))}
</Table.Body>
</Table>
<Box paddingBlock={"4"}>
<Button
as={Link}
underline={false}
variant={"tertiary"}
icon={<PlusCircleIcon aria-hidden/>}
href={`${basePath}${SETTINGS_APPLICATION_CATEGORY_CREATE}`}
>
Legg til ny kategori
</Button>
</Box>
</VStack>
)
}
27 changes: 27 additions & 0 deletions app/components/resource/settings/CustomLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {BodyShort, Box, Heading, VStack} from "@navikt/ds-react";

type CustomLinkPanelProps = {
title: string;
description?: string;
link: string;
}
export const CustomLink = ({title, description, link}: CustomLinkPanelProps) => {
return (
<Box
as={"a"}
padding="4"
// background="surface-action-subtle"
borderRadius="large"
href={link}
className={"custom-link"}

>
<VStack>
<Heading size="small">
{title}
</Heading>
{description && <BodyShort size="small">{description}</BodyShort>}
</VStack>
</Box>
);
}
11 changes: 11 additions & 0 deletions app/components/resource/settings/CustomLinkPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {HGrid} from "@navikt/ds-react";
import {ReactNode} from "react";

interface CustomLinkPanelProps {
children: ReactNode;
}

export const CustomLinkPanel = ({children}: CustomLinkPanelProps) =>
<HGrid gap="6" columns={{xs: 1, md: 2}}>
{children}
</HGrid>
8 changes: 8 additions & 0 deletions app/data/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const SETTINGS = "/innstillinger"

export const SETTINGS_APPLICATION_CATEGORY = "/innstillinger/applikasjonskategori"
export const SETTINGS_APPLICATION_CATEGORY_CREATE = "/innstillinger/applikasjonskategori/rediger"
export const getApplicationCategoryEditUrl = (id: number): string => `/innstillinger/applikasjonskategori/${id}/rediger`;
export const getApplicationCategoryDeleteUrl = (id: number): string => `/innstillinger/applikasjonskategori/${id}/slett`;

export const SETTINGS_USER_TYPES = "/innstillinger/user-types"
115 changes: 115 additions & 0 deletions app/data/fetch-kodeverk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {BASE_PATH, RESOURCE_API_URL} from "../../environment";
import logger from "~/logging/logger";
import {IKodeverkApplicationCategory} from "~/data/types";

export const fetchApplicationCategories = async (request: Request): Promise<IKodeverkApplicationCategory[]> => {
const response = await fetch(`${RESOURCE_API_URL}${BASE_PATH}/api/resources/kodeverk/applikasjonskategori/v1`, {
headers: request.headers
});

if (response.ok) {
return response.json();
}

if (response.status === 403) {
throw new Error("Det ser ut som om du mangler rettigheter i løsningen");
}
if (response.status === 401) {
throw new Error("Påloggingen din er utløpt");
}
throw new Error("En feil oppstod under henting av applikasjonskategorier");
}

export const fetchApplicationCategory = async (request: Request, id: string): Promise<IKodeverkApplicationCategory> => {
const response = await fetch(`${RESOURCE_API_URL}${BASE_PATH}/api/resources/kodeverk/applikasjonskategori/v1/${id}`, {
headers: request.headers
});

if (response.ok) {
return response.json();
}

if (response.status === 403) {
throw new Error("Det ser ut som om du mangler rettigheter i løsningen")
}
if (response.status === 401) {
throw new Error("Påloggingen din er utløpt")
}
throw new Error("En feil oppstod under henting av applikasjonskategorien")
}

export const createApplicationCategory = async (
token: string | null,
name: string,
description: string
) => {

const url = `${RESOURCE_API_URL}${BASE_PATH}/api/resources/kodeverk/applikasjonskategori/v1`

logger.info("POST New kodeverk - app. category to url:", url, " with body ", JSON.stringify({
name: name,
description: description,
}));

return await fetch(url, {
headers: {
Authorization: token ?? "",
'content-type': 'application/json'
},
method: 'POST',
body: JSON.stringify({
name: name,
description: description,
})

})
}

export const editApplicationCategory = async (
token: string | null,
id: string,
name: string,
description: string
) => {

const url = `${RESOURCE_API_URL}${BASE_PATH}/api/resources/kodeverk/applikasjonskategori/v1`

logger.info("PUT kodeverk - app. category to url:", url, " with body ", JSON.stringify({
id: id,
name: name,
description: description,
}));

return await fetch(url, {
headers: {
Authorization: token ?? "",
'content-type': 'application/json'
},
method: 'PUT',
body: JSON.stringify({
id: id,
name: name,
description: description,
})

})
}

export const deleteApplicationCategory = async (
token: string | null,
id: string
) => {

const url = `${RESOURCE_API_URL}${BASE_PATH}/api/resources/kodeverk/applikasjonskategori/v1/${id}`

logger.info("DELETE kodeverk - app. category to url:", url);

return await fetch(url, {
headers: {
Authorization: token ?? "",
'content-type': 'application/json'
},
method: 'DELETE'

})
}
7 changes: 7 additions & 0 deletions app/data/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ export interface IUnitTree {
orgUnits: IUnitItem[]
}

export interface IKodeverkApplicationCategory {
id: number
name: string
description: string
category: string
}

// General cookie interface
export interface ICookie {
key: string
Expand Down
38 changes: 38 additions & 0 deletions app/routes/innstillinger._index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {Heading, VStack} from "@navikt/ds-react";
import {CustomLink} from "~/components/resource/settings/CustomLink";
import {CustomLinkPanel} from "~/components/resource/settings/CustomLinkPanel";
import {BASE_PATH} from "../../environment";
import {json} from "@remix-run/node";
import {useLoaderData} from "@remix-run/react";
import {SETTINGS_APPLICATION_CATEGORY, SETTINGS_USER_TYPES} from "~/data/constants";

export async function loader() {
return json({
basePath: BASE_PATH === "/" ? "" : BASE_PATH
})
}


export default function ResourcesSettings() {
const loaderData = useLoaderData<typeof loader>()
const basePath = loaderData.basePath

return (
<VStack className={"content"} gap="4">
<Heading className={"heading"} level="1" size="xlarge">Ressursinnstillinger</Heading>

<CustomLinkPanel>
<CustomLink
title={"Brukertyper"}
description={"Endre navn på brukertyper som kan tildeles ressursen."}
link={`${basePath}${SETTINGS_USER_TYPES}`}
/>
<CustomLink
title={"Applikasjonskategori"}
description={"Innstillinger for applikasjonskategorier som kan brukes for å gruppere og beskrive ressurser."}
link={`${basePath}${SETTINGS_APPLICATION_CATEGORY}`}
/>
</CustomLinkPanel>
</VStack>
);
}
Loading

0 comments on commit a0534d1

Please sign in to comment.