diff --git a/.all-contributorsrc b/.all-contributorsrc index 0d6b90a64a2..8906610902d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -13970,6 +13970,15 @@ "contributions": [ "content" ] + }, + { + "login": "koyahness", + "name": "Koyah", + "avatar_url": "https://avatars.githubusercontent.com/u/210164754?v=4", + "profile": "https://github.com/koyahness", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 7, diff --git a/.claude/commands/prepare-release.md b/.claude/commands/prepare-release.md new file mode 100644 index 00000000000..b0496342e35 --- /dev/null +++ b/.claude/commands/prepare-release.md @@ -0,0 +1,163 @@ +--- +description: Prepare a release - version bump, branch sync, release notes cleanup, and deploy PR +allowed-tools: Bash, Read, Write, AskUserQuestion +argument-hints: --dry-run|--major|--minor|--patch +--- + +# Prepare Release Command + +Automates the ethereum.org deployment workflow using `src/scripts/prepare-release.sh` for deterministic operations and Claude for intelligent tasks (version suggestion, release note cleanup). + +## Arguments + +Details for $ARGUMENTS + +- `--dry-run` - Show what would happen without making any changes to remote +- `--major` - Major release (breaking/stack changes) +- `--minor` - Minor release (new features, content, translations) +- `--patch` - Patch release (bug fixes, typos, small updates) +- _(no flag)_ - Analyze changes and suggest version type + +**Dry-run mode**: If `--dry-run` is in `$ARGUMENTS`, pass it as the first argument to ALL script commands. This shows what would happen without pushing to remote or creating PRs. + +## Execution Flow + +### Step 1: Pre-flight Checks + +First, check if `--dry-run` is in `$ARGUMENTS`. If so, set `DRY_RUN_FLAG="--dry-run"`, otherwise set it to empty string. + +Run the script to verify environment and sync branches: + +```bash +./src/scripts/prepare-release.sh $DRY_RUN_FLAG preflight +``` + +This handles: `gh` authenticated, create worktree if not on `dev`, clean working tree, back-merge `master` → `staging` → `dev`, pull latest. + +**Note**: The script can run from any branch. If not on `dev`, it creates a worktree at `/tmp/claude/worktrees/ethereum-org-dev` and performs all operations there. + +If this fails, stop and report the error. + +### Step 2: Determine Version Type + +**If flag provided** (`--major`, `--minor`, `--patch`): +Extract from `$ARGUMENTS` and proceed to Step 3. + +**If no flag provided**: +1. Fetch draft release: `./src/scripts/prepare-release.sh fetch-draft` +2. Analyze the changes: + - **Major**: Stack/framework changes, significant breaking updates (rare) + - **Minor**: New features, new content pages, significant translations, new components + - **Patch**: Bug fixes, typo corrections, small content updates, dependency bumps +3. Provide a ONE-LINE suggestion with reasoning +4. Use `AskUserQuestion` to confirm: "Proceed with **X** release?" with options: Yes / Change to major / Change to minor / Change to patch +5. Proceed only after confirmation + +### Step 3: Version Bump + +```bash +VERSION=$(./src/scripts/prepare-release.sh $DRY_RUN_FLAG version ) +``` + +### Step 4: Merge to Staging + +```bash +./src/scripts/prepare-release.sh $DRY_RUN_FLAG merge-staging +``` + +### Step 5: Fetch Draft Release + +```bash +DRAFT_JSON=$(./src/scripts/prepare-release.sh fetch-draft) +``` + +Parse the JSON to extract `tagName` (DRAFT_TAG) and `body`. If no draft exists, error out. + +### Step 6: Clean Release Notes + +The draft release body needs cleanup. Apply these filters: + +**Remove from CHANGES sections** (lines matching these patterns): +- Author is `allcontributors` or `allcontributors[bot]` (these are just additions to our all-contributors list, not pertinent to actual changes) +- PR title contains "Release candidate v" (release management) +- PR title contains "Deploy v" (release management) +- PR title starts with "Staging -> dev" or "Staging -> Dev" (back-merge) +- PR title starts with "Master -> staging" or "Master -> Staging" (back-merge) +- PR title starts with "Back merge" (back-merge) +- PR title is just a version number like "v10.20.0" or "v11.0.0" (version bump commits) +- PR title starts with "Update translation contributors from Crowdin" (automated Crowdin) +- PR title starts with "Update translation progress from Crowdin" (automated Crowdin) + +Note: Keep entries from other bots like `dependabot`, `claude[bot]`, `github-actions` - their PRs ARE meaningful changes (dependency updates, code changes, etc). We just don't thank them as human contributors. + +**Remove from CONTRIBUTORS section** (the "Thank you @..." line): +These accounts should be filtered from the contributors list: +- `dependabot` +- `dependabot[bot]` +- `allcontributors` +- `allcontributors[bot]` +- `claude` +- `claude[bot]` +- `github-actions` +- `github-actions[bot]` +- `actions-user` + +**Keep the structure intact**: +- Keep section headers (⚡️ Changes, 🌐 Translations, 🐛 Bug Fix, etc.) +- Keep the `***` separators +- Keep the 🦄 Contributors section (just filter the bot names from it) +- Remove empty sections if all entries were filtered out + +Write cleaned body to temp file: +```bash +# Write the cleaned release notes to a temp file +cat << 'EOF' > /tmp/claude/release-notes.md + +EOF +``` + +### Step 7: Publish Release + +```bash +RELEASE_URL=$(./src/scripts/prepare-release.sh $DRY_RUN_FLAG publish "$VERSION" "$DRAFT_TAG" /tmp/claude/release-notes.md) +``` + +### Step 8: Create Deploy PR + +```bash +PR_URL=$(./src/scripts/prepare-release.sh $DRY_RUN_FLAG create-pr "$VERSION" /tmp/claude/release-notes.md) +``` + +### Step 9: Cleanup Worktree + +If a worktree was created, clean it up: + +```bash +./src/scripts/prepare-release.sh cleanup +``` + +### Step 10: Report Success + +Output summary: +``` +✅ Release prepared successfully! + +Version: vX.X.X +Release: +Deploy PR: + +Next step: Review the preview build, then merge the PR when ready. +``` + +## Error Handling + +- If any git operation fails, stop and report the error +- If `gh` commands fail, check authentication and permissions +- If no draft release exists, error with clear message +- If merge conflicts occur during back-merge, stop and instruct user to resolve manually + +## Notes + +- This command does NOT merge the deploy PR - that remains a manual step after QA +- The release can be edited after publishing if corrections are needed +- Always verify the cleaned release notes look correct before the PR is merged diff --git a/.claude/skills/data-layer/SKILL.md b/.claude/skills/data-layer/SKILL.md new file mode 100644 index 00000000000..5feb0bc4826 --- /dev/null +++ b/.claude/skills/data-layer/SKILL.md @@ -0,0 +1,80 @@ +--- +name: data-layer +description: This skill provides patterns for working with the data-layer module. Use when creating/editing files in src/data-layer/, src/lib/data/, or adding new data sources. +--- + +# Data Layer + +## Architecture + +``` +src/data-layer/ # Isolated, framework-agnostic module +├── api/ # Fetch functions (one per data source) +├── index.ts # Getter functions (pure passthrough) +└── registry.ts # Task registry (hourly/daily) + +src/lib/data/ # Next.js caching adapter +└── index.ts # Cached wrappers via createCachedGetter() +``` + +## Rules + +### 1. Getters must be pure passthrough + +In `src/data-layer/index.ts`, getter functions must only call `getData(TASK_ID)` with no transformations: + +```typescript +// Correct +export async function getEventsData(): Promise { + return getData(FETCH_EVENTS_TASK_ID) +} + +// Wrong - no transformations in getters +export async function getEventsData(): Promise { + const data = await getData(FETCH_EVENTS_TASK_ID) + return data?.map((e) => ({ ...e, computed: derive(e) })) ?? null +} +``` + +All transformations belong in the fetch task (`src/data-layer/api/`), not in the getter. + +### 2. Expose via lib/data for caching + +When a data function needs caching/revalidation, add a cached wrapper in `src/lib/data/index.ts`: + +```typescript +export const getEventsData = createCachedGetter( + dataLayer.getEventsData, + ["events-data"], + CACHE_REVALIDATE_DAY // or CACHE_REVALIDATE_HOUR +) +``` + +## Adding a New Data Source + +1. Create fetch function in `src/data-layer/api/fetchNewData.ts`: + ```typescript + export const FETCH_NEW_DATA_TASK_ID = "fetch-new-data" + + export async function fetchNewData(): Promise { + // Fetch and transform data here + } + ``` + +2. Add getter in `src/data-layer/index.ts`: + ```typescript + export async function getNewData(): Promise { + return getData(FETCH_NEW_DATA_TASK_ID) + } + ``` + +3. Register in `src/data-layer/registry.ts` (hourlyTasks or dailyTasks) + +4. Add cached wrapper in `src/lib/data/index.ts`: + ```typescript + export const getNewData = createCachedGetter( + dataLayer.getNewData, + ["new-data"], + CACHE_REVALIDATE_HOUR + ) + ``` diff --git a/.env.example b/.env.example index 4b2bf469066..a931dbf1d22 100644 --- a/.env.example +++ b/.env.example @@ -52,5 +52,8 @@ USE_MOCK_DATA=true # AWS SES Configuration for Enterprise Contact Form # SES_ACCESS_KEY_ID=your_iam_access_key_id -# SES_SECRET_ACCESS_KEY=your_iam_secret_access_key +# SES_SECRET_ACCESS_KEY=your_iam_secret_access_key # SES_REGION=us-east-2 + +# Supabase Events API (Geode Labs) +# SUPABASE_EVENTS_KEY=your-supabase-publishable-key diff --git a/.github/labeler.yml b/.github/labeler.yml index 2132c342090..1a0670642bf 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -32,6 +32,3 @@ "content 🖋️": - src/intl/en/** - public/content/**/* - -"event 📅": - - src/data/community-events.json diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 8cc9b169f30..4a59a7ef509 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -47,7 +47,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Run Claude PR Action - uses: anthropics/claude-code-action@beta + uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} timeout_minutes: "60" \ No newline at end of file diff --git a/.github/workflows/cleanup-netlify-previews.yml b/.github/workflows/cleanup-netlify-previews.yml new file mode 100644 index 00000000000..6ae195cb4bd --- /dev/null +++ b/.github/workflows/cleanup-netlify-previews.yml @@ -0,0 +1,104 @@ +name: Cleanup Netlify Preview Deploys +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Cleanup stale preview deploys + env: + NETLIFY_TOKEN: ${{ secrets.NETLIFY_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Set to "false" to actually delete, "true" for dry-run + DRY_RUN: "false" + run: | + set -euo pipefail + + echo "=== Netlify Preview Cleanup ===" + echo "DRY_RUN: $DRY_RUN" + echo "" + + # 1. Fetch deploys with safety pagination + ALL_DEPLOYS="[]" + for PAGE in {1..100}; do + RESPONSE=$(curl -s -H "Authorization: Bearer $NETLIFY_TOKEN" \ + "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys?per_page=100&page=$PAGE") + + [[ $(echo "$RESPONSE" | jq 'length') -eq 0 ]] && break + ALL_DEPLOYS=$(echo "$ALL_DEPLOYS $RESPONSE" | jq -s 'add') + done + + TOTAL_DEPLOYS=$(echo "$ALL_DEPLOYS" | jq 'length') + echo "Found $TOTAL_DEPLOYS total deploys" + + # 2. Extract context and state data + PREVIEW_DEPLOYS=$(echo "$ALL_DEPLOYS" | jq '[.[] | select(.context == "deploy-preview" or .context == "branch-deploy")]') + PREVIEW_COUNT=$(echo "$PREVIEW_DEPLOYS" | jq 'length') + echo "Found $PREVIEW_COUNT preview/branch deploys" + + OPEN_PRS=$(gh pr list --state open --json number --jq '.[].number') + REMOTE_BRANCHES=$(git branch -r | sed 's/origin\///' | tr -d ' ') + + echo "" + echo "=== Processing deploys ===" + + # 3. Process Deploys + echo "$PREVIEW_DEPLOYS" | jq -c '.[]' | while read -r DEPLOY; do + DEPLOY_ID=$(echo "$DEPLOY" | jq -r '.id') + BRANCH=$(echo "$DEPLOY" | jq -r '.branch') + CONTEXT=$(echo "$DEPLOY" | jq -r '.context') + CREATED=$(echo "$DEPLOY" | jq -r '.created_at') + URL=$(echo "$DEPLOY" | jq -r '.deploy_ssl_url // .deploy_url // "no-url"') + + # Never touch production/staging branches + if [[ "$BRANCH" == "main" ]] || [[ "$BRANCH" == "master" ]] || [[ "$BRANCH" == "production" ]] || [[ "$BRANCH" == "staging" ]] || [[ "$BRANCH" == "dev" ]]; then + continue + fi + + SHOULD_DELETE=false + REASON="" + + if [[ "$CONTEXT" == "deploy-preview" ]]; then + PR_NUM=$(echo "$DEPLOY" | jq -r '.review_url // ""' | grep -oP 'pull/\K[0-9]+' || echo "") + if [[ -n "$PR_NUM" ]] && ! echo "$OPEN_PRS" | grep -Fxq "$PR_NUM"; then + SHOULD_DELETE=true + REASON="PR #$PR_NUM is closed/merged" + fi + else + if ! echo "$REMOTE_BRANCHES" | grep -Fxq "$BRANCH"; then + SHOULD_DELETE=true + REASON="Branch '$BRANCH' no longer exists" + fi + fi + + if [ "$SHOULD_DELETE" = true ]; then + if [ "$DRY_RUN" = "true" ]; then + echo "[DRY-RUN] Would delete: $DEPLOY_ID" + echo " Branch: $BRANCH" + echo " Reason: $REASON" + echo " Created: $CREATED" + echo " URL: $URL" + echo "" + else + echo "Deleting: $DEPLOY_ID ($BRANCH) - $REASON" + curl -s -X DELETE -H "Authorization: Bearer $NETLIFY_TOKEN" \ + "https://api.netlify.com/api/v1/deploys/$DEPLOY_ID" + sleep 1 + fi + fi + done + + echo "=== Complete ===" diff --git a/.github/workflows/import-community-events.yml b/.github/workflows/import-community-events.yml deleted file mode 100644 index 0ffe1f358d0..00000000000 --- a/.github/workflows/import-community-events.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Import community events - -on: - schedule: - - cron: "0 0 * * SUN" # Runs every Sunday at midnight - workflow_dispatch: - -jobs: - get_data_and_create_pr: - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@master - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Set up Node.js - uses: actions/setup-node@v6 - with: - node-version: 20 - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install - - - name: Set up git - run: | - git config --global user.email "actions@github.com" - git config --global user.name "GitHub Action" - - - name: Generate timestamp and readable date - id: date - run: | - echo "TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - echo "READABLE_DATE=$(date +'%B %-d')" >> $GITHUB_ENV - - - name: Fetch latest dev and create new branch - run: | - git fetch origin dev - git checkout -b "automated-update-${{ env.TIMESTAMP }}" origin/dev - - - name: Run script - run: pnpm events-import - env: - GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} - - - name: Commit and push - run: | - git add -A - git commit -m "Update community events" - git push origin "automated-update-${{ env.TIMESTAMP }}" - - - name: Create PR body - run: | - echo "This PR was automatically created to update community events from external community spreadsheet." > pr_body.txt - echo "This workflows runs every Sunday at 00:00 (UTC)." >> pr_body.txt - echo "Source: https://docs.google.com/spreadsheets/d/1NEu_FCc1hnGAuRgPmbXXpf0h2lCrCOlMKbbFEqgkVDQ" >> pr_body.txt - - - name: Create Pull Request - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token - gh pr create --base dev --head "automated-update-${{ env.TIMESTAMP }}" --title "Update community events from external spreadsheet - ${{ env.READABLE_DATE }}" --body-file pr_body.txt diff --git a/.github/workflows/update-chains.yml b/.github/workflows/update-chains.yml index 402fb3e8e80..b3c4653e4cb 100644 --- a/.github/workflows/update-chains.yml +++ b/.github/workflows/update-chains.yml @@ -28,18 +28,37 @@ jobs: - name: Install ts-node run: pnpm add -g ts-node + - name: Set up git + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Action" + + - name: Generate timestamp and readable date + run: | + echo "TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV + echo "READABLE_DATE=$(date +'%B %-d')" >> $GITHUB_ENV + + - name: Fetch latest dev and create new branch + run: | + git fetch origin dev + git checkout -b "automated-update-${{ env.TIMESTAMP }}" origin/dev + - name: Update chains data run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/update-chains.ts + - name: Commit and push + run: | + git add -A + git commit -m "Update chains data" + git push origin "automated-update-${{ env.TIMESTAMP }}" + + - name: Create PR body + run: | + echo "Automated update of chains data from https://chainid.network/chains.json" > pr_body.txt + echo "" >> pr_body.txt + echo "This workflow runs every Friday at 16:20 (UTC)." >> pr_body.txt + - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: update-chains - branch-suffix: timestamp - commit-message: | - Update chains data - base: dev - title: Update chains data - body: Automated update of chains data from https://chainid.network/chains.json - labels: update 🔄 + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token + gh pr create --base dev --head "automated-update-${{ env.TIMESTAMP }}" --title "Update chains data - ${{ env.READABLE_DATE }}" --body-file pr_body.txt --label "update 🔄" diff --git a/CLAUDE.md b/CLAUDE.md index 95bd8a4be9d..e9ecc6edf0c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -172,6 +172,30 @@ pnpm events-import # Import community events 3. Translation strings in appropriate `src/intl/` JSON files 4. Data files in `src/data/` with TypeScript types +### Type-Safe Chain Names + +This project enforces type-safe chain names via TypeScript. When working with layer 2 networks or wallet data: + +**Critical Files:** + +- `src/data/chains.ts` - Canonical source of all chain names (auto-updated weekly) +- `src/lib/types.ts` - Defines `ChainName` type derived from chains.ts +- `src/data/networks/networks.ts` - Uses `chainName: ChainName` +- `src/data/wallets/wallet-data.ts` - Uses `supported_chains: ChainName[]` + +**Rules:** + +1. **Always look up exact names** - Before adding `chainName` or `supported_chains`, search `chains.ts` for the exact `name` value +2. **Names are case-sensitive and exact** - e.g., use `"Zircuit Mainnet"` not `"Zircuit"`, use `"OP Mainnet"` not `"Optimism"` +3. **Run type checking** - Use `npx tsc --noEmit` to verify chain names are valid before committing +4. **Non-EVM chains** - For Starknet and other non-EVM chains, use `NonEVMChainName` type + +**Common Mistakes:** + +- Using informal names: `"Optimism"` should be `"OP Mainnet"` +- Missing "Mainnet" suffix: `"Zircuit"` should be `"Zircuit Mainnet"` +- Wrong casing: `"zksync Mainnet"` should be `"zkSync Mainnet"` + ## Key Dependencies to Know ### UI & Styling diff --git a/LICENSE b/LICENSE index 42ae1e0431b..0199ecaceaa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2025 ethereum.org contributors +Copyright (c) 2019-2026 ethereum.org contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 724f0b0f1f5..d4dba9c4895 100644 --- a/README.md +++ b/README.md @@ -2161,6 +2161,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d shadow
shadow

🐛 Madison carter
Madison carter

🖋 + Koyah
Koyah

🖋 diff --git a/app/[locale]/[...slug]/page.tsx b/app/[locale]/[...slug]/page.tsx index b731f3f7c87..e959a3fb925 100644 --- a/app/[locale]/[...slug]/page.tsx +++ b/app/[locale]/[...slug]/page.tsx @@ -6,28 +6,26 @@ import { setRequestLocale, } from "next-intl/server" -import type { SlugPageParams } from "@/lib/types" +import type { GHIssue, SlugPageParams } from "@/lib/types" import I18nProvider from "@/components/I18nProvider" import mdComponents from "@/components/MdComponents" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { dateToString } from "@/lib/utils/date" import { getLayoutFromSlug } from "@/lib/utils/layout" import { checkPathValidity, getPostSlugs } from "@/lib/utils/md" import { getRequiredNamespacesForPage } from "@/lib/utils/translations" +import { getGFIs } from "@/data-layer" + import { LOCALES_CODES } from "@/lib/constants" import SlugJsonLD from "./page-jsonld" import { componentsMapping, layoutMapping } from "@/layouts" -import { fetchGFIs } from "@/lib/api/fetchGFIs" import { getPageData } from "@/lib/md/data" import { getMdMetadata } from "@/lib/md/metadata" -const loadData = dataLoader([["gfissues", fetchGFIs]]) - export default async function Page({ params }: { params: SlugPageParams }) { const { locale, slug: slugArray } = params @@ -40,7 +38,12 @@ export default async function Page({ params }: { params: SlugPageParams }) { // Enable static rendering setRequestLocale(locale) - const [gfissues] = await loadData() + let gfissues: GHIssue[] = [] + try { + gfissues = (await getGFIs()) ?? [] + } catch (error) { + console.warn("Failed to fetch GFIs for slug page:", error) + } const slug = slugArray.join("/") diff --git a/app/[locale]/apps/[application]/page.tsx b/app/[locale]/apps/[application]/page.tsx index b01482de762..a5b1c7835a2 100644 --- a/app/[locale]/apps/[application]/page.tsx +++ b/app/[locale]/apps/[application]/page.tsx @@ -32,7 +32,6 @@ import { Tag } from "@/components/ui/tag" import { APP_TAG_VARIANTS } from "@/lib/utils/apps" import { getAppPageContributorInfo } from "@/lib/utils/contributors" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { isValidDate } from "@/lib/utils/date" import { getMetadata } from "@/lib/utils/metadata" import { @@ -42,19 +41,12 @@ import { import { slugify } from "@/lib/utils/url" import { formatStringList } from "@/lib/utils/wallets" -import { BASE_TIME_UNIT } from "@/lib/constants" - import AppCard from "../_components/AppCard" import ScreenshotSwiper from "./_components/ScreenshotSwiper" import AppsAppJsonLD from "./page-jsonld" -import { fetchApps } from "@/lib/api/fetchApps" - -// 24 hours -const REVALIDATE_TIME = BASE_TIME_UNIT * 24 - -const loadData = dataLoader([["appsData", fetchApps]], REVALIDATE_TIME * 1000) +import { getAppsData } from "@/lib/data" const Page = async ({ params, @@ -72,8 +64,14 @@ const Page = async ({ const requiredNamespaces = getRequiredNamespacesForPage("/apps") const messages = pick(allMessages, requiredNamespaces) - // const [application] = application - const [appsData] = await loadData() + // Fetch apps data using the new data-layer function (already cached) + const appsData = await getAppsData() + + // Handle null case - throw error if required data is missing + if (!appsData) { + throw new Error("Failed to fetch apps data") + } + const app = Object.values(appsData) .flat() .find((app) => slugify(app.name) === application)! @@ -394,7 +392,13 @@ export async function generateMetadata({ }) { const { locale, application } = params - const [appsData] = await loadData() + // Fetch apps data using the new data-layer function (already cached) + const appsData = await getAppsData() + + // Handle null case - throw error if required data is missing + if (!appsData) { + throw new Error("Failed to fetch apps data") + } const app = Object.values(appsData) .flat() diff --git a/app/[locale]/apps/_components/AppCard.tsx b/app/[locale]/apps/_components/AppCard.tsx index 7e50e929218..f790fbf33b2 100644 --- a/app/[locale]/apps/_components/AppCard.tsx +++ b/app/[locale]/apps/_components/AppCard.tsx @@ -85,7 +85,6 @@ const AppCard = ({ {showDescription && ( {
VALID_CATEGORIES.includes(category as AppCategoryEnum) -// 24 hours -const REVALIDATE_TIME = BASE_TIME_UNIT * 24 - -const loadData = dataLoader([["appsData", fetchApps]], REVALIDATE_TIME * 1000) - const Page = async ({ params, }: { @@ -63,7 +55,13 @@ const Page = async ({ const { locale, catetgoryName } = params setRequestLocale(locale) - const [appsData] = await loadData() + // Fetch apps data using the new data-layer function (already cached) + const appsData = await getAppsData() + + // Handle null case - throw error if required data is missing + if (!appsData) { + throw new Error("Failed to fetch apps data") + } const t = await getTranslations({ locale, namespace: "page-apps" }) diff --git a/app/[locale]/apps/page.tsx b/app/[locale]/apps/page.tsx index 7a5e40c9596..c374f948c0c 100644 --- a/app/[locale]/apps/page.tsx +++ b/app/[locale]/apps/page.tsx @@ -15,14 +15,11 @@ import SubpageCard from "@/components/SubpageCard" import { getDiscoverApps, getHighlightedApps } from "@/lib/utils/apps" import { getAppPageContributorInfo } from "@/lib/utils/contributors" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { getMetadata } from "@/lib/utils/metadata" import { getRequiredNamespacesForPage } from "@/lib/utils/translations" import { appsCategories } from "@/data/apps/categories" -import { BASE_TIME_UNIT } from "@/lib/constants" - import AppCard from "./_components/AppCard" import AppsHighlight from "./_components/AppsHighlight" import CommunityPicks from "./_components/CommunityPicks" @@ -30,26 +27,26 @@ import SuggestAnApp from "./_components/SuggestAnApp" import TopApps from "./_components/TopApps" import AppsJsonLD from "./page-jsonld" -import { fetchApps } from "@/lib/api/fetchApps" -import { fetchCommunityPicks } from "@/lib/api/fetchCommunityPicks" - -// 24 hours -const REVALIDATE_TIME = BASE_TIME_UNIT * 24 - -const loadData = dataLoader( - [ - ["appsData", fetchApps], - ["communityPicks", fetchCommunityPicks], - ], - REVALIDATE_TIME * 1000 -) +import { getAppsData, getCommunityPicks } from "@/lib/data" const Page = async ({ params }: { params: PageParams }) => { const { locale } = params setRequestLocale(locale) - const [appsData, communityPicks] = await loadData() + // Fetch data using the new data-layer functions (already cached) + const [appsData, communityPicks] = await Promise.all([ + getAppsData(), + getCommunityPicks(), + ]) + + // Handle null cases - throw error if required data is missing + if (!appsData) { + throw new Error("Failed to fetch apps data") + } + if (!communityPicks) { + throw new Error("Failed to fetch community picks data") + } // Get 3 random highlighted apps const highlightedApps = getHighlightedApps(appsData, 3) diff --git a/app/[locale]/community/events/_components/ContinentTabs.tsx b/app/[locale]/community/events/_components/ContinentTabs.tsx new file mode 100644 index 00000000000..a63b383b471 --- /dev/null +++ b/app/[locale]/community/events/_components/ContinentTabs.tsx @@ -0,0 +1,214 @@ +"use client" + +import { useState } from "react" +import { ExternalLink, Globe } from "lucide-react" + +import type { + Continent, + EventItem, + MatomoEventOptions, + SectionNavDetails, +} from "@/lib/types" + +import Discord from "@/components/icons/discord.svg" +import Farcaster from "@/components/icons/farcaster.svg" +import Telegram from "@/components/icons/telegram.svg" +import Twitter from "@/components/icons/twitter.svg" +import { Image } from "@/components/Image" +import Link from "@/components/ui/Link" +import { LinkBox, LinkOverlay } from "@/components/ui/link-box" +import TabNav from "@/components/ui/TabNav" +import { Tag } from "@/components/ui/tag" + +import { cn } from "@/lib/utils/cn" +import { formatDateRange } from "@/lib/utils/date" + +import { TAG_STATUS_MAPPING } from "../utils" + +import Africa from "./svgs/africa.svg" +import Asia from "./svgs/asia.svg" +import Europe from "./svgs/europe.svg" +import MiddleEast from "./svgs/middle-east.svg" +import NorthAmerica from "./svgs/north-america.svg" +import Oceania from "./svgs/oceania.svg" +import SouthAmerica from "./svgs/south-america.svg" + +export const CONTINENT_VALUES: (Continent | "all")[] = [ + "all", + "europe", + "asia", + "north-america", + "south-america", + "africa", + "middle-east", + "oceania", +] + +// Continent icons/emojis as shown in design +const CONTINENT_ICONS: Record = { + all: , + europe: , + asia: , + "north-america": , + "south-america": , + africa: , + "middle-east": , + oceania: , +} + +interface ContinentTabsProps { + events: EventItem[] + labels: Record + locale: string + noEventsMessage: string + onlineLabel: string + maxEvents?: number + className?: string + matomoNavOptions: Pick + matomoLinkOptions: Pick & + Partial> +} + +export default function ContinentTabs({ + events, + labels, + locale, + noEventsMessage, + onlineLabel, + maxEvents = Infinity, + className, + matomoNavOptions, + matomoLinkOptions, +}: ContinentTabsProps) { + const [selectedContinent, setSelectedContinent] = useState( + "all" + ) + const [showAll, setShowAll] = useState(false) + + const handleSelect = (key: string) => { + setSelectedContinent(key as Continent | "all") + setShowAll(false) + } + + const filteredEvents = + selectedContinent === "all" + ? events + : events.filter((event) => event.continent === selectedContinent) + + const displayedEvents = showAll + ? filteredEvents + : filteredEvents.slice(0, maxEvents) + + // Count events per continent + const getContinentCount = (continent: Continent | "all"): number => { + if (continent === "all") return events.length + return events.filter((event) => event.continent === continent).length + } + + // Build sections for TabNav (no href = uses onSelect) + const sections: SectionNavDetails[] = CONTINENT_VALUES.map((value) => { + const count = getContinentCount(value) + return { + key: value, + label: `${labels[value]} (${count})`, + icon: CONTINENT_ICONS[value], + } + }) + + return ( +
+ + + {filteredEvents.length === 0 ? ( +

{noEventsMessage}

+ ) : ( +
+ {displayedEvents.map((event) => { + const socials = [ + { url: event.discord, Icon: Discord, label: "Discord" }, + { url: event.telegram, Icon: Telegram, label: "Telegram" }, + { url: event.farcaster, Icon: Farcaster, label: "Farcaster" }, + { url: event.twitter, Icon: Twitter, label: "Twitter" }, + ].filter((s) => s.url) + + return ( +
+ {/* Date */} +
+ {formatDateRange(event.startTime, event.endTime, locale)} +
+ + {/* Logo + Title + Location */} + + +
+ {event.title} +
+
+

+ {event.title} + +

+

{event.location}

+
+
+
+ + {/* Tags */} +
+ {event.isOnline && {onlineLabel}} + {(event.eventTypes ?? []).map((type, index) => ( + + {event.eventTypesLabels?.[index] || type} + + ))} +
+ + {/* Socials */} + {socials.length > 0 && ( +
+ {socials.map(({ url, Icon, label }) => ( + + + + ))} +
+ )} +
+ ) + })} +
+ )} +
+ ) +} diff --git a/app/[locale]/community/events/_components/EventCard.tsx b/app/[locale]/community/events/_components/EventCard.tsx new file mode 100644 index 00000000000..e0d945b0f7a --- /dev/null +++ b/app/[locale]/community/events/_components/EventCard.tsx @@ -0,0 +1,151 @@ +import { MapPin } from "lucide-react" + +import type { EventItem, MatomoEventOptions } from "@/lib/types" + +import { Image } from "@/components/Image" +import { LinkBox, LinkOverlay } from "@/components/ui/link-box" +import { Tag } from "@/components/ui/tag" + +import { cn } from "@/lib/utils/cn" +import { formatDate, formatDateRange } from "@/lib/utils/date" + +import { TAG_STATUS_MAPPING } from "../utils" + +interface EventCardProps { + event: EventItem + variant?: "grid" | "highlight" + className?: string + locale?: string + showTypeTag?: boolean + customEventOptions?: MatomoEventOptions +} + +function EventCardGrid({ + event, + showTypeTag, + locale, + customEventOptions, +}: EventCardProps) { + const primaryType = event.eventTypes?.[0] + + const hasDate = Boolean(event.startTime) + const formattedDate = hasDate + ? event.startTime === event.endTime + ? formatDate(event.startTime, locale, { year: undefined }) + : formatDateRange(event.startTime, event.endTime, locale) + : null + + return ( + + +
+
+ {event.logoImage ? ( + {event.title} + ) : ( + + )} +
+
+ {showTypeTag && primaryType && ( + + {event.eventTypesLabels?.[0] || primaryType} + + )} +

+ {event.title} +

+ {formattedDate &&

{formattedDate}

} +

{event.location}

+
+
+
+
+ ) +} + +function EventCardHighlight({ + event, + locale, + customEventOptions, +}: EventCardProps) { + return ( + + +
+ {`${event.title} +
+
+
+ {event.title} +
+
+

{event.title}

+

{event.location}

+

+ {formatDateRange(event.startTime, event.endTime, locale)} +

+
+
+
+
+ ) +} + +export default function EventCard({ + event, + variant, + className, + locale = "en", + showTypeTag, + customEventOptions, +}: EventCardProps) { + const Component = { + grid: EventCardGrid, + highlight: EventCardHighlight, + }[variant ?? "grid"] + + return ( +
+ +
+ ) +} diff --git a/app/[locale]/community/events/_components/FilterEvents.tsx b/app/[locale]/community/events/_components/FilterEvents.tsx new file mode 100644 index 00000000000..288d5b81bd1 --- /dev/null +++ b/app/[locale]/community/events/_components/FilterEvents.tsx @@ -0,0 +1,118 @@ +"use client" + +import { useState } from "react" +import { Info } from "lucide-react" +import { useLocale } from "next-intl" + +import type { EventItem } from "@/lib/types" + +import { Alert, AlertContent } from "@/components/ui/alert" +import { ButtonLink } from "@/components/ui/buttons/Button" +import Input from "@/components/ui/input" + +import EventCard from "../_components/EventCard" +import { sanitize } from "../utils" + +import useTranslation from "@/hooks/useTranslation" + +const MAX_RESULTS = 6 + +type FilterProps = { events: EventItem[] } + +export default function FilterEvents({ events }: FilterProps) { + const locale = useLocale() + const { t } = useTranslation("page-community-events") + const [filter, setFilter] = useState("") + + const filterEvents = (query: string): EventItem[] => { + if (!query) return events + + return events.filter((e) => { + const isOnline = e.isOnline ? t("page-events-tag-online") : "" + const searchable = [ + e.title, + e.location, + e.continent, + isOnline, + ...e.tags, + ].join(" ") + + return sanitize(searchable).includes(sanitize(query)) + }) + } + + const handleSearch = (event: React.ChangeEvent): void => { + setFilter(event.target.value) + // trackCustomEvent({ + // eventCategory: "events search", + // eventAction: "click", + // eventName: event.target.value, + // }) + } + + const filteredEvents = filterEvents(filter) + + const Results = () => { + if (!filter) return <> + + if (!filteredEvents.length) + return ( + + + + {t("page-events-search-no-results")} + + + ) + + return ( + <> +
+ {filteredEvents.slice(0, MAX_RESULTS).map((event) => ( + + ))} +
+ {filteredEvents.length > MAX_RESULTS && ( +
+ + {t("page-events-see-all")} ({filteredEvents.length}) + +
+ )} + + ) + } + + return ( + <> +
+ + {/* hidden for attachment to input only */} + + {t("page-events-search-sr-text")} + +
+ + + + ) +} diff --git a/app/[locale]/community/events/_components/OrganizerCTA.tsx b/app/[locale]/community/events/_components/OrganizerCTA.tsx new file mode 100644 index 00000000000..26004127f41 --- /dev/null +++ b/app/[locale]/community/events/_components/OrganizerCTA.tsx @@ -0,0 +1,43 @@ +import { getLocale, getTranslations } from "next-intl/server" + +import { ButtonLink } from "@/components/ui/buttons/Button" +import { Section } from "@/components/ui/section" + +import { cn } from "@/lib/utils/cn" + +interface OrganizerCTAProps { + className?: string +} + +export default async function OrganizerCTA({ className }: OrganizerCTAProps) { + const locale = await getLocale() + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + return ( +
+

{t("page-events-cta-title")}

+

{t("page-events-cta-body")}

+ + {t("page-events-cta-button")} + +
+ ) +} diff --git a/app/[locale]/community/events/_components/svgs/africa.svg b/app/[locale]/community/events/_components/svgs/africa.svg new file mode 100644 index 00000000000..7d1038400df --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/africa.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/[locale]/community/events/_components/svgs/asia.svg b/app/[locale]/community/events/_components/svgs/asia.svg new file mode 100644 index 00000000000..52604b78635 --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/asia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/[locale]/community/events/_components/svgs/europe.svg b/app/[locale]/community/events/_components/svgs/europe.svg new file mode 100644 index 00000000000..6b03c7eb8e1 --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/europe.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/[locale]/community/events/_components/svgs/middle-east.svg b/app/[locale]/community/events/_components/svgs/middle-east.svg new file mode 100644 index 00000000000..eca2b5801f8 --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/middle-east.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/[locale]/community/events/_components/svgs/north-america.svg b/app/[locale]/community/events/_components/svgs/north-america.svg new file mode 100644 index 00000000000..18875c5637e --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/north-america.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/[locale]/community/events/_components/svgs/oceania.svg b/app/[locale]/community/events/_components/svgs/oceania.svg new file mode 100644 index 00000000000..1492e736544 --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/oceania.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/[locale]/community/events/_components/svgs/south-america.svg b/app/[locale]/community/events/_components/svgs/south-america.svg new file mode 100644 index 00000000000..aae5e2fed30 --- /dev/null +++ b/app/[locale]/community/events/_components/svgs/south-america.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/[locale]/community/events/conferences/page.tsx b/app/[locale]/community/events/conferences/page.tsx new file mode 100644 index 00000000000..f56ac1cc7b5 --- /dev/null +++ b/app/[locale]/community/events/conferences/page.tsx @@ -0,0 +1,158 @@ +import { pick } from "lodash" +import { getMessages, getTranslations } from "next-intl/server" + +import type { PageParams } from "@/lib/types" + +import ContentHero from "@/components/Hero/ContentHero" +import I18nProvider from "@/components/I18nProvider" +import MainArticle from "@/components/MainArticle" +import { + EdgeScrollContainer, + EdgeScrollItem, +} from "@/components/ui/edge-scroll-container" +import { Section } from "@/components/ui/section" + +import { getLocaleYear } from "@/lib/utils/date" +import { getMetadata } from "@/lib/utils/metadata" +import { getRequiredNamespacesForPage } from "@/lib/utils/translations" + +import ContinentTabs from "../_components/ContinentTabs" +import EventCard from "../_components/EventCard" +import OrganizerCTA from "../_components/OrganizerCTA" +import { mapEventTranslations } from "../utils" + +import { getEventsData } from "@/lib/data" + +const Page = async ({ params }: { params: PageParams }) => { + const { locale } = params + + const _events = (await getEventsData()) ?? [] + + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + // Apply translations and compute eventTypes from tags if missing + const events = mapEventTranslations(_events, t) + + // Filter to conferences only (includes hackathons as they're often conference-adjacent) + const conferences = events.filter( + (e) => + e.eventTypes?.includes("conference") || + e.eventTypes?.includes("hackathon") + ) + + // Get highlighted conferences + const highlightedConferences = conferences + .filter((e) => e.highlight) + .slice(0, 3) + .concat(conferences.filter((e) => !e.highlight)) + .slice(0, 3) + + const allMessages = await getMessages({ locale }) + const requiredNamespaces = getRequiredNamespacesForPage("/community/events") + const messages = pick(allMessages, requiredNamespaces) + + // Continent labels for tabs + const continentLabels = { + all: t("page-events-filter-all"), + africa: t("page-events-continent-africa"), + asia: t("page-events-continent-asia"), + europe: t("page-events-continent-europe"), + "middle-east": t("page-events-continent-middle-east"), + "north-america": t("page-events-continent-north-america"), + oceania: t("page-events-continent-oceania"), + "south-america": t("page-events-continent-south-america"), + } + + return ( + + + + + {/* Major blockchain conferences */} +
+

{t("page-events-conferences-major-events")}

+ + {highlightedConferences.map((event) => ( + + + + ))} + +
+ + {/* All conferences - TABLE/ROW view */} +
+

+ {t("page-events-section-upcoming-conferences")} +

+ + +
+ + {/* Footer CTA */} + +
+
+ ) +} + +export async function generateMetadata({ + params, +}: { + params: { locale: string } +}) { + const { locale } = params + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + const year = getLocaleYear(locale) + + return await getMetadata({ + locale, + slug: ["community", "events", "conferences"], + title: t("page-events-conferences-hero-title", { year }), + description: t("page-events-meta-description", { year }), + }) +} + +export default Page diff --git a/app/[locale]/community/events/constants.ts b/app/[locale]/community/events/constants.ts new file mode 100644 index 00000000000..100df4c4203 --- /dev/null +++ b/app/[locale]/community/events/constants.ts @@ -0,0 +1,8 @@ +export const SECTION_IDS = { + hubs: "community-hubs", + meetups: "meetups", + conferences: "conferences", + organizers: "for-organizers", +} as const + +export const REVALIDATE_TIME = 60 * 60 * 24 // 24 hours in seconds diff --git a/app/[locale]/community/events/meetups/_components/FilterMeetups.tsx b/app/[locale]/community/events/meetups/_components/FilterMeetups.tsx new file mode 100644 index 00000000000..7aedaebde4a --- /dev/null +++ b/app/[locale]/community/events/meetups/_components/FilterMeetups.tsx @@ -0,0 +1,76 @@ +"use client" + +import { useState } from "react" +import { Info } from "lucide-react" +import { useLocale } from "next-intl" + +import type { EventItem } from "@/lib/types" + +import { Alert, AlertContent } from "@/components/ui/alert" +import Input from "@/components/ui/input" + +import EventCard from "../../_components/EventCard" +import { sanitize } from "../../utils" + +import useTranslation from "@/hooks/useTranslation" + +type FilterMeetupsProps = { events: EventItem[] } + +export default function FilterMeetups({ events }: FilterMeetupsProps) { + const locale = useLocale() + const { t } = useTranslation("page-community-events") + const [filter, setFilter] = useState("") + + const filterEvents = (query: string): EventItem[] => { + if (!query) return events + + return events.filter((e) => { + const searchable = [e.title, e.location, e.continent].join(" ") + return sanitize(searchable).includes(sanitize(query)) + }) + } + + const handleSearch = (event: React.ChangeEvent): void => { + setFilter(event.target.value) + } + + const filteredEvents = filterEvents(filter) + + return ( + <> + + {/* hidden for attachment to input only */} + + {t("page-events-search-sr-text")} + + {filteredEvents.length ? ( +
+ {filteredEvents.map((event) => ( + + ))} +
+ ) : ( + + + {t("page-events-search-no-results")} + + )} + + ) +} diff --git a/app/[locale]/community/events/meetups/page.tsx b/app/[locale]/community/events/meetups/page.tsx new file mode 100644 index 00000000000..46b563bab25 --- /dev/null +++ b/app/[locale]/community/events/meetups/page.tsx @@ -0,0 +1,100 @@ +import { pick } from "lodash" +import { getMessages, getTranslations } from "next-intl/server" + +import type { PageParams } from "@/lib/types" + +import ContentHero from "@/components/Hero/ContentHero" +import I18nProvider from "@/components/I18nProvider" +import MainArticle from "@/components/MainArticle" +import { Section } from "@/components/ui/section" + +import { getLocaleYear } from "@/lib/utils/date" +import { getMetadata } from "@/lib/utils/metadata" +import { getRequiredNamespacesForPage } from "@/lib/utils/translations" + +import OrganizerCTA from "../_components/OrganizerCTA" +import { getMeetupGroups, mapEventTranslations } from "../utils" + +import FilterMeetups from "./_components/FilterMeetups" + +import { getEventsData } from "@/lib/data" + +const Page = async ({ params }: { params: PageParams }) => { + const { locale } = params + + const _events = (await getEventsData()) ?? [] + + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + // Apply translations and compute eventTypes from tags if missing + const events = mapEventTranslations(_events, t) + + // Combine API meetup events with legacy meetup groups + // Exclude conferences and hackathons - they have their own section + const apiMeetups = events.filter( + (e) => + !e.eventTypes?.includes("conference") && + !e.eventTypes?.includes("hackathon") + ) + const meetupGroups = getMeetupGroups() + // Show API meetups first (sorted by date), then groups (sorted alphabetically) + const meetups = [...apiMeetups, ...meetupGroups] + + const allMessages = await getMessages({ locale }) + const requiredNamespaces = getRequiredNamespacesForPage("/community/events") + const messages = pick(allMessages, requiredNamespaces) + + return ( + <> + + + +
+
+

{t("page-events-section-find-events")}

+

{t("page-events-meetups-events-subtitle")}

+
+ {/* Client-side filter and list */} + + + +
+ + +
+ + ) +} + +export async function generateMetadata({ + params, +}: { + params: { locale: string } +}) { + const { locale } = params + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + const year = getLocaleYear(locale) + + return await getMetadata({ + locale, + slug: ["community", "events", "meetups"], + title: t("page-events-meetups-hero-title", { year }), + description: t("page-events-meta-description", { year }), + }) +} + +export default Page diff --git a/app/[locale]/community/events/page-jsonld.tsx b/app/[locale]/community/events/page-jsonld.tsx new file mode 100644 index 00000000000..128ecb97536 --- /dev/null +++ b/app/[locale]/community/events/page-jsonld.tsx @@ -0,0 +1,125 @@ +import { getTranslations } from "next-intl/server" + +import { FileContributor } from "@/lib/types" + +import PageJsonLD from "@/components/PageJsonLD" + +import { getLocaleYear } from "@/lib/utils/date" +import { + ethereumCommunityOrganization, + ethereumFoundationOrganization, +} from "@/lib/utils/jsonld" +import { normalizeUrlForJsonLd } from "@/lib/utils/url" + +export default async function EventsJsonLD({ + locale, + contributors, +}: { + locale: string + contributors: FileContributor[] +}) { + const t = await getTranslations({ namespace: "page-community-events" }) + const common = await getTranslations({ namespace: "common" }) + + const year = getLocaleYear(locale) + const url = normalizeUrlForJsonLd(locale, `/community/events/`) + + const contributorList = contributors.map((contributor) => ({ + "@type": "Person", + name: contributor.login, + url: contributor.html_url, + })) + + const jsonLd = { + "@context": "https://schema.org", + "@graph": [ + { + "@type": "WebPage", + "@id": url, + name: t("page-events-meta-title", { year }), + description: t("page-events-meta-description", { year }), + url: url, + inLanguage: locale, + contributor: contributorList, + author: [ethereumCommunityOrganization], + isPartOf: { + "@type": "WebSite", + "@id": "https://ethereum.org/#website", + name: "ethereum.org", + url: "https://ethereum.org", + }, + breadcrumb: { + "@type": "BreadcrumbList", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Home", + item: normalizeUrlForJsonLd(locale, "/"), + }, + { + "@type": "ListItem", + position: 2, + name: "Community", + item: normalizeUrlForJsonLd(locale, "/community/"), + }, + { + "@type": "ListItem", + position: 3, + name: common("events"), + item: url, + }, + ], + }, + publisher: ethereumFoundationOrganization, + reviewedBy: ethereumFoundationOrganization, + mainEntity: { "@id": `${url}#sections` }, + }, + { + "@type": "ItemList", + "@id": `${url}#sections`, + name: t("page-events-meta-title", { year }), + description: t("page-events-meta-description", { year }), + url: url, + numberOfItems: 4, + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: t("page-events-section-hubs"), + description: t("page-events-section-hubs-subtitle"), + url: `${url}#community-hubs`, + }, + { + "@type": "ListItem", + position: 2, + name: t("page-events-section-local-meetups"), + description: t("page-events-section-local-meetups-subtitle"), + url: normalizeUrlForJsonLd(locale, "/community/events/meetups/"), + }, + { + "@type": "ListItem", + position: 3, + name: t("page-events-section-upcoming-conferences"), + description: t("page-events-section-upcoming-conferences-subtitle"), + url: normalizeUrlForJsonLd( + locale, + "/community/events/conferences/" + ), + }, + { + "@type": "ListItem", + position: 4, + name: t("page-events-section-organizers"), + description: t("page-events-section-organizers-subtitle"), + url: `${url}#for-organizers`, + }, + ], + publisher: ethereumFoundationOrganization, + reviewedBy: ethereumFoundationOrganization, + }, + ], + } + + return +} diff --git a/app/[locale]/community/events/page.tsx b/app/[locale]/community/events/page.tsx new file mode 100644 index 00000000000..b7853cdae2b --- /dev/null +++ b/app/[locale]/community/events/page.tsx @@ -0,0 +1,612 @@ +import { pick } from "lodash" +import { + Banknote, + ChartNoAxesCombined, + Handshake, + Plus, + Presentation, +} from "lucide-react" +import { getMessages, getTranslations } from "next-intl/server" + +import type { + CommitHistory, + Lang, + PageParams, + SectionNavDetails, +} from "@/lib/types" + +import ContentHero from "@/components/Hero/ContentHero" +import I18nProvider from "@/components/I18nProvider" +import { Image } from "@/components/Image" +import MainArticle from "@/components/MainArticle" +import { ButtonLink } from "@/components/ui/buttons/Button" +import { + EdgeScrollContainer, + EdgeScrollItem, +} from "@/components/ui/edge-scroll-container" +import Link from "@/components/ui/Link" +import { Section } from "@/components/ui/section" +import TabNav, { StickyContainer } from "@/components/ui/TabNav" + +import { cn } from "@/lib/utils/cn" +import { getAppPageContributorInfo } from "@/lib/utils/contributors" +import { getLocaleYear } from "@/lib/utils/date" +import { getMetadata } from "@/lib/utils/metadata" +import { getRequiredNamespacesForPage } from "@/lib/utils/translations" + +import communityHubs from "@/data/community-hubs" + +import ContinentTabs from "./_components/ContinentTabs" +import EventCard from "./_components/EventCard" +import FilterEvents from "./_components/FilterEvents" +import { SECTION_IDS } from "./constants" +import EventsJsonLD from "./page-jsonld" +import { getMeetupGroups, mapEventTranslations } from "./utils" + +import { getEventsData } from "@/lib/data" +import ethereumEverywhereLogo from "@/public/images/community/ethereum-everywhere-logo.png" +import geodeLabsLogo from "@/public/images/community/geode-labs-logo.png" +import heroImage from "@/public/images/enterprise-eth.png" +import organizerImage from "@/public/images/people-learning.png" + +const Page = async ({ params }: { params: PageParams }) => { + const { locale } = params + + const _events = (await getEventsData()) ?? [] + + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + const allMessages = await getMessages({ locale }) + const requiredNamespaces = getRequiredNamespacesForPage("/community/events") + const messages = pick(allMessages, requiredNamespaces) + + const commitHistoryCache: CommitHistory = {} + const { contributors } = await getAppPageContributorInfo( + "community/events", + locale as Lang, + commitHistoryCache + ) + + const events = mapEventTranslations(_events, t) + + // Get highlighted conferences (with highlight flag or first 3) + const conferences = events.filter( + (e) => + e.eventTypes?.includes("conference") || + e.eventTypes?.includes("hackathon") + ) + const highlightedConferences = conferences + .filter((e) => e.highlight) + .slice(0, 3) + .concat(conferences.filter((e) => !e.highlight)) + .slice(0, 3) + + // Get meetups (API events first, then groups) + // Exclude conferences and hackathons - they have their own section + const apiMeetups = events.filter( + (e) => + !e.eventTypes?.includes("conference") && + !e.eventTypes?.includes("hackathon") + ) + const meetupGroups = getMeetupGroups() + const meetups = [...apiMeetups, ...meetupGroups] + + // Continent labels for tabs + const continentLabels = { + all: t("page-events-filter-all"), + africa: t("page-events-continent-africa"), + asia: t("page-events-continent-asia"), + europe: t("page-events-continent-europe"), + "middle-east": t("page-events-continent-middle-east"), + "north-america": t("page-events-continent-north-america"), + oceania: t("page-events-continent-oceania"), + "south-america": t("page-events-continent-south-america"), + } + + // TabNav sections with translated labels + const sections: SectionNavDetails[] = [ + { + key: SECTION_IDS.hubs, + label: `${t(`page-events-nav-${SECTION_IDS.hubs}`)} (${communityHubs.length})`, + icon: , + }, + { + key: SECTION_IDS.meetups, + label: `${t(`page-events-nav-${SECTION_IDS.meetups}`)} (${meetups.length})`, + icon: , + }, + { + key: SECTION_IDS.conferences, + label: `${t(`page-events-nav-${SECTION_IDS.conferences}`)} (${conferences.length})`, + icon: , + }, + { + key: SECTION_IDS.organizers, + label: t(`page-events-nav-${SECTION_IDS.organizers}`), + icon: , + }, + ] + + return ( + <> + + + + + {/* What's on this page? + TabNav */} + +

{t("page-events-whats-on-page")}

+ +
+ + + {/* Major blockchain conferences */} +
+

+ {t("page-events-section-major-conferences")} +

+ + {highlightedConferences.map((event) => ( + + + + ))} + +
+ + {/* Ethereum community hubs */} +
+
+

{t("page-events-section-hubs")}

+

+ {t("page-events-section-hubs-subtitle")} +

+
+ + {communityHubs.map( + ({ + id, + location, + descriptionKey, + ctaKey, + coworkingSignupUrl, + meetupUrl, + banner, + logoBgColor, + }) => ( + +
+
+ +
+

{location}

+
+

{t(descriptionKey)}

+

{t(ctaKey)}

+
+
+
+ + {t("page-events-hub-cowork-signup")} + + + {t("page-events-hub-meetups")} + +
+
+ ) + )} +
+
+ +
+ +
+ {t("page-events-hub-apply-cta")} +
+
+
+ + {/* Find events near you */} + + + {/* Local Ethereum community meetups */} +
+
+

{t("page-events-section-local-meetups")}

+

+ {t("page-events-section-local-meetups-subtitle")} +

+
+
+ {meetups.slice(0, 6).map((event) => ( + + ))} +
+
+ + {t("page-events-see-all")} ({meetups.length}) + +
+
+ + {/* Upcoming Ethereum conferences - TABLE/ROW view */} +
+
+

+ {t("page-events-section-upcoming-conferences")} +

+

+ {t("page-events-section-upcoming-conferences-subtitle")} +

+
+ +
+ + {t("page-events-see-all")} ({conferences.length}) + +
+
+ + {/* For event organizers */} +
+

{t("page-events-section-organizers")}

+

{t("page-events-section-organizers-subtitle")}

+
+
+ Planning an event +
+
+

+ {t("page-events-section-organizers-planning")} +

+

+ {t("page-events-section-organizers-planning-description")} +

+ + {t("page-events-section-organizers-planning-cta")} + +
+
+
+ + {/* Looking for support? */} +
+
+

{t("page-events-section-support")}

+

+ {t("page-events-section-support-subtitle")} +

+
+
+ {/* Ethereum Everywhere Card */} +
+
+
+ {t("item-logo", +
+

+ {t("page-events-support-ethereum-everywhere")} +

+
+ +
+

+ {t("page-events-support-ethereum-everywhere-description")} +

+ +
+

+ {t("page-events-support-ethereum-everywhere-guidance")} +

+

+ {t( + "page-events-support-ethereum-everywhere-guidance-description" + )} +

+
+ +
+

+ {t("page-events-support-ethereum-everywhere-resources")} +

+

+ {t( + "page-events-support-ethereum-everywhere-resources-description" + )} +

+
+ +
+

+ {t("page-events-support-ethereum-everywhere-connections")} +

+

+ {t( + "page-events-support-ethereum-everywhere-connections-description" + )} +

+
+
+ + + {t("page-events-get-in-touch")} + +
+ + {/* Geode Labs Card */} +
+
+
+ {t("item-logo", +
+

+ {t("page-events-support-geode-labs")} +

+
+
+

{t("page-events-support-geode-labs-description")}

+ +
+ + {t("page-events-support-geode-labs-grants")} + +

+ {t("page-events-support-geode-labs-grants-description")} +

+
+ +
+ + {t("page-events-support-geode-labs-local")} + +

+ {t("page-events-support-geode-labs-local-description")} +

+
+ +
+ + {t("page-events-support-geode-labs-ethstars")} + +

+ {t("page-events-support-geode-labs-ethstars-description")} +

+
+
+ + + {t("page-events-get-in-touch")} + +
+
+
+
+
+ + ) +} + +export async function generateMetadata({ + params, +}: { + params: { locale: string } +}) { + const { locale } = params + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + const year = getLocaleYear(locale) + + return await getMetadata({ + locale, + slug: ["community", "events"], + title: t("page-events-meta-title", { year }), + description: t("page-events-meta-description", { year }), + }) +} + +export default Page diff --git a/app/[locale]/community/events/search/page.tsx b/app/[locale]/community/events/search/page.tsx new file mode 100644 index 00000000000..85a28e3310f --- /dev/null +++ b/app/[locale]/community/events/search/page.tsx @@ -0,0 +1,160 @@ +import { Info } from "lucide-react" +import { getTranslations } from "next-intl/server" + +import type { EventItem, PageParams } from "@/lib/types" + +import ContentHero from "@/components/Hero/ContentHero" +import MainArticle from "@/components/MainArticle" +import { Alert, AlertContent } from "@/components/ui/alert" +import { Button } from "@/components/ui/buttons/Button" +import Input from "@/components/ui/input" +import { Section } from "@/components/ui/section" + +import { getMetadata } from "@/lib/utils/metadata" + +import EventCard from "../_components/EventCard" +import OrganizerCTA from "../_components/OrganizerCTA" +import { mapEventTranslations, sanitize } from "../utils" + +import { getEventsData } from "@/lib/data" + +const Page = async ({ + params, + searchParams, +}: { + params: PageParams + searchParams: { q?: string } +}) => { + const { locale } = params + const { q } = searchParams + + const _events = (await getEventsData()) ?? [] + + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + const tCommon = await getTranslations({ locale, namespace: "common" }) + + const events = mapEventTranslations(_events, t) + + const filteredEvents = ((): EventItem[] => { + if (!q) return [] + + return events.filter((e) => { + const isOnline = e.isOnline ? t("page-events-tag-online") : "" + const searchable = [ + e.title, + e.location, + e.continent, + isOnline, + ...e.tags, + ].join(" ") + + return sanitize(searchable).includes(sanitize(q)) + }) + })() + + const title = q + ? t("page-events-search-hero-title-q", { q: decodeURIComponent(q) }) + : t("page-events-search-hero-title") + + const Results = () => { + if (!q) return <> + + if (!filteredEvents.length) + return ( + + + + {t("page-events-search-no-results")} + + + ) + + return ( + <> +
+ {filteredEvents.map((event) => ( + + ))} +
+ + ) + } + + return ( + <> + + + +
+
+

{t("page-events-section-find-events")}

+

{t("page-events-meetups-events-subtitle")}

+
+
+ + + {t("page-events-search-submit-sr-text")} + + +
+ + +
+ + +
+ + ) +} + +export async function generateMetadata({ + params, +}: { + params: { locale: string } +}) { + const { locale } = params + const t = await getTranslations({ + locale, + namespace: "page-community-events", + }) + + return await getMetadata({ + locale, + slug: ["community", "events", "meetups"], + title: t("page-events-search-hero-title"), + description: t("page-events-search-metadata-description"), + }) +} + +export default Page diff --git a/app/[locale]/community/events/utils.ts b/app/[locale]/community/events/utils.ts new file mode 100644 index 00000000000..9faa472e46e --- /dev/null +++ b/app/[locale]/community/events/utils.ts @@ -0,0 +1,78 @@ +import type { useTranslations } from "next-intl" + +import type { EventItem, EventType } from "@/lib/types" + +import { TagProps } from "@/components/ui/tag" + +import { parseLocationToContinent } from "@/lib/utils/geography" +import { slugify } from "@/lib/utils/url" + +import communityMeetups from "@/data/community-meetups.json" +import { getEventTypes } from "@/data-layer/api/fetchEvents" + +// Map EventType to Tag component status colors +export const TAG_STATUS_MAPPING: Record = { + conference: "accent-a", + hackathon: "accent-b", + meetup: "accent-c", + popup: "tag-yellow", + group: "primary", + other: "normal", +} + +export const sanitize = (s: string) => + s.toLowerCase().replace(/\W+/g, " ").replace(/\s+/g, " ") + +export const mapEventTranslations = ( + events: EventItem[], + t: ReturnType +): EventItem[] => + events.map((event) => { + // Use existing eventTypes if they have values, otherwise compute from tags + const eventTypes: EventType[] = event.eventTypes?.length + ? event.eventTypes + : event.tags?.length + ? getEventTypes(event.tags) + : ["other"] + return { + ...event, + eventTypes, + eventTypesLabels: eventTypes.map((type) => t(`page-events-tag-${type}`)), + } + }) + +// Meetup group type from community-meetups.json +interface MeetupGroup { + title: string + location: string + link: string + logoImage?: string + bannerImage?: string +} + +function transformMeetupGroup(group: MeetupGroup): EventItem { + return { + title: group.title, + logoImage: group.logoImage || "", + bannerImage: group.bannerImage || "", + startTime: "", + endTime: null, + location: group.location, + link: group.link, + tags: ["meetup"], + id: slugify(`${group.title}-${group.location}`), + eventTypes: ["group"], + isOnline: false, + continent: parseLocationToContinent(group.location), + } +} + +/** + * Get meetup groups from community-meetups.json + * These are ongoing community groups (not individual events with dates) + */ +export function getMeetupGroups(): EventItem[] { + return (communityMeetups as MeetupGroup[]) + .map(transformMeetupGroup) + .sort((a, b) => a.title.localeCompare(b.title)) +} diff --git a/app/[locale]/developers/_components/HackathonCard.tsx b/app/[locale]/developers/_components/HackathonCard.tsx index 939c2a7fba3..46e60264c54 100644 --- a/app/[locale]/developers/_components/HackathonCard.tsx +++ b/app/[locale]/developers/_components/HackathonCard.tsx @@ -1,4 +1,6 @@ -import { CommunityConference } from "@/lib/types" +import { getLocale } from "next-intl/server" + +import type { EventItem } from "@/lib/types" import { Image } from "@/components/Image" import CardImage from "@/components/Image/CardImage" @@ -11,19 +13,25 @@ import { CardTitle, } from "@/components/ui/card" +import { formatDateRange } from "@/lib/utils/date" + import EventFallback from "@/public/images/events/event-placeholder.png" type HackathonCardProps = { - event: CommunityConference + event: EventItem className?: string } -const HackathonCard = ({ event, className }: HackathonCardProps) => { - const { title, href, description, imageUrl, formattedDate, location } = event +const HackathonCard = async ({ event, className }: HackathonCardProps) => { + const locale = await getLocale() + const { title, link, bannerImage, location, startTime, endTime } = event + const formattedDate = formatDateRange(startTime, endTime, locale, { + year: "numeric", + }) + return ( { className={className} > - {imageUrl ? ( - + {bannerImage ? ( + ) : ( )} {title} - {formattedDate} + {formattedDate} {location} diff --git a/app/[locale]/developers/_components/HackathonSwiper/index.tsx b/app/[locale]/developers/_components/HackathonSwiper/index.tsx deleted file mode 100644 index d239e06ea9f..00000000000 --- a/app/[locale]/developers/_components/HackathonSwiper/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client" - -import type { CommunityConference } from "@/lib/types" - -import { Swiper, SwiperSlide } from "@/components/ui/swiper" - -import HackathonCard from "../HackathonCard" - -type HackathonSwiperProps = { - events: CommunityConference[] -} - -const HackathonSwiper = ({ events }: HackathonSwiperProps) => ( - - {events.map((event, idx) => ( - - - - ))} - -) - -export default HackathonSwiper diff --git a/app/[locale]/developers/_components/HackathonSwiper/lazy.tsx b/app/[locale]/developers/_components/HackathonSwiper/lazy.tsx deleted file mode 100644 index c960f08e647..00000000000 --- a/app/[locale]/developers/_components/HackathonSwiper/lazy.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import dynamic from "next/dynamic" - -import Loading from "./loading" - -export default dynamic(() => import("."), { ssr: false, loading: Loading }) diff --git a/app/[locale]/developers/_components/HackathonSwiper/loading.tsx b/app/[locale]/developers/_components/HackathonSwiper/loading.tsx deleted file mode 100644 index 5256c8f6218..00000000000 --- a/app/[locale]/developers/_components/HackathonSwiper/loading.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { SkeletonCardGrid } from "@/components/ui/skeleton" - -const Loading = () => - -export default Loading diff --git a/app/[locale]/developers/local-environment/page.tsx b/app/[locale]/developers/local-environment/page.tsx index c8cf206e3e0..5633ffed495 100644 --- a/app/[locale]/developers/local-environment/page.tsx +++ b/app/[locale]/developers/local-environment/page.tsx @@ -6,29 +6,43 @@ import { } from "next-intl/server" import type { CommitHistory, Lang, PageParams } from "@/lib/types" +import type { Framework } from "@/lib/interfaces" import I18nProvider from "@/components/I18nProvider" import { getAppPageContributorInfo } from "@/lib/utils/contributors" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { getMetadata } from "@/lib/utils/metadata" import { getRequiredNamespacesForPage } from "@/lib/utils/translations" +import { frameworksList } from "@/data/frameworks" + import LocalEnvironmentPage from "./_components/local-environment" import LocalEnvironmentJsonLD from "./page-jsonld" -import { getLocalEnvironmentFrameworkData } from "@/lib/api/ghRepoData" - -const loadData = dataLoader([ - ["frameworksListData", getLocalEnvironmentFrameworkData], -]) +import { getGithubRepoData } from "@/lib/data" const Page = async ({ params }: { params: PageParams }) => { const { locale } = params setRequestLocale(locale) - const [frameworksListData] = await loadData() + // Fetch GitHub repo data using the new data-layer function (already cached) + const githubRepoData = await getGithubRepoData() + + // Handle null case - throw error if required data is missing + if (!githubRepoData) { + throw new Error("Failed to fetch GitHub repo data") + } + + // Merge static framework data with GitHub repo data + const frameworksListData: Framework[] = frameworksList.map((framework) => { + const repoData = githubRepoData[framework.githubUrl] + return { + ...framework, + starCount: repoData?.starCount, + languages: repoData?.languages?.slice(0, 2), // Limit to first 2 languages + } + }) // Get i18n messages const allMessages = await getMessages({ locale }) diff --git a/app/[locale]/developers/page-jsonld.tsx b/app/[locale]/developers/page-jsonld.tsx index 5f4cb2b3928..de7474d820c 100644 --- a/app/[locale]/developers/page-jsonld.tsx +++ b/app/[locale]/developers/page-jsonld.tsx @@ -1,6 +1,6 @@ import { getTranslations } from "next-intl/server" -import { CommunityConference, FileContributor } from "@/lib/types" +import { EventItem, FileContributor } from "@/lib/types" import PageJsonLD from "@/components/PageJsonLD" @@ -22,7 +22,7 @@ export default async function DevelopersPageJsonLD({ locale: string paths: DevelopersPath[] courses: VideoCourse[] - hackathons: CommunityConference[] + hackathons: EventItem[] contributors: FileContributor[] }) { const t = await getTranslations({ namespace: "page-developers-index" }) @@ -101,8 +101,7 @@ export default async function DevelopersPageJsonLD({ "@type": "ListItem", position: paths.length + courses.length + index + 1, name: hackathon.title, - description: hackathon.description, - url: hackathon.href, + url: hackathon.link, })), ], publisher: ethereumFoundationOrganization, diff --git a/app/[locale]/developers/page.tsx b/app/[locale]/developers/page.tsx index adfe4245bd7..f2b99203b1e 100644 --- a/app/[locale]/developers/page.tsx +++ b/app/[locale]/developers/page.tsx @@ -12,6 +12,10 @@ import { Image } from "@/components/Image" import MainArticle from "@/components/MainArticle" import { ButtonLink } from "@/components/ui/buttons/Button" import { Card } from "@/components/ui/card" +import { + EdgeScrollContainer, + EdgeScrollItem, +} from "@/components/ui/edge-scroll-container" import { VStack } from "@/components/ui/flex" import Link from "@/components/ui/Link" import InlineLink from "@/components/ui/Link" @@ -25,7 +29,6 @@ import { screens } from "@/lib/utils/screen" import BuilderCard from "./_components/BuilderCard" import BuilderSwiper from "./_components/BuilderSwiper/lazy" import HackathonCard from "./_components/HackathonCard" -import HackathonSwiper from "./_components/HackathonSwiper/lazy" import SpeedRunCard from "./_components/SpeedRunCard" import VideoCourseCard from "./_components/VideoCourseCard" import VideoCourseSwiper from "./_components/VideoCourseSwiper/lazy" @@ -527,20 +530,21 @@ const DevelopersPage = async ({ params }: { params: PageParams }) => {

{t("page-developers-hackathons-title")}

{t("page-developers-hackathons-desc")}

- {/* DESKTOP */} - - {hackathons.map((event, idx) => ( - + + {hackathons.map((event) => ( + + + ))} - - {/* MOBILE */} -
- -
+
=> { ] } -export const getHackathons = async (): Promise => { - const locale = await getLocale() - const allUpcomingEvents = getUpcomingEvents(events, locale) - return allUpcomingEvents.filter((e) => e.hackathon) as CommunityConference[] +export const getHackathons = async (): Promise => { + const events = await getEventsData() + if (!events) return [] + return events.filter( + (e) => + e.eventTypes?.includes("hackathon") || + e.tags?.some((tag) => tag.toLowerCase() === "hackathon") + ) } diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 2c919153cca..6f09b998d8e 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -61,19 +61,13 @@ import { Skeleton, SkeletonCardGrid } from "@/components/ui/skeleton" import { parseAppsOfTheWeek } from "@/lib/utils/apps" import { cn } from "@/lib/utils/cn" -import { dataLoader } from "@/lib/utils/data/dataLoader" -import { isValidDate } from "@/lib/utils/date" +import { formatDateRange } from "@/lib/utils/date" import { getDirection } from "@/lib/utils/direction" import { getMetadata } from "@/lib/utils/metadata" import { formatPriceUSD } from "@/lib/utils/numbers" import { polishRSSList } from "@/lib/utils/rss" -import events from "@/data/community-events.json" - import { - ATTESTANT_BLOG, - BASE_TIME_UNIT, - BLOG_FEEDS, BLOGS_WITHOUT_FEED, DEFAULT_LOCALE, GITHUB_REPO_URL, @@ -83,16 +77,19 @@ import { import AppsHighlight from "./apps/_components/AppsHighlight" import IndexPageJsonLD from "./page-jsonld" -import { getActivity, getUpcomingEvents } from "./utils" +import { getActivity } from "./utils" import { routing } from "@/i18n/routing" -import { fetchApps } from "@/lib/api/fetchApps" -import { fetchBeaconchainEpoch } from "@/lib/api/fetchBeaconchainEpoch" -import { fetchEthPrice } from "@/lib/api/fetchEthPrice" -import { fetchGrowThePie } from "@/lib/api/fetchGrowThePie" -import { fetchAttestantPosts } from "@/lib/api/fetchPosts" -import { fetchRSS } from "@/lib/api/fetchRSS" -import { fetchTotalValueLocked } from "@/lib/api/fetchTotalValueLocked" +import { + getAppsData, + getAttestantPosts, + getBeaconchainEpochData, + getEthPrice, + getEventsData, + getGrowThePieData, + getRSSData, + getTotalValueLockedData, +} from "@/lib/data" import EventFallback from "@/public/images/events/event-placeholder.png" const BentoCardSwiper = dynamic( @@ -129,27 +126,6 @@ const ValuesMarquee = dynamic( } ) -const fetchXmlBlogFeeds = async () => { - const xmlUrls = BLOG_FEEDS.filter((feed) => ![ATTESTANT_BLOG].includes(feed)) - return await fetchRSS(xmlUrls) -} - -// In seconds -const REVALIDATE_TIME = BASE_TIME_UNIT * 1 - -const loadData = dataLoader( - [ - ["ethPrice", fetchEthPrice], - ["beaconchainEpoch", fetchBeaconchainEpoch], - ["totalValueLocked", fetchTotalValueLocked], - ["growThePieData", fetchGrowThePie], - ["attestantPosts", fetchAttestantPosts], - ["rssData", fetchXmlBlogFeeds], - ["appsData", fetchApps], - ], - REVALIDATE_TIME * 1000 -) - const Page = async ({ params }: { params: PageParams }) => { const { locale } = params @@ -161,15 +137,61 @@ const Page = async ({ params }: { params: PageParams }) => { const tCommon = await getTranslations({ locale, namespace: "common" }) const { direction: dir, isRtl } = getDirection(locale) + // Fetch data using the new data-layer functions (already cached) const [ ethPrice, - { totalEthStaked }, + beaconchainEpochData, totalValueLocked, growThePieData, attestantPosts, - xmlBlogs, + rssData, appsData, - ] = await loadData() + eventsData, + ] = await Promise.all([ + getEthPrice(), + getBeaconchainEpochData(), + getTotalValueLockedData(), + getGrowThePieData(), + getAttestantPosts(), + getRSSData(), + getAppsData(), + getEventsData(), + ]) + + // Handle null cases - throw error if required data is missing + if (!ethPrice) { + throw new Error("Failed to fetch ETH price data") + } + if (!beaconchainEpochData) { + throw new Error("Failed to fetch Beaconchain epoch data") + } + if (!totalValueLocked) { + throw new Error("Failed to fetch total value locked data") + } + if (!growThePieData) { + throw new Error("Failed to fetch GrowThePie data") + } + if (!appsData) { + throw new Error("Failed to fetch apps data") + } + + // RSS feeds - graceful degradation: use what's available if we have enough items + const rssFeeds = rssData ?? [] + const attestantFeed = attestantPosts ?? [] + const totalRssItems = + rssFeeds.reduce((sum, feed) => sum + feed.length, 0) + attestantFeed.length + + if (totalRssItems < RSS_DISPLAY_COUNT) { + throw new Error( + `Insufficient RSS data: need at least ${RSS_DISPLAY_COUNT} items` + ) + } + + // Extract totalEthStaked from beaconchainEpochData + const { totalEthStaked } = beaconchainEpochData + + // Events - use empty array as fallback + const upcomingEvents = (eventsData ?? []).slice(0, 3) const appsOfTheWeek = parseAppsOfTheWeek(appsData) @@ -404,9 +426,6 @@ const Page = async ({ params }: { params: PageParams }) => { }, ] - const allUpcomingEvents = getUpcomingEvents(events, locale) - const upcomingEvents = allUpcomingEvents.slice(0, 3) - const metricResults: AllHomepageActivityData = { ethPrice, totalEthStaked, @@ -417,7 +436,8 @@ const Page = async ({ params }: { params: PageParams }) => { const metrics = await getActivity(metricResults, locale) // RSS feed items - const polishedRssItems = polishRSSList([attestantPosts, ...xmlBlogs], locale) + // polishRSSList expects RSSItem[][], so wrap attestantFeed in an array + const polishedRssItems = polishRSSList([attestantFeed, ...rssFeeds], locale) const rssItems = polishedRssItems.slice(0, RSS_DISPLAY_COUNT) const blogLinks = polishedRssItems.map(({ source, sourceUrl }) => ({ @@ -821,55 +841,45 @@ const Page = async ({ params }: { params: PageParams }) => { {upcomingEvents.map( ( { + id, title, - href, + link, location, - description, - startDate, - endDate, - imageUrl, + startTime, + endTime, + bannerImage, }, idx ) => ( - {imageUrl ? ( + {bannerImage ? ( ) : ( )} - {title} - {(isValidDate(startDate) || isValidDate(endDate)) && - new Intl.DateTimeFormat(locale, { - month: "long", - day: "numeric", - year: "numeric", - }).formatRange( - new Date( - isValidDate(startDate) ? startDate : endDate - ), - new Date( - isValidDate(endDate) ? endDate : startDate - ) - )} + {formatDateRange(startTime, endTime, locale, { + month: "long", + year: "numeric", + })} {location} diff --git a/app/[locale]/resources/page.tsx b/app/[locale]/resources/page.tsx index 03d0ab77cda..67d3feacaa3 100644 --- a/app/[locale]/resources/page.tsx +++ b/app/[locale]/resources/page.tsx @@ -16,59 +16,50 @@ import TabNav, { StickyContainer } from "@/components/ui/TabNav" import { cn } from "@/lib/utils/cn" import { getAppPageContributorInfo } from "@/lib/utils/contributors" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { getMetadata } from "@/lib/utils/metadata" import { GITHUB_REPO_URL } from "@/lib/constants" -import { BASE_TIME_UNIT } from "@/lib/constants" import { ResourceItem, ResourcesContainer } from "./_components/ResourcesUI" import ResourcesPageJsonLD from "./page-jsonld" import { getResources } from "./utils" -import { fetchBlobscanStats } from "@/lib/api/fetchBlobscanStats" -import { fetchGrowThePie } from "@/lib/api/fetchGrowThePie" +import { getBlobscanStats, getGrowThePieData } from "@/lib/data" import heroImg from "@/public/images/heroes/guides-hub-hero.jpg" -// In seconds -const REVALIDATE_TIME = BASE_TIME_UNIT * 1 const EVENT_CATEGORY = "dashboard" -const loadData = dataLoader( - [ - ["growThePieData", fetchGrowThePie], - ["blobscanOverallStats", fetchBlobscanStats], - ], - REVALIDATE_TIME * 1000 -) - const Page = async ({ params }: { params: PageParams }) => { const { locale } = params const t = await getTranslations({ locale, namespace: "page-resources" }) - // Load data - const [growThePieData, blobscanOverallStats] = await loadData() + // Fetch data using the new data-layer functions (already cached) + const [growThePieData, blobscanOverallStats] = await Promise.all([ + getGrowThePieData(), + getBlobscanStats(), + ]) + + // Handle null cases - throw error if required data is missing + if (!growThePieData) { + throw new Error("Failed to fetch GrowThePie data") + } + if (!blobscanOverallStats) { + throw new Error("Failed to fetch Blobscan stats data") + } const txCostsMedianUsd = growThePieData?.txCostsMedianUsd ?? { error: "No data available", } - const blobStats = - !blobscanOverallStats || - "error" in blobscanOverallStats || - !blobscanOverallStats.value - ? { - avgBlobFee: "—", - totalBlobs: "—", - } - : { - avgBlobFee: blobscanOverallStats.value.avgBlobFee, - totalBlobs: new Intl.NumberFormat(undefined, { - notation: "compact", - maximumFractionDigits: 1, - }).format(blobscanOverallStats.value.totalBlobs), - } + // Extract blob stats directly (getBlobscanStats returns BlobscanOverallStats, not wrapped in MetricReturnData) + const blobStats = { + avgBlobFee: blobscanOverallStats.avgBlobFee, + totalBlobs: new Intl.NumberFormat(undefined, { + notation: "compact", + maximumFractionDigits: 1, + }).format(blobscanOverallStats.totalBlobs), + } const resourceSections = await getResources({ txCostsMedianUsd, diff --git a/app/[locale]/resources/utils.tsx b/app/[locale]/resources/utils.tsx index 2003f3e1ef5..5d83d0236d3 100644 --- a/app/[locale]/resources/utils.tsx +++ b/app/[locale]/resources/utils.tsx @@ -16,7 +16,7 @@ import { getLocaleForNumberFormat } from "@/lib/utils/translations" import type { DashboardBox, DashboardSection } from "./types" -import { fetchEthPrice } from "@/lib/api/fetchEthPrice" +import { getEthPrice } from "@/lib/data" import IconBeaconchain from "@/public/images/resources/beaconcha-in.png" import IconBlobsGuru from "@/public/images/resources/blobsguru.png" import IconBlocknative from "@/public/images/resources/blocknative.png" @@ -88,7 +88,13 @@ export const getResources = async ({ const t = await getTranslations({ locale, namespace: "page-resources" }) const localeForNumberFormat = getLocaleForNumberFormat(locale as Lang) - const ethPrice = await fetchEthPrice() + // Fetch ETH price using the new data-layer function (already cached) + const ethPrice = await getEthPrice() + + // Handle null case + if (!ethPrice) { + throw new Error("Failed to fetch ETH price data") + } const avgBlobFeeUsd = "error" in ethPrice diff --git a/app/[locale]/stablecoins/page.tsx b/app/[locale]/stablecoins/page.tsx index 8db7c9c01ce..9f52b2fe50e 100644 --- a/app/[locale]/stablecoins/page.tsx +++ b/app/[locale]/stablecoins/page.tsx @@ -32,16 +32,13 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { cn } from "@/lib/utils/cn" import { getAppPageContributorInfo } from "@/lib/utils/contributors" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { getMetadata } from "@/lib/utils/metadata" import { getRequiredNamespacesForPage } from "@/lib/utils/translations" -import { BASE_TIME_UNIT } from "@/lib/constants" - import { stablecoins } from "./data" import StablecoinsPageJsonLD from "./page-jsonld" -import { fetchEthereumStablecoinsData } from "@/lib/api/stablecoinsData" +import { getStablecoinsData } from "@/lib/data" import sparkfiImg from "@/public/images/dapps/sparkfi.png" import summerfiImg from "@/public/images/dapps/summerfi.png" import dogeComputerImg from "@/public/images/doge-computer.png" @@ -59,14 +56,6 @@ import visaImg from "@/public/images/stablecoins/tools/visa.png" import usdcLargeImg from "@/public/images/stablecoins/usdc-large.png" import usdsLargeImg from "@/public/images/stablecoins/usds-large.png" -type CoinGeckoCoinMarketResponse = Array<{ - id: string - name: string - market_cap: number - image: string - symbol: string -}> - export type CoinDetails = { name: string marketCap: string @@ -77,8 +66,6 @@ export type CoinDetails = { symbol: string } -// In seconds -const REVALIDATE_TIME = BASE_TIME_UNIT * 1 const MIN_MARKET_CAP_USD = 500_000 const Section = ({ @@ -88,11 +75,6 @@ const Section = ({
) -const loadData = dataLoader<[CoinGeckoCoinMarketResponse]>( - [["ethereumStablecoinsData", fetchEthereumStablecoinsData]], - REVALIDATE_TIME * 1000 -) - async function Page({ params }: { params: PageParams }) { const { locale } = params const t = await getTranslations({ locale, namespace: "page-stablecoins" }) @@ -111,7 +93,13 @@ async function Page({ params }: { params: PageParams }) { try { marketsHasError = false - const [stablecoinsData] = await loadData() + // Fetch stablecoins data using the new data-layer function (already cached) + const stablecoinsData = await getStablecoinsData() + + // Handle null case - throw error if required data is missing + if (!stablecoinsData) { + throw new Error("Failed to fetch stablecoins data") + } const ethereumStablecoinData = stablecoins .map(({ id, ...rest }) => { diff --git a/app/[locale]/staking/page.tsx b/app/[locale]/staking/page.tsx index ae0f9750650..69241d873a2 100644 --- a/app/[locale]/staking/page.tsx +++ b/app/[locale]/staking/page.tsx @@ -10,35 +10,35 @@ import { CommitHistory, Lang, PageParams, StakingStatsData } from "@/lib/types" import I18nProvider from "@/components/I18nProvider" import { getAppPageContributorInfo } from "@/lib/utils/contributors" -import { dataLoader } from "@/lib/utils/data/dataLoader" import { getMetadata } from "@/lib/utils/metadata" import { getRequiredNamespacesForPage } from "@/lib/utils/translations" -import { BASE_TIME_UNIT } from "@/lib/constants" - import StakingPage from "./_components/staking" import StakingPageJsonLD from "./page-jsonld" -import { fetchBeaconchainEpoch } from "@/lib/api/fetchBeaconchainEpoch" -import { fetchBeaconchainEthstore } from "@/lib/api/fetchBeaconchainEthstore" - -// In seconds -const REVALIDATE_TIME = BASE_TIME_UNIT * 1 - -const loadData = dataLoader( - [ - ["beaconchainEpoch", fetchBeaconchainEpoch], - ["beaconchainApr", fetchBeaconchainEthstore], - ], - REVALIDATE_TIME * 1000 -) +import { getBeaconchainEpochData, getBeaconchainEthstoreData } from "@/lib/data" const Page = async ({ params }: { params: PageParams }) => { const { locale } = params setRequestLocale(locale) - const [{ totalEthStaked, validatorscount }, apr] = await loadData() + // Fetch data using the new data-layer functions (already cached) + const [beaconchainEpochData, apr] = await Promise.all([ + getBeaconchainEpochData(), + getBeaconchainEthstoreData(), + ]) + + // Handle null cases - throw error if required data is missing + if (!beaconchainEpochData) { + throw new Error("Failed to fetch Beaconchain epoch data") + } + if (!apr) { + throw new Error("Failed to fetch Beaconchain APR data") + } + + // Extract values from data structures + const { totalEthStaked, validatorscount } = beaconchainEpochData const data: StakingStatsData = { totalEthStaked: "value" in totalEthStaked ? totalEthStaked.value : 0, diff --git a/app/[locale]/utils.ts b/app/[locale]/utils.ts index ccf4cc6563c..cc4484c3aa8 100644 --- a/app/[locale]/utils.ts +++ b/app/[locale]/utils.ts @@ -5,18 +5,10 @@ import { getTranslations } from "next-intl/server" -import type { - AllHomepageActivityData, - CommunityConference, - Lang, - StatsBoxMetric, -} from "@/lib/types" +import type { AllHomepageActivityData, Lang, StatsBoxMetric } from "@/lib/types" -import { isValidDate } from "@/lib/utils/date" import { getLocaleForNumberFormat } from "@/lib/utils/translations" -import { DEFAULT_LOCALE } from "@/lib/constants" - const formatLargeUSD = (value: number, locale: string): string => { return new Intl.NumberFormat(locale, { style: "currency", @@ -132,38 +124,3 @@ export const getActivity = async ( return metrics } - -export const getUpcomingEvents = ( - events: CommunityConference[], - locale = DEFAULT_LOCALE -): CommunityConference[] => - events - .filter((event) => { - const isValid = isValidDate(event.endDate) - const beginningOfEndDate = new Date(event.endDate).getTime() - const endOfEndDate = beginningOfEndDate + 24 * 60 * 60 * 1000 - const isUpcoming = endOfEndDate >= new Date().getTime() - return isValid && isUpcoming - }) - .sort( - (a, b) => new Date(a.endDate).getTime() - new Date(b.endDate).getTime() - ) - .map(({ startDate, endDate, ...event }) => { - const formattedDate = - isValidDate(startDate) || isValidDate(endDate) - ? new Intl.DateTimeFormat(locale, { - month: "long", - day: "numeric", - year: "numeric", - }).formatRange( - new Date(isValidDate(startDate) ? startDate : endDate), - new Date(isValidDate(endDate) ? endDate : startDate) - ) - : "" - return { - ...event, - startDate, - endDate, - formattedDate, - } - }) diff --git a/docs/ab-testing.md b/docs/ab-testing.md index c69397aa130..0c13eb385f3 100644 --- a/docs/ab-testing.md +++ b/docs/ab-testing.md @@ -35,7 +35,7 @@ This step is important for measuring experiment effectiveness and should be done - **Hypothesis**: Explain what you predict to happen when you run the A/B test, what the outcome will be and why it will happen. - **Description**: Provide details about the test and its purpose - **Variations**: Add your variants (e.g., "NewDesign", "AlternativeLayout") -4. Define remainder of config: +4. Define the remainder of the config: - **Success metrics**: Goals you want to track (select from previously created goals) - **Success conditions**: Statistical thresholds - **Target pages**: Specify the pages where this test will run (e.g., `/`, `/wallet`, etc.) @@ -86,7 +86,7 @@ The experiment will automatically start running when: ## Multi-Variant Testing -Support for 3+ variants: +Supports 3+ variants: ```tsx // Matomo experiment configured with variations in this exact order: @@ -117,7 +117,7 @@ Support for 3+ variants: ### Cookie-less Tracking - Uses deterministic assignment based on IP address + User-Agent fingerprint -- Same user always gets same variant (consistent experience) +- The same user always gets the same variant (consistent experience) - GDPR compliant - no cookies or personal data storage required - Users can't manually switch variants (prevents data pollution) @@ -139,7 +139,7 @@ Support for 3+ variants: ### Preview Mode -- Preview deployments show debug panel with variant selector +- Preview deployments show a debug panel with a variant selector - No tracking occurs in preview mode - Allows manual testing of all variants diff --git a/docs/stack.md b/docs/stack.md index dfa41d6de0b..629eccc3ea0 100644 --- a/docs/stack.md +++ b/docs/stack.md @@ -5,10 +5,10 @@ - [Next.js 14](https://nextjs.org/) - React framework with App Router, SSG, SSR, i18n support, Image component, etc. - Configurable in `next.config.js` - - [NextJS Tutorial](https://nextjs.org/learn) - - [NextJS Docs](https://nextjs.org/docs) + - [Next.js Tutorial](https://nextjs.org/learn) + - [Next.js Docs](https://nextjs.org/docs) - [React](https://react.dev/) - A JavaScript library for building component-based user interfaces -- [Typescript](https://www.typescriptlang.org/) - TypeScript is a strongly typed programming language that builds on JavaScript +- [TypeScript](https://www.typescriptlang.org/) - TypeScript is a strongly typed programming language that builds on JavaScript - [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework - [shadcn/ui](https://ui.shadcn.com/) - Component library built on Radix UI and Tailwind CSS - [Radix UI](https://www.radix-ui.com/) - Accessible component primitives diff --git a/netlify.toml b/netlify.toml index f27a3445674..536b8948275 100644 --- a/netlify.toml +++ b/netlify.toml @@ -39,4 +39,4 @@ path = "en/developers/tutorials/creating-a-wagmi-ui-for-your-contract/" [functions] - included_files = ["i18n.config.json", "src/intl/**/*", "src/data/mocks/**/*", "src/data-layer/mocks/**/*"] + included_files = ["i18n.config.json", "src/intl/**/*", "src/data-layer/mocks/**/*"] diff --git a/next.config.js b/next.config.js index 853336f4aac..89f04de2d25 100644 --- a/next.config.js +++ b/next.config.js @@ -96,6 +96,10 @@ module.exports = (phase, { defaultConfig }) => { protocol: "https", hostname: "crowdin-static.cf-downloads.crowdin.com", }, + { + protocol: "https", + hostname: "pvvrtckedmrkyzfxubkk.supabase.co", + }, { protocol: "https", hostname: "avatars.githubusercontent.com", @@ -124,6 +128,26 @@ module.exports = (phase, { defaultConfig }) => { protocol: "https", hostname: "unavatar.io", }, + { + protocol: "https", + hostname: "secure.meetupstatic.com", + }, + { + protocol: "https", + hostname: "pbs.twimg.com", + }, + { + protocol: "https", + hostname: "images.lumacdn.com", + }, + { + protocol: "https", + hostname: "framerusercontent.com", + }, + { + protocol: "https", + hostname: "img.evbuc.com", + }, ], }, async headers() { diff --git a/package.json b/package.json index 355901dd06e..50eeda9576d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereum-org-website", - "version": "10.20.1", + "version": "10.21.0", "license": "MIT", "private": true, "scripts": { @@ -19,7 +19,6 @@ "crowdin-clean": "rm -rf .crowdin && mkdir .crowdin", "crowdin-import": "ts-node src/scripts/crowdin-import.ts", "markdown-checker": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/markdownChecker.ts", - "events-import": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/events-import.ts", "crowdin-needs-review": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/crowdin/reports/generateReviewReport.ts", "update-tutorials": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/update-tutorials-list.ts", "prepare": "husky", @@ -29,7 +28,8 @@ "test:e2e:ui": "playwright test --project=e2e --ui", "test:e2e:debug": "playwright test --project=e2e --debug", "test:e2e:report": "playwright show-report tests/__report__", - "trigger:dev": "dotenv -e .env -- npx trigger.dev@latest dev" + "trigger:dev": "dotenv -e .env -- npx trigger.dev@latest dev", + "trigger:deploy": "dotenv -e .env -- npx trigger.dev@latest deploy" }, "dependencies": { "@aws-sdk/client-ses": "^3.859.0", @@ -57,14 +57,13 @@ "@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", - "@radix-ui/react-visually-hidden": "^1.1.0", "@rainbow-me/rainbowkit": "^2.2.3", "@sentry/nextjs": "^10.5.0", "@socialgouv/matomo-next": "^1.8.0", "@tanstack/react-query": "^5.66.7", "@tanstack/react-table": "^8.19.3", "@tanstack/react-virtual": "^3.13.12", - "@trigger.dev/sdk": "4.3.0", + "@trigger.dev/sdk": "4.3.2", "@types/canvas-confetti": "^1.9.0", "@types/three": "^0.177.0", "@wagmi/core": "^2.17.3", @@ -174,6 +173,8 @@ "packageManager": "pnpm@10.12.4", "pnpm": { "overrides": { + "pbkdf2": ">=3.1.3", + "form-data": ">=4.0.4", "sha.js": ">=2.4.12" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e79aa9a8358..5aafd05d88d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,8 @@ settings: excludeLinksFromLockfile: false overrides: + pbkdf2: '>=3.1.3' + form-data: '>=4.0.4' sha.js: '>=2.4.12' importers: @@ -86,9 +88,6 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.1.2 version: 1.2.7(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-visually-hidden': - specifier: ^1.1.0 - version: 1.2.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rainbow-me/rainbowkit': specifier: ^2.2.3 version: 2.2.5(@tanstack/react-query@5.80.2(react@18.3.1))(@types/react@18.2.57)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.15.4(@netlify/blobs@10.4.1)(@tanstack/query-core@5.80.2)(@tanstack/react-query@5.80.2(react@18.3.1))(@types/react@18.2.57)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) @@ -108,8 +107,8 @@ importers: specifier: ^3.13.12 version: 3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@trigger.dev/sdk': - specifier: 4.3.0 - version: 4.3.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + specifier: 4.3.2 + version: 4.3.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@types/canvas-confetti': specifier: ^1.9.0 version: 1.9.0 @@ -4110,12 +4109,12 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' - '@trigger.dev/core@4.3.0': - resolution: {integrity: sha512-mOavnsfCEgEES67Lnfbq9vMV+A2JAtCAoMVxNI7umpSXbbKOmsxdcKRG46/0rclqHKp9X5hk2aF3TlFhvgl/EQ==} + '@trigger.dev/core@4.3.2': + resolution: {integrity: sha512-CToEt1w1jOmpvwOh/gqgC3UKHw36nrHSxaMYRnv5csXFOMgH2BksD/fZ2M/R2Q8kNehlQGagFWoussGmItxZqw==} engines: {node: '>=18.20.0'} - '@trigger.dev/sdk@4.3.0': - resolution: {integrity: sha512-54wyCtXaumNfv1ou8PMGs7gb5+wNz94RjwznTWLoBAMhMmPsiqSTa6qxwjjWbpxAb/FWvasUnfbHxOmio2xdMg==} + '@trigger.dev/sdk@4.3.2': + resolution: {integrity: sha512-XbzWBHoVFwvW1uyEbiEmztF86gvLgt0OO/aUqgeOixBAsTXRFxI/zd/scBMHK8vE9jB1D2biI8x5of6gRPEK4g==} engines: {node: '>=18.20.0'} peerDependencies: ai: ^4.2.0 || ^5.0.0 @@ -5225,11 +5224,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001720: - resolution: {integrity: sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==} - - caniuse-lite@1.0.30001760: - resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + caniuse-lite@1.0.30001763: + resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==} canvas-confetti@1.9.3: resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} @@ -6472,8 +6468,8 @@ packages: typescript: '>3.6.0' webpack: ^5.11.0 - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} forwarded-parse@2.1.2: @@ -6675,6 +6671,10 @@ packages: resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} engines: {node: '>= 0.10'} + hash-base@3.1.2: + resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} + engines: {node: '>= 0.8'} + hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} @@ -7905,9 +7905,9 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} - pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} + pbkdf2@3.1.5: + resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} + engines: {node: '>= 0.10'} pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -8623,6 +8623,10 @@ packages: ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + ripemd160@2.0.3: + resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} + engines: {node: '>= 0.8'} + robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -14752,7 +14756,7 @@ snapshots: dependencies: '@testing-library/dom': 10.4.0 - '@trigger.dev/core@4.3.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@trigger.dev/core@4.3.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@bugsnag/cuid': 3.2.1 '@electric-sql/client': 1.0.14 @@ -14793,11 +14797,11 @@ snapshots: - typescript - utf-8-validate - '@trigger.dev/sdk@4.3.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@trigger.dev/sdk@4.3.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.36.0 - '@trigger.dev/core': 4.3.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@trigger.dev/core': 4.3.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) chalk: 5.4.1 cronstrue: 2.59.0 debug: 4.4.3 @@ -14806,7 +14810,7 @@ snapshots: ulid: 2.4.0 uncrypto: 0.1.3 uuid: 9.0.1 - ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -16314,7 +16318,7 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.4): dependencies: browserslist: 4.25.0 - caniuse-lite: 1.0.30001720 + caniuse-lite: 1.0.30001763 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -16330,7 +16334,7 @@ snapshots: axios@1.9.0: dependencies: follow-redirects: 1.15.9 - form-data: 4.0.2 + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -16473,7 +16477,7 @@ snapshots: browserslist@4.25.0: dependencies: - caniuse-lite: 1.0.30001720 + caniuse-lite: 1.0.30001763 electron-to-chromium: 1.5.162 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.0) @@ -16549,9 +16553,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001720: {} - - caniuse-lite@1.0.30001760: {} + caniuse-lite@1.0.30001763: {} canvas-confetti@1.9.3: {} @@ -16830,7 +16832,7 @@ snapshots: diffie-hellman: 5.0.3 hash-base: 3.0.5 inherits: 2.0.4 - pbkdf2: 3.1.2 + pbkdf2: 3.1.5 public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 @@ -17905,7 +17907,7 @@ snapshots: extension-port-stream@3.0.0: dependencies: - readable-stream: 3.6.2 + readable-stream: 4.7.0 webextension-polyfill: 0.10.0 fast-deep-equal@3.1.3: {} @@ -18040,11 +18042,12 @@ snapshots: typescript: 5.8.3 webpack: 5.99.9(esbuild@0.25.12) - form-data@4.0.2: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 forwarded-parse@2.1.2: {} @@ -18267,6 +18270,13 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + hash-base@3.1.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + hash.js@1.1.7: dependencies: inherits: 2.0.4 @@ -19498,7 +19508,7 @@ snapshots: '@next/env': 14.2.35 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001760 + caniuse-lite: 1.0.30001763 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -19687,10 +19697,10 @@ snapshots: ox@0.6.7(typescript@5.8.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@scure/bip32': 1.6.2 - '@scure/bip39': 1.5.4 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 abitype: 1.0.8(typescript@5.8.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: @@ -19773,7 +19783,7 @@ snapshots: browserify-aes: 1.2.0 evp_bytestokey: 1.0.3 hash-base: 3.0.5 - pbkdf2: 3.1.2 + pbkdf2: 3.1.5 safe-buffer: 5.2.1 parse-entities@4.0.2: @@ -19823,13 +19833,14 @@ snapshots: pathval@2.0.0: {} - pbkdf2@3.1.2: + pbkdf2@3.1.5: dependencies: create-hash: 1.2.0 create-hmac: 1.1.7 - ripemd160: 2.0.2 + ripemd160: 2.0.3 safe-buffer: 5.2.1 sha.js: 2.4.12 + to-buffer: 1.2.2 pend@1.2.0: {} @@ -20590,6 +20601,11 @@ snapshots: hash-base: 3.0.5 inherits: 2.0.4 + ripemd160@2.0.3: + dependencies: + hash-base: 3.1.2 + inherits: 2.0.4 + robust-predicates@3.0.2: {} rollup@4.46.2: diff --git a/public/content/community/events/index.md b/public/content/community/events/index.md deleted file mode 100644 index f76de9e540f..00000000000 --- a/public/content/community/events/index.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Discover community events and community hubs -description: How to get involved in the Ethereum community. -lang: en -hideEditButton: true ---- - -# Discover community events and community hubs {#discover-community-events-and-community-hubs} - -Ethereum comes alive through the people—from small meetups and community gatherings to major conferences, hackathons, and permanent Community Hubs that anchor local ecosystems around the world. - -Every co-working day, meetup, or event is a chance to learn something new, meet like-minded people, share ideas, and collaborate on real projects that shape our future. Whether you’re a developer, researcher, designer, or just curious, there’s always a place for you to connect, contribute, and grow. - -💡 Organizing an event or have a community idea in mind? Reach out to the Ethereum Everywhere team for support! → [Get in touch here](https://docs.google.com/forms/d/e/1FAIpQLSeA-W8iy2PJxrY3TD4lMYXyky_wLd4QB_7NRwqSxCd0e19MUg/viewform) - -Want to discover Ethereum events or community hubs in your area? Take a look at the listings below to find your next meetup, co-working day or gathering! - -## Ethereum community hubs {#ethereum-community-hubs} - -Ethereum Community Hubs are permanent, inclusive spaces that function as co-working areas, host regular events, and serve as innovation and knowledge centers that spark collaboration and build strong, interconnected local ecosystems. - -Whether you're a local, digital nomad, or temporary resident, you can join co-working sessions, workshops, and meetups, discover opportunities, or even host your own event. Each community hub has its own registration process—you can find more information and links below. - - - -## Ethereum community events {#ethereum-community-events} - - - -This is a non-exhaustive list maintained by our community. Know of an upcoming Ethereum event to add to this list? [Please add it](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Ethereum meetups {#meetups} - -Don't see an event that works for you? Try joining a meetup. Meetups are smaller events held by groups of Ethereum enthusiasts - a chance for people interested in Ethereum to get together, talk about Ethereum, and learn about recent developments. - - - - diff --git a/public/content/translations/ca/community/events/index.md b/public/content/translations/ca/community/events/index.md deleted file mode 100644 index 33cb1e26af7..00000000000 --- a/public/content/translations/ca/community/events/index.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Esdeveniments d'Ethereum -description: Com involucrar-se amb la comunitat Ethereum. -lang: ca ---- - -# Pròxims esdeveniments {#events} - -**Cada mes, hi ha un gran nombre d'esdeveniments Ethereum a tot el món.** Teniu en consideració assistir a un que estigui a prop per conèixer més gent dins la comunitat, descobrir oportunitats de feina i desenvolupar noves habilitats. - - - -Aquesta llista no és exhaustiva. Està mantinguda per la nostra comunitat. Coneixeu algun esdeveniment d'Ethereum proper i no és en aquest llistat? [Afegiu-lo, si us plau](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Trobades Ethereum {#meetups} - -No veieu cap esdeveniment que us vagi bé? Uniu-vos a una trobada. Les trobades són esdeveniments més petits portades a terme per grups d'entusiastes d'Ethereum. És una oportunitat de reunir gent interessada en Ethereum, parlar sobre Ethereum i aprendre sobre desenvolupaments recents. - - - -Us interessaria crear la vostra pròpia trobada? Doneu una ullada a la [Xarxa BUIDL](https://consensys.net/developers/buidlnetwork/), una iniciativa de ConsenSys per oferir suport a les trobades de comunitats d'Ethereum. - -És un llistat no exhaustiu creat per la nostra comunitat. Podeu [veure més trobades Ethereum aquí](https://www.meetup.com/topics/ethereum/). Coneixeu cap grup actiu de trobades per afegir a aquest llistat? [Afegiu-lo](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/cs/community/events/index.md b/public/content/translations/cs/community/events/index.md deleted file mode 100644 index 6305faa32f2..00000000000 --- a/public/content/translations/cs/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Události Etherea -description: Jak se zapojit do Ethereum komunity. -lang: cs -hideEditButton: true ---- - -# Nadcházející události {#events} - -**Každý měsíc se po celém světě konají významné akce věnované Ethereu.** Zvažte účast na některé z nich ve vašem okolí, abyste se seznámili s dalšími lidmi z komunity, dozvěděli se o možnostech zaměstnání a rozvíjeli nové dovednosti. - - - -Toto je neúplný seznam spravován naší komunitou. Znáš nějaké další Ethereum události pro přidání do tohoto seznamu? [Prosím, přidej ji](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Ethereum setkání {#meetups} - -Nevidíš událost, která ti vyhovuje? Zkus se připojit ke schůzce. Setkání jsou menší akce pořádané skupinkami nadšenců Etherea – šance pro lidi, kteří mají zájem o Ethereum, aby se sešli, hovořili společně o Etheru a dozvěděli se o nejnovějším vývoji. - - - -Máš zájem o zahájení své vlastní schůzky? Podívejte se na [BUIDL Network](https://consensys.net/developers/buidlnetwork/), iniciativu ConsenSys na podporu komunit setkání Ethereum. - -Toto je neúplný seznam spravován naší komunitou. Další Ethereum schůzky [ lze najít zde](https://www.meetup.com/topics/ethereum/). Znáš aktivní schůzku pro přidání do seznamu? [Prosím, přidej ji](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/de/community/events/index.md b/public/content/translations/de/community/events/index.md deleted file mode 100644 index 684976f3ddb..00000000000 --- a/public/content/translations/de/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Ethereum-Veranstaltungen -description: So können Sie sich in der Ethereum-Community engagieren -lang: de -hideEditButton: true ---- - -# Bevorstehende Veranstaltungen {#events} - -**Jeden Monat finden auf der ganzen Welt große Ethereum-Veranstaltungen statt.** Besuchen Sie doch eine Veranstaltung in Ihrer Nähe, so lernen Sie andere aus der Community kennen und können sich über mögliche Karrierechancen informieren und neue Kompetenzen aufbauen. - - - -Diese Liste wird von unserer Community gepflegt und ist nicht abschließend. Wissen Sie von einer bevorstehenden Ethereum-Veranstaltung, die Sie dieser Liste hinzufügen möchten? [Fügen Sie gern eine Veranstaltung hinzu](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Ethereum-Treffen {#meetups} - -Sehen Sie keine Veranstaltung, an der Sie teilnehmen können? Dann bietet es sich an, an einem Treffen teilzunehmen. Treffen sind kleinere Veranstaltungen, die von Gruppen aus Ethereum-Enthusiasten abgehalten werden. Dort kommen Personen zusammen, die sich für Ethereum interessieren, es wird über Ethereum gesprochen und es findet ein Austausch über die jüngsten Entwicklungen statt. - - - -Sind Sie daran interessiert, ein eigenes Treffen zu organisieren? Im [BUIDL Network](https://consensys.net/developers/buidlnetwork/), eine Initiative von ConsenSys zur Unterstützung von Treffen der Ethereum-Community, finden Sie hilfreiche Informationen. - -Diese Liste wird von unserer Community gepflegt und ist nicht abschließend. [Hier](https://www.meetup.com/topics/ethereum/) finden Sie weitere Ethereum-Treffen. Kennen Sie eine aktive Gruppe, die sich trifft und die Sie dieser Liste hinzufügen können? [Fügen Sie sie gerne hinzu](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json). diff --git a/public/content/translations/el/community/events/index.md b/public/content/translations/el/community/events/index.md deleted file mode 100644 index 8dabc7e3ae2..00000000000 --- a/public/content/translations/el/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Εκδηλώσεις του Ethereum -description: Πώς να συμμετάσχετε στην κοινότητα του Ethereum. -lang: el -hideEditButton: true ---- - -# Προσεχείς εκδηλώσεις {#events} - -**Κάθε μήνα, πραγματοποιούνται εκδηλώσεις του Ethereum σε όλο τον κόσμο.** Μπορείτε να παρακολουθήσετε μια εκδήλωση κοντά σας για να γνωρίσετε περισσότερους ανθρώπους της κοινότητας, να μάθετε για τις ευκαιρίες απασχόλησης και να αναπτύξετε νέες δεξιότητες. - - - -Αυτή δεν είναι η πλήρη λίστα, η οποία διαχειρίζεται από την κοινότητά μας. Γνωρίζετε μια εκδήλωση του Ethereum που θα πραγματοποιηθεί για να την προσθέσετε στη λίστα; [Παρακαλούμε προσθέστε την](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Συναντήσεις Ethereum {#meetups} - -Δεν εμφανίζεται διαθέσιμη εκδήλωση για εσάς; Δοκιμάστε να συμμετάσχετε σε μια συνάντηση. Οι συναντήσεις είναι μικρότερες εκδηλώσεις που διοργανώνονται από ομάδες του Ethereum - μια ευκαιρία για αυτούς που ενδιαφέρονται για το Ethereum να έρθουν σε επαφή, να μιλήσουν για το Ethereum και να ενημερωθούν για τις πρόσφατες εξελίξεις. - - - -Ενδιαφέρεστε να οργανώσετε μια συνάντηση; Δείτε το [Δίκτυο BUIDL](https://consensys.net/developers/buidlnetwork/), μια πρωτοβουλία του ConsenSys για την υποστήριξη των κοινοτήτων συνάντησης του Ethereum. - -Αυτή δεν είναι μια πλήρη λίστα της κοινότητάς μας. Μπορείτε να βρείτε περισσότερες [συναντήσεις Ethereum εδώ](https://www.meetup.com/topics/ethereum/). Γνωρίζετε μια ενεργή ομάδα συναντήσεων για προσθήκη στη λίστα; [Παρακαλούμε προσθέστε τη](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/el/events/index.md b/public/content/translations/el/events/index.md deleted file mode 100644 index 382c0b546d6..00000000000 --- a/public/content/translations/el/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Εκδηλώσεις του Ethereum -description: Πώς να συμμετάσχετε στην κοινότητα του Ethereum. -lang: el -hideEditButton: true ---- - -# Προσεχείς εκδηλώσεις {#events} - -**Κάθε μήνα, πραγματοποιούνται εκδηλώσεις του Ethereum σε όλο τον κόσμο.** Μπορείτε να παρακολουθήσετε μια εκδήλωση κοντά σας για να γνωρίσετε περισσότερους ανθρώπους της κοινότητας, να μάθετε για τις ευκαιρίες απασχόλησης και να αναπτύξετε νέες δεξιότητες. - - - -Αυτή δεν είναι η πλήρη λίστα, η οποία διαχειρίζεται από την κοινότητά μας. Γνωρίζετε μια εκδήλωση του Ethereum που θα πραγματοποιηθεί για να την προσθέσετε στη λίστα; [Παρακαλούμε προσθέστε την](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Συναντήσεις Ethereum {#meetups} - -Δεν εμφανίζεται διαθέσιμη εκδήλωση για εσάς; Δοκιμάστε να συμμετάσχετε σε μια συνάντηση. Οι συναντήσεις είναι μικρότερες εκδηλώσεις που διοργανώνονται από ομάδες του Ethereum - μια ευκαιρία για αυτούς που ενδιαφέρονται για το Ethereum να έρθουν σε επαφή, να μιλήσουν για το Ethereum και να ενημερωθούν για τις πρόσφατες εξελίξεις. - - - -Ενδιαφέρεστε να οργανώσετε μια συνάντηση; Δείτε το [Δίκτυο BUIDL](https://consensys.net/developers/buidlnetwork/), μια πρωτοβουλία του ConsenSys για την υποστήριξη των κοινοτήτων συνάντησης του Ethereum. - -Αυτή δεν είναι μια πλήρη λίστα της κοινότητάς μας. Μπορείτε να βρείτε περισσότερες [συναντήσεις Ethereum εδώ](https://www.meetup.com/topics/ethereum/). Γνωρίζετε μια ενεργή ομάδα συναντήσεων για προσθήκη στη λίστα; [Παρακαλούμε προσθέστε τη](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/es/community/events/index.md b/public/content/translations/es/community/events/index.md deleted file mode 100644 index 8b2c61983d6..00000000000 --- a/public/content/translations/es/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Eventos de Ethereum -description: Cómo involucrarse en la comunidad Ethereum. -lang: es -hideEditButton: true ---- - -# Próximos eventos {#events} - -**Cada mes, se celebran grandes eventos de Ethereum en todo el mundo.** Plantéese asistir a alguno cerca suyo para conocer a más gente en la comunidad, aprender sobre oportunidades de empleo y desarrollar nuevas habilidades. - - - -Esta es una lista no exhaustiva mantenida por nuestra comunidad. ¿Sabe de algún evento próximo de Ethereum para añadir a esta lista? [¡Por favor, añádalo](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Encuentros sobre Ethereum {#meetups} - -¿No ve ningún evento que le venga bien? Intente asistir a un encuentro. Los encuentros son eventos más pequeños celebrados por grupos de entusiastas de Ethereum, es decir, son una oportunidad para que las personas interesadas en Ethereum se reúnan, hablen acerca de Ethereum y se enteren de las últimas novedades. - - - -¿Le interesaría organizar su propio encuentro? Eche un vistazo a [BUIDL Network](https://consensys.net/developers/buidlnetwork/): una iniciativa de ConsenSys para ayudar a apoyar a las comunidades de encuentros de Ethereum. - -Esta es una lista no exhaustiva mantenida por nuestra comunidad. Aquí [encontrará más información sobre los encuentros de Ethereum](https://www.meetup.com/topics/ethereum/). ¿Sabe de algún un grupo de encuentros activo para añadir a esta lista? [¡Por favor, añádalo!](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json) diff --git a/public/content/translations/es/events/index.md b/public/content/translations/es/events/index.md deleted file mode 100644 index a2079753e73..00000000000 --- a/public/content/translations/es/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Eventos de Ethereum -description: Cómo involucrarse en la comunidad Ethereum. -lang: es -hideEditButton: true ---- - -# Próximos eventos {#events} - -**Cada mes, se celebran grandes eventos de Ethereum en todo el mundo.** Plantéese asistir a alguno cerca suyo para conocer a más gente en la comunidad, aprender sobre oportunidades de empleo y desarrollar nuevas habilidades. - - - -Esta es una lista no exhaustiva mantenida por nuestra comunidad. ¿Sabe de algún evento próximo de Ethereum para añadir a esta lista? [¡Por favor, añádalo](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Encuentros sobre Ethereum {#meetups} - -¿No ve ningún evento que le venga bien? Intente asistir a un encuentro. Los encuentros son eventos más pequeños celebrados por grupos de entusiastas de Ethereum, es decir, son una oportunidad para que las personas interesadas en Ethereum se reúnan, hablen acerca de Ethereum y se enteren de las últimas novedades. - - - -¿Le interesaría organizar su propia reunión? Eche un vistazo a [BUIDL Network](https://consensys.net/developers/buidlnetwork/): una iniciativa de ConsenSys para ayudar a apoyar las comunidades de encuentros de Ethereum. - -Esta es una lista no exhaustiva mantenida por nuestra comunidad. Aquí [encontrará más información sobre los encuentros de Ethereum](https://www.meetup.com/topics/ethereum/). ¿Sabe de algún un grupo de encuentros activo para añadir a esta lista? [¡Por favor, añádalo!](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json) diff --git a/public/content/translations/fa/community/events/index.md b/public/content/translations/fa/community/events/index.md deleted file mode 100644 index e7f518ba37a..00000000000 --- a/public/content/translations/fa/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: رویدادهای اتریوم -description: چگونه در انجمن اتریوم شرکت کنیم؟ -lang: fa -hideEditButton: true ---- - -# رویدادهای پیش‌رو {#events} - -**هر ماه، رویدادهای مهم اتریوم در سرتاسر جهان برگزار می‌شود.** شرکت در یکی از رویدادهای نزدیک به خود را در نظر داشته باشید تا با افراد بیشتری در جامعه آشنا شوید، درباره فرصت‌های شغلی اطلاع کسب کنید و مهارت‌های جدید را توسعه دهید. - - - -این یک لیست غیرجامع است که توسط انجمن ما نگهداری می شود. از رویداد آتی اتریوم برای اضافه کردن به این لیست اطلاع دارید؟ [لطفاً آن را اضافه کنید](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## گردهمایی‌های اتریوم {#meetups} - -رویدادی را نمی‌بینید که برای شما مفید باشد؟ شرکت در یک گردهمایی را امتحان کنید. گردهمایی‌ها رویدادهای کوچک‌تری هستند که گروه‌هایی از علاقه‌مندان به اتریوم آن‌ها را برگزار می‌کنند - فرصتی برای افراد علاقه‌مند به اتریوم تا دور هم جمع شوند، درباره اتریوم صحبت کنند، و درباره پیشرفت‌های اخیر اطلاعات کسب کنند. - - - -علاقه‌مند به برگزاری گردهمایی خود هستید؟ [شبکه BUIDL](https://consensys.net/developers/buidlnetwork/)، ابتکاری توسط ConsenSys برای کمک به حمایت از انجمن‌های ملاقات اتریوم، را بررسی کنید. - -این فهرستی غیرجامع است که آن را انجمن ما ساخته است. می‌توانید [گردهمایی‌های اتریوم بیشتری را در اینجا بیابید](https://www.meetup.com/topics/ethereum/). گروه ملاقات فعالی برای اضافه کردن به این فهرست می‌شناسید؟ [لطفاً آن را اضافه کنید](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/fa/events/index.md b/public/content/translations/fa/events/index.md deleted file mode 100644 index 373618a54a3..00000000000 --- a/public/content/translations/fa/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: رویدادهای اتریوم -description: چگونه در انجمن اتریوم شرکت کنیم؟ -lang: fa -hideEditButton: true ---- - -# رویدادهای پیش‌رو {#events} - -**هر ماه، رویدادهای مهم اتریوم در سرتاسر جهان برگزار می‌شود.** شرکت در یکی از رویدادهای نزدیک به خود را در نظر داشته باشید تا با افراد بیشتری در جامعه آشنا شوید، درباره فرصت‌های شغلی اطلاع کسب کنید و مهارت‌های جدید را توسعه دهید. - - - -این یک لیست غیرجامع است که توسط انجمن ما نگهداری می شود. از رویداد آتی اتریوم برای اضافه کردن به این لیست اطلاع دارید؟ [لطفاً آن را اضافه کنید](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## گردهمایی‌های اتریوم {#meetups} - -رویدادی را نمی‌بینید که برای شما مفید باشد؟ شرکت در یک گردهمایی را امتحان کنید. گردهمایی‌ها رویدادهای کوچک‌تری هستند که توسط گروه‌هایی از علاقه‌مندان به اتریوم برگزار می‌شوند - فرصتی برای افراد علاقه‌مند به اتریوم تا دور هم جمع شوند، درباره اتریوم صحبت کنند، و در مورد پیشرفت‌های اخیر اطلاعات کسب کنند. - - - -علاقه‌مند به برگزاری گردهمایی خود هستید؟ [شبکه‌ی BUIDL](https://consensys.net/developers/buidlnetwork/) را بررسی کنید، ابتکاری توسط ConsenSys برای کمک به حمایت از انجمن‌های ملاقات اتریوم. - -این یک فهرست غیرجامع است که توسط انجمن ما ساخته شده است. می‌توانید [گردهمایی‌های اتریوم بیشتری را در اینجا بیابید](https://www.meetup.com/topics/ethereum/). گروه ملاقات فعالی برای اضافه کردن به این فهرست می‌شناسید؟ [لطفاً آن را اضافه کنید](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/fr/community/events/index.md b/public/content/translations/fr/community/events/index.md deleted file mode 100644 index eb72a63ecf2..00000000000 --- a/public/content/translations/fr/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Évènements Ethereum -description: Comment s'impliquer dans la communauté Ethereum. -lang: fr -hideEditButton: true ---- - -# Évènements à venir {#events} - -**Chaque mois, il y a des événements Ethereum majeurs dans le monde entier.** Envisagez d'assister à un événement près de chez vous pour rencontrer plus de personnes dans la communauté, en savoir plus sur les opportunités d'emploi et développer de nouvelles compétences. - - - -Ceci est une liste non exhaustive maintenue par notre communauté. Connaissez-vous un événement Ethereum à venir à ajouter à cette liste ? [Veuillez l'ajouter](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json) ! - - - -## Rencontres Ethereum {#meetups} - -Vous ne voyez pas d'événement qui vous convient ? Essayez de rejoindre une rencontre. Les rencontres sont de petits événements organisés par des groupes de passionnés d'Ethereum - une chance pour les personnes intéressées par Ethereum de se rencontrer, de parler d'Ethereum, et d'en savoir plus sur les développements récents. - - - -Vous souhaitez créer votre propre rencontre ? Découvrez le [BUIDL Network](https://consensys.net/developers/buidlnetwork/), une initiative de ConsenSys pour aider à soutenir les communautés de rencontre d'Ethereum. - -Ceci est une liste non exhaustive construite par notre communauté. Vous pouvez [trouver plus de rencontres Ethereum ici](https://www.meetup.com/topics/ethereum/). Vous connaissez un groupe de rencontre actif à ajouter à cette liste ? [N'hésitez pas à l'ajouter](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json) ! diff --git a/public/content/translations/ga/community/events/index.md b/public/content/translations/ga/community/events/index.md deleted file mode 100644 index 5e9b312fb67..00000000000 --- a/public/content/translations/ga/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Imeachtaí Ethereum -description: Conas a bheith páirteach sa phobal Ethereum. -lang: ga -hideEditButton: true ---- - -# Imeachtaí atá le teacht {#events} - -** Gach mí, bíonn mórimeachtaí Ethereum ar fud an domhain.** Smaoinigh ar fhreastal ar cheann in aice leat chun bualadh le níos mó daoine sa phobal, foghlaim faoi dheiseanna fostaíochta, agus scileanna nua a fhorbairt. - - - -Is liosta neamh-iomlán é seo arna chothabháil ag ár bpobal. Ar an eolas faoi imeacht Ethereum atá le teacht chun cur leis an liosta seo? [Cuir leis an liosta seo é le do thoil](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Cruinnithe Ethereum {#meetups} - -Nach bhfeiceann tú imeacht a oibríonn duit? Bain triail as páirt a ghlacadh i gcruinniú. Is imeachtaí níos lú iad Cruinnithe a reáchtálann grúpaí díograiseoirí Ethereum - deis do dhaoine a bhfuil suim acu in Ethereum teacht le chéile, labhairt faoi Ethereum, agus foghlaim faoi fhorbairtí le déanaí. - - - -An bhfuil suim agat do chruinniú féin a bhunú? Féach ar an [ Líonra BUIDL ](https://consensys.net/developers/buidlnetwork/), tionscnamh de chuid ConsenSys chun cabhrú le tacú le pobail chruinnithe Ethereum. - -Is liosta neamh-iomlán é seo a chuir ár bpobal le chéile. Is féidir leat [tuilleadh cruinnithe Ethereum a fháil anseo](https://www.meetup.com/topics/ethereum/). An bhfuil eolas agat ar ghrúpa cruinnithe gníomhach le cur leis an liosta seo? [Cuir leis an liosta seo é le do thoil](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/ga/events/index.md b/public/content/translations/ga/events/index.md deleted file mode 100644 index 20c9e6499ca..00000000000 --- a/public/content/translations/ga/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Imeachtaí Ethereum -description: Conas a bheith páirteach sa phobal Ethereum. -lang: ga -hideEditButton: true ---- - -# Imeachtaí atá le teacht {#events} - -** Gach mí, bíonn mórimeachtaí Ethereum ar fud an domhain.** Smaoinigh ar fhreastal ar cheann in aice leat chun bualadh le níos mó daoine sa phobal, foghlaim faoi dheiseanna fostaíochta, agus scileanna nua a fhorbairt. - - - -Is liosta neamh-iomlán é seo arna chothabháil ag ár bpobal. Ar an eolas faoi imeacht Ethereum atá le teacht chun cur leis an liosta seo? [Cuir leis an liosta seo é le do thoil](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Cruinnithe Ethereum {#meetups} - -Nach bhfeiceann tú imeacht a oibríonn duit? Bain triail as páirt a ghlacadh i gcruinniú. Is imeachtaí níos lú iad Cruinnithe a reáchtálann grúpaí díograiseoirí Ethereum - deis do dhaoine a bhfuil suim acu in Ethereum teacht le chéile, labhairt faoi Ethereum, agus foghlaim faoi fhorbairtí le déanaí. - - - -An bhfuil suim agat do chruinniú féin a bhunú? Féach ar an [ Líonra BUIDL ](https://consensys.net/developers/buidlnetwork/), tionscnamh de chuid ConsenSys chun cabhrú le tacú le pobail chruinnithe Ethereum. - -Is liosta neamh-iomlán é seo a chuir ár bpobal le chéile. Is féidir leat [tuilleadh cruinnithe Ethereum a fháil anseo](https://www.meetup.com/topics/ethereum/). An bhfuil eolas agat ar ghrúpa cruinnithe gníomhach le cur leis an liosta seo? [Cuir leis an liosta seo é le do thoil](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/hi/events/index.md b/public/content/translations/hi/events/index.md deleted file mode 100644 index 165c6cc84ae..00000000000 --- a/public/content/translations/hi/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: एथेरियम इवेंट्स -description: एथेरियम समुदाय में कैसे शामिल हों। -lang: hi -hideEditButton: true ---- - -# भविष्य में होने वाली इवेंट्स {#events} - -**हर महीने, दुनिया भर में एथेरियम की बड़ी इवेंट्स होते हैं।** अपने नज़दीक होने वाले किसी एक इवेंट में शामिल होने पर विचार करें, ताकि आप समुदाय के अधिक लोगों से मिल सकें, रोज़गार के अवसरों के बारे में जान सकें और नई क्षमताएं विकसित कर सकें। - - - -यह हमारे समुदाय द्वारा रखरखाव की जाने वाली एक गैर-विस्तृत सूची है। क्या आप इस सूची में जोड़ने के लिए किसी आगामी एथेरियम इवेंट्स के बारे में जानते हैं? [कृपया इसे जोड़ें](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## एथेरियम मीटअप्स {#meetups} - -क्या आपको ऐसा कोई इवेंट नहीं दिख रहा, जो आपके लिए उपयुक्त हो? किसी मीटअप में शामिल होने की कोशिश करें। मीटअप्स, एथेरियम के उत्साही समूहों द्वारा आयोजित छोटे इवेंट हैं - जिनमें एथेरियम में रुचि रखने वाले लोग एक साथ आते हैं, एथेरियम के बारे में बात करते हैं और हाल के विकासों के बारे में सीखते हैं।मीटअप्स - - - -क्‍या अपना खुद का मीटअप शुरू करने में आपकी रुचि है? [BUIDL नेटवर्क](https://consensys.net/developers/buidlnetwork/) पर नज़र डालें, जो एथेरियम के मीटअप समुदायों की सहायता करने के लिए, ConsenSys द्वारा शुरू की गई एक पहल है। - -यह हमारे समुदाय द्वारा बनाई गई एक गैर-विस्तृत सूची है। आप [और भी एथेरियम मीटअप्स यहां पा सकते हैं](https://www.meetup.com/topics/ethereum/)। क्‍या आप इस सूची में जोड़ने के लिए किसी सक्रिय मीटअप समूह के बारे में जानते हैं? [कृपया इसे जोड़ें](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/hu/community/events/index.md b/public/content/translations/hu/community/events/index.md deleted file mode 100644 index 0c6b04f1640..00000000000 --- a/public/content/translations/hu/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Ethereum események -description: Hogyan kapcsolódhat be az Ethereum közösségébe? -lang: hu -hideEditButton: true ---- - -# Közelgő események {#events} - -**Minden hónapban részt vehet Ethereum-eseményeken a világ bármely pontján.** Vegyen részt egy Önhöz közel eső eseményen, hogy találkozzon a közösség tagjaival, megismerje a munkalehetőségeket és új képességeket fejlesszen. - - - -A lista nem teljeskörű, a közösség tagjai frissítik. Tudomása van egy tervezett Ethereum-eseményről? [Kérjük, adja hozzá](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Ethereum-találkozók {#meetups} - -Nem talál olyan eseményt, amely megfelelő lenne Önnek? Érdemes lehet elmennie egy találkozóra. Ezek kisebb események, melyeket az Ethereum-rajongók szerveznek, hogy az Ethereum iránt érdeklődők összegyűljenek, beszélgessenek az Ethereumról és megismerjék az új fejlesztéseket. - - - -Saját találkozót szeretne szervezni? Nézze meg a ConsesSys által létrehozott, az Ethereum-közösségi találkozók szervezését segítő [BUIDL Network](https://consensys.net/developers/buidlnetwork/) hálózatot. - -Ez a lista nem teljes körű, a közösség tagjai írják. [Több Ethereum-találkozót találhat itt](https://www.meetup.com/topics/ethereum/). Ismer olyan találkozót szervező csoportot, amely nincs a listán? [Kérjük, adja hozzá](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/hu/events/index.md b/public/content/translations/hu/events/index.md deleted file mode 100644 index 0b564281725..00000000000 --- a/public/content/translations/hu/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Ethereum események -description: Hogyan lehet bekapcsolódni az Ethereum közösségébe. -lang: hu -hideEditButton: true ---- - -# Közelgő események {#events} - -**Minden hónapban részt vehet Ethereum-eseményeken a világ bármely pontján.** Vegyen részt egy Önhöz közel eső eseményen, hogy találkozzon a közösség tagjaival, megismerje a munkalehetőségeket és új képességeket fejlesszen. - - - -A lista nem teljeskörű, a közösség tagjai frissítik. Tudomása van egy tervezett Ethereum-eseményről? [Kérjük, adja hozzá](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Ethereum-találkozók {#meetups} - -Nem talál olyan eseményt, amely jó lenne Önnek? Próbáljon meg elmenni egy találkozóra. Ezek kisebb események, melyeket az Ethereum-rajongók szerveznek, hogy az Ethereum iránt érdeklődők összegyűljenek, beszélgessenek az Ethereumról, megismerjék az új fejlesztéseket. - - - -Saját találkozót szeretne szervezni? Nézze meg a [BUIDL Network-öt](https://consensys.net/developers/buidlnetwork/), ami a ConsesSys kezdeményezése, hogy támogassa az Ethereum találkozókat. - -Ez a lista nem teljeskörű, a közösség tagjai írják. [Több Ethereum találkozót](https://www.meetup.com/topics/ethereum/) találhat itt. Ismer olyan találkozót szervező csoportot, amelyik nincs a listán? [Kérjük, adja hozzá!](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json) diff --git a/public/content/translations/id/community/events/index.md b/public/content/translations/id/community/events/index.md deleted file mode 100644 index 41afcffab39..00000000000 --- a/public/content/translations/id/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Aksi Ethereum -description: Cara terlibat di dalam komunitas Ethereum. -lang: id -hideEditButton: true ---- - -# Aksi mendatang {#events} - -**Setiap bulan, ada aksi besar Ethereum di seluruh dunia.** Pertimbangkanlah untuk menghadiri salah satunya di dekat area Anda untuk bertemu dengan lebih banyak orang di komunitas, mendapatkan informasi tentang lowongan pekerjaan, dan mengembangkan keahlian baru. - - - -Ini adalah daftar tidak lengkap yang dipelihara oleh komunitas kami. Tahu aksi Ethereum mendatang untuk ditambahkan ke daftar ini? [Silakan tambahkan](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Pertemuan Ethereum {#meetups} - -Tidak melihat aksi yang cocok untuk Anda? Cobalah menghadiri sebuah pertemuan. Pertemuan adalah aksi yang lebih kecil yang diadakan oleh grup penggemar Ethereum - sebuah peluang bagi orang-orang yang tertarik dengan Ethereum untuk berkumpul, berbicara tentang Ethereum, dan mengetahui tentang perkembangan terkini. - - - -Anda tertarik dengan memulai pertemuan Anda sendiri? Lihat [Jaringan BUIDL](https://consensys.net/developers/buidlnetwork/), suatu inisiatif oleh ConsenSys untuk membantu mendukung pertemuan Ethereum. - -Ini adalah daftar tidak lengkap yang dibuat oleh komunitas kami. Anda dapat [menemukan lebih banyak pertemuan Ethereum di sini](https://www.meetup.com/topics/ethereum/). Ada grup pertemuan aktif yang ingin Anda tambahkan ke daftar ini? [Silahkan tambahkan](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/id/events/index.md b/public/content/translations/id/events/index.md deleted file mode 100644 index 7c97c06357c..00000000000 --- a/public/content/translations/id/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Aksi Ethereum -description: Cara terlibat di dalam komunitas Ethereum. -lang: id -hideEditButton: true ---- - -# Aksi mendatang {#events} - -**Setiap bulan, ada aksi besar Ethereum di seluruh dunia.** Pertimbangkanlah untuk menghadiri salah satunya di dekat area Anda untuk bertemu dengan lebih banyak orang di komunitas, mendapatkan informasi tentang lowongan pekerjaan, dan mengembangkan keahlian baru. - - - -Ini adalah daftar tidak lengkap yang dipelihara oleh komunitas kami. Tahu aksi Ethereum mendatang untuk ditambahkan ke daftar ini? [Silakan tambahkan](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Pertemuan Ethereum {#meetups} - -Tidak melihat aksi yang cocok untuk Anda? Cobalah menghadiri sebuah pertemuan. Pertemuan adalah aksi yang lebih kecil yang diadakan oleh grup penggemar Ethereum - sebuah peluang bagi orang-orang yang tertarik dengan Ethereum untuk berkumpul, berbicara tentang Ethereum, dan mengetahui tentang perkembangan terkini. - - - -Anda tertarik dengan memulai pertemuan Anda sendiri? Lihat [Jaringan BUIDL](https://consensys.net/developers/buidlnetwork/), suatu inisiatif oleh ConsenSys untuk membantu mendukung pertemuan Ethereum. - -Ini adalah daftar tidak lengkap yang dibuat oleh komunitas kami. Anda dapat [menemukan lebih banyak pertemuan Ethereum di sini](https://www.meetup.com/topics/ethereum/). Tahu suatu grup pertemuan aktif untuk ditambahkan ke daftar ini? [Silahkan tambahkan](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/it/community/events/index.md b/public/content/translations/it/community/events/index.md deleted file mode 100644 index 245bce6c421..00000000000 --- a/public/content/translations/it/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Eventi Ethereum -description: Come partecipare alla community Ethereum. -lang: it -hideEditButton: true ---- - -# Prossimi eventi {#events} - -**Ogni mese, ci sono importanti eventi Ethereum in tutto il mondo.** Prendi in considerazione di partecipare a un evento vicino a te per incontrare altre persone nella community, conoscere le opportunità di lavoro e sviluppare nuove competenze. - - - -Questa è una lista non esaustiva mantenuta dalla nostra community. Conosci un evento Ethereum imminente da aggiungere a questa lista? [Sei pregato di aggiungerlo](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Incontri di Ethereum {#meetups} - -Non vedi un evento che ti interessa? Prova a unirti a un incontro. Gli incontri sono eventi più piccoli tenuti da gruppi di appassionati di Ethereum; un'opportunità per chi è interessato a Ethereum di riunirsi, parlare di Ethereum e scoprire i recenti sviluppi. - - - -Sei interessato a iniziare il tuo incontro? Scopri la [Rete BUIDL](https://consensys.net/developers/buidlnetwork/), un'iniziativa di ConsenSys per supportare le community di incontri di Ethereum. - -Questo è un elenco non esaustivo creato dalla nostra community. Puoi [trovare altri incontri Ethereum qui](https://www.meetup.com/topics/ethereum/). Conosci un gruppo di incontri attivo da aggiungere a questo elenco? [Aggiungilo](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/ja/community/events/index.md b/public/content/translations/ja/community/events/index.md deleted file mode 100644 index 278f6ebb8ee..00000000000 --- a/public/content/translations/ja/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: イーサリアムのイベント -description: イーサリアムコミュニティへの参加方法 -lang: ja -hideEditButton: true ---- - -# 今後のイベント {#events} - -**毎月、世界各地でメジャーなイーサリアムのイベントが行われています。**お近くで開催されているイベントに参加し、コミュニティの人々と出会い、採用情報や新しいスキルを学んでみませんか? - - - -これはコミュニティにより、維持されているイベントのリストですが、すべてのイベントを網羅するものではありません。 リストに追加すべきイーサリアムのイベントをご存じの場合は、 [ぜひ追加してください](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)。 - -## イーサリアムのミートアップ {#meetups} - -自分に合うイベントが見つからない場合は、 ミートアップへの参加を検討してみましょう。 ミートアップは、イーサリアムの愛好家のグループが開催する小規模なイベントです。イーサリアムに関心のある方たちが集い、イーサリアムについて話し合ったり、最近の開発について学べる良い機会です。 - - - -自分自身でミートアップを開催することにご興味がある場合は、 ConsenSysのイニシアチブで、イーサリアムのミートアップ・コミュニティをサポートしている[BUIDL Network](https://consensys.net/developers/buidlnetwork/)をチェックしてみてください。 - -これはコミュニティにより作成されたリストで、すべてを網羅するものではありません。 こちらの[イーサリアムのミートアップ](https://www.meetup.com/topics/ethereum/)からさらに多くのミートアップを見つけることができます。 このリストに追加すべき、アクティブなミートアップグループをご存知の場合は、 [ぜひ追加してください](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)。 diff --git a/public/content/translations/ja/events/index.md b/public/content/translations/ja/events/index.md deleted file mode 100644 index 278f6ebb8ee..00000000000 --- a/public/content/translations/ja/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: イーサリアムのイベント -description: イーサリアムコミュニティへの参加方法 -lang: ja -hideEditButton: true ---- - -# 今後のイベント {#events} - -**毎月、世界各地でメジャーなイーサリアムのイベントが行われています。**お近くで開催されているイベントに参加し、コミュニティの人々と出会い、採用情報や新しいスキルを学んでみませんか? - - - -これはコミュニティにより、維持されているイベントのリストですが、すべてのイベントを網羅するものではありません。 リストに追加すべきイーサリアムのイベントをご存じの場合は、 [ぜひ追加してください](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)。 - -## イーサリアムのミートアップ {#meetups} - -自分に合うイベントが見つからない場合は、 ミートアップへの参加を検討してみましょう。 ミートアップは、イーサリアムの愛好家のグループが開催する小規模なイベントです。イーサリアムに関心のある方たちが集い、イーサリアムについて話し合ったり、最近の開発について学べる良い機会です。 - - - -自分自身でミートアップを開催することにご興味がある場合は、 ConsenSysのイニシアチブで、イーサリアムのミートアップ・コミュニティをサポートしている[BUIDL Network](https://consensys.net/developers/buidlnetwork/)をチェックしてみてください。 - -これはコミュニティにより作成されたリストで、すべてを網羅するものではありません。 こちらの[イーサリアムのミートアップ](https://www.meetup.com/topics/ethereum/)からさらに多くのミートアップを見つけることができます。 このリストに追加すべき、アクティブなミートアップグループをご存知の場合は、 [ぜひ追加してください](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)。 diff --git a/public/content/translations/kn/events/index.md b/public/content/translations/kn/events/index.md deleted file mode 100644 index dd13f73935b..00000000000 --- a/public/content/translations/kn/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: ಎಥೆರಿಯಮ್ ಕಾರ್ಯಕ್ರಮಗಳು -description: ಎಥೆರಿಯಮ್ ಸಮುದಾಯದಲ್ಲಿ ಹೇಗೆ ತೊಡಗಿಸಿಕೊಳ್ಳುವುದು. -lang: kn -hideEditButton: true ---- - -# ಮುಂಬರುವ ಕಾರ್ಯಕ್ರಮಗಳು {#events} - -**ಪ್ರತಿ ತಿಂಗಳು, ಜಗತ್ತಿನಾದ್ಯಂತ ಪ್ರಮುಖ ಎಥಿರಿಯಮ್ ಕಾರ್ಯಕ್ರಮಗಳು ನಡೆಯುತ್ತವೆ.** ಸಮುದಾಯದ ಇನ್ನಷ್ಟು ಜನರನ್ನು ಭೇಟಿಯಾಗಲು, ಉದ್ಯೋಗಾವಕಾಶಗಳ ಬಗ್ಗೆ ತಿಳಿಯಲು, ಮತ್ತು ಹೊಸ ಕೌಶಲ್ಯಗಳನ್ನು ಅಭಿವೃದ್ದಿ ಪಡಿಸಲು ನಿಮ್ಮ ಬಳಿ ಇರುವ ಒಂದು ಕಾರ್ಯಕ್ರಮದಲ್ಲಿ ಪಾಲ್ಗೊಳ್ಳುವುದನ್ನು ಪರಿಗಣಿಸಿ. - - - -ಇದು ನಮ್ಮ ಸಮುದಾಯದವರಿಂದ ನಿರ್ವಹಿಸಲಾದ ಸಮಗ್ರ ಪಟ್ಟಿ ಇಲ್ಲ. ಈ ಪಟ್ಟಿಗೆ ಸೇರಿಸಲು ಸಮೀಪದ ಎಥಿರಿಯಮ್ ಈವೆಂಟ್ ಬಗ್ಗೆ ತಿಳಿದಿರುವಿರಾ? [ದಯವಿಟ್ಟು ಅದನ್ನು ಸೇರಿಸಿ](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## ಎಥೆರಿಯಮ್ ಸಭೆಗಳು {#meetups} - -ನಿಮಗಾಗಿ ಸೂಕ್ತವಾದ ಯಾವುದೇ ಈವೆಂಟ್ ಕಾಣಿಸುತ್ತಿಲ್ಲವೆ? ಮೀಟಪ್‌ನಲ್ಲಿ ಸೇರಲು ಪ್ರಯತ್ನಿಸಿ. ಮೀಟಪ್‌ಗಳು ಎಥಿರಿಯಮ್ ಆಸ್ತಿ ವಿರೋಧಿಗಳ ಗುಂಪುಗಳಿಂದ ನಡೆಸುವ ಸಣ್ಣ ಕಾರ್ಯಕ್ರಮಗಳಾಗಿವೆ - ಎಥಿರಿಯಮ್‌ನಲ್ಲಿ ಆಸಕ್ತಿಯುಳ್ಳ ಜನರು ಒಟ್ಟಿಗೆ ಸೇರಲು, ಎಥಿರಿಯಮ್ ಬಗ್ಗೆ ಮಾತನಾಡಲು, ಮತ್ತು ಇತ್ತೀಚಿನ ಬೆಳವಣಿಗೆಗಳ ಬಗ್ಗೆ ತಿಳಿಯಲು ಒದಗಿಸಿದ ಅವಕಾಶ. - - - -ನಿಮ್ಮ ಸ್ವಂತ ಭೇಟಿಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಆಸಕ್ತಿ ಇದೆಯೇ? ಎಥಿರಿಯಮ್‌ ಮೀಟಪ್ ಸಮುದಾಯಗಳಿಗೆ ಬೆಂಬಲ ನೀಡಲು ConsenSys ನಿಂದ ಕೈಗೊಂಡ ಒಂದು ಯೋಜನೆ, [BUIDL ನೆಟ್‌ವರ್ಕ್‌](https://consensys.net/developers/buidlnetwork/) ಅನ್ನು ಪರಿಶೀಲಿಸಿ. - -ಇದು ನಮ್ಮ ಸಮುದಾಯವು ನಿರ್ಮಿಸಿದ ಅಪೂರ್ಣವಾದ ಪಟ್ಟಿಯಾಗಿದೆ. ನಿಮಗೆ [ಇನ್ನಷ್ಟು ಎಥಿರಿಯಮ್‌ ಮೀಟಪ್‌ಗಳನ್ನು ಇಲ್ಲಿ ಸಿಗಬಹುದು](https://www.meetup.com/topics/ethereum/). ಈ ಪಟ್ಟಿಗೆ ಸೇರಿಸಲು ಕ್ರಿಯಾಶೀಲ ಮೀಟಪ್‌ ಗುಂಪನ್ನು ತಿಳಿದಿರುವಿರಾ? [ದಯವಿಟ್ಟು ಅದನ್ನು ಸೇರಿಸಿ](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/ms/community/events/index.md b/public/content/translations/ms/community/events/index.md deleted file mode 100644 index aaeb51069e6..00000000000 --- a/public/content/translations/ms/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Acara Ethereum -description: Bagaimana untuk terlibat dalam komuniti Ethereum. -lang: ms -hideEditButton: true ---- - -# Acara akan datang {#events} - -** Setiap bulan, terdapat acara Ethereum utama di seluruh dunia. ** Cuba hadiri acara berhampiran anda untuk bertemu lebih ramai orang dalam komuniti ini, mengetahui tentang peluang pekerjaan dan membangunkan kemahiran baru. - - - -Ini merupakan senarai tidak menyeluruh yang dikekalkan oleh masyarakat kita. Ketahui acara Ethereum akan datang untuk ditambah ke dalam senarai ini? [Sila tambah](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Perjumpaan Ethereum {#meetups} - -Tidak melihat acara yang sesuai untuk anda? Cuba sertai perjumpaan. Perjumpaan ialah acara lebih kecil yang diadakan oleh kumpulan peminat Ethereum - peluang bagi orang yang berminat terhadap Ethereum untuk berkumpul, berbual tentang Ethereum dan mengikuti perkembangan terkini. - - - -Berminat untuk memulakan perjumpaan anda sendiri? Semak [Rangkaian BUIDL](https://consensys.net/developers/buidlnetwork/), inisiatif oleh ConsenSys untuk membantu menyokong komuniti perjumpaan Ethereum. - -Ini merupakan senarai tidak menyeluruh yang dibina oleh komuniti kita. Anda boleh [mencari lebih banyak perjumpaan Ethereum di sini ](https://www.meetup.com/topics/ethereum/). Tahu kumpulan perjumpaan aktif untuk ditambahkan pada senarai ini? [Sila tambah](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/nl/community/events/index.md b/public/content/translations/nl/community/events/index.md deleted file mode 100644 index 28b92e715af..00000000000 --- a/public/content/translations/nl/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Ethereum-evenementen -description: Hoe betrokken raken bij de Ethereum-gemeenschap. -lang: nl -hideEditButton: true ---- - -# Aankomende evenementen {#events} - -**Elke maand zijn er grote Ethereum-evenementen over de hele wereld.** Overweeg om er in de buurt een bij te wonen om meer mensen in de gemeenschap te ontmoeten, te leren over kansen op werk en nieuwe vaardigheden te ontwikkelen. - - - -Dit is een niet-uitputtende lijst die onze gemeenschap aanhoudt. Weet u van een aankomend Ethereum-evenement om toe te voegen aan deze lijst? [Voeg het toe](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Ethereum-bijeenkomsten {#meetups} - -Ziet u geen evenement dat u leuk vindt? Probeer dan aan een vergadering deel te nemen. Vergaderingen zijn kleinere evenementen die worden gehouden door groepen Ethereum-enthousiastelingen - een kans voor mensen die geïnteresseerd zijn in Ethereum om samen te komen, te praten over Ethereum en meer te weten te komen over recente ontwikkelingen. - - - -Geïnteresseerd in het starten van uw eigen vergadering? Bekijk de [BUIDL Network](https://consensys.net/developers/buidlnetwork/), een initiatief van ConsenSys om de vergaderingsgemeenschappen van Ethereum te ondersteunen. - -Dit is een niet-uitputtende lijst die door onze gemeenschap is opgesteld. Hier vindt u [meer Ethereum-vergaderingen](https://www.meetup.com/topics/ethereum/). Weet u van een actieve vergaderingsgroep om toe te voegen aan deze lijst? [Voeg het toe](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/pl/community/events/index.md b/public/content/translations/pl/community/events/index.md deleted file mode 100644 index 7e6be0d37dd..00000000000 --- a/public/content/translations/pl/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Wydarzenia -description: Jak zostać częścią społeczności Ethereum. -lang: pl -hideEditButton: true ---- - -# Nadchodzące wydarzenia {#events} - -**Co miesiąc na całym świecie odbywają się różne wydarzenia Ethereum.** Weź pod uwagę możliwość wzięcia udziału w jednym z nich niedaleko Ciebie, poznaj różne ścieżki kariery i rozwijaj swoje umiejętności. - - - -Ta lista to dopiero początek tego, co szykuje nasza społeczność. Jeśli wiesz o wydarzeniu, które jeszcze się na niej nie znajduje, możesz dodać je sam/-a [tutaj](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Spotkania Ethereum Meetups {#meetups} - -Nie możesz znaleźć wydarzenia, które Cię interesuje? Spróbuj dołączyć do spotkania Ethereum Meetups! Ethereum Meetups to mniejsze wydarzenia organizowane przez entuzjastów sieci Ethereum — szansa dla ludzi zainteresowanych tą siecią, żeby spotkać się, porozmawiać i dowiedzieć się więcej o najnowszych rozwiązaniach technologicznych. - - - -Jesteś zainteresowany zorganizowaniem Twojego własnego Ethereum Meetup? Sprawdź [BUIDL Network](https://consensys.net/developers/buidlnetwork/), stworzony przez ConsenSys, aby jeszcze bardziej wspierać społeczność biorącą udział w spotkaniach Ethereum Meetups. - -Poniższa lista nie wyczerpuje wszystkich spotkań organizowanych w społeczności Ethereum. Więcej spotkań Ethereum Meetups znajdziesz [tutaj](https://www.meetup.com/topics/ethereum/). Wiesz o innej aktywnej grupie lub społeczności, której nie ma na tej liście? Dodaj ją [tutaj](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/pt-br/community/events/index.md b/public/content/translations/pt-br/community/events/index.md deleted file mode 100644 index 0f27e452c9b..00000000000 --- a/public/content/translations/pt-br/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Eventos Ethereum -description: Como fazer parte da comunidade Ethereum. -lang: pt-br -hideEditButton: true ---- - -# Próximos eventos {#events} - -**Todos os meses há grandes eventos da Ethereum ao redor do mundo.** Considere participar de um perto de você para conhecer pessoas que fazem parte da comunidade, aprender sobre oportunidades de emprego e desenvolver novas habilidades. - - - -Essa é uma lista não exaustiva mantida pela nossa comunidade. Conhece um evento por vir da Ethereum? [Adicione-o à lista](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Encontros do Ethereum {#meetups} - -Nenhum dos eventos serve para você? Tente participar de um encontro. Os encontros são eventos menores realizados por grupos de entusiastas da Ethereum e são uma oportunidade para pessoas interessadas em Ethereum se reunirem, conversarem sobre ela e saber mais sobre os desenvolvimentos recentes. - - - -Interessado em organizar o seu próprio encontro? Confira a [BUIDL Network](https://consensys.net/developers/buidlnetwork/), uma iniciativa da ConsenSys para oferecer suporte às comunidades de encontros da Ethereum. - -Essa é uma lista não exaustiva mantida pela nossa comunidade. Você pode [ver mais encontros da Ethereum aqui](https://www.meetup.com/topics/ethereum/). Conhece algum grupo ativo de encontros para adicionar à lista? [Adicione-o à lista](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/pt-br/events/index.md b/public/content/translations/pt-br/events/index.md deleted file mode 100644 index bb121f1016d..00000000000 --- a/public/content/translations/pt-br/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Eventos Ethereum -description: Como fazer parte da comunidade Ethereum. -lang: pt-br -hideEditButton: true ---- - -# Próximos eventos {#events} - -**Todos os meses há grandes eventos da Ethereum ao redor do mundo.** Considere participar de um perto de você para conhecer pessoas que fazem parte da comunidade, aprender sobre oportunidades de emprego e desenvolver novas habilidades. - - - -Essa é uma lista não exaustiva mantida pela nossa comunidade. Conhece um evento por vir da Ethereum? [Adicione-o à lista](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Encontros do Ethereum {#meetups} - -Nenhum dos eventos serve para você? Tente ir a um encontro. Os encontros são eventos menores realizados por grupos de entusiastas da Ethereum. e uma oportunidade para pessoas com um interesse em comum em Ethereum se reunirem, conversarem sobre o projeto e conhecer mais sobre os desenvolvimentos recentes. - - - -Interessado em organizar o seu próprio encontro? Confira a [BUIDL Network](https://consensys.net/developers/buidlnetwork/), uma iniciativa da ConsenSys para oferecer suporte às comunidades de encontros da Ethereum. - -Essa é uma lista não exaustiva mantida pela nossa comunidade. Você pode [ver mais encontros da Ethereum aqui](https://www.meetup.com/topics/ethereum/). Conhece algum grupo ativo de encontros? [Adicione-o a lista](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/ro/community/events/index.md b/public/content/translations/ro/community/events/index.md deleted file mode 100644 index cdcfe13c902..00000000000 --- a/public/content/translations/ro/community/events/index.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Evenimente Ethereum -description: Cum să vă implicați în comunitatea Ethereum. -lang: ro ---- - -# Evenimente apropiate {#events} - -**În fiecare lună au loc evenimenteEthereum majore în întreaga lume.** Gândiți-vă să participați la unul ce se desfășoară în apropiere, pentru a vă întâlni cu mai multe persoane din comunitate, pentru a afla despre oportunități de angajare și pentru a vă dezvolta noi competențe. - - - -Aceasta este o listă neexhaustivă întreținută de comunitatea noastră. Cunoașteți un eveniment Ethereum apropiat pe care să-l adăugați la această listă? [Vă rugăm să-l adăugați](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Întruniri Ethereum {#meetups} - -Nu vedeți un eveniment care să vă convină? Încercați să vă alăturați unei întruniri. Întâlnirile sunt evenimente mai mici organizate de grupuri de entuziaști din Ethereum - o șansă pentru persoanele interesate de Ethereum de a se întâlni, de a vorbi despre Ethereum și de a afla despre evoluțiile recente. - - - -Sunteți interesat să vă organizați propria întrunire? Consultați [Rețeaua BUIDL](https://consensys.net/developers/buidlnetwork/), o inițiativă a ConsenSys pentru a sprijini comunitățile de întruniri din Ethereum. - -Aceasta este o listă neexhaustivă întreținută de comunitatea noastră. Puteți [găsi mai multe întruniri Ethereum aici](https://www.meetup.com/topics/ethereum/). Cunoașteți un grup de întrunire activ pe care să-l adăugați în această listă? [Vă rugăm să-l adăugați](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/ru/community/events/index.md b/public/content/translations/ru/community/events/index.md deleted file mode 100644 index 14dfc31894b..00000000000 --- a/public/content/translations/ru/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Мероприятия Ethereum -description: Как участвовать в жизни сообщества Ethereum. -lang: ru -hideEditButton: true ---- - -# Предстоящие мероприятия {#events} - -**Каждый месяц по всему миру проводятся крупные мероприятия Ethereum.** Посетите одно из ближайших к вам, чтобы познакомиться с другими участниками сообщества, узнать о возможностях трудоустройства и развить новые навыки. - - - -Это неполный список мероприятий, поддерживаемых нашим сообществом. Знаете о предстоящем мероприятии Ethereum, которое можно добавить в этот список? [Добавьте его](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Встречи Ethereum {#meetups} - -Не нашли подходящего для себя мероприятия? Попробуйте посетить встречу. Встречи — это небольшие мероприятия, которые проводят группы энтузиастов Ethereum. Это возможность для людей, интересующихся Ethereum, собраться вместе, обсудить Ethereum и узнать о недавних разработках. - - - -Заинтересованы в организации собственной встречи? Ознакомьтесь с [BUIDL Network](https://consensys.net/developers/buidlnetwork/) — инициативой ConsenSys для помощи сообществам, организующим встречи Ethereum. - -Это неполный список мероприятий, созданных нашим сообществом. Вы можете [найти больше встреч Ethereum здесь](https://www.meetup.com/topics/ethereum/). Знаете активную группу, организующую встречи, которую можно добавить в этот список? [Добавьте ее](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/sw/community/events/index.md b/public/content/translations/sw/community/events/index.md deleted file mode 100644 index 4cfd03e9b23..00000000000 --- a/public/content/translations/sw/community/events/index.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Matukio ya Ethereum -description: Jinsi ya kushiriki kwenye jamii ya Ethereum. -lang: sw ---- - -# Matukio yajayo {#events} - -**Kila mwezi, kuna matukio muhimu ulimwenguni kote.** Fikiria kuhusu kushiriki mkutano mmoja ulio jirani na wewe ukutane na watu wengi walio kwenye jamii, jifunze juu ya kupata fursa za kazi na jenga ujuzi mpya. - - - -Hii ni orodha isyo kamili inayohifadhiwa na jamii yetu. Unajua mkutano ujao wowote wa Ethereum wa kuongeze kwenye orodha hii? [Tafadhali uongeze](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Mikutano ya Ethereum {#meetups} - -Hauoni mkutano unaokufaa? Jaribu kuingia kwenye mkutano. Mikutano hii ni midigo inayoandaliwa na wapenzi wa Ethereum - hii ni nafasi ya watu wanaovutiwa na Ethereum kukutana, kujadili kuhusu Ethereum, na kujifunza maendeleo ya hivi karibuni. - - - -Unataka kuanzisha mkutano wako mwenyewe? Tupa jicho kwenye [Mtandao wa BUIDL](https://consensys.net/developers/buidlnetwork/), ambao ni mpango wa ConsenSys unaosaidia mikutano yote ya kijamii ndani ya Ethereum. - -Hii ni orodha isiyo kamili inayohifadhiwa na jamii yetu. Unaweza [kupata mikutano zaidi hapa](https://www.meetup.com/topics/ethereum/). Unajua mkutano wowote unaoendelea wa kuongeza kwenye orodha? [Tafadhali uongeze hapa](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/tr/community/events/index.md b/public/content/translations/tr/community/events/index.md deleted file mode 100644 index 509f0f2e00f..00000000000 --- a/public/content/translations/tr/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Ethereum etkinlikleri -description: Ethereum topluluğuna nasıl dahil olunur? -lang: tr -hideEditButton: true ---- - -# Yaklaşan etkinlikler {#events} - -**Her ay dünya çapında büyük Ethereum etkinlikleri olur.** Toplulukta daha fazla insanla tanışmak, istihdam fırsatları hakkında bilgi edinmek ve yeni beceriler geliştirmek için bunlardan birine katılmayı düşünebilirsiniz. - - - -Bu, topluluğumuz tarafından sağlanan kapsamlı olmayan bir listedir. Bu listeye eklenecek yaklaşan bir Ethereum etkinliğini biliyor musunuz? [Lütfen ekleyin](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## Ethereum buluşmaları {#meetups} - -İşinize yarayan bir etkinlik görmüyor musunuz? Bir buluşmaya katılmayı deneyin. Buluşmalar, Ethereum meraklılarından oluşan gruplar tarafından düzenlenen daha küçük etkinliklerdir. Ethereum ile ilgilenen kişilerin bir araya gelme, Ethereum hakkında konuşma ve son gelişmeler hakkında bilgi edinme şansı olur. - - - -Kendi buluşmanızı mı başlatmak istiyorsunuz? Ethereum'un buluşma topluluklarını desteklemeye yardımcı olmak için ConsenSys'in bir girişimi olan [BUIDL Network](https://consensys.net/developers/buidlnetwork/)'e göz atın. - -Bu, topluluğumuz tarafından sağlanan kapsamlı olmayan bir listedir. [Burada daha fazla Ethereum buluşması bulabilirsiniz](https://www.meetup.com/topics/ethereum/). Bu listeye eklenecek aktif bir buluşma grubu biliyor musunuz? [Lütfen ekleyin](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/tr/events/index.md b/public/content/translations/tr/events/index.md deleted file mode 100644 index a52881a7e73..00000000000 --- a/public/content/translations/tr/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Ethereum etkinlikleri -description: Ethereum topluluğuna nasıl dahil olunur? -lang: tr -hideEditButton: true ---- - -# Yaklaşan etkinlikler {#events} - -**Her ay dünya çapında büyük Ethereum etkinlikleri olur.** Toplulukta daha fazla insanla tanışmak, istihdam fırsatları hakkında bilgi edinmek ve yeni beceriler geliştirmek için bunlardan birine katılmayı düşünebilirsiniz. - - - -Bu, topluluğumuz tarafından sağlanan kapsamlı olmayan bir listedir. Bu listeye eklenecek yaklaşan bir Ethereum etkinliğini biliyor musunuz? [Lütfen ekleyin](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Ethereum buluşmaları {#meetups} - -İşinize yarayan bir etkinlik görmüyor musunuz? Bir buluşmaya katılmayı deneyin. Buluşmalar, Ethereum meraklılarından oluşan gruplar tarafından düzenlenen daha küçük etkinliklerdir. Ethereum ile ilgilenen kişilerin bir araya gelme, Ethereum hakkında konuşma ve son gelişmeler hakkında bilgi edinme şansı olur. - - - -Kendi buluşmanızı mı başlatmak istiyorsunuz? Ethereum'un buluşma topluluklarını desteklemeye yardımcı olmak için ConsenSys'in bir girişimi olan [BUIDL Network](https://consensys.net/developers/buidlnetwork/)'e göz atın. - -Bu, topluluğumuz tarafından sağlanan kapsamlı olmayan bir listedir. [Burada daha fazla Ethereum buluşması bulabilirsiniz](https://www.meetup.com/topics/ethereum/). Bu listeye eklenecek aktif bir buluşma grubu biliyor musunuz? [Lütfen ekleyin](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/uk/community/events/index.md b/public/content/translations/uk/community/events/index.md deleted file mode 100644 index 690dd543a32..00000000000 --- a/public/content/translations/uk/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Події спільноти Ethereum -description: Як брати участь у житті спільноти Ethereum. -lang: uk -hideEditButton: true ---- - -# Майбутні події {#events} - -**Щомісяця в усьому світі відбуваються значущі заходи спільноти Ethereum.** Подумайте про відвідування такої події поруч із вами, щоб зустріти більше людей зі спільноти, дізнатися про можливості працевлаштування та отримати нові навички. - - - -Це неповний список заходів, які підтримуються нашою спільнотою. Вам відомо про майбутню подію спільноти Ethereum, яку можна додати до цього переліку? [Додайте її](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## Зустрічі Ethereum {#meetups} - -Не можете знайти подію, яка б вам сподобалась? Спробуйте відвідати зустріч. Зустрічі — це невеликі заходи, які проводять групи ентузіастів Ethereum. Це шанс для людей, які цікавляться Ethereum, зустрітися, поговорити про Ethereum і ознайомитися з найновішими розробками. - - - -Хочете розпочати власну зустріч? Ознайомтеся з [мережею BUIDL](https://consensys.net/developers/buidlnetwork/) — ініціативою ConsenSys для підтримки спільнот, які організовують зустрічі в Ethereum. - -Це невичерпний список, створений нашою спільнотою. Ви можете [знайти більше зустрічей Ethereum тут](https://www.meetup.com/topics/ethereum/). Знаєте активну групу, що організує зустрічі, яку можна додати до цього списку? [Додайте її](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/zh-tw/community/events/index.md b/public/content/translations/zh-tw/community/events/index.md deleted file mode 100644 index 064c060c99c..00000000000 --- a/public/content/translations/zh-tw/community/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: 以太坊活動 -description: 如何加入以太坊社群。 -lang: zh-tw -hideEditButton: true ---- - -# 即將開始的活動 {#events} - -**每個月,世界各地都會舉辦重大的以太坊活動。**考慮參加你附近的活動,結識更多社群成員,了解就業機會,並培養新技能。 - - - -這是一個由我們社群所維護的部分活動清單。 若你知曉其他未於此清單列出的以太坊活動, [請幫忙新增](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## 以太坊聚會 {#meetups} - -沒看到適合你的活動嗎? 試試看加入以太坊聚會。 聚會是由以太坊愛好者團體所舉辦的小型活動 - 是對以太坊感興趣的同好們聚在一起的機會,一起談論並了解以太坊的各項進展。 - - - -有興趣舉辦自己的聚會嗎? 請查看 [BUIDL Network](https://consensys.net/developers/buidlnetwork/),這是個由 ConsenSys 為了幫助以太坊社群聚會而提出的提案。 - -這是一個由我們社群所維護的部分活動清單, 你可以從[這裡找到更多以太坊聚會。](https://www.meetup.com/topics/ethereum/) 若你知曉其他未於此清單列出的聚會, [請幫忙加入!](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json) diff --git a/public/content/translations/zh/community/events/index.md b/public/content/translations/zh/community/events/index.md deleted file mode 100644 index 783d50e7e6e..00000000000 --- a/public/content/translations/zh/community/events/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: 以太坊活动 -description: 如何加入以太坊社区。 -lang: zh -hideEditButton: true ---- - -# 活动预告 {#events} - -**每个月,世界各地都会举办大型以太坊活动。**你可以考虑参加附近的一个活动,认识更多的社区成员,了解就业机会,并培养新的技能。 - - - -这是一个由我们社区维护的不完整列表。 想要将了解到的即将举行的以太坊活动添加到此列表? [请添加](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - - - -## 以太坊聚会 {#meetups} - -找不到适合你的活动? 尝试加入一个聚会。 聚会是指由各个以太坊爱好者团体举办的小型活动——这是对以太坊感兴趣的人们聚集在一起的机会,他们可以一起谈论并了解以太坊的最新发展。 - - - -有兴趣举办自己的聚会? 查看 [BUIDL 网络](https://consensys.net/developers/buidlnetwork/),这是由 ConsenSys 发起的一项举措,旨在帮助支持以太坊的各个聚会社区。 - -这是一个由我们社区制定的不完整列表。 你可以[在此处查找更多以太坊聚会](https://www.meetup.com/topics/ethereum/)。 想要将了解到的活跃聚会小组添加到此列表? [请添加](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/content/translations/zh/events/index.md b/public/content/translations/zh/events/index.md deleted file mode 100644 index 79260f8b713..00000000000 --- a/public/content/translations/zh/events/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: 以太坊活动 -description: 如何加入以太坊社区。 -lang: zh -hideEditButton: true ---- - -# 活动预告 {#events} - -**每个月,世界各地都会举办大型以太坊活动。**你可以考虑参加附近的一个活动,认识更多的社区成员,了解就业机会,并培养新的技能。 - - - -这是一个由我们社区维护的不完整列表。 想要将了解到的即将举行的以太坊活动添加到此列表? [请添加](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-events.json)! - -## 以太坊聚会 {#meetups} - -找不到适合你的活动? 尝试加入一个聚会。 聚会是指由各个以太坊爱好者团体举办的小型活动——这是对以太坊感兴趣的人们聚集在一起的机会,他们可以一起谈论并了解以太坊的最新发展。 - - - -有兴趣举办自己的聚会? 查看 [BUIDL 网络](https://consensys.net/developers/buidlnetwork/),这是由 ConsenSys 发起的一项举措,旨在帮助支持以太坊的各个聚会社区。 - -这是一个由我们社区制定的不完整列表。 你可以[在此处查找更多以太坊聚会](https://www.meetup.com/topics/ethereum/)。 想要将了解到的活跃聚会小组添加到此列表? [请添加](https://github.com/ethereum/ethereum-org-website/blob/dev/src/data/community-meetups.json)! diff --git a/public/images/community/ethereum-everywhere-logo.png b/public/images/community/ethereum-everywhere-logo.png new file mode 100644 index 00000000000..3483cae84fe Binary files /dev/null and b/public/images/community/ethereum-everywhere-logo.png differ diff --git a/public/images/community/geode-labs-logo.png b/public/images/community/geode-labs-logo.png new file mode 100644 index 00000000000..33ed376ab0d Binary files /dev/null and b/public/images/community/geode-labs-logo.png differ diff --git a/public/images/community/hubs/berlin-hub-banner.png b/public/images/community/hubs/berlin-hub-banner.png index c84f36d33d0..bbdc11bb42b 100644 Binary files a/public/images/community/hubs/berlin-hub-banner.png and b/public/images/community/hubs/berlin-hub-banner.png differ diff --git a/public/images/community/hubs/dubai-hub-banner.png b/public/images/community/hubs/dubai-hub-banner.png index 74c9a659e64..9ef9a7cf479 100644 Binary files a/public/images/community/hubs/dubai-hub-banner.png and b/public/images/community/hubs/dubai-hub-banner.png differ diff --git a/public/images/community/hubs/lagos-hub-banner.png b/public/images/community/hubs/lagos-hub-banner.png new file mode 100644 index 00000000000..74cdc4064ce Binary files /dev/null and b/public/images/community/hubs/lagos-hub-banner.png differ diff --git a/public/images/community/hubs/london-hub-banner.png b/public/images/community/hubs/london-hub-banner.png index d9525ff906e..79c43998fa9 100644 Binary files a/public/images/community/hubs/london-hub-banner.png and b/public/images/community/hubs/london-hub-banner.png differ diff --git a/public/images/community/hubs/sf-hub-banner.png b/public/images/community/hubs/sf-hub-banner.png new file mode 100644 index 00000000000..080f3d17784 Binary files /dev/null and b/public/images/community/hubs/sf-hub-banner.png differ diff --git a/public/images/layer-2/zircuit.png b/public/images/layer-2/zircuit.png new file mode 100644 index 00000000000..2af1dc2fab6 Binary files /dev/null and b/public/images/layer-2/zircuit.png differ diff --git a/public/images/meetups/crypto-wednesdays.jpg b/public/images/meetups/crypto-wednesdays.jpg new file mode 100644 index 00000000000..76cc5e7897e Binary files /dev/null and b/public/images/meetups/crypto-wednesdays.jpg differ diff --git a/public/images/meetups/eth-belgrade-community.jpg b/public/images/meetups/eth-belgrade-community.jpg new file mode 100644 index 00000000000..fffaa5c78b5 Binary files /dev/null and b/public/images/meetups/eth-belgrade-community.jpg differ diff --git a/public/images/meetups/eth-sd.png b/public/images/meetups/eth-sd.png new file mode 100644 index 00000000000..347aab22e3a Binary files /dev/null and b/public/images/meetups/eth-sd.png differ diff --git a/public/images/meetups/ethereum-italia-hub-telegram-group.jpg b/public/images/meetups/ethereum-italia-hub-telegram-group.jpg new file mode 100644 index 00000000000..a662de5b6ef Binary files /dev/null and b/public/images/meetups/ethereum-italia-hub-telegram-group.jpg differ diff --git a/public/images/meetups/ethereum-japan.png b/public/images/meetups/ethereum-japan.png new file mode 100644 index 00000000000..0fe42507c86 Binary files /dev/null and b/public/images/meetups/ethereum-japan.png differ diff --git a/public/images/meetups/ethereum-venezia-meetup.jpg b/public/images/meetups/ethereum-venezia-meetup.jpg new file mode 100644 index 00000000000..8352fa80b20 Binary files /dev/null and b/public/images/meetups/ethereum-venezia-meetup.jpg differ diff --git a/public/images/meetups/ethtbilisi.png b/public/images/meetups/ethtbilisi.png new file mode 100644 index 00000000000..a4c7728b15c Binary files /dev/null and b/public/images/meetups/ethtbilisi.png differ diff --git a/public/images/meetups/kyiv-ethereum.png b/public/images/meetups/kyiv-ethereum.png new file mode 100644 index 00000000000..414ce293f57 Binary files /dev/null and b/public/images/meetups/kyiv-ethereum.png differ diff --git a/public/images/meetups/matos-cryptobar.png b/public/images/meetups/matos-cryptobar.png new file mode 100644 index 00000000000..8cff4e1777c Binary files /dev/null and b/public/images/meetups/matos-cryptobar.png differ diff --git a/public/images/wallets/pillar.png b/public/images/wallets/pillar.png deleted file mode 100644 index 1b46e2f1eac..00000000000 Binary files a/public/images/wallets/pillar.png and /dev/null differ diff --git a/public/images/wallets/pillarx.png b/public/images/wallets/pillarx.png new file mode 100644 index 00000000000..3ce5ee1d486 Binary files /dev/null and b/public/images/wallets/pillarx.png differ diff --git a/src/components/CommunityHubsList.tsx b/src/components/CommunityHubsList.tsx deleted file mode 100644 index ddd87a25e93..00000000000 --- a/src/components/CommunityHubsList.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { getTranslations } from "next-intl/server" - -import { Image } from "@/components/Image" -import { ButtonLink } from "@/components/ui/buttons/Button" - -import communityHubs from "@/data/community-hubs" - -const CommunityHubsList = async () => { - const t = await getTranslations({ - namespace: "page-community", - }) - return ( -
- {communityHubs.map((hub) => ( -
- {hub.location} -
-
-

- {t("page-community-community-hub-list-h3")} -

- {hub.location} -
-
-

{hub.description}

-

{hub.cta}

-
-
- - {t("page-community-community-hub-list-cta-label-1")} - - - {t("page-community-community-hub-list-cta-label-2")} - -
-
-
- ))} -
- ) -} - -export default CommunityHubsList diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx deleted file mode 100644 index f1e890f02b9..00000000000 --- a/src/components/EventCard.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from "react" -import { CalendarDays } from "lucide-react" -import { useLocale } from "next-intl" - -import type { EventCardProps } from "@/lib/types" - -import { ButtonLink } from "@/components/ui/buttons/Button" -import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card" - -import { cn } from "@/lib/utils/cn" - -import ImageClientSide from "./Image/CardImage" -import { Image } from "./Image" - -import { useTranslation } from "@/hooks/useTranslation" -import EventFallback from "@/public/images/events/event-placeholder.png" - -const EventCard: React.FC = ({ - title, - href, - description, - className, - location, - imageUrl, - endDate, - startDate, -}) => { - const locale = useLocale() - const { t } = useTranslation("page-community") - - const formattedDate = new Intl.DateTimeFormat(locale, { - day: "2-digit", - month: "short", - }).formatRange( - new Date(startDate?.replace(/-/g, "/")), - new Date(endDate?.replace(/-/g, "/")) - ) - - return ( - - - - - {formattedDate} - - -
- {imageUrl ? ( - - ) : ( - - )} -
- -
-

{title}

- {location} -
-

- {description} -

-
- - - {t("page-community-upcoming-events-view-event")} - - -
- ) -} - -export default EventCard diff --git a/src/components/Hero/ContentHero/index.tsx b/src/components/Hero/ContentHero/index.tsx index bd12a953b95..479753904ba 100644 --- a/src/components/Hero/ContentHero/index.tsx +++ b/src/components/Hero/ContentHero/index.tsx @@ -5,32 +5,53 @@ import type { CommonHeroProps } from "@/lib/types" import Breadcrumbs from "@/components/Breadcrumbs" import { Image } from "@/components/Image" +import { cn } from "@/lib/utils/cn" import { breakpointAsNumber, screens } from "@/lib/utils/screen" import { CallToAction } from "../CallToAction" export type ContentHeroProps = Omit< CommonHeroProps, - "header" | "blurDataURL" + "heroImg" | "header" | "blurDataURL" > & { blurDataURL?: CommonHeroProps["blurDataURL"] + heroImg?: CommonHeroProps["heroImg"] } const ContentHero = (props: ContentHeroProps) => { - const { breadcrumbs, heroImg, buttons, title, description, blurDataURL } = - props - if (blurDataURL) heroImg.blurDataURL = blurDataURL + const { + breadcrumbs, + heroImg, + buttons, + title, + description, + blurDataURL, + className, + } = props + if (blurDataURL && heroImg) heroImg.blurDataURL = blurDataURL return ( -
-
- +
+
+ {heroImg && ( + + )}
diff --git a/src/components/Image/CardImage.tsx b/src/components/Image/CardImage.tsx index 22e7ef4ec71..21d4f8138dc 100644 --- a/src/components/Image/CardImage.tsx +++ b/src/components/Image/CardImage.tsx @@ -9,7 +9,7 @@ type CardImageProps = ComponentProps<"img"> const CardImage = ({ src, className, ...props }: CardImageProps) => ( // eslint-disable-next-line @next/next/no-img-element { diff --git a/src/components/LanguagePicker/index.tsx b/src/components/LanguagePicker/index.tsx index f8f48529843..df7bd80dad1 100644 --- a/src/components/LanguagePicker/index.tsx +++ b/src/components/LanguagePicker/index.tsx @@ -94,7 +94,7 @@ const LanguagePicker = ({ return (
{ className="text-body no-underline" href={hasGitHub ? `${GITHUB_URL}${username}` : "#"} > - {`In place number ${ + {`In place number ${ idx + 1 - } with ${score} points`} + } with ${score} points`} {name}{" "} {hasGitHub && ( - (See Github Profile) + (See Github Profile) )} diff --git a/src/components/MeetupList.tsx b/src/components/MeetupList.tsx deleted file mode 100644 index fa2267d2eef..00000000000 --- a/src/components/MeetupList.tsx +++ /dev/null @@ -1,122 +0,0 @@ -"use client" - -import { useState } from "react" -import { sortBy } from "lodash" -import { ChevronRight } from "lucide-react" - -import Emoji from "@/components/Emoji" -import Translation from "@/components/Translation" - -import { cn } from "@/lib/utils/cn" -import { trackCustomEvent } from "@/lib/utils/matomo" - -import meetups from "@/data/community-meetups.json" - -import { Alert, AlertContent, AlertDescription, AlertEmoji } from "./ui/alert" -import { Flex } from "./ui/flex" -import Input from "./ui/input" -import InlineLink, { BaseLink } from "./ui/Link" - -export interface Meetup { - title: string - emoji: string - location: string - link: string -} - -const filterMeetups = (query: string): Array => { - if (!query) return sortedMeetups - - return sortedMeetups.filter((meetup) => { - const flag = meetup.emoji.replace(/[:_]/g, " ") - const searchable = [meetup.title, meetup.location, flag].join(" ") - return searchable.toLowerCase().includes(query.toLowerCase()) - }) -} - -// sort meetups by country and then by city -const sortedMeetups: Array = sortBy(meetups, ["emoji", "location"]) - -// TODO create generalized CardList / TableCard -// TODO prop if ordered list or unordered -const MeetupList = () => { - const [searchField, setSearchField] = useState("") - const filteredMeetups = filterMeetups(searchField) - - const handleSearch = (event: React.ChangeEvent): void => { - setSearchField(event.target.value) - trackCustomEvent({ - eventCategory: "events search", - eventAction: "click", - eventName: event.target.value, - }) - } - - return ( -
- - {/* hidden for attachment to input only */} - - results update as you type - -
    - {filteredMeetups.map((meetup, idx) => ( - - -
    {idx + 1}
    -
    -

    - {meetup.title} -

    -
    -
    - - -

    {meetup.location}

    -
    - - - -
    - ))} -
-
- {!filteredMeetups.length && ( - - - - - {" "} - - - - - - - )} -
-
- ) -} - -export default MeetupList diff --git a/src/components/Nav/MobileMenu/index.tsx b/src/components/Nav/MobileMenu/index.tsx index 8625b941f46..141e3d2e9cc 100644 --- a/src/components/Nav/MobileMenu/index.tsx +++ b/src/components/Nav/MobileMenu/index.tsx @@ -57,7 +57,7 @@ export default async function MobileMenu({ value="navigation" className="mt-0 hidden min-h-0 flex-1 flex-col border-none p-0 data-[state=active]:flex" > - + { className="ms-0 mt-4 min-h-fit gap-0 self-center rounded-sm px-6 py-1 sm:ms-8 sm:mt-0" > {actionLabel} - to {title} website + to {title} website )} diff --git a/src/components/ProductTable/List.tsx b/src/components/ProductTable/List.tsx index f23bf6b26d9..1c78bb913ab 100644 --- a/src/components/ProductTable/List.tsx +++ b/src/components/ProductTable/List.tsx @@ -114,7 +114,7 @@ const List = ({
- + {subComponent?.(item as T, filters, virtualItem.index)} diff --git a/src/components/UpcomingEventsList.tsx b/src/components/UpcomingEventsList.tsx deleted file mode 100644 index 458e56285e6..00000000000 --- a/src/components/UpcomingEventsList.tsx +++ /dev/null @@ -1,167 +0,0 @@ -"use client" - -import { useEffect, useState } from "react" -import { groupBy } from "lodash" -import { useLocale } from "next-intl" - -import type { CommunityConference, Lang } from "@/lib/types" - -import EventCard from "@/components/EventCard" -import { Button } from "@/components/ui/buttons/Button" -import Link from "@/components/ui/Link" - -import { trackCustomEvent } from "@/lib/utils/matomo" -import { getLocaleTimestamp } from "@/lib/utils/time" - -import communityEvents from "@/data/community-events.json" - -import { Alert, AlertContent, AlertDescription, AlertEmoji } from "./ui/alert" - -import { useTranslation } from "@/hooks/useTranslation" - -const UpcomingEventsList = () => { - const locale = useLocale() - const { t } = useTranslation("page-community") - const monthsPerLoad = 2 - - const [monthGroupedEvents, setMonthGroupedEvents] = useState({}) - const [maxRange, setMaxRange] = useState(monthsPerLoad) - - // Create Date object from each YYYY-MM-DD JSON date string - const dateParse = (dateString: string): Date => { - const parts = dateString.split("-") - return new Date( - parseInt(parts[0]), - parseInt(parts[1]) - 1, - parseInt(parts[2]) - ) - } - - useEffect(() => { - const eventsList = communityEvents as CommunityConference[] - const yesterday = new Date() - yesterday.setDate(yesterday.getDate() - 1) - - // Remove events that have ended - const upcomingEvents = eventsList.filter(({ endDate }) => { - return dateParse(endDate) > yesterday - }) - - // Sort events by start date - const orderedEvents = upcomingEvents.sort( - (a, b) => - dateParse(a.startDate).getTime() - dateParse(b.startDate).getTime() - ) - - // Add formatted string to display - const formattedEvents = orderedEvents.map((event) => { - const getDate = (date) => - getLocaleTimestamp(locale! as Lang, dateParse(date).toString(), {}) - - const dateRange = - event.startDate === event.endDate - ? getDate(event.startDate) - : `${getDate(event.startDate)} - ${getDate(event.endDate)}` - - const details = `${event.description}` - - return { - ...event, - date: dateRange, - formattedDetails: details, - } - }) - const groupedEvents = groupBy(formattedEvents, ({ startDate }) => { - const start = new Date(startDate.replace(/-/g, "/")) - const formatYearMonth = new Intl.DateTimeFormat(locale, { - month: "short", - year: "numeric", - }).format(start) - return `${formatYearMonth}` - }) - - setMonthGroupedEvents(groupedEvents) - }, [locale]) - - const loadMoreEvents = () => { - setMaxRange((counter) => { - if (counter + monthsPerLoad > Object.keys(monthGroupedEvents)?.length) - return Object.keys(monthGroupedEvents)?.length - return counter + monthsPerLoad - }) - trackCustomEvent({ - eventCategory: "more events button", - eventAction: "click", - eventName: "load more", - }) - } - - if (Object.keys(monthGroupedEvents)?.length === 0) { - return ( - - - - - {t("page-community-upcoming-events-no-events")}{" "} - - {t("page-community-please-add-to-page")} - - - - - ) - } - - return ( -
- {Object.keys(monthGroupedEvents) - ?.slice(0, maxRange) - ?.map((month) => { - const events = monthGroupedEvents[month] - return ( -
-

{month}

-
- {events.map( - ( - { - title, - to, - href, - formattedDetails, - location, - imageUrl, - startDate, - endDate, - }, - idx - ) => ( - - ) - )} -
-
- ) - })} - - {Object.keys(monthGroupedEvents)?.length !== maxRange && ( -
- -
- )} -
- ) -} - -export default UpcomingEventsList diff --git a/src/components/icons/discord.svg b/src/components/icons/discord.svg index f9f8c2138e8..8d3dbab5310 100644 --- a/src/components/icons/discord.svg +++ b/src/components/icons/discord.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/icons/telegram.svg b/src/components/icons/telegram.svg new file mode 100644 index 00000000000..1dba73d8ab0 --- /dev/null +++ b/src/components/icons/telegram.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ui/Link.tsx b/src/components/ui/Link.tsx index c3839bd5bb6..19f1d69628e 100644 --- a/src/components/ui/Link.tsx +++ b/src/components/ui/Link.tsx @@ -3,7 +3,6 @@ import { AnchorHTMLAttributes, ComponentProps, forwardRef } from "react" import { ArrowRight, ExternalLink, Mail } from "lucide-react" import NextLink from "next/link" -import { VisuallyHidden } from "@radix-ui/react-visually-hidden" import { MatomoEventOptions } from "@/lib/types" @@ -100,6 +99,8 @@ export const BaseLink = forwardRef(function Link( } if (isExternal) { + const { className, ...rest } = commonProps + return ( (function Link( } ) } - {...commonProps} + className={cn("relative", className)} + {...rest} > {isMailto ? ( @@ -126,9 +128,9 @@ export const BaseLink = forwardRef(function Link( ) : ( children )} - + {isMailto ? "opens email client" : "opens in a new tab"} - + {!hideArrow && !isMailto && } ) diff --git a/src/components/ui/TabNav.tsx b/src/components/ui/TabNav.tsx index f61eabbc1b9..9a2f7a3b6c7 100644 --- a/src/components/ui/TabNav.tsx +++ b/src/components/ui/TabNav.tsx @@ -4,7 +4,11 @@ import { motion } from "framer-motion" import type { MatomoEventOptions, SectionNavDetails } from "@/lib/types" -import { ButtonLink } from "@/components/ui/buttons/Button" +import { + Button, + ButtonLink, + type ButtonVariantProps, +} from "@/components/ui/buttons/Button" import { cn } from "@/lib/utils/cn" @@ -24,6 +28,7 @@ interface TabNavProps { useMotion?: boolean motionLayoutId?: string customEventOptions?: Pick + onSelect?: (key: string) => void } const TabNav = ({ @@ -33,6 +38,7 @@ const TabNav = ({ useMotion = false, motionLayoutId = "active-section-highlight", customEventOptions, + onSelect, }: TabNavProps) => { const activeHash = useActiveHash( sections.map(({ key }) => key), @@ -42,41 +48,44 @@ const TabNav = ({ return (
-