1+ import { Menu } from '@/types'
2+ import Image from 'next/image'
13import Link from 'next/link'
24import { FaGithub , FaInstagram , FaLinkedin , FaMastodon , FaTwitter } from 'react-icons/fa'
5+ import { getNavigation } from '../server/nav'
36import { SignUp } from './components/SignUp'
47
58/*
@@ -18,53 +21,82 @@ import { SignUp } from './components/SignUp'
1821 }
1922 ```
2023*/
21- const navigation = {
22- about : [
23- { name : 'About Us' , href : '/about' } ,
24- { name : 'Meet the Team' , href : '/about#team' } ,
25- { name : 'Manifesto' , href : 'https://doi.org/10.36850/ed1' } ,
24+ const defaultNav : Menu = {
25+ slug : 'main-footer' ,
26+ title : 'Trial and Error' ,
27+ items : [
28+ {
29+ title : 'About' ,
30+ url : '' ,
31+ children : [
32+ { title : 'About Us' , url : '/about' } ,
33+ { title : 'Meet the Team' , url : '/about#team' } ,
34+ { title : 'Manifesto' , url : 'https://doi.org/10.36850/ed1' } ,
35+ ] ,
36+ } ,
37+ {
38+ title : 'Projects' ,
39+ children : [
40+ { title : 'Journal' , url : 'https://blog.trialanderror.org' } ,
41+ { title : 'Blog' , url : 'https://journal.trialanderror.org' } ,
42+ { title : 'Development' , url : 'https://github.com/trialanderrororg' } ,
43+ ] ,
44+ } ,
45+ { title : 'Company' , children : [ { title : 'Jobs' , url : 'https://positions.trialanderror.org' } ] } ,
46+ {
47+ title : 'Legal' ,
48+ children : [
49+ { title : 'Non-profit Status' , url : 'https://journal.trialanderror.org/legal-status' } ,
50+ // { name: 'Privacy', href: '#' },
51+ // { name: 'Terms', href: '#' },
52+ ] ,
53+ } ,
2654 ] ,
27- projects : [
28- { name : 'Journal' , href : 'https://blog.trialanderror.org' } ,
29- { name : 'Blog' , href : 'https://journal.trialanderror.org' } ,
30- { name : 'Development' , href : 'https://github.com/trialanderrororg' } ,
31- ] ,
32- company : [ { name : 'Jobs' , href : 'https://positions.trialanderror.org' } ] ,
33- legal : [
34- { name : 'Non-profit Status' , href : 'https://journal.trialanderror.org/legal-status' } ,
35- // { name: 'Privacy', href: '#' },
36- // { name: 'Terms', href: '#' },
55+ }
56+
57+ const socialMap : Record < string , React . FC < React . ComponentProps < 'svg' > > > = {
58+ twitter : ( props : React . ComponentProps < 'svg' > ) => < FaTwitter { ...props } /> ,
59+ github : ( props : React . ComponentProps < 'svg' > ) => < FaGithub { ...props } /> ,
60+ mastodon : ( props : React . ComponentProps < 'svg' > ) => < FaMastodon { ...props } /> ,
61+ linkedin : ( props : React . ComponentProps < 'svg' > ) => < FaLinkedin { ...props } /> ,
62+ instagram : ( props : React . ComponentProps < 'svg' > ) => < FaInstagram { ...props } /> ,
63+ }
64+
65+ const defaultSocials : Menu = {
66+ title : 'Social Items' ,
67+ slug : 'main-socials' ,
68+ items : [
69+ {
70+ title : 'Twitter' ,
71+ url : 'https://twitter.com/jtrialerror' ,
72+ } ,
73+ {
74+ title : 'GitHub' ,
75+ url : 'https://github.com/TrialAndErrorOrg' ,
76+ } ,
77+ {
78+ title : 'Mastodon' ,
79+ url : 'https://akademienl.social/@trialanderror' ,
80+ } ,
81+ {
82+ title : 'LinkedIn' ,
83+ url : 'https://www.linkedin.com/company/jtrialanderror/' ,
84+ } ,
85+ {
86+ title : 'Instagram' ,
87+ url : 'https://instagram.com/journaltrialerror' ,
88+ } ,
3789 ] ,
3890}
39- const social = [
40- {
41- name : 'Twitter' ,
42- href : 'https://twitter.com/jtrialerror' ,
43- icon : ( props : React . ComponentProps < 'svg' > ) => < FaTwitter { ...props } /> ,
44- } ,
45- {
46- name : 'GitHub' ,
47- href : 'https://github.com/TrialAndErrorOrg' ,
48- icon : ( props : React . ComponentProps < 'svg' > ) => < FaGithub { ...props } /> ,
49- } ,
50- {
51- name : 'Mastodon' ,
52- href : 'https://akademienl.social/@trialanderror' ,
53- icon : ( props : React . ComponentProps < 'svg' > ) => < FaMastodon { ...props } /> ,
54- } ,
55- {
56- name : 'LinkedIn' ,
57- href : 'https://www.linkedin.com/company/jtrialanderror/' ,
58- icon : ( props : React . ComponentProps < 'svg' > ) => < FaLinkedin { ...props } /> ,
59- } ,
60- {
61- name : 'Instagram' ,
62- href : 'https://instagram.com/journaltrialerror' ,
63- icon : ( props : React . ComponentProps < 'svg' > ) => < FaInstagram { ...props } /> ,
64- } ,
65- ]
6691
67- export function Footer ( ) {
92+ export async function Footer ( ) {
93+ const navPromise = getNavigation ( 'main-footer' )
94+ const socialPromise = getNavigation ( 'main-socials' )
95+ const [ nav , socials ] = await Promise . all ( [ navPromise , socialPromise ] )
96+
97+ const navigation = nav ?? defaultNav
98+ const social = socials ?? defaultSocials
99+
68100 return (
69101 < footer className = "bg-blue-500" aria-labelledby = "footer-heading" >
70102 < h2 id = "footer-heading" className = "sr-only" >
@@ -73,30 +105,30 @@ export function Footer() {
73105 < div className = "mx-auto max-w-7xl py-12 px-4 sm:px-6 lg:py-16 lg:px-8" >
74106 < div className = "xl:grid xl:grid-cols-3 xl:gap-8" >
75107 < div className = "grid grid-cols-2 gap-8 xl:col-span-2" >
76- { Object . keys ( navigation )
77- . slice ( 0 , Math . floor ( Object . keys ( navigation ) . length / 2 + 0.9 ) )
78- ?. map ( ( name , idx ) => (
108+ { navigation . items
109+ ? .slice ( 0 , Math . floor ( ( navigation . items ?? [ ] ) . length / 2 + 0.9 ) )
110+ ?. map ( ( { title } , idx ) => (
79111 // {Object.entries(navigation).map(([name, links]) => (
80- < div className = "md:grid md:grid-cols-2 md:gap-8" key = { name } >
81- { Object . entries ( navigation )
112+ < div className = "md:grid md:grid-cols-2 md:gap-8" key = { title } >
113+ { ( navigation . items ?? [ ] )
82114 ?. slice ( idx * 2 , idx * 2 + 2 )
83- . map ( ( [ name , links ] ) => (
115+ . map ( ( { title , children , url , target } ) => (
84116 < div
85- key = { name } //className={idx % 2 === 0 ? 'mt-12 md:mt-0' : ''}>
117+ key = { title } //className={idx % 2 === 0 ? 'mt-12 md:mt-0' : ''}>
86118 className = "mt-12 md:mt-0"
87119 >
88120 < h3 className = "text-sm font-semibold uppercase tracking-wider text-slate-100" >
89- { name }
121+ { title }
90122 </ h3 >
91123 < ul role = "list" className = "mt-4 space-y-4" >
92- { links . map ( ( item ) => (
93- < li key = { item . name } >
124+ { ( children ?? [ ] ) . map ( ( { title , url , target } ) => (
125+ < li key = { title } >
94126 < Link
95- href = { item . href }
96- target = { item . href . startsWith ( 'http' ) ? '_blank' : undefined }
127+ href = { url ! }
128+ target = { target || url ? .startsWith ( 'http' ) ? '_blank' : undefined }
97129 className = "text-base text-slate-300 transition-colors hover:text-orange-500"
98130 >
99- { item . name }
131+ { title }
100132 </ Link >
101133 </ li >
102134 ) ) }
@@ -141,19 +173,34 @@ export function Footer() {
141173 </ div >
142174 < div className = "mt-8 border-t border-gray-200 pt-8 md:flex md:items-center md:justify-between" >
143175 < div className = "flex space-x-6 text-white md:order-2" >
144- { social . map ( ( item ) => (
145- < a
146- key = { item . name }
147- href = { item . href }
148- target = { item . href . startsWith ( 'http' ) ? '_blank' : undefined }
149- className = "text-slate-100 hover:text-orange-500"
150- >
151- < span className = "sr-only" > { item . name } </ span >
152- < item . icon //className="h-6 w-6 text-white"
153- // aria-hidden="true"
176+ { social . items ?. map ( ( { title, icon, url } ) => {
177+ const lowercaseTitle = title . toLowerCase ( )
178+ const Icon = icon ? (
179+ < Image
180+ src = { icon ?. url }
181+ alt = { title }
182+ width = { 24 }
183+ height = { 24 }
184+ className = "h-6 w-6 text-white"
154185 />
155- </ a >
156- ) ) }
186+ ) : (
187+ socialMap [ lowercaseTitle ]
188+ )
189+ return (
190+ < a
191+ key = { title }
192+ href = { url }
193+ target = { url ?. startsWith ( 'http' ) ? '_blank' : undefined }
194+ className = "text-slate-100 hover:text-orange-500"
195+ >
196+ < span className = "sr-only" > { title } </ span >
197+ { /* <icon //className="h-6 w-6 text-white"
198+ // aria-hidden="true"
199+ /> */ }
200+ < Icon />
201+ </ a >
202+ )
203+ } ) }
157204 </ div >
158205 < p className = "mt-8 text-base text-slate-100 md:order-1 md:mt-0" >
159206 © { new Date ( ) . getFullYear ( ) } Center of Trial and Error. CC BY 4.0
0 commit comments