diff --git a/apps/data-norge/public/content/docs/metadata-quality/metadata-quality.nb.mdx b/apps/data-norge/public/content/docs/metadata-quality/metadata-quality.nb.mdx index f1e1b3e7..203c813c 100644 --- a/apps/data-norge/public/content/docs/metadata-quality/metadata-quality.nb.mdx +++ b/apps/data-norge/public/content/docs/metadata-quality/metadata-quality.nb.mdx @@ -17,12 +17,12 @@ description: Om vurdering av metadatakvalitet på data.norge.no og hvofor vi gj Data.norge.no har fire ulike nivåer for metadatakvalitet: -| Nivå | Begrunnelse | -| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Utmerket** | Dette er bra saker! 75 % eller flere av kvalitetskriteriene er innfridd. Beskrivelsen er informativ og gir stor nytteverdi for brukeren, noe som bør være målet for alle beskrivelser. | -| **God** | 50-75 % av kvalitetskriteriene er innfridd. | -| **Tilstrekkelig** (bør forbedres) | Bare 25-50 % av kvalitetskriteriene er innfridd, og beskrivelsen bør absolutt forbedres. | -| **Dårlig** | 25 % eller færre av kvalitetskriteriene er innfridd. Beskrivelsen er mangelfull og dekker sjelden. | +| Nivå | Begrunnelse | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Utmerket | Dette er bra saker! 75 % eller flere av kvalitetskriteriene er innfridd. Beskrivelsen er informativ og gir stor nytteverdi for brukeren, noe som bør være målet for alle beskrivelser. | +| God | 50-75 % av kvalitetskriteriene er innfridd. | +| Tilstrekkelig | Bare 25-50 % av kvalitetskriteriene er innfridd, og beskrivelsen bør absolutt forbedres. | +| Dårlig | 25 % eller færre av kvalitetskriteriene er innfridd. Beskrivelsen er mangelfull og dekker sjelden. | ## Hvorfor måler vi metadatakvalitet diff --git a/apps/data-norge/src/app/[lang]/view/api/page.tsx b/apps/data-norge/src/app/[lang]/view/api/page.tsx new file mode 100644 index 00000000..596aa3e1 --- /dev/null +++ b/apps/data-norge/src/app/[lang]/view/api/page.tsx @@ -0,0 +1,24 @@ +import { i18n, getDictionary } from '@fdk-frontend/dictionaries'; + +import DetailsPage from '../../../components/details/details-page/api'; +import { DetailsPageWrapperProps } from '../page'; + +const APIPageWrapper = async (props: DetailsPageWrapperProps) => { + const params = await props.params; + const locale = params.lang ?? i18n.defaultLocale; + const commonDictionary = await getDictionary(locale, 'common'); + + return ( + + ); +}; + +export const metadata = { + title: 'API: Inntektsmottakere API - data.norge.no', + description: 'POC for detaljvisning', +}; + +export default APIPageWrapper; diff --git a/apps/data-norge/src/app/[lang]/view/begrep/page.tsx b/apps/data-norge/src/app/[lang]/view/begrep/page.tsx new file mode 100644 index 00000000..cbf04061 --- /dev/null +++ b/apps/data-norge/src/app/[lang]/view/begrep/page.tsx @@ -0,0 +1,24 @@ +import { i18n, getDictionary } from '@fdk-frontend/dictionaries'; + +import DetailsPage from '../../../components/details/details-page/begrep'; +import { DetailsPageWrapperProps } from '../page'; + +const BegrepsPageWrapper = async (props: DetailsPageWrapperProps) => { + const params = await props.params; + const locale = params.lang ?? i18n.defaultLocale; + const commonDictionary = await getDictionary(locale, 'common'); + + return ( + + ); +}; + +export const metadata = { + title: 'Begrep: egenandel på dagpenger - data.norge.no', + description: 'POC for detaljvisning', +}; + +export default BegrepsPageWrapper; diff --git a/apps/data-norge/src/app/[lang]/view/datasett-worstcase/page.tsx b/apps/data-norge/src/app/[lang]/view/datasett-worstcase/page.tsx new file mode 100644 index 00000000..1306e593 --- /dev/null +++ b/apps/data-norge/src/app/[lang]/view/datasett-worstcase/page.tsx @@ -0,0 +1,24 @@ +import { i18n, getDictionary } from '@fdk-frontend/dictionaries'; + +import DetailsPage from '../../../components/details/details-page/datasett-worstcase'; +import { DetailsPageWrapperProps } from '../page'; + +const DatasettRichPageWrapper = async (props: DetailsPageWrapperProps) => { + const params = await props.params; + const locale = params.lang ?? i18n.defaultLocale; + const commonDictionary = await getDictionary(locale, 'common'); + + return ( + + ); +}; + +export const metadata = { + title: 'Datasett: Energimålinger kommunale bygg - data.norge.no', + description: 'POC for detaljvisning', +}; + +export default DatasettRichPageWrapper; diff --git a/apps/data-norge/src/app/[lang]/view/layout.tsx b/apps/data-norge/src/app/[lang]/view/layout.tsx new file mode 100644 index 00000000..df188c14 --- /dev/null +++ b/apps/data-norge/src/app/[lang]/view/layout.tsx @@ -0,0 +1,6 @@ +import DocsLayout from '@fdk-frontend/ui/layouts/docs-layout'; + +export const revalidate = 0; +export const dynamic = 'force-dynamic'; + +export default DocsLayout; diff --git a/apps/data-norge/src/app/[lang]/view/page.tsx b/apps/data-norge/src/app/[lang]/view/page.tsx new file mode 100644 index 00000000..206bca3b --- /dev/null +++ b/apps/data-norge/src/app/[lang]/view/page.tsx @@ -0,0 +1,30 @@ +import { i18n, getDictionary, type LocaleCodes } from '@fdk-frontend/dictionaries'; + +import DetailsPage from '../../components/details/details-page'; + +export type DetailsPageWrapperProps = { + params: Promise<{ + lang: LocaleCodes; + slug: string[]; + }>; +}; + +const DetailsPageWrapper = async (props: DetailsPageWrapperProps) => { + const params = await props.params; + const locale = params.lang ?? i18n.defaultLocale; + const commonDictionary = await getDictionary(locale, 'common'); + + return ( + + ); +}; + +export const metadata = { + title: 'Datasett: Energimålinger kommunale bygg - data.norge.no', + description: 'POC for detaljvisning', +}; + +export default DetailsPageWrapper; diff --git a/apps/data-norge/src/app/components/details/community-tab/community-tab.module.scss b/apps/data-norge/src/app/components/details/community-tab/community-tab.module.scss new file mode 100644 index 00000000..89cd965e --- /dev/null +++ b/apps/data-norge/src/app/components/details/community-tab/community-tab.module.scss @@ -0,0 +1,64 @@ +.notice { + margin-top:2rem; + + .toolbar { + @media (max-width: 500px) { + flex-direction:column; + align-items:flex-start; + } + } +} + +.forumStats { + display:inline-flex; + flex-direction:column; + text-align:center; + padding:0.25rem; +} + +.section { + :global(.table) { + + .threadLink { + margin-bottom:0.5rem; + line-height:1.5rem; + } + + :global(.fds-tag) { + white-space: nowrap; + font-size:0.9rem; + + :global(.fds-link) { + text-decoration:none; + // color: var(--color-text); + + &:hover { + text-decoration:underline; + text-decoration-thickness: max(0.12em); + } + } + } + + tr:hover { + :global(.fds-tag) { + // background:white; + } + } + } +} + +.tableScroller { + margin:1rem -2rem; + + > div { + padding:0 2rem; + } + + @media (max-width: 400px) { + margin:1rem -1.5rem; + + > div { + padding:0 1.5rem; + } + } +} \ No newline at end of file diff --git a/apps/data-norge/src/app/components/details/community-tab/index.tsx b/apps/data-norge/src/app/components/details/community-tab/index.tsx new file mode 100644 index 00000000..6fc19873 --- /dev/null +++ b/apps/data-norge/src/app/components/details/community-tab/index.tsx @@ -0,0 +1,126 @@ +import { PropsWithChildren } from 'react'; +import NextLink from 'next/link'; + +import { Heading, Button, Link, Paragraph, Alert, Tag } from '@digdir/designsystemet-react'; +import { ExternalLinkIcon, Chat2Icon } from '@navikt/aksel-icons'; + +import Badge from '@fdk-frontend/ui/badge'; +import HStack from '@fdk-frontend/ui/hstack'; +import VStack from '@fdk-frontend/ui/vstack'; +import { Subtext } from '@fdk-frontend/ui/typography'; +import ScrollShadows from '@fdk-frontend/ui/scroll-shadows'; +import IconBadge from '@fdk-frontend/ui/icon-badge'; + +import styles from './community-tab.module.scss'; + +const CommunityTab = ({ children }: PropsWithChildren) => { + return ( +
+ + {/**/} + + Diskusjoner på Datalandsbyen 2 + + + + + + + + + + + + + + + + +
+ + + + + Vannverk - transportsystem + + Kommentartråder + Postet av fdk-community-admin + + + +
+ 7 + Stemmer +
+
+ 12 + Innlegg +
+
+ 16k + Visninger +
+
+
+ + + + + Strømstøtten minutt for minutt - beregnet med åpne data + + Gode eksempler på bruk + Postet av fdk-community-admin + + + +
+ 7 + Stemmer +
+
+ 12 + Innlegg +
+
+ 16k + Visninger +
+
+
+
+ + + + Hva er Datalandsbyen? + + + Datalandsbyen er vårt nettforum hvor du kan etterspørre data, dele erfaringer og spørre om råd som gjelder datadeling og informasjonsforvaltning. + + + + + + + +
+ ); +} + +export default CommunityTab; \ No newline at end of file diff --git a/apps/data-norge/src/app/components/details/dataset-description/index.tsx b/apps/data-norge/src/app/components/details/dataset-description/index.tsx new file mode 100644 index 00000000..05842884 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-description/index.tsx @@ -0,0 +1,56 @@ +import { PropsWithChildren } from 'react'; + +import { Link, type LinkProps } from '@digdir/designsystemet-react'; + +import Markdown from '@fdk-frontend/ui/markdown'; + +const allowedElements = [ + 'p', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'blockquote', + 'pre', + 'code', + 'ul', + 'ol', + 'li', + 'hr', + 'table', + 'thead', + 'tbody', + 'tr', + 'th', + 'td', + 'a', + 'strong', + 'em', + 'img', + 'br', + 'del', + 'span', +]; + +type Props = PropsWithChildren & { + className?: string; +}; + +const DatasetDescription = ({ className, children }: Props) => { + return ( +
+ , + }} + > + {children} + +
+ ); +}; + +export default DatasetDescription; diff --git a/apps/data-norge/src/app/components/details/dataset-details/components/concept-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/components/concept-details/index.tsx new file mode 100644 index 00000000..6fd005f8 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/components/concept-details/index.tsx @@ -0,0 +1,52 @@ +import { PropsWithChildren, useContext } from 'react'; + +import { Heading, Link } from '@digdir/designsystemet-react'; +import Article from '@fdk-frontend/ui/article'; + +import PlaceholderBox from '../../../placeholder-box'; +import { DatasetDetailsContext } from '../..'; + +const ConceptDetails = ({ fields, ...props }: { fields: any } & PropsWithChildren) => { + const { showEmptyRows } = useContext(DatasetDetailsContext); + + if (!showEmptyRows && fields === null) return false; + + return ( +
+ + Begreper brukt i datasett + + {fields !== null ? ( +
+
+ felles omsorg +
+
+
+ omsorgssituasjon der begge foreldre til et gitt barn bor sammen og har omsorgen for barnet i + skattleggingsperioden +
+
+
+ samlet uføreytelse fra andre enn folketrygden +
+
+
+ samlet brutto uføreytelser (uføreytelser før skatt) som du får fra andre enn folketrygden ( + herunder uføreytelser fra SPK, uføreytelser fra andre pensjonsordninger herunder + uføreytelser fra IPA/IPS og uføreytelser fra utlandet) . Uføreytelser regnes som en del av + inntektene dine og skattlegges som vanlig lønnsinntekt. +
+
+
+ ) : ( + Ingen begreper oppgitt + )} +
+ ); +}; + +export default ConceptDetails; diff --git a/apps/data-norge/src/app/components/details/dataset-details/components/contact-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/components/contact-details/index.tsx new file mode 100644 index 00000000..193fdca7 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/components/contact-details/index.tsx @@ -0,0 +1,50 @@ +import { PropsWithChildren, useContext } from 'react'; + +import { Heading, Link } from '@digdir/designsystemet-react'; +import { ExternalLinkIcon } from '@navikt/aksel-icons'; + +import PlaceholderText from '../../../placeholder-text'; +import { DatasetDetailsContext } from '../../'; + +const ContactDetails = ({ fields, ...props }: { fields: any } & PropsWithChildren) => { + const { showEmptyRows } = useContext(DatasetDetailsContext); + + return ( +
+ + Kontaktinformasjon + +
+
Kontaktpunkt:
+
+ + {fields['Kontaktpunkt']} + + +
+
E-post:
+
+ {fields['E-post']} +
+ {fields['Telefon'] !== null || + (showEmptyRows && ( + <> +
Telefon:
+
+ {fields['Telefon'] ? ( + {fields['Telefon']} + ) : ( + Ikke oppgitt + )} +
+ + ))} +
+
+ ); +}; + +export default ContactDetails; diff --git a/apps/data-norge/src/app/components/details/dataset-details/components/content-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/components/content-details/index.tsx new file mode 100644 index 00000000..8e5356b7 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/components/content-details/index.tsx @@ -0,0 +1,76 @@ +import React, { PropsWithChildren, useContext } from 'react'; +import { Heading, Link, HelpText, Paragraph } from '@digdir/designsystemet-react'; +import Article from '@fdk-frontend/ui/article'; +import HStack from '@fdk-frontend/ui/hstack'; + +import PlaceholderText from '../../../placeholder-text'; +import { DatasetDetailsContext } from '../../'; + +const ContentDetails = ({ fields, ...props }: { fields: any } & PropsWithChildren) => { + const { showEmptyRows } = useContext(DatasetDetailsContext); + + const renderContentValue = (value: any) => { + if (!value) return Ikke oppgitt; + if (Array.isArray(value)) { + return ( +
+
    + {value.map((v) => ( +
  1. + {v} +
  2. + ))} +
+
+ ); + } + if (value.startsWith('http')) return {value}; + return value; + }; + + return ( +
+ + Innhold + +
+ {Object.entries(fields).map(([key, value]) => { + if (!showEmptyRows && fields[key] === null) return false; + + return ( + +
+ {key === 'I samsvar med' ? ( + + {key}: + + + Referanse til en implementasjonsregel eller annen spesifikasjon, som + ligger til grunn for opprettelsen av datasettet. + + Les mer her + + + + + ) : ( + `${key}:` + )} +
+
{renderContentValue(value)}
+
+ ); + })} +
+
+ ); +}; + +export default ContentDetails; diff --git a/apps/data-norge/src/app/components/details/dataset-details/components/general-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/components/general-details/index.tsx new file mode 100644 index 00000000..30e4b10a --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/components/general-details/index.tsx @@ -0,0 +1,90 @@ +import { PropsWithChildren } from 'react'; + +import { Heading, Link, Tag, type TagProps, HelpText, Paragraph } from '@digdir/designsystemet-react'; +import { ExternalLinkIcon } from '@navikt/aksel-icons'; +import HStack from '@fdk-frontend/ui/hstack'; + +import PlaceholderText from '../../../placeholder-text'; + +const GeneralDetails = ({ fields, ...props }: { fields: any } & PropsWithChildren) => { + const getMetadataQuality = (value: number) => { + if (value < 25) return { color: 'danger', label: `Dårlig (${value}%)` }; + if (value < 50) return { color: 'warning', label: `Tilstrekkelig (${value}%)` }; + if (value < 75) return { color: 'success', label: `God (${value}%)` }; + return { color: 'success', label: `Utmerket (${value}%)` }; + }; + + const metadataQuality = getMetadataQuality(fields['Metadatakvalitet']); + + return ( +
+ + Generelt + +
+
Ansvarlig virksomhet:
+
+ {fields['Ansvarlig virksomhet'] ? ( + {fields['Ansvarlig virksomhet']} + ) : ( + Ikke oppgitt + )} +
+
+ + Publisert: + + + Denne datoen sier når datasettet ble publisert på data.norge.no. Det kan ha vært + tilgjengelig tidligere andre steder. + + + +
+
9. mars 2022
+
Dokumentasjon:
+
+ + https://github.com/opendatalab-no/open-municipal-data + + +
+
+ + Metadatakvalitet: + + + Metadatakvalitet er en indikator på hvor godt datasettene er beskrevet ved hjelp av + metadata. + + + Les mer om metadatakvalitet her + + + +
+
+ + {metadataQuality.label} + +
+
+
+ ); +}; + +export default GeneralDetails; diff --git a/apps/data-norge/src/app/components/details/dataset-details/components/legal-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/components/legal-details/index.tsx new file mode 100644 index 00000000..11d77c51 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/components/legal-details/index.tsx @@ -0,0 +1,51 @@ +import React, { PropsWithChildren, useContext } from 'react'; + +import { Heading, Link } from '@digdir/designsystemet-react'; + +import Article from '@fdk-frontend/ui/article'; + +import PlaceholderBox from '../../../placeholder-box'; +import { DatasetDetailsContext } from '../..'; + +const LegalDetails = ({ fields, ...props }: { fields: any } & PropsWithChildren) => { + const { showEmptyRows } = useContext(DatasetDetailsContext); + + if (!showEmptyRows && fields === null) return false; + + return ( +
+ + Lovhjemler + + {fields !== null ? ( +
+ {Object.entries(fields).map(([key, value]) => { + return ( + +
{key}:
+
+
+
    + {(value as string[]).map((v) => ( +
  1. + {v} +
  2. + ))} +
+
+
+
+ ); + })} +
+ ) : ( + Ingen lovhjemler oppgitt + )} +
+ ); +}; + +export default LegalDetails; diff --git a/apps/data-norge/src/app/components/details/dataset-details/components/relation-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/components/relation-details/index.tsx new file mode 100644 index 00000000..9a4fefd4 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/components/relation-details/index.tsx @@ -0,0 +1,45 @@ +import { PropsWithChildren, useContext } from 'react'; + +import { Heading, Link, Tag } from '@digdir/designsystemet-react'; + +import PlaceholderBox from '../../../placeholder-box'; +import { DatasetDetailsContext } from '../..'; + +const RelationDetails = ({ fields, ...props }: { fields: any } & PropsWithChildren) => { + const { showEmptyRows } = useContext(DatasetDetailsContext); + + if (!showEmptyRows && fields === null) return false; + + return ( +
+ + Relasjoner til datasett + + {fields !== null ? ( +
+
+ Hydrologiske data +
+
+
+ Ramsund og rognan revisjon + + Åpne data + +
+
+
+ ) : ( + Ingen relasjoner til datasett oppgitt + )} +
+ ); +}; + +export default RelationDetails; diff --git a/apps/data-norge/src/app/components/details/dataset-details/data/full.json b/apps/data-norge/src/app/components/details/dataset-details/data/full.json new file mode 100644 index 00000000..0cb84f50 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/data/full.json @@ -0,0 +1,68 @@ +{ + "sections": [ + { + "sectionTitle": "Generelt", + "fields": { + "Ansvarlig virksomhet": "Arbeids- og velferdsetaten", + "Publisert": "9. mars 2022", + "Dokumentasjon": "https://github.com/opendatalab-no/open-municipal-data", + "Metadatakvalitet": 75 + } + }, + { + "sectionTitle": "Kontaktinformasjon", + "fields": { + "Kontaktpunkt": "https://www.sintef.no/alle-ansatte/ansatt/erlend.stav/", + "E-post": "erlend.stav@sintef.no", + "Telefon": "22 00 22 00" + } + }, + { + "sectionTitle": "Innhold", + "fields": { + "Språk": "Engelsk", + "Innholdsleverandører": "Kunstklubben 9", + "Opphav": "Autoritativ kilde", + "Oppdateringsfrekvens": "daglig", + "Utgitt": "09.03.2016", + "Sist oppdatert": "22.03.2021", + "Aktualitet": "Høyaktuelt", + "Kompletthet": "Helt komplett", + "Nøyaktighet": "Ekstremt nøyaktig", + "Relevans": "Relevant for testing", + "Tilgjengelighet": "Kun til intern bruk", + "Geografisk avgrenset til": "https://example.com/norge", + "Tidsmessig avgrenset til": "07.12.2020 - 08.12.2020", + "I samsvar med": [ + "Standard for rapportering av data", + "Konsolidering i samarbeidende grupper (KRT-1130)" + ] + } + }, + { + "sectionTitle": "Lovhjemler", + "fields": { + "Skjermingshjemmel": ["Lov om Enhetsregisteret §22"], + "Behandlingsgrunnlag": ["Lov om Enhetsregisteret §5", "Lov om Enhetsregisteret §6"], + "Utleveringshjemmel": ["Forskrift om gebyr til Brønnøysundregistrene §6", "Lov om Enhetsregisteret §22"] + } + }, + { + "sectionTitle": "Begreper brukt i datasett", + "fields": { + "felles omsorg": "omsorgssituasjon der begge foreldre til et gitt barn bor sammen og har omsorgen for barnet i skattleggingsperioden", + "samlet uføreytelse fra andre enn folketrygden": "samlet brutto uføreytelser (uføreytelser før skatt) som du får fra andre enn folketrygden ( herunder uføreytelser fra SPK, uføreytelser fra andre pensjonsordninger herunder uføreytelser fra IPA/IPS og uføreytelser fra utlandet). Uføreytelser regnes som en del av inntektene dine og skattlegges som vanlig lønnsinntekt." + } + }, + { + "sectionTitle": "Relasjoner til datasett", + "fields": [ + { + "title": "Hydrologiske data", + "publisher": "Ramsund of rognan revisjon", + "access": "Åpne data" + } + ] + } + ] +} diff --git a/apps/data-norge/src/app/components/details/dataset-details/data/worst.json b/apps/data-norge/src/app/components/details/dataset-details/data/worst.json new file mode 100644 index 00000000..b06b7cc4 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/data/worst.json @@ -0,0 +1,52 @@ +{ + "sections": [ + { + "sectionTitle": "Generelt", + "fields": { + "Ansvarlig virksomhet": "Arbeids- og velferdsetaten", + "Publisert": "9. mars 2022", + "Dokumentasjon": "https://github.com/opendatalab-no/open-municipal-data", + "Metadatakvalitet": 21 + } + }, + { + "sectionTitle": "Kontaktinformasjon", + "fields": { + "Kontaktpunkt": "https://www.sintef.no/alle-ansatte/ansatt/erlend.stav/", + "E-post": "erlend.stav@sintef.no", + "Telefon": null + } + }, + { + "sectionTitle": "Innhold", + "fields": { + "Språk": "Norsk", + "Innholdsleverandører": null, + "Opphav": null, + "Oppdateringsfrekvens": null, + "Utgitt": null, + "Sist oppdatert": "22.03.2021", + "Aktualitet": null, + "Kompletthet": null, + "Nøyaktighet": null, + "Relevans": null, + "Tilgjengelighet": null, + "Geografisk avgrenset til": null, + "Tidsmessig avgrenset til": null, + "I samsvar med": null + } + }, + { + "sectionTitle": "Lovhjemler", + "fields": null + }, + { + "sectionTitle": "Begreper brukt i datasett", + "fields": null + }, + { + "sectionTitle": "Relasjoner til datasett", + "fields": null + } + ] +} diff --git a/apps/data-norge/src/app/components/details/dataset-details/dataset-details.module.scss b/apps/data-norge/src/app/components/details/dataset-details/dataset-details.module.scss new file mode 100644 index 00000000..0ef1c335 --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/dataset-details.module.scss @@ -0,0 +1,35 @@ +@import '@fdk-frontend/ui/core/mixins'; + +.details { + > section { + &:not(:last-child) { + margin-bottom: 2rem; + } + + > :global(.fds-heading) { + margin-bottom: 1em; + } + } + + .toggleButton { + float: right; + position: relative; + top: -0.75rem; + } +} + +.article { + @include article(); +} + +.placeholderText { + opacity: 0.5; + font-style: italic; +} + +.greyBox { + border: 1px solid #ddd; + border-radius: 0.25rem; + padding: 0.75rem 0.75rem; + background: #fafafa; +} diff --git a/apps/data-norge/src/app/components/details/dataset-details/index.tsx b/apps/data-norge/src/app/components/details/dataset-details/index.tsx new file mode 100644 index 00000000..e4759fca --- /dev/null +++ b/apps/data-norge/src/app/components/details/dataset-details/index.tsx @@ -0,0 +1,87 @@ +import { useState, createContext } from 'react'; +import { Heading, ChipGroup, ChipToggle, Button } from '@digdir/designsystemet-react'; +import { EyeSlashIcon, EyeIcon } from '@navikt/aksel-icons'; + +import GeneralDetails from './components/general-details'; +import ContactDetails from './components/contact-details'; +import ContentDetails from './components/content-details'; +import LegalDetails from './components/legal-details'; +import ConceptDetails from './components/concept-details'; +// import RelationDetails from './components/relation-details'; + +import styles from './dataset-details.module.scss'; + +const DatasetDetailsContext = createContext<{ showEmptyRows: boolean }>({ showEmptyRows: true }); + +const DatasetDetails = ({ details }: any) => { + const [showEmptyRows, setShowEmptyRows] = useState(true); + + const { sections } = details; + + const general = sections.find((s: any) => s.sectionTitle === 'Generelt'); + const contact = sections.find((s: any) => s.sectionTitle === 'Kontaktinformasjon'); + const content = sections.find((s: any) => s.sectionTitle === 'Innhold'); + const legal = sections.find((s: any) => s.sectionTitle === 'Lovhjemler'); + const concept = sections.find((s: any) => s.sectionTitle === 'Begreper brukt i datasett'); + // const relation = sections.find((s: any) => s.sectionTitle === 'Relasjoner til datasett'); + + return ( + +
+ + + + + + + {/**/} +
+ + Tema + + + {['Energi', 'Forvaltning og offentlig sektor'].map((theme) => ( + {theme} + ))} + +
+
+ + Søkeord + + + {['arbeidsledige', 'statistikk', 'arbeidsmarked', 'nav'].map((term) => ( + {term} + ))} + +
+
+
+ ); +}; + +export default DatasetDetails; +export { DatasetDetailsContext }; diff --git a/apps/data-norge/src/app/components/details/details-page/api/index.tsx b/apps/data-norge/src/app/components/details/details-page/api/index.tsx new file mode 100644 index 00000000..e6049d71 --- /dev/null +++ b/apps/data-norge/src/app/components/details/details-page/api/index.tsx @@ -0,0 +1,357 @@ +'use client'; + +import { useState } from 'react'; +import cn from 'classnames'; + +import { type Dictionary, type LocaleCodes } from '@fdk-frontend/dictionaries'; + +import Breadcrumbs from '@fdk-frontend/ui/breadcrumbs'; +import Badge from '@fdk-frontend/ui/badge'; +import StarButton from '@fdk-frontend/ui/star-button'; +import { BrandDivider } from '@fdk-frontend/ui/divider'; +import ScrollShadows from '@fdk-frontend/ui/scroll-shadows'; +import { + Heading, + Button, + Link, + Tag, + Tabs, + TabList, + Tab, + TabContent, + ChipGroup, + ChipToggle, +} from '@digdir/designsystemet-react'; +import { ExternalLinkIcon } from '@navikt/aksel-icons'; + +import CommunityTab from '../../community-tab'; + +import styles from '../details-page.module.scss'; + +export type DetailsPageType = { + locale: LocaleCodes; + commonDictionary: Dictionary; +}; + +export default function DetailsPage({ locale, commonDictionary }: DetailsPageType) { + const [activeTab, setActiveTab] = useState('oversikt'); + const [highlight, setHighlight] = useState(false); + + const blink = () => { + setHighlight(true); + setTimeout(() => setHighlight(false), 1000); + }; + + const breadcrumbList = [ + { + href: '/data-services', + text: 'API-er', + }, + { + href: '#', + text: 'Energimålinger kommunale bygg', + }, + ]; + + const relatedDatasets = [ + { name: "Vannverk - transportsystem", publisher: "Mattilsynet", accessLevel: "open" }, + { name: "Grunnboken", publisher: "Kartverket", accessLevel: "open" }, + { name: "Kjøretøyopplysninger", publisher: "Statens vegvesen", accessLevel: "limited" }, + // { name: "Merverdiavgiftsregisteret", publisher: "Skatteetaten", accessLevel: "open" }, + // { name: "Bemanningsforetak som er godkjent av Arbeidstilsynet", publisher: "Arbeidstilsynet", accessLevel: "open" }, + // { name: "Tilda tilsynsrapport", publisher: "Arbeidstilsynet", accessLevel: "open" }, + // { name: "Tilda tilsynskoordinering", publisher: "Arbeidstilsynet", accessLevel: "limited" }, + // { name: "Tilda trender", publisher: "Arbeidstilsynet", accessLevel: "limited" }, + // { name: "Foretaksregisteret", publisher: "Brønnøysundregistrene", accessLevel: "restricted" }, + // { name: "Tilda melding til annen myndighet", publisher: "Arbeidstilsynet", accessLevel: "open" }, + // { name: "Enhetsregisteret", publisher: "Brønnøysundregistrene", accessLevel: "open" }, + // { name: "Folkeregisteret", publisher: "Skatteetaten", accessLevel: "restricted" }, + // { name: "Renholdsvirksomheter godkjent av Arbeidstilsynet", publisher: "Arbeidstilsynet", accessLevel: "restricted" }, + // { name: "Oppgaveregisteret", publisher: "Brønnøysundregistrene", accessLevel: "open" }, + // { name: "Matrikkelen - Adresse", publisher: "Kartverket", accessLevel: "open" }, + ]; + + const getColorFromAccessLevel = (accessLevel: any) => { + switch (accessLevel) { + case 'open': return 'success'; + case 'limited': return 'warning'; + case 'restricted': return 'danger'; + default: return 'success'; + } + } + + const getLabelFromAccessLevel = (accessLevel: any) => { + switch (accessLevel) { + case 'open': return 'Åpne data'; + case 'limited': return 'Begrenset tilgang'; + case 'restricted': return 'Ikke-allmenn tilgang'; + default: return 'Åpne data'; + } + } + + return ( +
+ +
+
+ Digdir +
+ + data.altinn.no + +
+ + +
+
+ + API + + Publisert 9. mars 2022 +
+
+
+ + + + Oversikt + + Endepunkter 2 + + Detaljer + + Kommentarer 2 + + + + + + Beskrivelse + +
+ Api for å benytte tjenester på data.altinn.no. Tilgang krever både autentisert virksomhet (maskinporten) og api-nøkkel som kan fås på data.altinn.no +
+
+ + Endepunkter 2 + +
+
Endepunkt:
+
+ + https://api.data.altinn.no/v1 + + +
+
Endepunktbeskrivelse:
+
+ + https://api.data.altinn.no/v1/public/metadata/oas/json + + +
+
Dokumentasjon:
+
+ + https://docs.data.altinn.no/ + + +
+
Formater:
+
+ + {['json', 'xml'].map((format) => ( + {format} + ))} + +
+
+
+ +
+ + {/*Relaterte datasett {relatedDatasets.length}*/} + Tilgjengeliggjør datasett + + + + + {relatedDatasets.map(dataset => ( + + + + + + ))} + +
+ {dataset.name} + + + {dataset.publisher} + + + + {getLabelFromAccessLevel(dataset.accessLevel)} + +
+
+
+
+ +
+ + Endepunkter + +
+
Endepunkt:
+
+ + https://inntektsmottakere.api.skatteetaten-test.no/v1 + + +
+
Endepunkt:
+
+ + https://inntektsmottakere.api.skatteetaten.no/v1 + + +
+
Endepunktbeskrivelse:
+
+ + Gå til spesifikasjon + + +
+
Formater:
+
+ + {['json', 'xml'].map((format) => ( + {format} + ))} + +
+
+
+ {/*
+ + Formater + + + {['json', 'xml'].map((tema) => ( + {tema} + ))} + +
*/} +
+ +
+ + Generelt + +
+
Utgiver:
+
+ Arbeids- og velferdsetaten +
+
Publisert:
+
9. mars 2022
+
Språk:
+
Engelsk
+
Dokumentasjon:
+
+ https://github.com/opendatalab-no/open-municipal-data +
+
+
+
+ + Kontaktinformasjon + +
+
Kontaktpunkt:
+
+ https://www.sintef.no/alle-ansatte/ansatt/erlend.stav/ +
+
E-post:
+
+ erlend.stav@sintef.no +
+
+
+
+ + Formater + + + {['json', 'xml'].map((format) => ( + {format} + ))} + +
+
+ + + +
+
+
+ ); +} diff --git a/apps/data-norge/src/app/components/details/details-page/begrep/index.tsx b/apps/data-norge/src/app/components/details/details-page/begrep/index.tsx new file mode 100644 index 00000000..4ea39d25 --- /dev/null +++ b/apps/data-norge/src/app/components/details/details-page/begrep/index.tsx @@ -0,0 +1,283 @@ +'use client'; + +import { type Dictionary, type LocaleCodes } from '@fdk-frontend/dictionaries'; + +import Breadcrumbs from '@fdk-frontend/ui/breadcrumbs'; +import Badge from '@fdk-frontend/ui/badge'; +import StarButton from '@fdk-frontend/ui/star-button'; +import { + Heading, + Link, + Tag, + Tabs, + TabList, + Tab, + TabContent, + Paragraph, + ChipGroup, + ChipToggle, +} from '@digdir/designsystemet-react'; +import { ExternalLinkIcon } from '@navikt/aksel-icons'; + +import styles from '../details-page.module.scss'; + +export type DetailsPageType = { + locale: LocaleCodes; + commonDictionary: Dictionary; +}; + +export default function DetailsPage({ locale, commonDictionary }: DetailsPageType) { + const breadcrumbList = [ + { + href: '/concepts', + text: 'Begreper', + }, + { + href: '#', + text: 'Energimålinger kommunale bygg', + }, + ]; + + return ( +
+ +
+
+ Arbeids- og velferdsetaten +
+ + egenandel på dagpenger + +
+ +
+
+
+ + Begrep + + Sist oppdatert 2. januar 2023 +
+
+
+ + + Oversikt + Detaljer + + Kommentarer 2 + + + +
+ + Bokmål + +
+
Begrep:
+
egenandel på dagpenger
+
Definisjon:
+
+ + "beregnet beløp som trekkes fra de første utbetalingene av dagpenger" + +
+
+
+
+ + Nynorsk + +
+
Begrep:
+
eigenandel på dagpengar
+
Definisjon:
+
+ + "berekna beløp som blir trekt frå dei første utbetalingane av + dagpengar" + +
+
+
+ {/*
+ + Begrep + +
+
Bokmål:
+
egenandel på dagpenger
+
Nynorsk:
+
eigenandel på dagpengar
+
+
*/} + {/*
+ + Definisjoner + +
+
Bokmål:
+
"beregnet beløp som trekkes fra de første utbetalingene av dagpenger"
+
Nynorsk:
+
"berekna beløp som blir trekt frå dei første utbetalingane av dagpengar"
+
Kilde:
+
Basert på LOV-1997-02-28-19 Lov om folketrygd (folketrygdloven) § 4-9
+
Merknad
+
+ Når dagpenger er innvilget, beregnes en egenandel. Regelen gjelder i saker der ny dagpengeperiode er innvilget fra og med 1. januar 2024, og erstatter den tidligere regelen om ventetid. Egenandelen tilsvarer tre dagsatser medregnet eventuelt barnetillegg og etter eventuell samordning. Egenandelen reduserer brutto utbetaling av dagpenger. Det beregnes ikke egenandel ved permittering i fiskeindustrien eller ved forskuttering av lønnsgarantimidler i form av dagpenger. +
+
+
*/} +
+ + Merknad + + + Når dagpenger er innvilget, beregnes en egenandel. Regelen gjelder i saker der ny + dagpengeperiode er innvilget fra og med 1. januar 2024, og erstatter den tidligere + regelen om ventetid. Egenandelen tilsvarer tre dagsatser medregnet eventuelt + barnetillegg og etter eventuell samordning. Egenandelen reduserer brutto utbetaling av + dagpenger. Det beregnes ikke egenandel ved permittering i fiskeindustrien eller ved + forskuttering av lønnsgarantimidler i form av dagpenger. + +
+
+ +
+ + Endepunkter + +
+
Endepunkt:
+
+ + https://inntektsmottakere.api.skatteetaten-test.no/v1 + + +
+
Endepunkt:
+
+ + https://inntektsmottakere.api.skatteetaten.no/v1 + + +
+
Endepunktbeskrivelse:
+
+ + Gå til spesifikasjon + + +
+
+
+
+ + Formater + + + {['json', 'xml'].map((format) => ( + {format} + ))} + +
+
+ +
+ + Bruk av datasettet + +
+
Utgiver:
+
+ Arbeids- og velferdsetaten +
+
Publisert:
+
9. mars 2022
+
Språk:
+
Engelsk
+
Dokumentasjon:
+
+ https://github.com/opendatalab-no/open-municipal-data +
+
+
+
+ + Kontaktinformasjon + +
+
Kontaktpunkt:
+
+ https://www.sintef.no/alle-ansatte/ansatt/erlend.stav/ +
+
E-post:
+
+ erlend.stav@sintef.no +
+
+
+
+ + Formater + + + {['json', 'xml'].map((format) => ( + {format} + ))} + +
+
+ Kommentarer her +
+
+
+ ); +} diff --git a/apps/data-norge/src/app/components/details/details-page/datasett-worstcase/index.tsx b/apps/data-norge/src/app/components/details/details-page/datasett-worstcase/index.tsx new file mode 100644 index 00000000..52a7f38c --- /dev/null +++ b/apps/data-norge/src/app/components/details/details-page/datasett-worstcase/index.tsx @@ -0,0 +1,363 @@ +'use client'; + +import { useState } from 'react'; +import cn from 'classnames'; + +import { type Dictionary, type LocaleCodes } from '@fdk-frontend/dictionaries'; + +import Breadcrumbs from '@fdk-frontend/ui/breadcrumbs'; +import Badge from '@fdk-frontend/ui/badge'; +import { BrandDivider } from '@fdk-frontend/ui/divider'; +import StarButton from '@fdk-frontend/ui/star-button'; +import ScrollShadows from '@fdk-frontend/ui/scroll-shadows'; +import { + Heading, + Button, + Link, + Tag, + HelpText, + Tabs, + TabList, + Tab, + TabContent, + Paragraph, +} from '@digdir/designsystemet-react'; + +import Distributions, { type Distribution } from '../../distributions'; + +import DatasetDescription from '../../dataset-description'; +import DatasetDetails from '../../dataset-details'; +import MetadataPage from '../../metadata-page'; +import CommunityTab from '../../community-tab'; + +import worstDetails from '../../dataset-details/data/worst.json'; + +import styles from '../details-page.module.scss'; + +export type DetailsPageType = { + locale: LocaleCodes; + commonDictionary: Dictionary; +}; + +export default function DetailsPage({ locale, commonDictionary }: DetailsPageType) { + const [activeTab, setActiveTab] = useState('oversikt'); + const [highlight, setHighlight] = useState(false); + + const blink = () => { + setHighlight(true); + setTimeout(() => setHighlight(false), 1000); + }; + + const breadcrumbList = [ + { + href: '/datasets', + text: 'Datasett', + }, + { + href: '#', + text: 'Energimålinger kommunale bygg', + }, + ]; + + const datasets: Distribution[] = [ + { + title: 'Location measurement points', + tags: ['csv', 'json', 'xml', 'yaml'], + description: 'API i formatene JSON, XML, CSV og YAML. Komplett nedlasting som CSV', + accessUrl: 'https://hotell.difi.no/?dataset=npd/survey/last-updates', + downloadUrl: 'https://hotell.difi.no/download/npd/survey/last-updates?download', + }, + ]; + + return ( +
+ +
+
+ Arbeids- og velferdsetaten +
+ + Energimålinger kommunale bygg + +
+ + +
+
+ + Datasett + + + Åpne data  + + Åpne data er data som er fritt tilgjengelig for alle. + + + Les mer om tilgangsnivåer her + + + + + Publisert 9. mars 2022 +
+
+
+ + + + Oversikt + + Distribusjoner og API 1 + + Detaljer + + Kommentarer 2 + + RDF + + + + {/*
+

+ Statistikk over helt arbeidsledige ved utgangen av måneden fordelt på bostedskommune og + fylke Helt ledige arbeidssøkere omfatter alle arbeidssøkere som de to siste ukene har + meldt til NAV at de er helt uten arbeid, søker nytt arbeid og er tilgjengelig for det + arbeid som søkes. Se om statistikken på www.nav.no for ytterligere forklaringer. +

+
*/} +
+ + Beskrivelse + +
+ + {` +**Datasettet Tilsyn** [https://data.mattilsynet.no/smilefjes-tilsyn.csv](https://data.mattilsynet.no/smilefjes-tilsyn.csv) inneholder den tilsvarende informasjonen som plakaten som henges opp hos spisestedene etter at de har hatt tilsyn. Datasettet er visualisert med et søk på [https://smilefjes.mattilsynet.no/](https://smilefjes.mattilsynet.no/) + +**Datasettet Kravpunkter** [https://data.mattilsynet.no/smilefjes-kravpunkter.csv](https://data.mattilsynet.no/smilefjes-kravpunkter.csv) inneholder hvert enkelt kravpunkt som inngår i ett tilsyn, sammen med karakteren kravpunktet er gitt. Hvert tilsyn vil ha et sett med rader i dette tilsynet, knyttet sammen av tilsynid. + +**Beskrivelse av elementene i datasett for smilefjes** + +**Tilsynsobjekt:** +Et tilsynsobjekt er et sted hvor det foregår aktivitet som Mattilsynet skal føre tilsyn med (for smilefjesordningen er dette serveringssteder som er tilrettelagt og beregnet for at maten skal spises på stedet). Et tilsynsobjekt identifiseres entydig ved en intern id, kalt tilsynsobjektid. I tillegg angis hvilken bedrift i Brønnøysundregisteret som eier tilsynsobjektet. Ansvarlig foretak kan da finnes ved å slå opp på <[www.brreg.no](http://www.brreg.no)> (eller ved bruk av en nettjeneste). Adressene som angis for tilsynsobjektet er interne data med variabel kvalitet, men dette vil bli utbedret etterhvert som tilsynet blir utført. Tilsynsobjekter skifter normalt ikke adresse. Dersom en bedrift flytter en aktivitet fra ett sted til ett annet, vil det oppstå ett nytt tilsynsobjekt. + +**Kravpunkt:** +Krav i regelverket satt opp som punkter. Kravene retter seg mot tilsynsobjektene og Mattilsynet fører tilsyn med kravene. Kravpunktene er ett spesifikt element i ett tilsyn. Disse kan være obligatoriske ved ordinært tilsyn (se tilsyn). + +**Avvik:** +Ett kravpunkt som har blitt undersøkt, og hvor Mattilsynet har funnet brudd på regelverket som fører til at det varsles eller fattes vedtak overfor tilsynsobjektet. + +**Tema:** +Organisering av kravpunkter som benyttes på smilefjesplakaten for å gruppere kravpunkter. + +**Tilsyn:** +Mattilsynets tilsynsprosess består av et ordinært tilsyn, med påfølgende oppfølgingstilsyn til alle avvik er lukket/utbedret eller tilsynsobjektet legger ned aktiviteten. Når vi utfører et ordinært tilsyn vil alle kravpunkter være mulig å vurdere, men det legges ikke opp til at alle kravpunkter vil bli vurdert ved hvert ordinære tilsyn. På oppfølgingstilsyn vurderes normalt bare de kravpunkt som det ble funnet avvik på, med mindre det har oppstått helt åpenbare avvik (eller det er mistanke om avvik) ved øvrige kravpunkter siden forrige tilsyn. + +**Karakterskala:** +0 = Ingen brudd på regelverket funnet. Stort smil. +1 = Mindre brudd på regelverket som ikke krever oppfølging. Stort smil. +2 = Brudd på regelverket som krever oppfølging. Strekmunn. +3 = Alvorlig brudd på regelverket. Sur munn. +4 = Ikke aktuelt - Virksomheten har ikke denne aktiviteten ved tilsynsobjektet. Påvirker ikke smilefjeskarakter. +5 = Ikke vurdert - Mattilsynet har ikke vurdert kravpunktet ved dette tilsynet. Påvirker ikke smilefjeskarakter. Dersom det hadde blitt avdekket mistanke om vesentlige eller åpenbare avvik i forbindelse med inspeksjonen, ville kravpunktet blitt vurdert. + +**Karaktersetting:** +Det totale smilefjesymbolet etter et tilsyn tilsvarer den dårligste karakteren som blir gitt på tilsynet. Karakter for hvert tema er den dårligste karakteren gitt til kravpunkter under tema. Hvert enkelt kravpunkt gis karakterer på skalaen. + +**Invalidering av tilsyn:** +Dersom Mattilsynet ikke overholder sine forpliktelser i smilefjesforskriften, eller et påklaget vedtak gis medhold, vil tilsynet og tilsynsresultatet bli trukket tilbake fra åpne data. Informasjonen om dette tilsynet vil ikke lenger være ansett som korrekt. Det er derfor viktig at brukere av datasettet er klar over dette slik at de kan holde sine data oppdatert (1 gang pr. døgn) for å unngå publisering av feilaktig informasjon om et tilsynsobjekt. + +**Deklarasjon av innholdet i datasettene** + +**Tilsyn:** +[https://data.mattilsynet.no/smilefjes-tilsyn.csv](https://data.mattilsynet.no/smilefjes-tilsyn.csv) - Dette datasettet inneholder den tilsvarende informasjonen som plakaten som henges opp hos spisestedene etter at de har hatt tilsyn. + +- tilsynsobjektid; - Nøkkel for å identifisere tilsynsobjektet +- orgnummer; - Nøkkel for å koble tilsynsobjektet til eier (i [www.brreg.no](http://www.brreg.no)) +- navn; - Navnet på tilsynsobjektet +- adrlinje1; - Adresse +- adrlinje2; - Adresse +- postnr; - Postnr +- poststed; - Poststed +- tilsynid; - Nøkkel for å identifisere tilsynet +- sakref; - Referansenummer for arkivinformasjon +- status; - Gjennomføres = utestående avvik finnes. Gjennomført = alle avvik lukket. +- dato; - Dato tilsynet er utført (ddmmyyyy) +- karakter; - Smilefjeskarakter for hele tilsynet +- tilsynsbesoektype; - Ordinært eller oppfølgings-tilsyn +- tema_karakter1; - Temanavn:karakter (e.g. Ledelse og rutiner:1) +- tema_karakter2; - Temanavn:karakter (e.g. Lokaler og utstyr:0) +- tema_karakter3; - Temanavn:karakter (e.g. Mattilberedning og håndtering:0) +- tema_karakter4; - Temanavn:karakter (e.g. Sporbarhet og Merking:2) +- kravpunkter_href; - Lenke til datasett som gir kravpunktene som inngikk i tilsynet. + +**Kravpunkter:** +[https://data.mattilsynet.no/smilefjes-kravpunkter.csv](https://data.mattilsynet.no/smilefjes-kravpunkter.csv) - Dette datasettet inneholder hvert enkelt kravpunkt som inngår i ett tilsyn, sammen med karakteren kravpunktet er gitt. Hvert tilsyn vil ha ett sett med rader i dette tilsynet, knyttet sammen av tilsynid. + +- "tilsynid"; - Nøkkel for identifisere ett tilsyn +- "dato"; - Dato tilsynet er utført +- "tema.kravpunkt"; - Nøkkel for å identifisere ett kravpunkt (ikke unik over tid) +- "kravpunktnavn"; - Navn på kravpunktet +- "karakter"; - Karakter (utifra skala) +- "tekst_no"; - Tekstlig beskrivelse av karakter, bokmål +- "tekst_nn"; - Tekstlig beskrivelse av karakter, nynorsk +- "tilsyn_href"; - Lenke til tilsynet, slik at kravpunkter kan kobles sammen med tilsynsobjektet + +--- + +Formål: Data fra smilefjestilsyn gir en samlet oversikt over alle serveringssteder som er omfattet av smilefjesordningen (smilefjesforskriften) og tilsynsresultatene fra Mattilsynets tilsyn fra 1.1. 2016 fram til dags dato. + +`} + +
+
+
+ +
+ +
+ + Relaterte datasett + + + + + + + + + + + + + + + + + + + + +
+ Hydrologiske data + + + Norges vassdrags- og energidirektorat (nve) + + + + Åpne data + +
+ Standard for yrkesklassifisering (STYRK08) + + Statistisk sentralbyrå + + + Åpne data + +
+ Folketeljinga 1910 + + Arkivverket + + + Begrenset tilgang + +
+
+ {/*
    +
  • + + Hydrologiske data + Norges vassdrags- og energidirektorat (nve) + Åpne data + +
  • +
  • + + Standard for yrkesklassifisering (STYRK08) + Statistisk sentralbyrå + Åpne data + +
  • +
  • + + Folketeljinga 1910 + Arkivverket + Begrenset tilgang + +
  • +
*/} +
+
+ + + + + + + + + + + + +
+
+
+ ); +} diff --git a/apps/data-norge/src/app/components/details/details-page/details-page.module.scss b/apps/data-norge/src/app/components/details/details-page/details-page.module.scss new file mode 100644 index 00000000..2a857fe5 --- /dev/null +++ b/apps/data-norge/src/app/components/details/details-page/details-page.module.scss @@ -0,0 +1,249 @@ +@import '@fdk-frontend/ui/core/mixins'; + +.highlight { + :global(.fds-accordion) { + // animation: blink-border 1s ease-in-out 2; + } +} + +@keyframes blink-border { + 0% { + box-shadow: 0px 0px 0px 10px transparent; + } + 25% { + box-shadow: 0px 0px 0px 10px var(--fds-semantic-surface-focus-default); + } + 50% { + box-shadow: 0px 0px 0px 10px transparent; + } + 75% { + box-shadow: 0px 0px 0px 10px var(--fds-semantic-surface-focus-default); + } + 100% { + box-shadow: 0px 0px 0px 10px transparent; + } +} + +.detailsPage { + margin: 0 2rem 3rem; + container-type: inline-size; + + @media (max-width: 400px) { + margin: 0 1.5rem 3rem; + } + + a { + &:visited { + color: var(--color-link); + } + } + + .mainContent { + max-width: 1000px; + margin: 2rem auto 0; + + @media (max-width: 650px) { + margin-top:1rem; + } + + dl { + display: grid; + grid-template-columns: 1fr 3fr; + border: 1px solid #ddd; + border-radius: 0.25rem; + + dt { + font-weight:500; + } + + dt, + dd { + padding: 0.75rem 0.75rem; + border-top: 1px solid #ddd; + background: #f8f8f8; + background: #fafafa; + } + + dt:first-child, + dt:first-child + dd { + border-top: 0; + } + + // @media (max-width: 900px) { + // grid-template-columns: 2fr 3fr; + // } + + @media (max-width: 600px) { + display:block; + + dt { + padding-bottom:0.25rem; + } + + dd { + // padding-left:2rem; + border-top:0; + padding-top:0.25rem; + } + } + } + + .header { + margin-bottom: 1.5rem; + display:flex; + flex-direction:column; + gap:0.5rem; + + .headerGrid { + display:grid; + grid-template-areas: "a b" "c c"; + grid-template-columns: auto auto; + grid-template-rows: auto auto; + gap: 0.75rem; /* Optional for spacing */ + // display: flex; + // justify-content: space-between; + // margin: 0.5rem 0 1rem 0; + + .title { + grid-area: a; + font-size: 2.25rem; + } + + .headerToolbar { + grid-area: b; + display: flex; + align-items: flex-start; + gap: 1rem; + justify-self: end; + + button { + white-space: nowrap; + } + } + + .headerTags { + grid-area: c; + grid-column: 1 / -1; + + :global(.fds-tag) { + display:inline-flex; + white-space:nowrap; + margin-right:0.5rem; + } + + a:not(:hover) { + text-decoration: none; + } + + .lastUpdated { + font-size: 0.9em; + opacity: 0.6; + margin-left: 0.25rem; + white-space:nowrap; + line-height:2em; + } + } + + @media (max-width: 500px) { + grid-template-areas: "a" "c" "b"; + grid-template-columns: 1fr; + + .headerToolbar { + justify-self: start; + margin-top:0.5rem; + } + } + } + } + + .tabsScrollShadows { + margin:0 -2rem; + padding:0 2rem; + + @media (max-width: 1000px) { + padding:0; + border-bottom:1px solid #ddd; + + :global(.fds-tabs__tablist) { + margin:0 2rem; + border-bottom:0; + } + } + + @media (max-width: 400px) { + margin:0 -1.5rem; + + :global(.fds-tabs__tablist) { + margin:0 1.5rem; + } + } + } + + :global(.fds-tabs__tab) { + padding: 0.75rem 1rem; + white-space: nowrap; + } + + :global(.fds-tabs__content) { + padding: 1.5rem 0; + + > * + * { + margin-top: 1.5rem; + } + } + + .article { + @include article(); + } + + .section { + > :global(.fds-heading) { + margin-bottom: 1em; + } + } + + .box { + padding: 1rem 1.5rem; + border-radius: 0.25rem; + border: 1px solid var(--fds-semantic-border-neutral-subtle); + } + + .related { + :global(.fds-link) { + display: flex; + justify-content: space-between; + text-decoration: none; + } + } + + .relatedPublisher { + font-size: 1rem; + color: #444; + font-weight: normal; + } + + .divider { + margin: 3rem auto 2rem !important; + } + + .scrollContainer { + overflow-x:auto; + } + } + + .tableScroller { + margin:1rem -2rem; + + > div { + padding:0 2rem; + } + + @media (max-width: 400px) { + margin:1rem -1.5rem; + + > div { + padding:0 1.5rem; + } + } + } +} diff --git a/apps/data-norge/src/app/components/details/details-page/index.tsx b/apps/data-norge/src/app/components/details/details-page/index.tsx new file mode 100644 index 00000000..0d210570 --- /dev/null +++ b/apps/data-norge/src/app/components/details/details-page/index.tsx @@ -0,0 +1,319 @@ +'use client'; + +import { useState } from 'react'; +import cn from 'classnames'; + +import { type Dictionary, type LocaleCodes } from '@fdk-frontend/dictionaries'; + +import Breadcrumbs from '@fdk-frontend/ui/breadcrumbs'; +import Badge from '@fdk-frontend/ui/badge'; +import StarButton from '@fdk-frontend/ui/star-button'; +import { BrandDivider } from '@fdk-frontend/ui/divider'; +import ScrollShadows from '@fdk-frontend/ui/scroll-shadows'; +import { + Button, + Heading, + Link, + Tag, + HelpText, + Tabs, + TabList, + Tab, + TabContent, + Paragraph, +} from '@digdir/designsystemet-react'; +// import { DownloadIcon } from '@navikt/aksel-icons'; + +import Distributions, { type Distribution } from '../distributions'; +import DatasetDescription from '../dataset-description'; +import DatasetDetails from '../dataset-details'; +import MetadataPage from '../metadata-page'; +import CommunityTab from '../community-tab'; + +import fullDetails from '../dataset-details/data/full.json'; + +import styles from './details-page.module.scss'; + +export type DetailsPageType = { + locale: LocaleCodes; + commonDictionary: Dictionary; +}; + +export default function DetailsPage({ locale, commonDictionary }: DetailsPageType) { + const [activeTab, setActiveTab] = useState('oversikt'); + const [highlight, setHighlight] = useState(false); + + const blink = () => { + setHighlight(true); + setTimeout(() => setHighlight(false), 1000); + }; + + const breadcrumbList = [ + { + href: '/datasets', + text: 'Datasett', + }, + { + href: '#', + text: 'Energimålinger kommunale bygg', + }, + ]; + + const exampleData: Distribution[] = [ + { + title: 'Eksempel på tabelloppføring', + tags: ['csv', 'json', 'xml', 'yaml'], + description: 'API i formatene JSON, XML, CSV og YAML. Komplett nedlasting som CSV', + accessUrl: 'https://hotell.difi.no/?dataset=npd/survey/last-updates', + downloadUrl: 'https://hotell.difi.no/download/npd/survey/last-updates?download', + }, + ]; + + const datasets: Distribution[] = [ + { + title: 'Oversikt over transportsystemet (ledningsnettet) i de enkelte vannforsyningssystemene', + tags: ['csv', 'json', 'xml', 'yaml'], + description: 'API i formatene JSON, XML, CSV og YAML. Komplett nedlasting som CSV', + accessUrl: 'https://hotell.difi.no/?dataset=npd/survey/last-updates', + downloadUrl: 'https://hotell.difi.no/download/npd/survey/last-updates?download', + }, + { + title: 'Historiske data om endringer i transportsystemet', + tags: ['csv', 'json', 'xml', 'yaml'], + description: 'API i formatene JSON, XML, CSV og YAML. Komplett nedlasting som CSV', + accessUrl: 'https://hotell.difi.no/?dataset=npd/survey/last-updates', + downloadUrl: 'https://hotell.difi.no/download/npd/survey/last-updates?download', + }, + ]; + + const apis: any[] = [ + { + title: 'data.altinn.no', + tags: ['json'], + description: 'Api for å benytte tjenester på data.altinn.no. Tilgang krever både autentisert virksomhet (maskinporten) og api-nøkkel som kan fås på data.altinn.no', + endpoint: 'https://api.data.altinn.no/v1', + endpointSpec: 'https://api.data.altinn.no/v1/public/metadata/oas/json', + documentation: 'https://docs.data.altinn.no/' + } + ]; + + return ( +
+ +
+
+ Mattilsynet +
+ + Vannverk - transportsystem + +
+ + +
+
+ + Datasett + + + Åpne data  + + Åpne data er data som er fritt tilgjengelig for alle. + + + Les mer om tilgangsnivåer her + + + + + {/* + ⚖ Har lisens + */} + Publisert 9. mars 2022 +
+
+
+ + + + Oversikt + + Distribusjoner og API {[...datasets, ...exampleData, ...apis].length} + + Detaljer + + Diskusjoner 2 + + RDF + + + + {/*
+

+ Statistikk over helt arbeidsledige ved utgangen av måneden fordelt på bostedskommune og + fylke Helt ledige arbeidssøkere omfatter alle arbeidssøkere som de to siste ukene har + meldt til NAV at de er helt uten arbeid, søker nytt arbeid og er tilgjengelig for det + arbeid som søkes. Se om statistikken på www.nav.no for ytterligere forklaringer. +

+
*/} +
+ + Beskrivelse + +
+ + {` +Datasettene omfatter offentlige eller private vannverk som forsyner **50 personer eller mer**. I tillegg inkluderer de alle kommunalt eide virksomheter med egen vannforsyning, uavhengig av størrelse. Datasettene inneholder også data om nedlagte anlegg, for de som ønsker å se historiske data. + +#### Tilsynsobjekter i Datasettene +Datasettene dekker følgende tilsynsobjekter: +1. **Vannforsyningssystem** – inkludert analyser av drikkevannet. +2. **Transportsystem** +3. **Behandlingsanlegg** +4. **Inntakspunkt** – inkludert analyser av vannkilden. + +Datasett for **Transportsystem** er tilgjengelig nedenfor. I tillegg finnes det en fil (_informasjon.txt_) som gir en oversikt over produksjonstidspunktet for uttrekkene og antall linjer i hver fil. Uttrekkene oppdateres ukentlig. + +#### Historiske Data +For datasettene *Vannforsyningssystem*, *Transportsystem*, og *Inntakspunkt* er det mulig å se historiske data som en del av den årlige innrapporteringen. For å nyttiggjøre informasjonen må filen kobles til en "moderfil" for å hente navn og annen statisk informasjon. Disse filene har endelsen _innrapportering i filnavnet. +`} + +
+
+
+ +
+ +
+ + Relaterte datasett + + + + + + + + + + + + + + + + + + + + +
+ Hydrologiske data + + + Norges vassdrags- og energidirektorat (nve) + + + + Åpne data + +
+ Standard for yrkesklassifisering (STYRK08) + + Statistisk sentralbyrå + + + Åpne data + +
+ Folketeljinga 1910 + + Arkivverket + + + Begrenset tilgang + +
+
+
+
+ + + + + + + + + + + + +
+
+
+ ); +} diff --git a/apps/data-norge/src/app/components/details/distributions/distribution-content.tsx b/apps/data-norge/src/app/components/details/distributions/distribution-content.tsx new file mode 100644 index 00000000..3726c390 --- /dev/null +++ b/apps/data-norge/src/app/components/details/distributions/distribution-content.tsx @@ -0,0 +1,216 @@ +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; + +import { + Heading, + Link, + Table, + TableHead, + TableBody, + TableHeaderCell, + TableCell, + TableRow, +} from '@digdir/designsystemet-react'; +import { ExternalLinkIcon } from '@navikt/aksel-icons'; + +import HStack from '@fdk-frontend/ui/hstack'; + +import detailsPageStyles from '../details-page/details-page.module.scss'; + +const SimpleContent = () => { + return ( + <> +
+
Beskrivelse:
+
+
+

+ Statistikk over helt arbeidsledige ved utgangen av måneden fordelt på bostedskommune og + fylke Helt ledige arbeidssøkere omfatter alle arbeidssøkere som de to siste ukene har meldt + til NAV at de er helt uten arbeid, søker nytt arbeid og er tilgjengelig for det arbeid som + søkes. Se om statistikken på www.nav.no for ytterligere forklaringer. +

+
+
+ {/*
TilgangsURL:
+
https://www.brreg.no/produkter-og-tjenester/bestille-produkter/maskinlesbare-data-enhetsregisteret/full-tilgang-enhetsregisteret/
+
Direkte nedlasting:
+
https://www.brreg.no/produkter-og-tjenester/bestille-produkter/maskinlesbare-data-enhetsregisteret/full-tilgang-enhetsregisteret/data.csv
*/} +
API:
+
+ + https://api.data.altinn.no/v1 + + {/*
+ */} + +
+
Lisens:
+
Creative Commons Navngivelse 4.0 Internasjonal
+
+ + ); +} + +const ExampleContent = () => { + return ( + <> +
+
Beskrivelse
+
+
+

+ Utdrag av datasett +

+
+
+ {/*
TilgangsURL
+
https://www.brreg.no/produkter-og-tjenester/bestille-produkter/maskinlesbare-data-enhetsregisteret/full-tilgang-enhetsregisteret/
+
Direkte nedlasting
+
https://www.brreg.no/produkter-og-tjenester/bestille-produkter/maskinlesbare-data-enhetsregisteret/full-tilgang-enhetsregisteret/sample.csv
*/} +
Lisens
+
Creative Commons Navngivelse 4.0 Internasjonal
+
+ + ); +} + +const APIContent = ({ api }: any) => { + return ( + <> +
+
Beskrivelse
+
+
+

+ {api.description} +

+
+
+
Endepunkt:
+
{api.endpoint}
+
Endepunktbeskrivelse:
+
{api.endpointSpec}
+
Dokumentasjon:
+
{api.documentation}
+
+ + ); +} + + +const RichContent = () => { + return ( + <> +
+
Beskrivelse
+
+
+

+ Statistikk over helt arbeidsledige ved utgangen av måneden fordelt på bostedskommune og + fylke Helt ledige arbeidssøkere omfatter alle arbeidssøkere som de to siste ukene har meldt + til NAV at de er helt uten arbeid, søker nytt arbeid og er tilgjengelig for det arbeid som + søkes. Se om statistikken på www.nav.no for ytterligere forklaringer. +

+ + Datastruktur + + + + Code + ParentCode + Level + Name + ShortName + + + + + 0 + 0 + 1 + Militære yrker og uoppgitt + n/a + + + 0 + 0 + 1 + Ledere + n/a + + + 0 + 0 + 1 + Akademiske yrker + n/a + + +
+ Eksempel på data + + {`{ + "id": "https://register.geonorge.no/sosi-kodelister/inndelinger/inndelingsbase/kommunenummer/5427", + "label": "Skjervøy", + "lang": "no", + "itemclass": "CodelistValue", + "uuid": "56d0f814-f25e-48fe-b190-00363e84d020", + "status": "Tilbaketrukket", + "description": "Skjervøy", + "seoname": "skjervøy", + "owner": "Kartverket", + "versionNumber": 1, + "lastUpdated": "2023-01-02T10:03:45.647", + "dateSubmitted": "2023-01-02T10:03:45.457", + "codevalue": "5427", + "ValidFrom": "2020-01-01T00:00:00", + "ValidTo": "2023-12-31T00:00:00" +}`} + +
+
+ {/*
Få tilgang til datasettet:
+
+ + {distribution.accessUrl} + +
+
Direkte nedlastingslenke:
+
+ + {distribution.downloadUrl} + +
*/} +
Lisens
+
+ + Norsk lisens for offentlige data + + +
+
+ {/* + + + + + + + + + + +
Beskrivelse:{distribution.description}
Lisens: + + Norsk lisens for offentlige data + +
*/} + + ); +}; + +export { RichContent, SimpleContent, ExampleContent, APIContent }; diff --git a/apps/data-norge/src/app/components/details/distributions/distributions.module.scss b/apps/data-norge/src/app/components/details/distributions/distributions.module.scss new file mode 100644 index 00000000..2f2abd9f --- /dev/null +++ b/apps/data-norge/src/app/components/details/distributions/distributions.module.scss @@ -0,0 +1,79 @@ +.distributions { + border-radius: 3px; + + > :global(.fds-heading) { + margin: 2rem 0 1rem; + + &:first-child { + margin-top: 0; + } + } +} + +.header { + :global(.fds-accordion__button) { + > span { + display: flex; + flex-grow: 1; + } + .tag { + font-size: 0.9rem; + } + + @media (max-width: 500px) { + flex-direction:row-reverse; + align-items:flex-start; + } + } + + .headerContent { + display: flex; + justify-content: space-between; + align-items: center; + flex-grow: 1; + gap: 1rem; + + .title { + display: block; + font-size: 1.1rem; + font-weight: 600; + flex-grow: 1; + + .tags { + display: flex; + gap: 0.5rem; + font-weight: normal; + font-size: 0.8em; + align-items: center; + margin-top: 0.5rem; + flex-wrap:wrap; + + .tag { + min-height: 1.5rem; + // background:white; + } + } + } + + :global(.fds-link) { + white-space: nowrap; + flex-shrink: 0; + } + + @media (max-width: 500px) { + flex-direction:column; + align-items:flex-start; + } + } +} + +.content { + table { + width: 100%; + + td, + th { + background: transparent; + } + } +} diff --git a/apps/data-norge/src/app/components/details/distributions/index.tsx b/apps/data-norge/src/app/components/details/distributions/index.tsx new file mode 100644 index 00000000..a42edaa3 --- /dev/null +++ b/apps/data-norge/src/app/components/details/distributions/index.tsx @@ -0,0 +1,270 @@ +'use client'; + +import cn from 'classnames'; + +import { Accordion, Heading, Button, Link, Tag } from '@digdir/designsystemet-react'; +import { DownloadIcon, ArrowRightIcon } from '@navikt/aksel-icons'; +import Badge from '@fdk-frontend/ui/badge'; +import HStack from '@fdk-frontend/ui/hstack'; +import PlaceholderBox from '../placeholder-box'; + +import styles from './distributions.module.scss'; +import { SimpleContent, APIContent } from './distribution-content'; + +export type Distribution = { + title: string; + tags: string[]; + description: string; + accessUrl?: string; + downloadUrl?: string; +}; + +export type DistributionsProps = { + datasets: Distribution[]; + exampleData?: Distribution[]; + apis?: any[]; + className?: string; +}; + +const Distributions = ({ exampleData = [], datasets, apis = [], className }: DistributionsProps) => { + return ( +
+ + +
Distribusjoner
+ {[...datasets, ...exampleData].length} +
+
+ + {datasets.map((distribution, index) => ( + + +
+ + {distribution.title} +
+ {distribution.tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ +
+
+
+ + + +
+ ))} + {exampleData.map((distribution, index) => ( + + +
+ + {distribution.title} +
+ + Eksempeldata + + {distribution.tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ +
+
+
+ + + +
+ ))} +
+ {/*exampleData && exampleData.length && ( + <> + + +
Eksempeldata
+ {exampleData.length} +
+
+ + {exampleData.map((distribution, index) => ( + + +
+ + {distribution.title} +
+ {distribution.tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ +
+
+
+ + + +
+ ))} +
+ + )*/} + + +
API-er som tilgjengeliggjør dette datasettet
+ {apis.length} +
+
+ { + apis && apis.length ? + + {apis.map((api, index) => ( + + +
+ + {api.title} +
+ {api.tags.map((tag: string) => ( + + {tag} + + ))} +
+
+
+ +
+
+
+ + + +
+ ))} +
: + + Ingen registrerte API-er tilgjengeliggjør dette datasettet. + + } +
+ ); +}; + +export default Distributions; diff --git a/apps/data-norge/src/app/components/details/metadata-page/index.tsx b/apps/data-norge/src/app/components/details/metadata-page/index.tsx new file mode 100644 index 00000000..7a60de70 --- /dev/null +++ b/apps/data-norge/src/app/components/details/metadata-page/index.tsx @@ -0,0 +1,125 @@ +import { PropsWithChildren, useState, useEffect } from 'react'; +import cn from 'classnames'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; + +import CopyButton from '@fdk-frontend/ui/copy-button'; +import { ToggleGroup, Heading, Spinner, Textfield, HelpText, Paragraph, Link } from '@digdir/designsystemet-react'; + +import HStack from '@fdk-frontend/ui/hstack'; + +import styles from './metadata-page.module.scss'; + +const MetadataPage = ({ children }: PropsWithChildren) => { + const [contentType, setContentType] = useState('text/turtle'); + const [source, setSource] = useState(''); + const [loading, setLoading] = useState(false); + + const syntax = { + 'text/turtle': 'turtle', + 'application/rdf+xml': 'xml', + 'application/json': 'json', + 'application/ld+json': 'json', + }; + + const getMetadata = async () => { + setLoading(true); + try { + await fetch(`https://data.norge.no/datasets/e4c67aa2-af5a-36c2-b5c2-96b571ddd850`, { + headers: { Accept: contentType }, + }) + .then(async (response) => { + const responseContentType = response.headers.get('Content-Type'); + let data = await response.text(); + + if (responseContentType && responseContentType.includes('application/json')) { + const jsonData = JSON.parse(data); + data = JSON.stringify(jsonData, null, 2); // Format JSON with 2 spaces for indentation + } + + setSource(data); + }) + .finally(() => { + setLoading(false); + }); + } catch (error) { + console.error('Error parsing JSON:', error); + } + }; + + useEffect(() => { + getMetadata(); + }, [contentType]); + + return ( +
+
+ + + Resource Description Framework (RDF) + + + Alle URL-er til ressurser på data.norge.no kan levere RDF-metadata i flere ulike + formater, avhengig av hvilken Accept header man sender med. + + Les mer om RDF og hvilke formater vi støtter her + + + + + + {loading && ( +
+ Laster... + +
+ )} +
+
+
+ + +
+
+ setContentType(value)} + > + Turtle + RDF/XML + JSON-LD + +
+
+
+ + + {loading && !source.length ? 'Laster...' : source} + +
+
+ ); +}; + +export default MetadataPage; diff --git a/apps/data-norge/src/app/components/details/metadata-page/metadata-page.module.scss b/apps/data-norge/src/app/components/details/metadata-page/metadata-page.module.scss new file mode 100644 index 00000000..98b5041d --- /dev/null +++ b/apps/data-norge/src/app/components/details/metadata-page/metadata-page.module.scss @@ -0,0 +1,58 @@ +@import '@fdk-frontend/ui/core/mixins'; + +.article { + @include article(); +} + +.wrapper { + .header { + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 0 1rem 0; + gap: 1rem; + + @media (max-width: 500px) { + display:block; + + .urlbar { + margin-bottom:1rem; + } + } + + .urlbar { + position: relative; + flex-grow: 1; + + :global(.fds-textfield) { + input[readOnly] { + color: #888; + } + } + + :global(.fds-btn) { + position: absolute; + top: 0.25rem; + right: 0.25rem; + z-index: 1; + } + } + } + + .toolbar { + display: flex; + gap: 0.5rem; + align-items: center; + } + + .content { + position: relative; + + :global(.fds-btn) { + position: absolute; + top: 0.25rem; + right: 0.25rem; + z-index: 1; + } + } +} diff --git a/apps/data-norge/src/app/components/details/placeholder-box/index.tsx b/apps/data-norge/src/app/components/details/placeholder-box/index.tsx new file mode 100644 index 00000000..f0abba31 --- /dev/null +++ b/apps/data-norge/src/app/components/details/placeholder-box/index.tsx @@ -0,0 +1,15 @@ +import { PropsWithChildren } from 'react'; + +import PlaceholderText from '../placeholder-text'; + +import styles from './placeholder-box.module.scss'; + +const PlaceholderBox = ({ children }: PropsWithChildren) => { + return ( +
+ {children} +
+ ); +}; + +export default PlaceholderBox; diff --git a/apps/data-norge/src/app/components/details/placeholder-box/placeholder-box.module.scss b/apps/data-norge/src/app/components/details/placeholder-box/placeholder-box.module.scss new file mode 100644 index 00000000..6ef53982 --- /dev/null +++ b/apps/data-norge/src/app/components/details/placeholder-box/placeholder-box.module.scss @@ -0,0 +1,6 @@ +.placeholderBox { + border: 1px solid #ddd; + border-radius: 0.25rem; + padding: 0.75rem 0.75rem; + background: #fafafa; +} diff --git a/apps/data-norge/src/app/components/details/placeholder-text/index.tsx b/apps/data-norge/src/app/components/details/placeholder-text/index.tsx new file mode 100644 index 00000000..6b5aea2a --- /dev/null +++ b/apps/data-norge/src/app/components/details/placeholder-text/index.tsx @@ -0,0 +1,9 @@ +import { PropsWithChildren } from 'react'; + +import styles from './placeholder-text.module.scss'; + +const PlaceholderText = ({ children }: PropsWithChildren) => { + return
{children}
; +}; + +export default PlaceholderText; diff --git a/apps/data-norge/src/app/components/details/placeholder-text/placeholder-text.module.scss b/apps/data-norge/src/app/components/details/placeholder-text/placeholder-text.module.scss new file mode 100644 index 00000000..985d8eab --- /dev/null +++ b/apps/data-norge/src/app/components/details/placeholder-text/placeholder-text.module.scss @@ -0,0 +1,4 @@ +.placeholderText { + opacity: 0.5; + font-style: italic; +} diff --git a/apps/data-norge/src/app/components/docs/catalog-promo/index.tsx b/apps/data-norge/src/app/components/docs/catalog-promo/index.tsx index de1eaeaf..d7fc087b 100644 --- a/apps/data-norge/src/app/components/docs/catalog-promo/index.tsx +++ b/apps/data-norge/src/app/components/docs/catalog-promo/index.tsx @@ -1,4 +1,4 @@ -import { Alert, Button, Link } from '@digdir/designsystemet-react'; +import { Alert, Button, Link, Paragraph } from '@digdir/designsystemet-react'; import styles from './catalog-promo.module.scss'; @@ -16,7 +16,7 @@ const CatalogPromo = ({ text, button, link }: CatalogPromoProps) => { className={styles.catalogPromo} >
- {text} + {text}
); export default Breadcrumbs; diff --git a/libs/ui/src/lib/catalog-symbol/index.tsx b/libs/ui/src/lib/catalog-icon/index.tsx similarity index 52% rename from libs/ui/src/lib/catalog-symbol/index.tsx rename to libs/ui/src/lib/catalog-icon/index.tsx index 7681bf7e..fd29382d 100644 --- a/libs/ui/src/lib/catalog-symbol/index.tsx +++ b/libs/ui/src/lib/catalog-icon/index.tsx @@ -1,17 +1,12 @@ import React from 'react'; -import cn from 'classnames'; - import { FilesIcon, CodeIcon, ChatElipsisIcon, TenancyIcon, CompassIcon, SparklesIcon } from '@navikt/aksel-icons'; - import { CatalogTypes } from '@fdk-frontend/types'; -import styles from './catalog-symbol.module.scss'; - -export type CatalogSymbolProps = { +export type CatalogIconProps = { catalog: CatalogTypes; }; -const CatalogIcon = ({ catalog, ...rest }: CatalogSymbolProps & React.SVGProps) => { +const CatalogIcon = ({ catalog, ...rest }: CatalogIconProps & React.SVGProps) => { switch (catalog) { case 'datasets': return ; @@ -28,17 +23,4 @@ const CatalogIcon = ({ catalog, ...rest }: CatalogSymbolProps & React.SVGProps) => { - return ( -
- -
- ); -}; - -export default CatalogSymbol; -export { CatalogIcon }; +export default CatalogIcon; \ No newline at end of file diff --git a/libs/ui/src/lib/catalog-symbol/catalog-symbol.stories.tsx b/libs/ui/src/lib/catalog-symbol/catalog-symbol.stories.tsx deleted file mode 100644 index a23bdb9e..00000000 --- a/libs/ui/src/lib/catalog-symbol/catalog-symbol.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import CatalogSymbol, { CatalogIcon } from '.'; - -const meta: Meta = { - component: CatalogSymbol, - title: 'CatalogSymbol', -}; - -export default meta; -type Story = StoryObj; - -export const Primary: Story = { - render: () => ( -
-
- - - - - - -
-
- - - - - - -
-
- ), -}; diff --git a/libs/ui/src/lib/copy-button/index.tsx b/libs/ui/src/lib/copy-button/index.tsx new file mode 100644 index 00000000..48fae6ce --- /dev/null +++ b/libs/ui/src/lib/copy-button/index.tsx @@ -0,0 +1,51 @@ +import { useState } from 'react'; +import { Button, type ButtonProps, Tooltip } from '@digdir/designsystemet-react'; +import { FilesIcon, CheckmarkCircleFillIcon } from '@navikt/aksel-icons'; + +import HStack from '../hstack'; + +type CopyButtonProps = { + copyOnClick: string; + buttonProps?: ButtonProps; +}; + +const CopyButton = ({ copyOnClick, buttonProps, ...props }: CopyButtonProps) => { + const [clicked, setClicked] = useState(false); + + const copyToClipboard = (text: string) => { + setClicked(true); + navigator.clipboard.writeText(text); + }; + + return ( + + + Kopiert! + + ) : ( + 'Kopier' + ) + } + placement='top' + > + + + ); +}; + +export default CopyButton; diff --git a/libs/ui/src/lib/core/fds-overrides.scss b/libs/ui/src/lib/core/fds-overrides.scss index 47d2f9d8..21674934 100644 --- a/libs/ui/src/lib/core/fds-overrides.scss +++ b/libs/ui/src/lib/core/fds-overrides.scss @@ -3,11 +3,7 @@ } .fds-heading { - &--xxs { - font-size: 1.1rem; - font-weight: 600; - line-height: 1.3em; - } + font-weight: 500; } .fds-ingress { @@ -54,3 +50,68 @@ font-size: 1.3rem; } } + +.fds-table { + width: 100%; + + td, + th { + padding: 0.5rem 0.75rem; + border-color: var(--fds-semantic-border-neutral-subtle); + } + + tr { + &:hover { + td, + th { + background: #fafafa; + } + } + } + + tbody { + &:first-child { + tr { + &:first-child { + td { + border-top: 1px solid var(--fds-semantic-border-neutral-subtle); + } + } + } + } + } +} + +.fds-accordion__item { + > h3 button:hover { + background: rgba(0, 0, 0, 0.05); + } +} +.fds-accordion__item--open { + > h3 button { + background: white; + + &:hover { + background: rgba(0, 0, 0, 0.025); + } + } +} + +.fds-accordion__expand-icon { + flex-shrink:0; +} + +/* +Fix for weird code block inside Alert overflow bug. +https://data.norge.no/nb/docs/sharing-data/how-to-dataset/2-dataset-description +*/ +.fds-alert__content { + overflow:auto; +} + +/* +Fix for bug where items doesnt wrap +*/ +.fds-chip--group-container { + flex-wrap:wrap; +} \ No newline at end of file diff --git a/libs/ui/src/lib/core/global.scss b/libs/ui/src/lib/core/global.scss index bfbab48e..44eaa925 100644 --- a/libs/ui/src/lib/core/global.scss +++ b/libs/ui/src/lib/core/global.scss @@ -9,6 +9,7 @@ @import './mixins'; @import './fds-overrides'; +@import './table'; /* Small devices such as large phones (640px and up) */ @@ -20,6 +21,7 @@ --breakpoint-x-large: 1200px; --breakpoint-xx-large: 1400px; --breakpoint-xxx-large: 1600px; + --color-text: #444; --color-link: var(--fds-semantic-surface-action-first-default); --color-link-negative: var(--fds-semantic-border-action-first-subtle); --color-fdk-navy: var(--fds-semantic-surface-neutral-inverted); @@ -44,7 +46,16 @@ body { } code { - font-family: monospace; + &:not([class]) { + display: inline-flex; + background: rgba(0, 0, 0, 0.05); + font-family: monospace; + font-size: 0.85em; + padding: 0.2em 0.4em; + white-space: break-spaces; + border-radius: 6px; + color: #444; + } } html { diff --git a/libs/ui/src/lib/core/mixins.scss b/libs/ui/src/lib/core/mixins.scss index 91b3e7ef..7067c2b8 100644 --- a/libs/ui/src/lib/core/mixins.scss +++ b/libs/ui/src/lib/core/mixins.scss @@ -15,3 +15,80 @@ outline: var(--fds-focus-border-width) solid var(--fds-semantic-border-focus-outline); outline-offset: var(--fds-focus-border-width); } + +@mixin article { + line-height: 1.5em; + + > * + * { + margin-bottom: 1rem; + } + + > h1, + > h2, + > h3, + > h4, + > h5, + > h6 { + font-weight: 500; + margin-bottom: 1em; + line-height: 1.25em; + } + + > h1 { + font-size: 2rem; + } + + > h2 { + font-size: 1.5rem; + } + + > h3 { + font-size: 1.25rem; + } + + > h4 { + font-size: 1.125rem; + } + + > h1, + > h2, + > h3, + > h4, + > h5 { + &:not(:first-child) { + margin-top: 1.5em; + } + } + + > div { + margin: 2rem 0; + } + + ul, + ol { + list-style: none; + padding: 0 0 0 1.25rem; + margin: 0 0 1rem 0; + } + + ul, + ol { + li { + margin-bottom: 0.5rem; + } + } + + ul { + list-style: disc; + } + + ol { + list-style: decimal; + } + + hr { + height: 1px; + border: 0; + background: var(--fds-semantic-border-neutral-subtle); + } +} diff --git a/libs/ui/src/lib/core/table.scss b/libs/ui/src/lib/core/table.scss new file mode 100644 index 00000000..0d393023 --- /dev/null +++ b/libs/ui/src/lib/core/table.scss @@ -0,0 +1,24 @@ +.table { + width: 100%; + overflow: hidden; + + th, + td { + padding: 0.75rem 0.75rem; + border-top: 1px solid #eee; + border-bottom: 1px solid #eee; + vertical-align: middle; + } + + tr { + &:nth-child(2n + 1) { + // background:#fafafa; + } + &:hover { + th, + td { + background: rgba(0, 0, 0, 0.03); + } + } + } +} diff --git a/libs/ui/src/lib/divider/divider.module.scss b/libs/ui/src/lib/divider/divider.module.scss new file mode 100644 index 00000000..022a5796 --- /dev/null +++ b/libs/ui/src/lib/divider/divider.module.scss @@ -0,0 +1,7 @@ +.brandDivider { + border: none; + background: var(--fds-brand-alt3-600); + height: 4px; + border-radius: 3px; + width: 2.5rem; +} \ No newline at end of file diff --git a/libs/ui/src/lib/divider/index.tsx b/libs/ui/src/lib/divider/index.tsx new file mode 100644 index 00000000..6bbc759c --- /dev/null +++ b/libs/ui/src/lib/divider/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import cn from 'classnames'; +import styles from './divider.module.scss'; + +const BrandDivider = ({ className, ...props }: React.HTMLAttributes) => { + return ( +
+ ); +} + +export { BrandDivider }; \ No newline at end of file diff --git a/libs/ui/src/lib/footer/footer.module.scss b/libs/ui/src/lib/footer/footer.module.scss index 9de18d7e..5e134f00 100644 --- a/libs/ui/src/lib/footer/footer.module.scss +++ b/libs/ui/src/lib/footer/footer.module.scss @@ -9,8 +9,8 @@ padding: 4rem 2rem; container-type: inline-size; - @media (max-width: 800px) { - padding-bottom: 2rem; + @media (max-width: 400px) { + padding: 3rem 1.5rem; } } @@ -51,6 +51,7 @@ @container (max-width: 400px) { flex-direction: column; gap: 1.5rem; + padding: 2rem 1.5rem; .digdirCredit { justify-content: flex-end; diff --git a/libs/ui/src/lib/header/header.module.scss b/libs/ui/src/lib/header/header.module.scss index d4009cba..92bd0b9e 100644 --- a/libs/ui/src/lib/header/header.module.scss +++ b/libs/ui/src/lib/header/header.module.scss @@ -3,6 +3,10 @@ .header { + main { padding-top: 74px; + + @media (max-width: 650px) { + padding-top: 63px; + } } &.frontpageHeader { @@ -56,9 +60,12 @@ width: 100%; transition: all linear 200ms; max-height: 100vh; - overflow: auto; background: white; + @media (max-width: 460px) { + overflow: auto; + } + &.headerSticky { position: fixed; background: white; @@ -121,8 +128,12 @@ } } } + } + + @media (max-width: 400px) { + padding: 1rem 1.5rem; - @media (max-width: 400px) { + .headerToolbar { a:last-child { display: none; } @@ -134,5 +145,9 @@ max-width: 1000px; margin: auto; padding: 1rem 2rem; + + @media (max-width: 400px) { + padding: 1rem 1.5rem; + } } } diff --git a/libs/ui/src/lib/hstack/hstack.module.scss b/libs/ui/src/lib/hstack/hstack.module.scss new file mode 100644 index 00000000..e601dec4 --- /dev/null +++ b/libs/ui/src/lib/hstack/hstack.module.scss @@ -0,0 +1,5 @@ +.wrapper { + display: flex; + align-items: center; + gap: 0.25rem; +} diff --git a/libs/ui/src/lib/hstack/index.tsx b/libs/ui/src/lib/hstack/index.tsx new file mode 100644 index 00000000..bc68133d --- /dev/null +++ b/libs/ui/src/lib/hstack/index.tsx @@ -0,0 +1,10 @@ +import React, { PropsWithChildren } from 'react'; +import cn from 'classnames'; + +import styles from './hstack.module.scss'; + +const HStack = ({ children, className, ...props }: PropsWithChildren & React.HTMLAttributes) => { + return
{children}
; +}; + +export default HStack; diff --git a/libs/ui/src/lib/catalog-symbol/catalog-symbol.module.scss b/libs/ui/src/lib/icon-badge/icon-badge.module.scss similarity index 55% rename from libs/ui/src/lib/catalog-symbol/catalog-symbol.module.scss rename to libs/ui/src/lib/icon-badge/icon-badge.module.scss index 9746cfa7..e4ca11d3 100644 --- a/libs/ui/src/lib/catalog-symbol/catalog-symbol.module.scss +++ b/libs/ui/src/lib/icon-badge/icon-badge.module.scss @@ -1,9 +1,9 @@ -.catalogSymbol { - display: flex; +.iconBadge { + display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; - padding: 0.25rem; - background: #eee; + padding: 0.2em; + background: #f4f4f4; font-size: 1.5rem; } diff --git a/libs/ui/src/lib/icon-badge/icon-badge.stories.tsx b/libs/ui/src/lib/icon-badge/icon-badge.stories.tsx new file mode 100644 index 00000000..52a39b95 --- /dev/null +++ b/libs/ui/src/lib/icon-badge/icon-badge.stories.tsx @@ -0,0 +1,34 @@ +// import type { Meta, StoryObj } from '@storybook/react'; + +// import CatalogSymbol, { CatalogIcon } from '.'; + +// const meta: Meta = { +// component: CatalogSymbol, +// title: 'CatalogSymbol', +// }; + +// export default meta; +// type Story = StoryObj; + +// export const Primary: Story = { +// render: () => ( +//
+//
+// +// +// +// +// +// +//
+//
+// +// +// +// +// +// +//
+//
+// ), +// }; diff --git a/libs/ui/src/lib/icon-badge/index.tsx b/libs/ui/src/lib/icon-badge/index.tsx new file mode 100644 index 00000000..97caf5af --- /dev/null +++ b/libs/ui/src/lib/icon-badge/index.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import cn from 'classnames'; + +import styles from './icon-badge.module.scss'; + +const IconBadge = ({ children, className, fontSize = '1.5rem', ...rest }: React.HTMLAttributes & { fontSize?: string }) => { + return ( +
+ {children} +
+ ); +}; + +export default IconBadge; diff --git a/libs/ui/src/lib/language-switcher/index.tsx b/libs/ui/src/lib/language-switcher/index.tsx index 770e2a30..a70555e6 100644 --- a/libs/ui/src/lib/language-switcher/index.tsx +++ b/libs/ui/src/lib/language-switcher/index.tsx @@ -31,7 +31,6 @@ const LanguageSwitcher = ({ inverted }: LanguageSwitcherProps) => { defaultValue={defaultCode} size='sm' onChange={(code) => onLanguageSelect(code as LocaleCodes)} - name='Select language' > {i18n.locales.map((locale) => ( * + * { margin-top: 0.5rem; } + + @media (max-width: 400px) { + padding: 3rem 1.5rem; + } } } } diff --git a/libs/ui/src/lib/main-menu/index.tsx b/libs/ui/src/lib/main-menu/index.tsx index e65eda57..bdb60924 100644 --- a/libs/ui/src/lib/main-menu/index.tsx +++ b/libs/ui/src/lib/main-menu/index.tsx @@ -5,11 +5,9 @@ import cn from 'classnames'; import { ForwardRefComponent, motion } from 'framer-motion'; import { Link, Heading } from '@digdir/designsystemet-react'; import { ExternalLinkIcon } from '@navikt/aksel-icons'; - import { Dictionary } from '@fdk-frontend/dictionaries'; import styles from './main-menu.module.scss'; - import GithubLogo from './images/github-logo'; import getMainMenuData from './data'; diff --git a/libs/ui/src/lib/scroll-shadows/index.tsx b/libs/ui/src/lib/scroll-shadows/index.tsx new file mode 100644 index 00000000..ba99f288 --- /dev/null +++ b/libs/ui/src/lib/scroll-shadows/index.tsx @@ -0,0 +1,68 @@ +'use client'; + +import React, { useState, useRef, useEffect } from "react"; +import cn from 'classnames'; + +import styles from "./scroll-shadows.module.scss"; + +const ScrollShadows = ({ children, className, ...props }: React.HTMLAttributes) => { + const scrollerRef = useRef(null); + const [overflow, setOverflow] = useState({ + overflowLeft: false, + overflowRight: false, + overflowTop: false, + overflowBottom: false, + }); + + const updateOverflow = () => { + const container = scrollerRef.current; + if (!container) return; + + const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight } = container; + + setOverflow({ + overflowLeft: scrollLeft > 0, + overflowRight: scrollLeft + clientWidth < scrollWidth, + overflowTop: scrollTop > 0, + overflowBottom: scrollTop + clientHeight < scrollHeight, + }); + }; + + useEffect(() => { + const container = scrollerRef.current; + if (!container) return; + + updateOverflow(); // Initial check + (container as HTMLElement).addEventListener("scroll", updateOverflow); + + const resizeObserver = new ResizeObserver(updateOverflow); + resizeObserver.observe(container); + + return () => { + (container as HTMLElement).removeEventListener("scroll", updateOverflow) + resizeObserver.disconnect(); + }; + }, []); + + return ( +
p === true)} + {...props} + > +
+ {children} +
+
+ ); +}; + +export default ScrollShadows; diff --git a/libs/ui/src/lib/scroll-shadows/scroll-shadows.module.scss b/libs/ui/src/lib/scroll-shadows/scroll-shadows.module.scss new file mode 100644 index 00000000..f634f3e3 --- /dev/null +++ b/libs/ui/src/lib/scroll-shadows/scroll-shadows.module.scss @@ -0,0 +1,75 @@ +.container { + position: relative; + + .scroller { + overflow: auto; + + &:before, + &:after { + content: ''; + position: absolute; + pointer-events: none; + z-index: 1; + transition: opacity 0.3s; + opacity:0; + } + + &:before { + left: 0; + right: 0; + height: 1rem; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent); + } + + &:after { + left: 0; + right: 0; + bottom: 0; + height: 1rem; + background: linear-gradient(to top, rgba(0, 0, 0, 0.2), transparent); + } + } + + &:before, + &:after { + content: ''; + position: absolute; + pointer-events: none; + z-index: 1; + transition: opacity 0.3s; + opacity:0; + } + + &:before { + top: 0; + left: 0; + bottom: 0; + width: 1rem; + background: linear-gradient(to right, rgba(0, 0, 0, 0.2), transparent); + } + + &:after { + top: 0; + right: 0; + bottom: 0; + width: 1rem; + background: linear-gradient(to left, rgba(0, 0, 0, 0.2), transparent); + } + + &.shadowLeft::before { + opacity: 0.5; + } + + &.shadowRight::after { + opacity: 0.5; + } + + &.shadowTop .inner::before { + opacity: 0.5; + } + + &.shadowBottom .inner::after { + opacity: 0.5; + } + +} \ No newline at end of file diff --git a/libs/ui/src/lib/star-button/index.tsx b/libs/ui/src/lib/star-button/index.tsx new file mode 100644 index 00000000..9d2b2495 --- /dev/null +++ b/libs/ui/src/lib/star-button/index.tsx @@ -0,0 +1,39 @@ +import { useState } from 'react'; +import { Button, Tooltip } from '@digdir/designsystemet-react'; +import { StarIcon, StarFillIcon } from '@navikt/aksel-icons'; + +import styles from './star-button.module.scss'; + +const StarButton = ({ defaultNumber = 0, defaultStarred = false }) => { + const [starred, setStarred] = useState(defaultStarred); + + return ( + + + + ); +}; + +export default StarButton; diff --git a/libs/ui/src/lib/star-button/star-button.module.scss b/libs/ui/src/lib/star-button/star-button.module.scss new file mode 100644 index 00000000..55cc763d --- /dev/null +++ b/libs/ui/src/lib/star-button/star-button.module.scss @@ -0,0 +1,30 @@ +.button { + .content { + display: flex; + gap: 0.25rem; + } + + &[aria-pressed='true'] { + font-weight: 500; + background: var(--fds-semantic-surface-success-no_fill-hover); + + svg { + animation: grow linear 100ms; + } + } + + &:active { + svg { + transform: scale(0.9); + } + } +} + +@keyframes grow { + 0% { + transform: scale(1.3); + } + 100% { + transform: scale(1); + } +} diff --git a/libs/ui/src/lib/typography/index.tsx b/libs/ui/src/lib/typography/index.tsx index 6cf6ce25..e19e8fd0 100644 --- a/libs/ui/src/lib/typography/index.tsx +++ b/libs/ui/src/lib/typography/index.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import cn from 'classnames'; import { Heading, HeadingProps } from '@digdir/designsystemet-react'; @@ -12,4 +13,8 @@ const HeadingWithDivider = ({ children, className, ...rest }: HeadingProps) => ( ); -export { HeadingWithDivider }; +const Subtext = ({ className, ...props }: React.HTMLAttributes) => ( + +); + +export { HeadingWithDivider, Subtext }; diff --git a/libs/ui/src/lib/typography/typography.module.scss b/libs/ui/src/lib/typography/typography.module.scss index 9d75ef66..1f49b7d9 100644 --- a/libs/ui/src/lib/typography/typography.module.scss +++ b/libs/ui/src/lib/typography/typography.module.scss @@ -15,3 +15,9 @@ border-radius: 0.25rem; } } + +.subtext { + font-size:0.9rem; + // opacity:0.6; + color: #888; +} \ No newline at end of file diff --git a/libs/ui/src/lib/vstack/index.tsx b/libs/ui/src/lib/vstack/index.tsx new file mode 100644 index 00000000..512a398a --- /dev/null +++ b/libs/ui/src/lib/vstack/index.tsx @@ -0,0 +1,9 @@ +import { PropsWithChildren } from 'react'; + +import styles from './vstack.module.scss'; + +const VStack = ({ children, ...props }: PropsWithChildren) => { + return
{children}
; +}; + +export default VStack; diff --git a/libs/ui/src/lib/vstack/vstack.module.scss b/libs/ui/src/lib/vstack/vstack.module.scss new file mode 100644 index 00000000..3a699f1e --- /dev/null +++ b/libs/ui/src/lib/vstack/vstack.module.scss @@ -0,0 +1,5 @@ +.wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; +}