diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f322f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0dfd38 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +This is a [Next.js](https://nextjs.org/) + [Gluestack-ui](https://ui.gluestack.io/) project template bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`@gluestack-ui`](https://ui.gluestack.io/docs/overview/introduction) library that provides optionally styled and accessible components. These components are designed for easy integration into applications developed with React and React Native. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js + Gluestack UI template, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Gluestack UI Documenatation](https://ui.gluestack.io/docs/overview/introduction) - learn about core concepts and architecture of gluestack-ui. +- [Gluestack Style Documentaion](https://style.gluestack.io/docs/overview/introduction) - learn about the universal styling library that is used in Gluestack-ui + +You can check out: +- [the gluestack-ui GitHub repository](https://github.com/gluestack/gluestack-ui) +- [the gluestack-style GitHub repository](https://github.com/gluestack/gluestack-style) +Your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/app/components/About.tsx b/app/components/About.tsx new file mode 100644 index 0000000..9ceb583 --- /dev/null +++ b/app/components/About.tsx @@ -0,0 +1,45 @@ +'use client'; + +import React from 'react'; +import { Box, VStack, Text, Link, LinkText } from '@gluestack-ui/themed'; +import RootLayout from '../layout'; + +const AboutUs: React.FC = () => { + const title = 'About lexic.swissgeol.ch - Controlled Vocabularies Interface'; + + return ( + + + + Home > + + + About + + + + + {/* lexic.swissgeol.ch */} + About lexic.swissgeol.ch,{"\n\n"} + Our platform aims to provide geological classifications and nomenclatures in four languages.{"\n"}By integrating different semantic models used in our data and map products, we offer a unified approach.{"\n"}The platform is based on knowledge graphs and supports various formats, including machine-readable knowledge representations such as SKOS.{"\n"}Each concept is assigned a unique URI for easy reference. + + < VStack space="xl" p="$6" alignItems="center" > + + + Current Focus Areas:{"\n"} During the pilot phase, we are concentrating on three main components:{"\n\n"} + {" • "}User-Friendly Interface: Explore vocabularies and their terminology through our intuitive landing page and main user interface.{"\n"} + {" • "}Web Map Query Tool: Use our data and build queries using the integrated vocabularies.{"\n"} + {" • "}Vocabulary Engineering Environment: Build, manage, and publish vocabularies or ontologies with our advanced tools.{"\n\n"} + + + + + + ); +} +export default AboutUs; diff --git a/app/components/Breacrumbs.tsx b/app/components/Breacrumbs.tsx new file mode 100644 index 0000000..284ff4f --- /dev/null +++ b/app/components/Breacrumbs.tsx @@ -0,0 +1,44 @@ +import { Box, Link, LinkText } from '@gluestack-ui/themed'; +import React from 'react'; +import { BreadCrumbsData } from '../models/breadCrumbsInterface'; + +export interface BreadcrumbsProps { + data: BreadCrumbsData; +} + +const Breadcrumbs: React.FC = ({ data }) => { + const extractLabel = (url: string) => { + const parts = url.replace('#', '/').split('/'); + return parts.pop(); + }; + return ( + + + {data.startPage} > + + + {data.vocabulary} > + + {/* + ... > + */} + {data.broader.slice().reverse().map((term, index) => ( + + + {extractLabel(term)} > + + + ))} + + {data.term} + + + ); +} + +export default Breadcrumbs; diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx new file mode 100644 index 0000000..e913906 --- /dev/null +++ b/app/components/Footer.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { Box, Button, ButtonIcon, SearchIcon, Input, InputField, Icon, Text, Divider, Link } from '@gluestack-ui/themed'; +import { Github } from 'lucide-react'; +import React from 'react'; +import { TermData } from '../models/termDataInterface'; + +export interface FooterProps { + showReleaseGitHub: boolean; + termData: TermData | null; +} + +const Footer: React.FC = ({ showReleaseGitHub, termData }) => { + return ( + + + {showReleaseGitHub && ( + + + + {termData?.version} + + + )} + + + About lexic.swissgeol.ch + + + + Contact + + + + ); +} + +export default Footer; diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx new file mode 100644 index 0000000..00096b1 --- /dev/null +++ b/app/components/Navbar.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { Box, Button, ButtonIcon, SearchIcon, Input, InputField, Icon, Text, Link } from '@gluestack-ui/themed'; +import { UserRound } from 'lucide-react'; +import React from 'react'; + +const Navbar: React.FC = () => { + return ( + + + + + Pilot Project + + + + + + + + + + + + + ); +} + +export default Navbar; diff --git a/app/components/VocabolaryTerm.tsx b/app/components/VocabolaryTerm.tsx new file mode 100644 index 0000000..3a89126 --- /dev/null +++ b/app/components/VocabolaryTerm.tsx @@ -0,0 +1,242 @@ +import React from 'react'; +import { Box, Text, Link, Badge, Menu, MenuItemLabel, MenuItem, Button, ButtonIcon, ButtonText, ChevronDownIcon, Accordion, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent, AccordionIcon, BadgeText, LinkText } from '@gluestack-ui/themed'; +import { DownloadIcon, Map, Search, ChevronUpIcon } from 'lucide-react'; +import Breadcrumbs from './Breacrumbs'; +import RootLayout from '../layout'; +import { TermData } from '../models/termDataInterface'; +import { BreadCrumbsData } from '../models/breadCrumbsInterface'; + +interface VocabolaryTermProps { + termData: TermData | null; + breadCrumbsData: BreadCrumbsData; +} + + +const VocabolaryTerm: React.FC = ({ termData, breadCrumbsData }) => { + + const extractLabel = (url: string) => { + const parts = url.replace('#', '/').split('/'); + return parts.pop(); + }; + + const extractLabelRelatedConcept = (url: string) => { + const parts = url.split('#'); + return parts.pop(); + }; + + const getFlagImageUrl = (lang: string) => { + switch (lang) { + case 'en': + return '/Bandiera-inglese.jpg'; + case 'de': + return '/GERMANIA.jpg'; + case 'it': + return '/italia.png'; + case 'fr': + return '/Flag_of_France_(1794–1815,_1830–1974).svg.png'; + default: + return ''; + } + }; + + if (termData) { + const title = termData.term + ' - ' + termData.vocabulary + ' - Controlled Vocabularies Interface'; + + return ( + + + + + + {termData.vocabulary} + [{termData.version}] + + + {Object.entries(termData.languages).map(([lang, translation]) => ( + lang === 'en' && ( + + {translation} + + ) + ))} + + + {extractLabel(termData.termStatus)} + + + + + + URI + + + {termData.uri} + + + + + {Object.entries(termData.languages).map(([lang, translation]) => ( + + + + + {translation} + + ))} + + + ( + + )}> + + Download JSON + + + Download RDF + + + + + + + + Details + {termData.definition || 'No description...'} + + - {termData.isDefinedBy} + {/* - Wikipedia: Quaternary Period. - Wikipedia, the Free Encyclopedia, 2023. Retrieved from https://en.wikipedia.org/wiki/Quaternary_Period + - Wikipedia: Quaternary Period. - Wikipedia, the Free Encyclopedia, 2023. Retrieved from https://en.wikipedia.org/wiki/Quaternary_Period */} + + + + Related Terms + + + + + + {({ isExpanded }) => ( + <> + Broader + {isExpanded ? ( + + ) : ( + + )} + + )} + + + + {termData.relatedTerms.Broader.length > 0 ? ( + termData.relatedTerms.Broader.map(term => ( + + • {extractLabel(term)} + + )) + ) : ( + No Broader concepts... + )} + + + + + + {({ isExpanded }) => ( + <> + Narrowers + {isExpanded ? ( + + ) : ( + + )} + + )} + + + + {termData.relatedTerms.Narrower.length > 0 ? ( + termData.relatedTerms.Narrower.map(term => ( + + • {extractLabel(term)} + + )) + ) : ( + No Narrowers concepts... + )} + + + + + + {({ isExpanded }) => ( + <> + Other Relations + {isExpanded ? ( + + ) : ( + + )} + + )} + + + + {Object.keys(termData.relatedTerms.OtherRelation).length > 0 ? ( + Object.entries(termData.relatedTerms.OtherRelation).map(([predicate, terms]) => ( + terms.map((term, index) => ( + + + + + {extractLabelRelatedConcept(predicate)} + + + + {term.startsWith('http') ? ( + + + {term} + + + ) : ( + + {term} + + )} + + + )) + )) + ) : ( + No related concepts... + )} + + + + + + + + + + ); + } +}; + +export default VocabolaryTerm; diff --git a/app/components/homepage.tsx b/app/components/homepage.tsx new file mode 100644 index 0000000..765408b --- /dev/null +++ b/app/components/homepage.tsx @@ -0,0 +1,141 @@ +'use client'; + +import React from 'react'; +import { Box, VStack, HStack, Text, Image, Link } from '@gluestack-ui/themed'; +import RootLayout from '../layout'; + +interface Translation { + [key: string]: string; +} + +type FlagCode = 'EN' | 'DE' | 'IT' | 'FR'; + +interface TopConcept { + label: string; + url: string; +} + +interface Vocabulary { + name: string; + translations: Translation; + description: string; + topConcept: TopConcept[]; +} + +const Homepage: React.FC = () => { + const title = 'Homepage - Controlled Vocabularies Interface'; + const vocabularies: Vocabulary[] = [ + { + name: 'Chronostratigraphy', + translations: { + 'EN': 'Chronostratigraphy', + 'DE': 'Chronostratigraphie', + 'IT': 'Cronostratigrafia', + 'FR': 'Chronostratigraphie' + }, + description: 'The controlled vocabulary on "Chronostratigraphy" includes terms used to describe the age of geological units in Switzerland, as seen in maps, boreholes, and other assets managed by the Swiss Geological Survey. This vocabulary is based on the International Stratigraphic Chart (www.stratigraphy.org), adapted for national specificities and formatted for Swiss multilingual usage. It also includes historical and informal nomenclature, linked to official terms.', + topConcept: [ + { label: "Phanerozoikum (de), Phanerozoic (en), Phanérozoïque (fr), Fanerozoico (it)", url: '/Chronostratigraphy/Phanerozoic' }, + { label: "Präkambrium (de), Precambrian (en), Précambrien (fr), Precambriano (it)", url: '/Chronostratigraphy/Precambrian' }, + ] + }, + { + name: 'Tectonic Units', + translations: { + 'EN': 'Tectonic Units', + 'DE': 'Tektonische Einheiten', + 'IT': 'Unità tettoniche', + 'FR': 'Unités tectoniques' + }, + description: 'The controlled vocabulary on "Tectonic Units" includes terms used to describe the tectonic units of Switzerland in maps, boreholes, and other assets managed by the Swiss Geological Survey. This vocabulary is based on the Tectonic Map of Switzerland 1:500,000 (Gouffon et al. 2024), adapted and complemented for more detailed attributions, considering additional internal and external links.', + topConcept: [ + { label: "Abgescherte Nordalpines Vorland (de), Detached North Alpine Foreland (en), Avant-pays nord-alpin décolle (fr), Avampaese nordalpino distaccato (it)", url: '/TectonicUnits/DetachedNorthAlpineForeland' }, + { label: "Autochthones Nordalpines Vorland (de), Autochthonous North Alpine Foreland (en), Avant-pays nord-alpin autochtone (fr), Avampaese nordalpino autoctono (it)", url: '/TectonicUnits/AutochthonousNorthAlpineForeland' }, + { label: "Helvetikum (de), Helvetic (en), Helvétique (fr), Elvetico (it)", url: '/TectonicUnits/Helvetic' }, + { label: "Lepontikum (de), Lepontic (en), Lépontique (fr), Lepontico (it)", url: '/TectonicUnits/Lepontic' }, + { label: "Ostalpin (de), Austroalpine (en), Austroalpin (fr), Austroalpino (it)", url: '/TectonicUnits/Austroalpine' }, + { label: "Penninikum (de), Penninic (en), Pennique (fr), Pennidico (it)", url: '/TectonicUnits/Penninic' }, + { label: "Salassikum (de), Salassic (en), Salassique (fr), Salassico (it)", url: '/TectonicUnits/Salassic' }, + { label: "Südalpin (de), South Alpine (en), Sudalpin (fr), Sudalpino (it)", url: '/TectonicUnits/South_Alpine' }, + { label: "Känozoische magmatische Gesteine (de), Cenozoic magmatic rocks (en), Roches magmatiques cénozoïques (fr), Rocce magmatiche cenozoiche (it)", url: '/TectonicUnits/Cenozoicmagmaticrocks' }, + ], + } + ]; + + const flagImages = { + 'EN': 'Bandiera-inglese.jpg', + 'DE': 'GERMANIA.jpg', + 'IT': 'italia.png', + 'FR': 'Flag_of_France_(1794–1815,_1830–1974).svg.png' + }; + + const flags: FlagCode[] = ['EN', 'DE', 'IT', 'FR']; + + return ( + + + + {/* lexic.swissgeol.ch */} + Welcome to lexic.swissgeol.ch,{"\n"}the controlled vocabulary platform for geology developed by swisstopo. + + < VStack space="xl" p="$6" alignItems="center" > + + + Explore Our Features:{"\n\n"} + {" • "}User-Friendly Interface: Discover geological vocabularies and terminology through our intuitive landing page and main user interface.{"\n"} + {" • "}Web Map Query Tool: Use our data and build queries with the integrated vocabularies for enhanced research.{"\n"} + {" • "}Vocabulary Engineering Environment: Develop, manage, and publish vocabularies or ontologies using our advanced tools.{"\n\n"} + Currently, lexic.swissgeol.ch is in its pilot phase. We are actively developing and refining features, and more vocabularies are planned for publication. Your feedback, suggestions, and ideas are most welcome.{"\n"} + Please do not hesitate to contact us at swissgeol@swisstopo.ch. + + + + Vocabularies + + {vocabularies.map((vocab, index) => ( + + + {vocab.name} + + {flags.map((flag, flagIndex) => ( + + + + + {vocab.translations[flag]} + + ))} + + + + {vocab.description} + + Top Concepts + + {vocab.topConcept.map((concept, conceptIndex) => ( + + {concept.label} + + ))} + + + ))} + + + + + ); +} +export default Homepage; diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000..dad0136 Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..3873b09 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,105 @@ +:root { + --max-width: 1100px; + --border-radius: 12px; + --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', + 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', + 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; + + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; + --background-rgb: 248, 248, 248; + + --primary-glow: conic-gradient( + from 180deg at 50% 50%, + #16abff33 0deg, + #0885ff33 55deg, + #54d6ff33 120deg, + #0071ff33 160deg, + transparent 360deg + ); + --secondary-glow: radial-gradient( + rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0) + ); + + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient( + #00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080 + ); + + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; +} + +@media (prefers-color-scheme: light) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + --background-rgb: 248, 248, 248; + + + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); + --secondary-glow: linear-gradient( + to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3) + ); + + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient( + #ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80 + ); + + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +body { + color: rgb(var(--foreground-rgb)); + background: rgb(var(--background-rgb)); +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: light) { + html { + color-scheme: light; + } +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..ebac385 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,40 @@ +import './globals.css'; +import { Roboto } from 'next/font/google'; +import { Providers } from './providers'; +import StyledJsxRegistry from './registry'; +import Navbar from './components/Navbar'; +import Footer from './components/Footer'; +import { TermData } from './models/termDataInterface'; + +const inter = Roboto({ + subsets: ['latin'], + weight: '100' +}); + +export default function RootLayout({ + children, title, showReleaseGitHub, termData +}: { + title: string; + showReleaseGitHub: boolean; + termData: TermData | null; + children: React.ReactNode; +}) { + return ( + + + + + {title} + + + + + +
{children}
+