From 15135f02bd0f0046530c53f4761906a3428050ec Mon Sep 17 00:00:00 2001 From: jonulak Date: Wed, 1 Jan 2025 19:46:03 -0500 Subject: [PATCH] Add octokit for github auth & API, add headline to grid card, update project data --- next-env.d.ts | 2 +- package-lock.json | 627 +++++++++++------- package.json | 5 +- ...v.js => fallback-2bdgnhQnpj2fUbEVgg7ti.js} | 0 src/components/vwc-card/index.tsx | 12 +- src/data/projects/vets-ai.json | 27 +- src/data/projects/vets-who-code-app.json | 28 +- src/data/projects/web-curriculum.json | 30 + .../projects/windows-dev-setup-guide.json | 30 + src/lib/github.ts | 59 ++ src/lib/project.ts | 51 +- src/pages/projects.tsx | 19 +- 12 files changed, 561 insertions(+), 329 deletions(-) rename public/{fallback-V6Ee7KjBBk3_9xilHmQYv.js => fallback-2bdgnhQnpj2fUbEVgg7ti.js} (100%) create mode 100644 src/data/projects/web-curriculum.json create mode 100644 src/data/projects/windows-dev-setup-guide.json create mode 100644 src/lib/github.ts diff --git a/next-env.d.ts b/next-env.d.ts index a4a7b3f5c..52e831b43 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/package-lock.json b/package-lock.json index 868e04ee0..645a6494f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,8 @@ "@ai-sdk/azure": "^1.0.7", "@googlemaps/react-wrapper": "^1.1.35", "@googlemaps/typescript-guards": "^2.0.1", - "@radix-ui/react-dialog": "^1.1.4", "@playwright/test": "^1.49.0", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^1.1.0", "@types/express": "^4.17.17", @@ -38,6 +38,7 @@ "next-react-svg": "^1.1.3", "next-seo": "^5.5.0", "next-sitemap": "^3.1.11", + "octokit": "^4.0.3", "react": "^18.3.1", "react-ace": "^11.0.1", "react-dom": "^18.3.1", @@ -3096,6 +3097,322 @@ "node": ">=12.4.0" } }, + "node_modules/@octokit/app": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-15.1.1.tgz", + "integrity": "sha512-fk8xrCSPTJGpyBdBNI+DcZ224dm0aApv4vi6X7/zTmANXlegKV2Td+dJ+fd7APPaPN7R+xttUsj2Fm+AFDSfMQ==", + "dependencies": { + "@octokit/auth-app": "^7.0.0", + "@octokit/auth-unauthenticated": "^6.0.0", + "@octokit/core": "^6.1.2", + "@octokit/oauth-app": "^7.0.0", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/types": "^13.0.0", + "@octokit/webhooks": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-app": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-7.1.3.tgz", + "integrity": "sha512-GZdkOp2kZTIy5dG9oXqvzUAZiPvDx4C/lMlN6yQjtG9d/+hYa7W8WXTJoOrXE8UdfL9A/sZMl206dmtkl9lwVQ==", + "dependencies": { + "@octokit/auth-oauth-app": "^8.1.0", + "@octokit/auth-oauth-user": "^5.1.0", + "@octokit/request": "^9.1.1", + "@octokit/request-error": "^6.1.1", + "@octokit/types": "^13.4.1", + "toad-cache": "^3.7.0", + "universal-github-app-jwt": "^2.2.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-app": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-8.1.1.tgz", + "integrity": "sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==", + "dependencies": { + "@octokit/auth-oauth-device": "^7.0.0", + "@octokit/auth-oauth-user": "^5.0.1", + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-device": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-7.1.1.tgz", + "integrity": "sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==", + "dependencies": { + "@octokit/oauth-methods": "^5.0.0", + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-user": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-5.1.1.tgz", + "integrity": "sha512-rRkMz0ErOppdvEfnemHJXgZ9vTPhBuC6yASeFaB7I2yLMd7QpjfrL1mnvRPlyKo+M6eeLxrKanXJ9Qte29SRsw==", + "dependencies": { + "@octokit/auth-oauth-device": "^7.0.1", + "@octokit/oauth-methods": "^5.0.0", + "@octokit/request": "^9.0.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-unauthenticated": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-6.1.0.tgz", + "integrity": "sha512-zPSmfrUAcspZH/lOFQnVnvjQZsIvmfApQH6GzJrkIunDooU1Su2qt2FfMTSVPRp7WLTQyC20Kd55lF+mIYaohQ==", + "dependencies": { + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz", + "integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==", + "dependencies": { + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.2.tgz", + "integrity": "sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==", + "dependencies": { + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-app": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-7.1.4.tgz", + "integrity": "sha512-Au4zSGsWOZtShLxVUXcZ9TZJVQjpEK/OW2L1SWLE030QVYaZ+69TP4vHBdXakZUifvOELD1VBYEY6eprPcY2Mg==", + "dependencies": { + "@octokit/auth-oauth-app": "^8.0.0", + "@octokit/auth-oauth-user": "^5.0.1", + "@octokit/auth-unauthenticated": "^6.0.0", + "@octokit/core": "^6.1.2", + "@octokit/oauth-authorization-url": "^7.0.0", + "@octokit/oauth-methods": "^5.0.0", + "@types/aws-lambda": "^8.10.83", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-authorization-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-7.1.1.tgz", + "integrity": "sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-methods": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-5.1.3.tgz", + "integrity": "sha512-M+bDBi5H8FnH0xhCTg0m9hvcnppdDnxUqbZyOkxlLblKpLAR+eT2nbDPvJDp0eLrvJWA1I8OX0KHf/sBMQARRA==", + "dependencies": { + "@octokit/oauth-authorization-url": "^7.0.0", + "@octokit/request": "^9.1.4", + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" + }, + "node_modules/@octokit/openapi-webhooks-types": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-8.5.1.tgz", + "integrity": "sha512-i3h1b5zpGSB39ffBbYdSGuAd0NhBAwPyA3QV3LYi/lx4lsbZiu7u2UHgXVUR6EpvOI8REOuVh1DZTRfHoJDvuQ==" + }, + "node_modules/@octokit/plugin-paginate-graphql": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-5.2.4.tgz", + "integrity": "sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.6.tgz", + "integrity": "sha512-zcvqqf/+TicbTCa/Z+3w4eBJcAxCFymtc0UAIsR3dEVoNilWld4oXdscQ3laXamTszUZdusw97K8+DrbFiOwjw==", + "dependencies": { + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.6.tgz", + "integrity": "sha512-wMsdyHMjSfKjGINkdGKki06VEkgdEldIGstIEyGX0wbYHGByOwN/KiM+hAAlUwAtPkP3gvXtVQA9L3ITdV2tVw==", + "dependencies": { + "@octokit/types": "^13.6.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.2.tgz", + "integrity": "sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==", + "dependencies": { + "@octokit/request-error": "^6.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.3.2.tgz", + "integrity": "sha512-FqpvcTpIWFpMMwIeSoypoJXysSAQ3R+ALJhXXSG1HTP3YZOIeLmcNcimKaXxTcws+Sh6yoRl13SJ5r8sXc1Fhw==", + "dependencies": { + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^6.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.4.tgz", + "integrity": "sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==", + "dependencies": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz", + "integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==", + "dependencies": { + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz", + "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==", + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/@octokit/webhooks": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-13.4.1.tgz", + "integrity": "sha512-I5YPUtfWidh+OzyrlDahJsUpkpGK0kCTmDRbuqGmlCUzOtxdEkX3R4d6Cd08ijQYwkVXQJanPdbKuZBeV2NMaA==", + "dependencies": { + "@octokit/openapi-webhooks-types": "8.5.1", + "@octokit/request-error": "^6.0.1", + "@octokit/webhooks-methods": "^5.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/webhooks-methods": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-5.1.0.tgz", + "integrity": "sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ==", + "engines": { + "node": ">= 18" + } + }, "node_modules/@panva/hkdf": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", @@ -3191,41 +3508,6 @@ } } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", - "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", @@ -3439,184 +3721,6 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz", - "integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", - "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", - "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", - "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", - "dependencies": { - "@radix-ui/react-slot": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz", - "integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -5571,6 +5675,11 @@ "node": ">=10.13.0" } }, + "node_modules/@types/aws-lambda": { + "version": "8.10.146", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.146.tgz", + "integrity": "sha512-3BaDXYTh0e6UCJYL/jwV/3+GRslSc08toAiZSmleYtkAUyV5rtvdPYxrG/88uqvTuT6sb27WE9OS90ZNTIuQ0g==" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -7205,6 +7314,11 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -7271,6 +7385,11 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -9535,6 +9654,11 @@ "node >=0.6.0" ] }, + "node_modules/fast-content-type-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.0.tgz", + "integrity": "sha512-fCqg/6Sps8tqk8p+kqyKqYfOF0VjPNYrqpLiqNl0RBKmD80B080AJWVV6EkSkscjToNExcXg1+Mfzftrx6+iSA==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -12889,6 +13013,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/octokit": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-4.0.3.tgz", + "integrity": "sha512-kfqH80Yuuux4fLbZ4SvObjCvVu89U0eCh5+fulh6tr/hJkDYS1inXDGnNJDOp312GANMEhWWMLYmjQR8MvSSMQ==", + "dependencies": { + "@octokit/app": "^15.1.1", + "@octokit/core": "^6.0.0", + "@octokit/oauth-app": "^7.0.0", + "@octokit/plugin-paginate-graphql": "^5.0.0", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-rest-endpoint-methods": "^13.0.0", + "@octokit/plugin-retry": "^7.0.0", + "@octokit/plugin-throttling": "^9.0.0", + "@octokit/request-error": "^6.0.0", + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/oidc-token-hash": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", @@ -13328,17 +13472,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/popmotion": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", - "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", - "dependencies": { - "framesync": "6.0.1", - "hey-listen": "^1.0.8", - "style-value-types": "5.0.0", - "tslib": "^2.1.0" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -13694,32 +13827,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -16000,6 +16107,14 @@ "node": ">=8.0" } }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "engines": { + "node": ">=12" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -16370,6 +16485,16 @@ "node": ">=8" } }, + "node_modules/universal-github-app-jwt": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.0.tgz", + "integrity": "sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ==" + }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", diff --git a/package.json b/package.json index 72db218a1..15d57db0e 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@ai-sdk/azure": "^1.0.7", "@googlemaps/react-wrapper": "^1.1.35", "@googlemaps/typescript-guards": "^2.0.1", + "@playwright/test": "^1.49.0", "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^1.1.0", @@ -46,6 +47,7 @@ "next-react-svg": "^1.1.3", "next-seo": "^5.5.0", "next-sitemap": "^3.1.11", + "octokit": "^4.0.3", "react": "^18.3.1", "react-ace": "^11.0.1", "react-dom": "^18.3.1", @@ -53,8 +55,7 @@ "react-simple-typewriter": "^3.0.1", "swiper": "^8.3.1", "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7", - "@playwright/test": "^1.49.0" + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@fullhuman/postcss-purgecss": "^4.1.3", diff --git a/public/fallback-V6Ee7KjBBk3_9xilHmQYv.js b/public/fallback-2bdgnhQnpj2fUbEVgg7ti.js similarity index 100% rename from public/fallback-V6Ee7KjBBk3_9xilHmQYv.js rename to public/fallback-2bdgnhQnpj2fUbEVgg7ti.js diff --git a/src/components/vwc-card/index.tsx b/src/components/vwc-card/index.tsx index 37bc772b2..7112b626b 100644 --- a/src/components/vwc-card/index.tsx +++ b/src/components/vwc-card/index.tsx @@ -8,10 +8,11 @@ type TProps = { className?: string; thumbnail: ImageType; title: string; + headline?: string; }; export const VWCGridCard = forwardRef( - ({ className, thumbnail, title }, ref) => { + ({ className, thumbnail, title, headline }, ref) => { return ( ( )}
-

-
{title}
-

+

{title}

+ {headline && ( +
+ {headline} +
+ )}
); diff --git a/src/data/projects/vets-ai.json b/src/data/projects/vets-ai.json index ba72227dd..4502f6fed 100644 --- a/src/data/projects/vets-ai.json +++ b/src/data/projects/vets-ai.json @@ -1,17 +1,26 @@ { - "index": 1, + "index": 2, "name": "VetsAI", - "headline": "AI for veterans", - "long_description": [ - "Qui incididunt commodo aliqua magna laborum. Veniam laboris cillum occaecat voluptate minim minim. Qui culpa voluptate aliquip mollit ea veniam deserunt duis. Nisi voluptate sit sint ad quis veniam duis excepteur commodo officia cillum nisi sunt. Tempor laboris ad deserunt eiusmod. Ex nostrud pariatur incididunt ullamco laborum ad cupidatat voluptate tempor. Amet nulla minim sit duis do cillum.", - "Nostrud amet ex minim ipsum duis proident quis eiusmod fugiat ut. In nisi velit ea nisi nisi. Laborum irure excepteur ea nisi commodo proident occaecat consequat duis voluptate consequat tempor. Ex consectetur excepteur irure et labore ipsum mollit nisi labore.", - "Anim consectetur veniam ullamco laboris eiusmod minim mollit laborum. Dolore irure dolore est dolore. Non in duis nulla aute tempor duis. Magna elit elit ut Lorem occaecat culpa aute culpa amet cillum. Quis veniam culpa nulla amet ea est duis mollit ex nostrud sit nulla. Ad id incididunt id voluptate officia dolore. Ullamco elit nisi amet occaecat elit pariatur eu." + "headline": "AI-Powered Employment Assistance for Veterans", + "long_description": [ + "VetsAI is an AI-powered virtual assistant designed to help veterans navigate employment transitions and find opportunities in civilian careers. The application allows users to interact via chat, upload resumes in PDF or DOCX format, and receive tailored assistance, such as translating military job codes to civilian job suggestions.", + "Key features include:", + "- **Chat Assistant**: Ask questions and receive advice on job searching and career transitions.", + "- **Military Job Code Translation**: Provide a military job code (e.g., MOS, AFSC) to get suggestions for related civilian careers.", + "- **Document Upload**: Upload employment-related documents for personalized feedback and guidance." + ], + "technologies": [ + "Python", + "Streamlit", + "OpenAI API", + "Pandas", + "NumPy" ], - "technologies": ["Next.js", "Typescript", "Tailwind CSS", "Playwright"], "owner": "Vets-Who-Code", "repo": "VetsAI", - "live_url": "https://vetswhocode.com", + "live_url": null, "thumbnail": { - "src": "https://res.cloudinary.com/vetswhocode/image/upload/f_auto,q_auto/v1688176572/command-line_azvdf6.jpg" + "src": "https://res.cloudinary.com/vetswhocode/image/upload/f_auto,q_auto/v1688176572/command-line_azvdf6.jpg", + "alt": "VetsAI Project Thumbnail" } } diff --git a/src/data/projects/vets-who-code-app.json b/src/data/projects/vets-who-code-app.json index e1a189c5d..d17fa81bf 100644 --- a/src/data/projects/vets-who-code-app.json +++ b/src/data/projects/vets-who-code-app.json @@ -1,17 +1,27 @@ { - "index": 0, - "name": "Vets Who Code Site", - "headline": "A community for veterans to learn to code", + "index": 1, + "name": "Vets Who Code App", + "headline": "Empowering Veterans Through a Communal Coding Platform", "long_description": [ - "Qui incididunt commodo aliqua magna laborum. Veniam laboris cillum occaecat voluptate minim minim. Qui culpa voluptate aliquip mollit ea veniam deserunt duis. Nisi voluptate sit sint ad quis veniam duis excepteur commodo officia cillum nisi sunt. Tempor laboris ad deserunt eiusmod. Ex nostrud pariatur incididunt ullamco laborum ad cupidatat voluptate tempor. Amet nulla minim sit duis do cillum.", - "Nostrud amet ex minim ipsum duis proident quis eiusmod fugiat ut. In nisi velit ea nisi nisi. Laborum irure excepteur ea nisi commodo proident occaecat consequat duis voluptate consequat tempor. Ex consectetur excepteur irure et labore ipsum mollit nisi labore.", - "Anim consectetur veniam ullamco laboris eiusmod minim mollit laborum. Dolore irure dolore est dolore. Non in duis nulla aute tempor duis. Magna elit elit ut Lorem occaecat culpa aute culpa amet cillum. Quis veniam culpa nulla amet ea est duis mollit ex nostrud sit nulla. Ad id incididunt id voluptate officia dolore. Ullamco elit nisi amet occaecat elit pariatur eu." + "The Vets Who Code App serves as a communal codebase where military veterans and their spouses can enhance their coding skills. This production-grade application is continuously evolving to address the unique needs of the veteran community.", + "Built with a modern tech stack, the app offers hands-on experience in web development, enabling users to learn by doing. It provides valuable tools and features that mimic real-world challenges, fostering an environment of growth and learning.", + "The project emphasizes collaboration, mentorship, and practical application of coding skills, preparing veterans for successful careers in the tech industry." + ], + "technologies": [ + "Next.js", + "TypeScript", + "Tailwind CSS", + "Playwright", + "MDX", + "shadcn/ui components", + "Server Actions", + "Server Components" ], - "technologies": ["Next.js", "Typescript", "Tailwind CSS", "Playwright", "React", "Motion for React"], "owner": "Vets-Who-Code", "repo": "vets-who-code-app", - "live_url": "https://vetswhocode.com", + "live_url": "https://vetswhocode.io/", "thumbnail": { - "src": "https://res.cloudinary.com/vetswhocode/image/upload/f_auto,q_auto/v1688176572/command-line_azvdf6.jpg" + "src": "https://res.cloudinary.com/vetswhocode/image/upload/f_auto,q_auto/v1688176572/command-line_azvdf6.jpg", + "alt": "Vets Who Code App Thumbnail" } } diff --git a/src/data/projects/web-curriculum.json b/src/data/projects/web-curriculum.json new file mode 100644 index 000000000..77bf792ac --- /dev/null +++ b/src/data/projects/web-curriculum.json @@ -0,0 +1,30 @@ +{ + "index": 4, + "name": "Web Curriculum", + "headline": "Structured Learning Path for Aspiring Web Developers", + "long_description": [ + "The Web Curriculum is a comprehensive educational resource designed to guide learners through the fundamentals and advanced topics of web development. It offers a structured learning path, starting with foundational concepts and progressing to more complex subjects, ensuring a well-rounded understanding of web technologies.", + "The curriculum is divided into two main collections:", + "- **Collection I: Web Fundamentals**: Covers essential topics such as command line usage, Git and GitHub for version control, HTML and CSS for web page structure and styling, accessibility considerations, user experience design, and an introduction to JavaScript.", + "- **Collection II: JavaScript Engineering**: Focuses on advanced JavaScript topics, including Node.js, TypeScript, React, Next.js, and testing with Playwright, providing learners with the skills needed to build robust and scalable web applications." + ], + "technologies": [ + "Git", + "GitHub", + "HTML", + "CSS", + "JavaScript", + "Node.js", + "TypeScript", + "React", + "Next.js", + "Playwright" + ], + "owner": "Vets-Who-Code", + "repo": "web-curriculum", + "live_url": null, + "thumbnail": { + "src": "https://res.cloudinary.com/vetswhocode/image/upload/f_auto,q_auto/v1688176572/command-line_azvdf6.jpg", + "alt": "Web Curriculum Thumbnail" + } +} diff --git a/src/data/projects/windows-dev-setup-guide.json b/src/data/projects/windows-dev-setup-guide.json new file mode 100644 index 000000000..c7b2a18ce --- /dev/null +++ b/src/data/projects/windows-dev-setup-guide.json @@ -0,0 +1,30 @@ +{ + "index": 3, + "name": "Windows Dev Guide", + "headline": "Comprehensive Guide to Setting Up a Windows Development Environment", + "long_description": [ + "The Windows Dev Guide provides a step-by-step walkthrough for setting up a development environment on Windows. It covers essential tools and configurations to enhance productivity for developers working on Windows platforms.", + "Key sections include:", + "- **Prerequisites**: Guidance on system requirements and initial setup.", + "- **Installing Windows Subsystem for Linux (WSL)**: Instructions for enabling WSL to run a Linux environment on Windows.", + "- **Setting Up Development Tools**: Recommendations for IDEs, text editors, and other essential software.", + "- **Configuring Version Control**: Steps to set up Git and GitHub for source code management.", + "- **Additional Resources**: Links to further reading and tutorials to deepen your understanding." + ], + "technologies": [ + "Windows Subsystem for Linux (WSL)", + "Git", + "Visual Studio Code", + "Windows Terminal", + "Docker", + "Node.js", + "Python" + ], + "owner": "Vets-Who-Code", + "repo": "windows-dev-guide", + "live_url": null, + "thumbnail": { + "src": "https://res.cloudinary.com/vetswhocode/image/upload/f_auto,q_auto/v1688176572/command-line_azvdf6.jpg", + "alt": "Windows Dev Guide Thumbnail" + } +} diff --git a/src/lib/github.ts b/src/lib/github.ts new file mode 100644 index 000000000..bb8443bb3 --- /dev/null +++ b/src/lib/github.ts @@ -0,0 +1,59 @@ +import { Octokit } from "octokit"; +import { VWCContributor, GithubRepo, GithubContributor } from "@utils/types"; + +const token = process.env.GITHUB_ACCESS_TOKEN || ""; +const octokit = new Octokit({ auth: token }); + +export const getProjectContributors = async ( + owner: string, + repo: string, + top: number = 4 +): Promise => { + const topContributors = await getGithubRepoContributors(owner, repo, 4); + const projectContributors = Promise.all( + topContributors.map(async (contributor) => { + const user = await octokit.rest.users.getByUsername({ + username: contributor.login, + }); + if (user.data.name) { + return { + ...contributor, + ...user.data, + name: user.data.name!, + }; + } + return; + }) + ); + return (await projectContributors).filter((contributor) => contributor !== undefined); +}; + +export const getGithubRepo = async (owner: string, repo: string): Promise => { + const response = await octokit.rest.repos.get({ + owner: owner, + repo: repo, + }); + return response.data; +}; + +export const getGithubRepoContributors = async ( + owner: string, + repo: string, + top: number = 4 +): Promise => { + const response = await octokit.rest.repos.listContributors({ + owner: owner, + repo: repo, + per_page: top, + }); + const contributors = response.data.map((contributor) => { + if (contributor.login) { + return { + ...contributor, + login: contributor.login!, + }; + } + return; + }); + return contributors.filter((contributor) => contributor !== undefined); +}; diff --git a/src/lib/project.ts b/src/lib/project.ts index 8382b6201..95a1498a9 100644 --- a/src/lib/project.ts +++ b/src/lib/project.ts @@ -1,15 +1,8 @@ import path from "path"; -import { - GithubContributor, - GithubRepo, - GithubUser, - VWCContributor, - VWCProject, - VWCProjectDetails, -} from "@utils/types"; +import { VWCProject, VWCProjectDetails } from "@utils/types"; import fs from "fs"; import { getSlugs } from "./util"; -import axios from "axios"; +import { getGithubRepo, getProjectContributors } from "./github"; const projectDirectory = path.join(process.cwd(), "src/data/projects"); @@ -42,43 +35,3 @@ export const getProjectData = async (): Promise => { // Sort projects by index return (await data).sort((a, b) => a.details.index - b.details.index); }; - -export const getProjectContributors = async ( - owner: string, - repo: string, - top: number = 4 -): Promise => { - const gitContributors = await getGithubRepoContributors(owner, repo); - const topContributors = gitContributors.slice(0, top); - const projectContributors = Promise.all( - topContributors.map(async (contributor) => { - const user = await getGithubUser(contributor.login); - return { - ...user, - ...contributor, - }; - }) - ); - return projectContributors; -}; - -export const getGithubRepo = async (owner: string, repo: string): Promise => { - const apiURL = `https://api.github.com/repos/${owner}/${repo}`; - const response = await axios.get(apiURL); - return response.data; -}; - -export const getGithubRepoContributors = async ( - owner: string, - repo: string -): Promise => { - const apiURL = `https://api.github.com/repos/${owner}/${repo}/contributors`; - const response = await axios.get(apiURL); - return response.data; -}; - -export const getGithubUser = async (username: string): Promise => { - const apiURL = `https://api.github.com/users/${username}`; - const response = await axios.get(apiURL); - return response.data; -}; diff --git a/src/pages/projects.tsx b/src/pages/projects.tsx index 6368b1fce..4ec4c6643 100644 --- a/src/pages/projects.tsx +++ b/src/pages/projects.tsx @@ -12,6 +12,7 @@ import { getProjectData } from "../lib/project"; import { CircleX, Star, CircleDot, Eye, GitFork } from "lucide-react"; import clsx from "clsx"; import Link from "next/link"; +import MarkdownRenderer from "@components/markdown-renderer"; interface TechStackProps { techStack: string[]; @@ -21,7 +22,7 @@ const TechStack = ({ techStack }: TechStackProps) => { return (
{techStack.map((tech) => ( -
+
{tech}
))} @@ -38,6 +39,7 @@ const ProjectDetailModal = ({ project, className }: ProjectModalProps) => { const duration = 0.35; const xOffset = 25; const delay = 0.1; + return (
{ src={contributor.avatar_url} alt={contributor.name} draggable="false" + loading="eager" />
@@ -142,7 +145,7 @@ const ProjectDetailModal = ({ project, className }: ProjectModalProps) => {
@@ -157,7 +160,11 @@ const ProjectDetailModal = ({ project, className }: ProjectModalProps) => {

{project.details.headline}

{project.details.long_description.map((pg) => { - return

{pg}

; + return ( +

+ +

+ ); })}
@@ -174,7 +181,11 @@ const ProjectCard = ({ project }: ProjectCardProps) => { return ( - + {isOpen && (