From 61f73aa7754da3a638cd0db34dadc1a8fa3a9c89 Mon Sep 17 00:00:00 2001 From: langbart Date: Mon, 6 Apr 2026 21:47:41 +0200 Subject: [PATCH] Refactor project structure and update configurations. Removed TinaCMS references, added ESLint configuration, and updated package dependencies. Enhanced error handling and improved styling across various components. Introduced CI workflow for linting and building. Updated README and environment variables for clarity. --- .env.example | 12 +- .eslintrc.json | 3 - .github/workflows/ci.yml | 19 + .gitignore | 5 - README.md | 144 +- app/blog/[slug]/page.js | 14 +- app/blog/page.js | 53 +- app/blog/peskas/page.js | 86 + app/data-resources/page.js | 105 +- app/error.js | 32 + app/how-it-works/how-it-works.css | 222 + app/how-it-works/page.js | 544 +- app/layout.js | 57 +- app/page-terms/page.js | 15 +- app/page.js | 9 +- app/products/peskas/page.js | 22 + app/sitemap.js | 12 + app/under-costruction/page.js | 10 +- components/ErrorBoundary.jsx | 10 +- components/content/BlogCoverImage.js | 47 + components/content/RichText.js | 38 +- components/elements/BackToTop.js | 2 +- components/elements/WowInit.js | 4 +- components/layout/Footer.js | 7 +- components/layout/Footer.module.css | 8 + components/layout/Header.js | 273 +- components/layout/Header.module.css | 262 + components/layout/Layout.js | 85 +- components/layout/Sidebar.js | 355 +- components/layout/Sidebar.module.css | 233 + components/sections/AboutSection.js | 37 +- components/sections/BlogPostClient.js | 46 +- components/sections/BlogSection.js | 68 +- components/sections/CTASection.js | 16 +- components/sections/EcosystemHeroSection.js | 50 + components/sections/FeaturesSection.js | 45 +- components/sections/HeroSection.js | 95 +- components/sections/HomePageClient.js | 39 +- components/sections/ImpactStatsSection.js | 30 + components/sections/PeskasPageClient.js | 41 + components/sections/PlatformsSection.js | 48 + components/sections/RegionsSection.js | 100 +- components/sections/StatsSection.js | 35 +- components/sections/TracksHeroSection.js | 116 +- components/slider/Blog.js | 4 +- components/slider/Featured.js | 6 +- components/slider/Intro2.js | 12 +- components/slider/Offer.js | 3 +- components/slider/Team.js | 26 +- components/slider/Testimonial.js | 2 +- components/slider/Thumb.js | 84 +- content/global/navigation.json | 26 + content/global/regions.json | 10 +- content/global/settings.json | 10 +- content/pages/homepage.json | 110 +- content/pages/peskas.json | 114 + data/regions.js | 45 - eslint.config.mjs | 10 + lib/blogPostSanitizeSchema.js | 20 + lib/constants.js | 62 +- lib/posts.js | 176 +- lib/publicAssetUrl.js | 13 + package-lock.json | 9439 +++++++++++++++++ package.json | 20 +- posts/africa-trip.md | 1 + posts/domain-based-architecture.md | 1 + posts/nutrients.md | 1 + posts/peskaas-plos-one.md | 1 + posts/shorthands.md | 1 + posts/tracks-digital-window.md | 1 + posts/world-fishers-day.md | 1 + public/assets/css/style.css | 494 +- .../assets/scss/abstracts/_tokens-root.scss | 107 + public/assets/scss/abstracts/_variables.scss | 7 +- public/assets/scss/style.scss | 3 +- styles/app.css | 180 + styles/wf-components.css | 1068 ++ tina/config.js | 888 -- tina/tina-lock.json | 1 - 79 files changed, 13730 insertions(+), 2671 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 .github/workflows/ci.yml create mode 100644 app/blog/peskas/page.js create mode 100644 app/error.js create mode 100644 app/how-it-works/how-it-works.css create mode 100644 app/products/peskas/page.js create mode 100644 components/content/BlogCoverImage.js create mode 100644 components/layout/Footer.module.css create mode 100644 components/layout/Header.module.css create mode 100644 components/layout/Sidebar.module.css create mode 100644 components/sections/EcosystemHeroSection.js create mode 100644 components/sections/ImpactStatsSection.js create mode 100644 components/sections/PeskasPageClient.js create mode 100644 components/sections/PlatformsSection.js create mode 100644 content/global/navigation.json create mode 100644 content/pages/peskas.json delete mode 100644 data/regions.js create mode 100644 eslint.config.mjs create mode 100644 lib/blogPostSanitizeSchema.js create mode 100644 lib/publicAssetUrl.js create mode 100644 package-lock.json create mode 100644 public/assets/scss/abstracts/_tokens-root.scss create mode 100644 styles/app.css create mode 100644 styles/wf-components.css delete mode 100644 tina/config.js delete mode 100644 tina/tina-lock.json diff --git a/.env.example b/.env.example index 6004a7d..e9111fe 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,6 @@ -# TinaCMS — Copy this file to .env and fill in your values. -# Values are generated at app.tina.io after connecting your GitHub repo. -# -# IMPORTANT: Tina reads .env (not .env.local) during tinacms build. -# For local development you can put values in .env.local too, but -# for Vercel add them directly in the project's Environment Variables. - -NEXT_PUBLIC_TINA_CLIENT_ID= -TINA_TOKEN= +# Canonical site URL (metadataBase, Open Graph absolute URLs, sitemap). Set in production / Vercel. +# Example: https://www.worldfishdigital.org +# NEXT_PUBLIC_SITE_URL=https://www.example.org # Automatically set by Vercel — no action needed: # NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF= diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index ea782d7..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..adb1733 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + +jobs: + lint-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + - run: npm ci + - run: npm run lint + - run: npm run build diff --git a/.gitignore b/.gitignore index 73c71af..0f4f4e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. -package-lock.json yarn.lock # dependencies @@ -36,10 +35,6 @@ yarn-error.log* # vercel .vercel -# TinaCMS — generated at build time, do not commit -/public/admin -/tina/__generated__ - #vscode settings .vscode diff --git a/README.md b/README.md index 2d29df2..db7e0f0 100644 --- a/README.md +++ b/README.md @@ -1,140 +1,32 @@ -# Peskas.show +# WorldFish Digital -Official website for Peskas™ - An open-source digital platform for small-scale fisheries. +Main site for [WorldFish](https://www.worldfishcenter.org/) digital tools and platforms—including **Peskas™** and related products—news, and resources. -## Tech Stack +## Run locally -- **Framework**: [Next.js 16.1.1](https://nextjs.org/) with App Router -- **React**: 19.0.0 -- **Styling**: SCSS/CSS with Bootstrap-based theme -- **Content**: Markdown-based blog system -- **Deployment**: Vercel +You need **Node.js 20+** (see `.nvmrc`). -## Getting Started - -### Prerequisites - -- Node.js >= 20.9.0 (see `.nvmrc`) -- npm or yarn - -### Installation - -1. Clone the repository -2. Install dependencies: - ```bash - npm install - ``` - -3. Copy environment variables: - ```bash - cp .env.example .env.local - ``` - Edit `.env.local` with your configuration if needed. - -4. Run the development server: - ```bash - npm run dev - ``` - -5. Open [http://localhost:3000](http://localhost:3000) in your browser. - -## Project Structure - -``` -peskas.show/ -├── app/ # Next.js App Router pages -│ ├── blog/ # Blog listing and individual posts -│ ├── data-resources/ # Data resources page -│ ├── how-it-works/ # How it works page -│ ├── layout.js # Root layout -│ ├── page.js # Homepage -│ ├── sitemap.js # Dynamic sitemap generation -│ └── robots.js # Robots.txt generation -├── components/ # React components -│ ├── elements/ # Reusable UI elements -│ ├── layout/ # Layout components (Header, Footer, etc.) -│ └── sections/ # Page sections -├── lib/ # Utility functions -│ ├── posts.js # Blog post utilities -│ └── constants.js # Application constants -├── posts/ # Markdown blog posts -├── public/ # Static assets -└── styles/ # Global styles +```bash +npm install +cp .env.example .env.local # optional; set your public site URL for previews +npm run dev ``` -## Adding Blog Posts - -1. Create a new `.md` file in the `posts/` directory -2. Add frontmatter with required fields: - ```yaml - --- - title: "Your Post Title" - author: "Author Name" - date: "2024-01-01" - draft: false - description: "Post description" - tags: - - tag1 - - tag2 - coverImage: "/assets/imgs/page/blog/image.jpg" - --- - ``` -3. Write your content below the frontmatter -4. Posts with `draft: true` will not appear in production - -See `docs/BLOG_SETUP.md` for more details. - -## Available Scripts +Open [http://localhost:3000](http://localhost:3000). -- `npm run dev` - Start development server -- `npm run build` - Build for production -- `npm run start` - Start production server -- `npm run lint` - Run ESLint -- `npm run sass` - Watch and compile SCSS (if needed) +## Blog posts -## Deployment +Add Markdown files under `posts/`. Use frontmatter for title, date, `draft` (hide when `true`), and optionally `channel` (`worldfish` or `peskas`). Images and assets live under `public/`. -This project is configured for deployment on Vercel. See `docs/VERCEL_SETUP.md` for deployment details. +## Production build -### Environment Variables - -Configure the following in your deployment platform: - -- `NODE_ENV` - Set to `production` for production builds -- `NEXT_PUBLIC_SITE_URL` - Full URL of your site (e.g., `https://peskas.show`) - -See `.env.example` for all available environment variables. - -## Features - -- ✅ Modern Next.js App Router architecture -- ✅ Server-side rendering (SSR) and static site generation (SSG) -- ✅ Markdown-based blog system -- ✅ SEO optimized (sitemap, robots.txt, metadata) -- ✅ Error boundaries for graceful error handling -- ✅ Responsive design -- ✅ Performance optimized - -## Development - -### Code Style - -- ESLint is configured with Next.js recommended rules -- Prettier is configured for consistent code formatting -- Use 4-space indentation (as per Prettier config) - -### Key Features - -- **Error Boundaries**: Graceful error handling in production -- **Constants**: Centralized configuration in `lib/constants.js` -- **Type Safety**: JSDoc comments for better IDE support (TypeScript migration optional) - -## Learn More +```bash +npm run build +npm start +``` -- [Next.js Documentation](https://nextjs.org/docs) -- [React Documentation](https://react.dev) -- [Next.js App Router](https://nextjs.org/docs/app) +Deploy however you host Node apps; many teams use **Vercel**. Set **`NEXT_PUBLIC_SITE_URL`** to your live site URL so links and social previews resolve correctly. ## License -See LICENSE file for details. +See `LICENSE`. diff --git a/app/blog/[slug]/page.js b/app/blog/[slug]/page.js index 419b787..3eac1ce 100644 --- a/app/blog/[slug]/page.js +++ b/app/blog/[slug]/page.js @@ -1,14 +1,20 @@ import BlogPostClient from "@/components/sections/BlogPostClient"; import { getPostBySlug, getAllPostSlugs } from "@/lib/posts"; import { notFound } from "next/navigation"; +import { POST_CHANNELS, WORLD_FISH_SITE, PESKAS_PRODUCT } from "@/lib/constants"; export async function generateMetadata({ params }) { const { slug } = await params; const post = getPostBySlug(slug); - if (!post) return { title: 'Post Not Found' }; + if (!post || post.draft) return { title: 'Post Not Found' }; + + const titleSuffix = + post.channel === POST_CHANNELS.peskas + ? `${PESKAS_PRODUCT.name} — ${WORLD_FISH_SITE.name}` + : WORLD_FISH_SITE.name; return { - title: `${post.title} - Peskas Blog`, + title: `${post.title} | ${titleSuffix}`, description: post.description || post.content?.substring(0, 160), authors: post.author ? [{ name: post.author }] : [], robots: { @@ -30,8 +36,8 @@ export async function generateStaticParams() { export default async function BlogPost({ params }) { const { slug } = await params; const post = getPostBySlug(slug); - - if (!post) { + + if (!post || post.draft) { notFound(); } diff --git a/app/blog/page.js b/app/blog/page.js index 542a398..92a5db8 100644 --- a/app/blog/page.js +++ b/app/blog/page.js @@ -1,11 +1,12 @@ -import Link from "next/link"; -import Layout from "@/components/layout/Layout"; -import { getAllPosts } from "@/lib/posts"; -import { BLOG_CONFIG } from "@/lib/constants"; +import Link from 'next/link'; +import Layout from '@/components/layout/Layout'; +import BlogCoverImage from '@/components/content/BlogCoverImage'; +import { getAllPosts } from '@/lib/posts'; +import { BLOG_WORLD_FISH, WORLD_FISH_SITE } from '@/lib/constants'; export const metadata = { - title: `${BLOG_CONFIG.title} - ${BLOG_CONFIG.description}`, - description: BLOG_CONFIG.description, + title: `${BLOG_WORLD_FISH.title} | ${WORLD_FISH_SITE.name}`, + description: BLOG_WORLD_FISH.description, robots: { index: true, follow: true, @@ -18,30 +19,27 @@ export const metadata = { export default async function Blog() { const posts = getAllPosts(); - + return ( -
-
-
-
-
-

{BLOG_CONFIG.title}

-

- {BLOG_CONFIG.description} -

-
+
+
+
+
+

{BLOG_WORLD_FISH.title}

+

{BLOG_WORLD_FISH.description}

-
-
+
+
{posts.length === 0 ? (
-

- No blog posts yet. Add markdown files to the posts/ directory to get started. +

+ No blog posts yet. Add markdown files to the{' '} + posts/ directory to get started.

@@ -55,7 +53,7 @@ export default async function Blog() { {new Date(post.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', - day: 'numeric' + day: 'numeric', })} )} @@ -63,20 +61,15 @@ export default async function Blog() { {post.title} {post.description && ( -

- {post.description} -

+

{post.description}

)} {post.coverImage && (
- {post.title}
diff --git a/app/blog/peskas/page.js b/app/blog/peskas/page.js new file mode 100644 index 0000000..00cf109 --- /dev/null +++ b/app/blog/peskas/page.js @@ -0,0 +1,86 @@ +import Link from 'next/link'; +import Layout from '@/components/layout/Layout'; +import BlogCoverImage from '@/components/content/BlogCoverImage'; +import { getPostsByChannel } from '@/lib/posts'; +import { BLOG_PESKAS, WORLD_FISH_SITE } from '@/lib/constants'; + +export const metadata = { + title: `${BLOG_PESKAS.title} | ${WORLD_FISH_SITE.name}`, + description: BLOG_PESKAS.description, + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + }, + }, +}; + +export default async function BlogPeskas() { + const posts = getPostsByChannel('peskas'); + + return ( + +
+
+
+
+

{BLOG_PESKAS.title}

+

{BLOG_PESKAS.description}

+
+
+
+
+
+
+ {posts.length === 0 ? ( +
+
+

+ No Peskas posts yet. Assign channel: peskas in{' '} + posts/*.md frontmatter. +

+
+
+ ) : ( +
+ {posts.map((post) => ( +
+
+ {post.date && ( + + {new Date(post.date).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + })} + + )} + + {post.title} + + {post.description && ( +

{post.description}

+ )} + {post.coverImage && ( +
+ + + +
+ )} +
+
+ ))} +
+ )} +
+
+
+ ); +} diff --git a/app/data-resources/page.js b/app/data-resources/page.js index d8f6078..6567eba 100644 --- a/app/data-resources/page.js +++ b/app/data-resources/page.js @@ -1,8 +1,8 @@ 'use client' import { useEffect, useRef } from 'react'; -import Layout from "@/components/layout/Layout"; -import RichText from "@/components/content/RichText"; -import data from "@/content/pages/data-resources.json"; +import Layout from '@/components/layout/Layout'; +import RichText from '@/components/content/RichText'; +import data from '@/content/pages/data-resources.json'; export default function DataResources() { const dataverseRef = useRef(null); @@ -25,7 +25,8 @@ export default function DataResources() { script.onerror = () => { console.error('Failed to load Dataverse widget'); if (currentRef) { - currentRef.innerHTML = '

Unable to load Dataverse widget. Please try refreshing the page.

'; + currentRef.innerHTML = + '

Unable to load Dataverse widget. Please try refreshing the page.

'; } }; @@ -40,70 +41,48 @@ export default function DataResources() { }, []); return ( - <> - -
-
-
-
-
-

{data.hero.title}

- -
-
+ +
+
+
+
+

{data.hero.title}

+
-
-
-
-

{data.publications.title}

-
-
- -
+
+
+ +
+
+

{data.publications.title}

+
+
+
-
-
- -
-
-

{data.data.title}

-
-
- -
-
-
-
-
+