Skip to content

Commit af1c989

Browse files
djwar42Danielphilsturgeon
authored
Frontend and updated readme (#222)
* Update Logo * Add frontend next.js app * Eslint silence for Vercel Build * Update description * Update Readme with Screenshot * Update gitignore * Update README.md * Delete .DS_Store * Revert "Delete .DS_Store" This reverts commit 285370a. * Update .gitignore * Clean out defunct links in README.md * Delete .DS_Store * Update .gitignore --------- Co-authored-by: Daniel <[email protected]> Co-authored-by: Phil Sturgeon <[email protected]>
1 parent e30d22b commit af1c989

24 files changed

+3238
-169
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
node_modules/
22
package-lock.json
33

4+
.vercel
5+
6+
.DS_Store

AWESOME EARTH.png

-349 KB
Loading

README.md

+87-169
Large diffs are not rendered by default.

checkReadme.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Checks and rewrites the README.md for non working links
2+
const fs = require('fs')
3+
const axios = require('axios')
4+
5+
// Read the README.md file
6+
fs.readFile('README.md', 'utf8', async (err, data) => {
7+
if (err) {
8+
console.error('Error reading README.md:', err)
9+
return
10+
}
11+
12+
// Regular expression to match links in the specified format
13+
const linkRegex = /- \[(.*?)\]\((https?:\/\/.*?)\)(.*)/g
14+
15+
let content = data
16+
let match
17+
const removedLinks = []
18+
19+
while ((match = linkRegex.exec(data)) !== null) {
20+
const [fullMatch, linkText, linkUrl, description] = match
21+
22+
try {
23+
// Check the link validity with a maximum of 3 retries
24+
await checkLinkValidity(linkUrl, 3)
25+
} catch (error) {
26+
console.error(`Removing invalid link: ${linkUrl}`)
27+
content = content.replace(fullMatch, '')
28+
removedLinks.push(linkUrl)
29+
}
30+
}
31+
32+
// Remove extra newline characters
33+
content = content.replace(/\n{2,}/g, '\n\n')
34+
35+
// Write the updated content back to README.md
36+
fs.writeFile('README.md', content, 'utf8', (err) => {
37+
if (err) {
38+
console.error('Error writing README.md:', err)
39+
return
40+
}
41+
console.log('Link validation completed. README.md updated.')
42+
43+
if (removedLinks.length > 0) {
44+
console.log('Removed links:')
45+
removedLinks.forEach((link) => {
46+
console.log(link)
47+
})
48+
}
49+
})
50+
})
51+
52+
async function checkLinkValidity(url, retries) {
53+
try {
54+
await axios.get(url)
55+
} catch (error) {
56+
if (retries > 0) {
57+
console.log(`Retrying link: ${url} (${retries} retries left)`)
58+
await new Promise((resolve) => setTimeout(resolve, 1000))
59+
await checkLinkValidity(url, retries - 1)
60+
} else {
61+
throw error
62+
}
63+
}
64+
}

frontend/.eslintrc.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}

frontend/.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

frontend/README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
A front end for the Awesome Earth project.
2+
3+
Deployed at https://awesome-earth.org
4+
5+
![awesome-earth.org](/frontend/awesome-earth-org.png?raw=true)
6+
7+
## Development
8+
9+
First, run the development server:
10+
11+
```bash
12+
npm run dev
13+
```
14+
15+
Open [http://localhost:3000](http://localhost:3000) with your browser.
16+
17+
## Production
18+
19+
```bash
20+
npm run build
21+
```
22+
23+
The README.md file in the root folder is automatically converted to /frontend/app/data/links.js at build time.

frontend/app/components/Footer.tsx

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// app/components/Footer.tsx
2+
import { GlobeIcon } from './icons'
3+
4+
export default function Footer() {
5+
return (
6+
<footer className='bg-[#1E824C] text-white py-6 px-6 relative'>
7+
<div className='container mx-auto flex items-center justify-center'>
8+
<div className='flex items-center gap-2 z-10'>
9+
<GlobeIcon className='h-6 w-6' />
10+
<span>Awesome Earth</span>
11+
</div>
12+
</div>
13+
<div className='absolute bottom-0 right-0 w-[200px] h-[200px] rounded-full bg-gradient-to-r from-[#1E824C] to-[#3CB371] opacity-20 -z-10' />
14+
<div className='absolute bottom-0 left-0 w-[150px] h-[150px] rounded-full bg-gradient-to-r from-[#3CB371] to-[#00FA9A] opacity-20 -z-10' />
15+
</footer>
16+
)
17+
}

frontend/app/components/Header.tsx

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// app/components/Header.tsx
2+
'use client'
3+
import { useState } from 'react'
4+
import { GlobeIcon, SearchIcon } from './icons'
5+
6+
interface HeaderProps {
7+
onSearchQueryChange: (query: string) => void
8+
}
9+
10+
export default function Header({ onSearchQueryChange }: HeaderProps) {
11+
const [searchQuery, setSearchQuery] = useState('')
12+
13+
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
14+
const query = e.target.value
15+
setSearchQuery(query)
16+
onSearchQueryChange(query)
17+
}
18+
19+
return (
20+
<header className='bg-[#1E824C] text-white py-4 px-6 flex flex-wrap justify-center sm:items-center sm:justify-between relative'>
21+
<div className='flex items-center gap-2 z-10'>
22+
<GlobeIcon className='h-8 w-8' />
23+
<h1 className='text-2xl font-bold'>Awesome Earth</h1>
24+
</div>
25+
<div className='relative z-10'>
26+
<input
27+
className='bg-white text-gray-900 rounded-md px-4 py-2 pr-10 w-[350px] mt-5 sm:mt-0'
28+
placeholder='Search for climate solutions...'
29+
type='text'
30+
value={searchQuery}
31+
onChange={handleSearch}
32+
/>
33+
<SearchIcon className='absolute right-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-500 mt-[10px] sm:mt-0' />
34+
</div>
35+
<div className='absolute top-0 right-0 w-[300px] h-[300px] rounded-full bg-gradient-to-r from-[#1E824C] to-[#3CB371] opacity-20 -z-10' />
36+
<div className='absolute top-0 left-0 w-[250px] h-[250px] rounded-full bg-gradient-to-r from-[#3CB371] to-[#00FA9A] opacity-20 -z-10' />
37+
<div className='absolute top-0 right-[500px] w-[200px] h-[200px] rounded-full bg-gradient-to-r from-[#00FA9A] to-[#1E824C] opacity-20 -z-10' />
38+
</header>
39+
)
40+
}

0 commit comments

Comments
 (0)