diff --git a/.eslintrc.js b/.eslintrc.js
index d28a32e08..8f59b3990 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -77,6 +77,7 @@ module.exports = {
'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
'react/jsx-filename-extension': ['error', { extensions: ['.js', '.tsx'] }],
'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],
+ 'react/no-unescaped-entities': 'off',
'react/jsx-no-target-blank': 'off', // browsers protect against this vulnerability now
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
'react/jsx-one-expression-per-line': 'off',
@@ -284,18 +285,13 @@ module.exports = {
},
},
{
- files: ['./pages/api/**/*.ts'],
+ files: ['./app/api/**/*.ts'],
rules: {
'no-console': 'off',
},
},
{
- files: [
- 'pages/**.js',
- 'components/head.js',
- 'components/nav.js',
- 'components/Timeline/historyData.js',
- ],
+ files: ['components/nav.js', 'components/Timeline/historyData.js'],
rules: {
'react/react-in-jsx-scope': 'off',
},
diff --git a/.gitignore b/.gitignore
index c3f7b497c..081a9cd0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,8 @@ out
# storybook build output
.storybook-dist
+.storybook-static
+storybook-static
# WebStorm Config
.idea
@@ -97,3 +99,5 @@ tsconfig.tsbuildinfo
# MCP config (local tool settings)
.mcp.json
+# next-agents-md
+.next-docs/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..f688de020
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,3 @@
+[Next.js Docs Index]|root: ./.next-docs|STOP. What you remember about Next.js is WRONG for this project. Always search docs and read before any task.|If docs missing, run this command first: npx @next/codemod agents-md --output AGENTS.md|01-app:{04-glossary.mdx}|01-app/01-getting-started:{01-installation.mdx,02-project-structure.mdx,03-layouts-and-pages.mdx,04-linking-and-navigating.mdx,05-server-and-client-components.mdx,06-fetching-data.mdx,07-mutating-data.mdx,08-caching.mdx,09-revalidating.mdx,10-error-handling.mdx,11-css.mdx,12-images.mdx,13-fonts.mdx,14-metadata-and-og-images.mdx,15-route-handlers.mdx,16-proxy.mdx,17-deploying.mdx,18-upgrading.mdx}|01-app/02-guides:{ai-agents.mdx,analytics.mdx,authentication.mdx,backend-for-frontend.mdx,caching-without-cache-components.mdx,ci-build-caching.mdx,content-security-policy.mdx,css-in-js.mdx,custom-server.mdx,data-security.mdx,debugging.mdx,draft-mode.mdx,environment-variables.mdx,forms.mdx,incremental-static-regeneration.mdx,instant-navigation.mdx,instrumentation.mdx,internationalization.mdx,json-ld.mdx,lazy-loading.mdx,local-development.mdx,mcp.mdx,mdx.mdx,memory-usage.mdx,migrating-to-cache-components.mdx,multi-tenant.mdx,multi-zones.mdx,open-telemetry.mdx,package-bundling.mdx,prefetching.mdx,preserving-ui-state.mdx,production-checklist.mdx,progressive-web-apps.mdx,public-static-pages.mdx,redirecting.mdx,sass.mdx,scripts.mdx,self-hosting.mdx,single-page-applications.mdx,static-exports.mdx,streaming.mdx,tailwind-v3-css.mdx,third-party-libraries.mdx,videos.mdx}|01-app/02-guides/migrating:{app-router-migration.mdx,from-create-react-app.mdx,from-vite.mdx}|01-app/02-guides/testing:{cypress.mdx,jest.mdx,playwright.mdx,vitest.mdx}|01-app/02-guides/upgrading:{codemods.mdx,version-14.mdx,version-15.mdx,version-16.mdx}|01-app/03-api-reference:{07-edge.mdx,08-turbopack.mdx}|01-app/03-api-reference/01-directives:{use-cache-private.mdx,use-cache-remote.mdx,use-cache.mdx,use-client.mdx,use-server.mdx}|01-app/03-api-reference/02-components:{font.mdx,form.mdx,image.mdx,link.mdx,script.mdx}|01-app/03-api-reference/03-file-conventions/01-metadata:{app-icons.mdx,manifest.mdx,opengraph-image.mdx,robots.mdx,sitemap.mdx}|01-app/03-api-reference/03-file-conventions/02-route-segment-config:{dynamicParams.mdx,instant.mdx,maxDuration.mdx,preferredRegion.mdx,runtime.mdx}|01-app/03-api-reference/03-file-conventions:{default.mdx,dynamic-routes.mdx,error.mdx,forbidden.mdx,instrumentation-client.mdx,instrumentation.mdx,intercepting-routes.mdx,layout.mdx,loading.mdx,mdx-components.mdx,not-found.mdx,page.mdx,parallel-routes.mdx,proxy.mdx,public-folder.mdx,route-groups.mdx,route.mdx,src-folder.mdx,template.mdx,unauthorized.mdx}|01-app/03-api-reference/04-functions:{after.mdx,cacheLife.mdx,cacheTag.mdx,catchError.mdx,connection.mdx,cookies.mdx,draft-mode.mdx,fetch.mdx,forbidden.mdx,generate-image-metadata.mdx,generate-metadata.mdx,generate-sitemaps.mdx,generate-static-params.mdx,generate-viewport.mdx,headers.mdx,image-response.mdx,next-request.mdx,next-response.mdx,not-found.mdx,permanentRedirect.mdx,redirect.mdx,refresh.mdx,revalidatePath.mdx,revalidateTag.mdx,unauthorized.mdx,unstable_cache.mdx,unstable_noStore.mdx,unstable_rethrow.mdx,updateTag.mdx,use-link-status.mdx,use-params.mdx,use-pathname.mdx,use-report-web-vitals.mdx,use-router.mdx,use-search-params.mdx,use-selected-layout-segment.mdx,use-selected-layout-segments.mdx,userAgent.mdx}|01-app/03-api-reference/05-config/01-next-config-js:{adapterPath.mdx,allowedDevOrigins.mdx,appDir.mdx,assetPrefix.mdx,authInterrupts.mdx,basePath.mdx,cacheComponents.mdx,cacheHandlers.mdx,cacheLife.mdx,compress.mdx,crossOrigin.mdx,cssChunking.mdx,deploymentId.mdx,devIndicators.mdx,distDir.mdx,env.mdx,expireTime.mdx,exportPathMap.mdx,generateBuildId.mdx,generateEtags.mdx,headers.mdx,htmlLimitedBots.mdx,httpAgentOptions.mdx,images.mdx,incrementalCacheHandlerPath.mdx,inlineCss.mdx,logging.mdx,mdxRs.mdx,onDemandEntries.mdx,optimizePackageImports.mdx,output.mdx,pageExtensions.mdx,poweredByHeader.mdx,productionBrowserSourceMaps.mdx,proxyClientMaxBodySize.mdx,reactCompiler.mdx,reactMaxHeadersLength.mdx,reactStrictMode.mdx,redirects.mdx,rewrites.mdx,sassOptions.mdx,serverActions.mdx,serverComponentsHmrCache.mdx,serverExternalPackages.mdx,staleTimes.mdx,staticGeneration.mdx,taint.mdx,trailingSlash.mdx,transpilePackages.mdx,turbopack.mdx,turbopackFileSystemCache.mdx,turbopackIgnoreIssue.mdx,typedRoutes.mdx,typescript.mdx,urlImports.mdx,useLightningcss.mdx,viewTransition.mdx,webVitalsAttribution.mdx,webpack.mdx}|01-app/03-api-reference/05-config:{02-typescript.mdx,03-eslint.mdx}|01-app/03-api-reference/06-cli:{create-next-app.mdx,next.mdx}|02-pages/01-getting-started:{01-installation.mdx,02-project-structure.mdx,04-images.mdx,05-fonts.mdx,06-css.mdx,11-deploying.mdx}|02-pages/02-guides:{analytics.mdx,authentication.mdx,babel.mdx,ci-build-caching.mdx,content-security-policy.mdx,css-in-js.mdx,custom-server.mdx,debugging.mdx,draft-mode.mdx,environment-variables.mdx,forms.mdx,incremental-static-regeneration.mdx,instrumentation.mdx,internationalization.mdx,lazy-loading.mdx,mdx.mdx,multi-zones.mdx,open-telemetry.mdx,package-bundling.mdx,post-css.mdx,preview-mode.mdx,production-checklist.mdx,redirecting.mdx,sass.mdx,scripts.mdx,self-hosting.mdx,static-exports.mdx,tailwind-v3-css.mdx,third-party-libraries.mdx}|02-pages/02-guides/migrating:{app-router-migration.mdx,from-create-react-app.mdx,from-vite.mdx}|02-pages/02-guides/testing:{cypress.mdx,jest.mdx,playwright.mdx,vitest.mdx}|02-pages/02-guides/upgrading:{codemods.mdx,version-10.mdx,version-11.mdx,version-12.mdx,version-13.mdx,version-14.mdx,version-9.mdx}|02-pages/03-building-your-application/01-routing:{01-pages-and-layouts.mdx,02-dynamic-routes.mdx,03-linking-and-navigating.mdx,05-custom-app.mdx,06-custom-document.mdx,07-api-routes.mdx,08-custom-error.mdx}|02-pages/03-building-your-application/02-rendering:{01-server-side-rendering.mdx,02-static-site-generation.mdx,04-automatic-static-optimization.mdx,05-client-side-rendering.mdx}|02-pages/03-building-your-application/03-data-fetching:{01-get-static-props.mdx,02-get-static-paths.mdx,03-forms-and-mutations.mdx,03-get-server-side-props.mdx,05-client-side.mdx}|02-pages/03-building-your-application/06-configuring:{12-error-handling.mdx}|02-pages/04-api-reference:{06-edge.mdx,08-turbopack.mdx}|02-pages/04-api-reference/01-components:{font.mdx,form.mdx,head.mdx,image-legacy.mdx,image.mdx,link.mdx,script.mdx}|02-pages/04-api-reference/02-file-conventions:{instrumentation.mdx,proxy.mdx,public-folder.mdx,src-folder.mdx}|02-pages/04-api-reference/03-functions:{get-initial-props.mdx,get-server-side-props.mdx,get-static-paths.mdx,get-static-props.mdx,next-request.mdx,next-response.mdx,use-params.mdx,use-report-web-vitals.mdx,use-router.mdx,use-search-params.mdx,userAgent.mdx}|02-pages/04-api-reference/04-config/01-next-config-js:{adapterPath.mdx,allowedDevOrigins.mdx,assetPrefix.mdx,basePath.mdx,bundlePagesRouterDependencies.mdx,compress.mdx,crossOrigin.mdx,deploymentId.mdx,devIndicators.mdx,distDir.mdx,env.mdx,exportPathMap.mdx,generateBuildId.mdx,generateEtags.mdx,headers.mdx,httpAgentOptions.mdx,images.mdx,logging.mdx,onDemandEntries.mdx,optimizePackageImports.mdx,output.mdx,pageExtensions.mdx,poweredByHeader.mdx,productionBrowserSourceMaps.mdx,proxyClientMaxBodySize.mdx,reactStrictMode.mdx,redirects.mdx,rewrites.mdx,serverExternalPackages.mdx,trailingSlash.mdx,transpilePackages.mdx,turbopack.mdx,typescript.mdx,urlImports.mdx,useLightningcss.mdx,webVitalsAttribution.mdx,webpack.mdx}|02-pages/04-api-reference/04-config:{01-typescript.mdx,02-eslint.mdx}|02-pages/04-api-reference/05-cli:{create-next-app.mdx,next.mdx}|03-architecture:{accessibility.mdx,fast-refresh.mdx,nextjs-compiler.mdx,supported-browsers.mdx}|04-community:{01-contribution-guide.mdx,02-rspack.mdx}
+
+Use test:e2e:headless:agent instead of test:e2e:headless to run tests without a never-ending process.
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 000000000..43c994c2d
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+@AGENTS.md
diff --git a/pages/about.tsx b/app/about/page.tsx
similarity index 88%
rename from pages/about.tsx
rename to app/about/page.tsx
index cb90667b4..a73168041 100644
--- a/pages/about.tsx
+++ b/app/about/page.tsx
@@ -1,5 +1,5 @@
import Link from 'next/link';
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import ImageCard from 'components/Cards/ImageCard/ImageCard';
@@ -7,13 +7,13 @@ import ValueCard from 'components/Cards/ValueCard/ValueCard';
import OutboundLink from 'components/OutboundLink/OutboundLink';
import { s3 } from 'common/constants/urls';
+export const metadata: Metadata = { title: 'About Us' };
+
const pageTitle = 'About Us';
function About() {
return (
-
-
We at Operation Code strive to provide an efficient way into a tech career for
veterans, military spouses, and transitioning servicemembers. Read about our{' '}
- organization's history
+ organization's history
to learn more!
@@ -48,8 +48,8 @@ function About() {
As a non-profit organization, we rely heavily on your support. If you are interested
in helping us financially, please donate here or set your Amazon Smile organization to
- “Operation Code”. If you have questions about our organization, platforms,
- or services, please reference our FAQ page. Otherwise, do not
+ “Operation Code”. If you have questions about our organization, platforms, or
+ services, please reference our FAQ page. Otherwise, do not
hesitate to reach out to our staff.
,
@@ -69,8 +69,8 @@ function About() {
>
Mentorship Program
- Operation Code's mentorship program connects members with seasoned software
- developers to help you progress and achieve your goals.
+ Operation Code's mentorship program connects members with seasoned software developers
+ to help you progress and achieve your goals.
,
Online Scholarships
- Operation Code's online scholarships provide you the opportunity to kickstart
- your career in software development.
+ Operation Code's online scholarships provide you the opportunity to kickstart your
+ career in software development.
,
Career Services
- Operation Code's career services team provides job opportunities, resume reviews,
+ Operation Code's career services team provides job opportunities, resume reviews,
technical interview prep, and career guidance.
,
@@ -138,7 +138,7 @@ function About() {
Podcast
We have a podcast! You can listen into the amazing
- stories of our members. Visualize your success through others' footsteps.
+ stories of our members. Visualize your success through others' footsteps.
,
]}
@@ -152,7 +152,7 @@ function About() {
Operation Code is leading the way to expand opportunities for military veterans and
their families. We aim to help veterans learn new skills and build their careers in the
- fast-growing technology sector. Our team's mission - led by veterans and other
+ fast-growing technology sector. Our team's mission - led by veterans and other
dedicated, passionate volunteers - is to help open doors for our diverse member base
through unique program offerings, such as our Software Mentor Program, conference
scholarships, and employment services. All of this is made possible by individual
diff --git a/app/api/registration/new/route.ts b/app/api/registration/new/route.ts
new file mode 100644
index 000000000..dce91cbd1
--- /dev/null
+++ b/app/api/registration/new/route.ts
@@ -0,0 +1,40 @@
+import { NextResponse, type NextRequest } from 'next/server';
+import Airtable from 'airtable';
+import { AIR_TABLE_BASE_ID, AIR_TABLE_TABLE_NAME } from 'common/config/environment';
+import type { RegistrationFormValues } from 'components/Forms/RegistrationForm/RegistrationForm';
+
+const base = new Airtable({ apiKey: process.env.AIRTABLE_PAT }).base(AIR_TABLE_BASE_ID);
+
+export async function POST(request: NextRequest) {
+ const { email, firstName, lastName, zipcode } = (await request.json()) as RegistrationFormValues;
+
+ try {
+ const records = await base(AIR_TABLE_TABLE_NAME)
+ .select({ filterByFormula: `{Email} = '${email}'` })
+ .firstPage();
+
+ if (records.length > 0) {
+ return NextResponse.json(
+ { message: `This email has already been registered with an application.` },
+ { status: 409 },
+ );
+ }
+
+ await base(AIR_TABLE_TABLE_NAME).create({
+ Name: lastName ? `${firstName} ${lastName}` : firstName,
+ Email: email,
+ Zipcode: zipcode,
+ Date: new Date().toISOString(),
+ });
+
+ const response = NextResponse.json({ message: 'Success' });
+ response.cookies.set('opCodeApplicantEmail', email, { path: '/', httpOnly: true });
+ return response;
+ } catch (error) {
+ console.error('Error with /api/registration/new POST request:', error);
+ return NextResponse.json(
+ { message: `Unexpected Error: Please contact us via staff@operationcode.org` },
+ { status: 500 },
+ );
+ }
+}
diff --git a/pages/api/registration/update.ts b/app/api/registration/update/route.ts
similarity index 56%
rename from pages/api/registration/update.ts
rename to app/api/registration/update/route.ts
index aff0eff97..7073b1077 100644
--- a/pages/api/registration/update.ts
+++ b/app/api/registration/update/route.ts
@@ -1,31 +1,22 @@
-import type { NextApiRequest, NextApiResponse } from 'next';
+import { NextResponse, type NextRequest } from 'next/server';
import Airtable from 'airtable';
import { AIR_TABLE_BASE_ID, AIR_TABLE_TABLE_NAME } from 'common/config/environment';
import type { UpdateProfileFormShape } from 'components/Forms/UpdateProfileForm/UpdateProfileForm';
const base = new Airtable({ apiKey: process.env.AIRTABLE_PAT }).base(AIR_TABLE_BASE_ID);
-export default async function handler(req: NextApiRequest, res: NextApiResponse) {
- if (req.method !== 'PATCH') {
- return res.status(405).json({ message: 'Method Not Allowed' });
- }
-
- const email = req.cookies?.opCodeApplicantEmail;
+export async function PATCH(request: NextRequest) {
+ const email = request.cookies.get('opCodeApplicantEmail')?.value;
- // The cookie is cleared when the client sends `finalize: true` on the final submit.
- // Additional PATCH requests can still arrive after that (e.g. user double-clicking),
- // so we need to bail out early rather than querying Airtable with an undefined email.
if (!email) {
- return res.status(401).json({ message: 'Missing registration cookie' });
+ return NextResponse.json({ message: 'Missing registration cookie' }, { status: 401 });
}
try {
- // Search for a record with the relevant email
const records = await base(AIR_TABLE_TABLE_NAME)
.select({ filterByFormula: `{Email} = '${email}'` })
.firstPage();
- // Record found, return initial values
if (records.length > 0) {
const relevantRecord = records[0];
@@ -40,7 +31,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
gender,
ethnicity: selectedEthnicityOptions,
educationLevel,
- } = req.body as Partial;
+ finalize: shouldFinalize,
+ } = (await request.json()) as Partial & { finalize?: boolean };
const branchOfService = selectedBranchOfServiceOptions?.map(option => option.value) ?? [];
const ethnicity = selectedEthnicityOptions?.map(option => option.value) ?? [];
@@ -66,46 +58,40 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
'Education Level': educationLevel,
};
- /**
- * Since we call this endpoint as the user progresses through the
- * form, we may not have all the fields defined. AirTable can then
- * throw an error because value would not match the expected type
- * for many fields.
- */
const parsedPayload = Object.fromEntries(
Object.entries(payload).filter(([, value]) => {
- // No undefined keys
if (!value) return false;
-
- // No empty arrays
if (Array.isArray(value) && value.length === 0) return false;
-
- // No empty strings
if (value === '') return false;
-
return true;
}),
);
- if (req.body.finalize) {
- res.setHeader('Set-Cookie', [
- `opCodeApplicantEmail=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`,
- ]);
+ const response = NextResponse.json({ message: 'Success' });
+
+ if (shouldFinalize) {
+ response.cookies.set('opCodeApplicantEmail', '', {
+ path: '/',
+ expires: new Date(0),
+ });
}
- // Update the record with the new values
await base(AIR_TABLE_TABLE_NAME).update(relevantRecord.id, parsedPayload);
- return res.status(200).json({ message: 'Success' });
+ return response;
} else {
- // Clear the stale cookie so the /join/form page guard redirects to /join
- res.setHeader('Set-Cookie', [
- `opCodeApplicantEmail=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`,
- ]);
- return res.status(404).json({ message: `No record found for this email (${email})` });
+ const response = NextResponse.json(
+ { message: `No record found for this email (${email})` },
+ { status: 404 },
+ );
+ response.cookies.set('opCodeApplicantEmail', '', {
+ path: '/',
+ expires: new Date(0),
+ });
+ return response;
}
} catch (error) {
console.error('Error with /api/registration/update PATCH request:', error);
- return res.status(500).json({ message: 'Server Error' });
+ return NextResponse.json({ message: 'Server Error' }, { status: 500 });
}
}
diff --git a/app/blog/page.tsx b/app/blog/page.tsx
new file mode 100644
index 000000000..a4da39534
--- /dev/null
+++ b/app/blog/page.tsx
@@ -0,0 +1,10 @@
+import type { Metadata } from 'next';
+import HeroBanner from 'components/HeroBanner/HeroBanner';
+
+export const metadata: Metadata = { title: 'Blog' };
+
+const pageTitle = 'Blog';
+
+export default function BlogIndex() {
+ return ;
+}
diff --git a/pages/branding.tsx b/app/branding/page.tsx
similarity index 91%
rename from pages/branding.tsx
rename to app/branding/page.tsx
index 85d405595..d8d87dea4 100644
--- a/pages/branding.tsx
+++ b/app/branding/page.tsx
@@ -1,4 +1,4 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import LogoSection from 'components/Branding/LogoSection/LogoSection';
import ColorSection from 'components/Branding/ColorSection/ColorSection';
@@ -7,13 +7,13 @@ import OutboundLink from 'components/OutboundLink/OutboundLink';
import { s3 } from 'common/constants/urls';
import LinkButton from 'components/Buttons/LinkButton/LinkButton';
+export const metadata: Metadata = { title: 'Branding Guide' };
+
const pageTitle = 'Branding Guide';
function Branding() {
return (
<>
-
-
@@ -45,7 +45,7 @@ function Branding() {
Please note:
Images may appear larger or smaller than they appear on your device, but the files
- linked are "large" or "small" as described.
+ linked are “large” or “small” as described.
diff --git a/pages/challenge.tsx b/app/challenge/page.tsx
similarity index 72%
rename from pages/challenge.tsx
rename to app/challenge/page.tsx
index 056f45a26..681b07190 100644
--- a/pages/challenge.tsx
+++ b/app/challenge/page.tsx
@@ -1,5 +1,5 @@
+import type { Metadata } from 'next';
import { s3 } from 'common/constants/urls';
-import Head from 'components/head';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import OutboundLink from 'components/OutboundLink/OutboundLink';
@@ -7,6 +7,8 @@ import challengers from 'static/operationcode_challenge/names';
import range from 'lodash/range';
import Image from 'next/image';
+export const metadata: Metadata = { title: 'Challenge' };
+
const pageTitle = 'Challenge';
const RepoLink = 'https://github.com/OperationCode/front-end/';
@@ -42,14 +44,12 @@ export const NamesColumns = () => {
function Challenge() {
return (
-
-
Welcome to the Operation Code challenge! The goal of this challenge is to get you to
easily commit your first change to a program, see the results of the change, and leave
- your mark on Operation Code itself! To do this we're going to take a look at a source
- code repository, clone the repository, make a change to a file and finally create a pull
+ your mark on Operation Code itself! To do this we're going to take a look at a source code
+ repository, clone the repository, make a change to a file and finally create a pull
request.
@@ -88,16 +88,16 @@ function Challenge() {
width={65}
height={65}
/>{' '}
- In a few moments, you will be redirected to your own copy of this website's
- source code.
+ In a few moments, you will be redirected to your own copy of this website's source
+ code.
- Congratulations! You have "forked" the "repo"!
+ Congratulations! You have “forked” the “repo”!
- Now that you have a fork of the "repo", it's time to edit the
- necessary file to add your name to the list below! Go to the /public
+ Now that you have a fork of the “repo”, it's time to edit the necessary file to add
+ your name to the list below! Go to the /public
{` folder`}, then the /static {` folder`}, click on the
operationcode_challenge directory and click on the file called
names.js. On the right-hand side, you should see
@@ -114,11 +114,11 @@ function Challenge() {
Scroll to the bottom for the Commit changes form. There are two input boxes.
- In the input field with "Update names.js", type{' '}
+ In the input field with “Update names.js”, type{' '}
Add <YOUR NAME> to challenge list. You will leave the second,
- large input field blank. There are two "radio" buttons below the input
- fields. Check the one that says "Create a new branch". Your screen should
- now have something like this:
+ large input field blank. There are two “radio” buttons below the input fields. Check
+ the one that says “Create a new branch”. Your screen should now have something like
+ this:
- Click on the "Pull requests" tab. It rests between the "Issues"
- and "Project" tabs.
+ Click on the “Pull requests” tab. It rests between the “Issues” and “Project” tabs.
-
Click on the green "New pull request" button.
+
Click on the green “New pull request” button.
- You should now be at the "
- Open a pull request
- " screen. We do not wish to ask ourselves for permission to merge our new
- branch into our own fork! Instead, click{' '}
+ You should now be at the “Open a pull request“ screen. We do not wish to ask
+ ourselves for permission to merge our new branch into our own fork! Instead,
+ click{' '}
{' '}
- , to open Operation Code's "New pull request" interface. You
- should see a "Compare changes" headline. Just below that is a link within
- the text: 'compare across forks' - click it. Now, click on the
- selector that says ' head fork' at the beginning, and choose your fork.
- Click the next selector to the right, and choose your new branch. Now, you're
- comparing Operation Code's main branch with your new fork's branch, and
- you may click
+ , to open Operation Code's “New pull request” interface. You should see a
+ “Compare changes” headline. Just below that is a link within the text:
+ 'compare across forks' - click it. Now, click on the selector that says ' head fork'
+ at the beginning, and choose your fork. Click the next selector to the right, and
+ choose your new branch. Now, you're comparing Operation Code's main branch with your
+ new fork's branch, and you may click
NOTE:
{' '}
A pull request is how people throughout the world are able to contribute to open
- source software - like Operation Code's website! When you submit a pull request
- it notifies the maintainers of the project, and runs some automated checks. The
+ source software - like Operation Code's website! When you submit a pull request it
+ notifies the maintainers of the project, and runs some automated checks. The
maintainers then look at the new changes, and decide if they want it merged into
their repository.
- When you're ready, click the "Create pull request" button. Our staff
- will be notified and a few minutes after the pull request is accepted and merged
- your name will show up below!
+ When you're ready, click the “Create pull request” button. Our staff will be
+ notified and a few minutes after the pull request is accepted and merged your name
+ will show up below!
- Congratulations - you've made your first open source commit!
+ Congratulations - you've made your first open source commit!
,
]}
diff --git a/pages/chapter_leader.tsx b/app/chapter_leader/page.tsx
similarity index 97%
rename from pages/chapter_leader.tsx
rename to app/chapter_leader/page.tsx
index dd3d6a4b6..348d60b31 100644
--- a/pages/chapter_leader.tsx
+++ b/app/chapter_leader/page.tsx
@@ -1,15 +1,15 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import OutboundLink from 'components/OutboundLink/OutboundLink';
+export const metadata: Metadata = { title: 'Chapter Leaders' };
+
const pageTitle = 'Chapter Leaders';
function ChapterLeader() {
return (
-
-
Operation Code is looking for volunteer Chapter Leaders to build local communities
diff --git a/pages/chapters.tsx b/app/chapters/page.tsx
similarity index 93%
rename from pages/chapters.tsx
rename to app/chapters/page.tsx
index 77d513b98..cb58fe694 100644
--- a/pages/chapters.tsx
+++ b/app/chapters/page.tsx
@@ -1,9 +1,11 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import FlatCard from 'components/Cards/FlatCard/FlatCard';
import OutboundLink from 'components/OutboundLink/OutboundLink';
+export const metadata: Metadata = { title: 'Chapters' };
+
const pageTitle = 'Chapters';
const unsortedChapterLocations = [
@@ -36,8 +38,6 @@ const chapterLocations = unsortedChapterLocations.sort(({ name: nameA }, { name:
function Chapters() {
return (
<>
-
-
Get involved by joing your local chapter!
@@ -62,7 +62,7 @@ function Chapters() {
})}
,
- Don't see your a location in your area?
+ Don't see your a location in your area?
-
-
- Department of Labor, Women's Bureau
+ Department of Labor, Women's Bureau
]
@@ -109,7 +109,6 @@ const biases: Bias[] = [
const CorporateTraining = () => {
return (
<>
- {
Operation Code has provided corporate training for tech employers since 2019. In order to
break barriers and blockers for our military community, we must address the implicit and
overt biases. Reach out to us if you would like more information on how our military
- cultural competency training works, if you'd like us to help create a military
- Diversity, Equity, Inclusion and Belonging strategy, provide ongoing professional
- development or set up a military Employee Resource Group with you: Contact the{' '}
+ cultural competency training works, if you'd like us to help create a military Diversity,
+ Equity, Inclusion and Belonging strategy, provide ongoing professional development or set
+ up a military Employee Resource Group with you: Contact the{' '}
{
key={bias.title}
className={cx(
'flex md:even:flex-row-reverse md:flex-row flex-col-reverse flex-wrap md:flex-nowrap md:[&>*]:flex-1',
- 'even:bg-secondary even:text-white', // mobile alternating colors per li
- 'md:[&:nth-child(1n)]:bg-white md:[&:nth-child(2n)]:bg-theme-gray-800 md:[&:nth-child(3n)]:bg-secondary md:[&:nth-child(1n)]:text-secondary md:[&:nth-child(3n)]:text-white', // non-mobile alternating colors per li
+ 'even:bg-secondary even:text-white',
+ 'md:[&:nth-child(1n)]:bg-white md:[&:nth-child(2n)]:bg-theme-gray-800 md:[&:nth-child(3n)]:bg-secondary md:[&:nth-child(1n)]:text-secondary md:[&:nth-child(3n)]:text-white',
)}
>
@@ -156,7 +155,7 @@ const CorporateTraining = () => {
diff --git a/pages/donate.tsx b/app/donate/page.tsx
similarity index 96%
rename from pages/donate.tsx
rename to app/donate/page.tsx
index 5c1b38155..4526dd77d 100644
--- a/pages/donate.tsx
+++ b/app/donate/page.tsx
@@ -1,15 +1,15 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import Container from 'components/Container/Container';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import OutboundLink from 'components/OutboundLink/OutboundLink';
const pageTitle = 'Donate';
+export const metadata: Metadata = { title: pageTitle };
+
function DonatePage() {
return (
<>
-
-
@@ -29,7 +29,7 @@ function DonatePage() {
Your donations also helps our community reduce the risk facing our transitioning
military, military spouses and military veterans by growing social connectedness,
building camaraderie and teaching tangible technical and personal skills that combat
- chronic unemployment, homelessness, and suicide. You're providing members with the
+ chronic unemployment, homelessness, and suicide. You're providing members with the
opportunity to learn software development, enter the tech industry, and Deploy The
Future!
diff --git a/app/error.tsx b/app/error.tsx
new file mode 100644
index 000000000..83954b6b5
--- /dev/null
+++ b/app/error.tsx
@@ -0,0 +1,13 @@
+'use client';
+
+import { useEffect } from 'react';
+import * as Sentry from '@sentry/nextjs';
+import ErrorDisplay from 'components/ErrorDisplay/ErrorDisplay';
+
+export default function Error({ error }: { error: Error & { digest?: string } }) {
+ useEffect(() => {
+ Sentry.captureException(error);
+ }, [error]);
+
+ return ;
+}
diff --git a/pages/faq.tsx b/app/faq/page.tsx
similarity index 81%
rename from pages/faq.tsx
rename to app/faq/page.tsx
index e965f9c30..1e8cafc44 100644
--- a/pages/faq.tsx
+++ b/app/faq/page.tsx
@@ -1,10 +1,12 @@
import Link from 'next/link';
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import Accordion from 'components/Accordion/Accordion';
import OutboundLink from 'components/OutboundLink/OutboundLink';
+export const metadata: Metadata = { title: 'FAQ' };
+
const questions = {
general: [
{
@@ -42,11 +44,11 @@ const questions = {
content: (
<>
Operation Code, much like software, is built from anywhere with an internet connection,
- and is not based in one location. While we're headquartered in Portland, the entire
+ and is not based in one location. While we're headquartered in Portland, the entire
organization is decentralized, including the board of directors and the core team. This
- allows us to more effectively serve the entire military community, whether they're
- veterans or military spouses, whether they're OCONUS or in-country. We have chapters
- all over the nation. Use Slack chat and join the closest town to you!
+ allows us to more effectively serve the entire military community, whether they're
+ veterans or military spouses, whether they're OCONUS or in-country. We have chapters all
+ over the nation. Use Slack chat and join the closest town to you!
>
),
},
@@ -54,10 +56,10 @@ const questions = {
title: 'Who does Operation Code serve?',
content: (
<>
- Operation Code serves our nation's finest who've worn the uniform and their
- families who are interested in coding and software development. Our programs are offered
- at no cost to the military community, including veterans, transitioning service members,
- and military spouses and families.
+ Operation Code serves our nation's finest who've worn the uniform and their families who
+ are interested in coding and software development. Our programs are offered at no cost to
+ the military community, including veterans, transitioning service members, and military
+ spouses and families.
>
),
},
@@ -105,7 +107,7 @@ const questions = {
<>
While we do not have a long-term mentorship program, mentors are available for 30-minute
sessions to assist you with things like mock interviews, code reviews, or general
- guidance. To request a mentorship session, type "/mentor" in any of our{' '}
+ guidance. To request a mentorship session, type “/mentor” in any of our{' '}
Slack channels
{' '}
@@ -117,8 +119,8 @@ const questions = {
title: 'What are the hours of operation for Operation Code?',
content: (
<>
- Operation Code is different in that we don't have regular business office hours. The
- team can usually be found in, our{' '}
+ Operation Code is different in that we don't have regular business office hours. The team
+ can usually be found in, our{' '}
Slack channel
{' '}
@@ -160,8 +162,8 @@ const questions = {
Facebook
{' '}
- to put out updates and news since it's faster to put out info and respond. Given our
- chosen craft, we don't do regular emails as often.
+ to put out updates and news since it's faster to put out info and respond. Given our
+ chosen craft, we don't do regular emails as often.
>
),
},
@@ -169,14 +171,14 @@ const questions = {
title: "My question isn't listed. How do I contact Operation Code?",
content: (
<>
- If you have a question that isn't listed here on our FAQ, write to{' '}
+ If you have a question that isn't listed here on our FAQ, write to{' '}
staff@operationcode.org
{' '}
- , and we'll get back to you as soon as we can.
+ , and we'll get back to you as soon as we can.
>
),
},
@@ -195,8 +197,8 @@ const questions = {
title: 'I would rather mail a check. To whom do I make it out and where do I send it?',
content: (
<>
- It's less administrative work to accept online donations. Get in touch so we can
- assess your situation and contribution commitment.
+ It's less administrative work to accept online donations. Get in touch so we can assess
+ your situation and contribution commitment.
>
),
},
@@ -225,7 +227,7 @@ const questions = {
directly benefit the organization, transitioning military, citizen-soldiers, veterans and
their families in learning to code and building software to change the world. Items that
are needed, include (but not limited to): frequent flyer miles, Adobe Cloud, used or new
- MacBook Air's, and grant writers.
+ MacBook Air's, and grant writers.
>
),
},
@@ -234,9 +236,8 @@ const questions = {
do that?`,
content: (
<>
- Get in touch, and we'll make an announcement in our Slack, tweet and/or write a blog
- post, and find a veteran to take your spot. Even then, travel and lodging is often a
- barrier.
+ Get in touch, and we'll make an announcement in our Slack, tweet and/or write a blog post,
+ and find a veteran to take your spot. Even then, travel and lodging is often a barrier.
>
),
},
@@ -266,14 +267,13 @@ const questions = {
https://smile.amazon.com
{' '}
, you continue to have the same shopping experience as the same and most products
- available on amazon.com but you help Operation Code realize it 's mission. Once
- you’ve selected "Operation Code" everything else functions the same. Shop for
- your favorite products or the perfect gift. Most products are eligible on Amazon Smile, if
- not, you’ll be notified. You can checkout normally as well. No extra cost is passed onto
- you–Amazon will donate 0.5% of your purchase to Operation Code! After you’ve successfully
- completed a purchase on AmazonSmile you can share the news with your friends on Facebook,
- Twitter or via email. This option appears on the confirmation page after your order is
- complete.
+ available on amazon.com but you help Operation Code realize it 's mission. Once you've
+ selected “Operation Code” everything else functions the same. Shop for your favorite
+ products or the perfect gift. Most products are eligible on Amazon Smile, if not, you'll
+ be notified. You can checkout normally as well. No extra cost is passed onto you–Amazon
+ will donate 0.5% of your purchase to Operation Code! After you've successfully completed a
+ purchase on AmazonSmile you can share the news with your friends on Facebook, Twitter or
+ via email. This option appears on the confirmation page after your order is complete.
>
),
},
@@ -303,8 +303,6 @@ const questions = {
function FAQ() {
return (
- We're always looking for volunteers who are dedicated to making an impact in the
- lives of military veterans, service members, and spouses.
+ We're always looking for volunteers who are dedicated to making an impact in the lives
+ of military veterans, service members, and spouses.
You can help us with:
,
diff --git a/app/global-error.tsx b/app/global-error.tsx
new file mode 100644
index 000000000..61a644911
--- /dev/null
+++ b/app/global-error.tsx
@@ -0,0 +1,19 @@
+'use client';
+
+import { useEffect } from 'react';
+import * as Sentry from '@sentry/nextjs';
+import ErrorDisplay from 'components/ErrorDisplay/ErrorDisplay';
+
+export default function GlobalError({ error }: { error: Error & { digest?: string } }) {
+ useEffect(() => {
+ Sentry.captureException(error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/pages/history.tsx b/app/history/page.tsx
similarity index 80%
rename from pages/history.tsx
rename to app/history/page.tsx
index 68c3a8caa..dc3c6c9d7 100644
--- a/pages/history.tsx
+++ b/app/history/page.tsx
@@ -1,16 +1,17 @@
+import type { Metadata } from 'next';
import { s3 } from 'common/constants/urls';
import Content from 'components/Content/Content';
-import Head from 'components/head';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Timeline from 'components/Timeline/Timeline';
import TimelineNav from 'components/Timeline/TimelineNav/TimelineNav';
const pageTitle = 'History';
+export const metadata: Metadata = { title: pageTitle };
+
function History() {
return (
-
- “There are no secrets to success. It is the result of preparation, hard work,
- learning from failure.”
+ “There are no secrets to success. It is the result of preparation, hard work, learning
+ from failure.”
- Colin Powell
diff --git a/pages/jobs.tsx b/app/jobs/page.tsx
similarity index 90%
rename from pages/jobs.tsx
rename to app/jobs/page.tsx
index 0fa4695d3..a464598e6 100644
--- a/pages/jobs.tsx
+++ b/app/jobs/page.tsx
@@ -1,4 +1,4 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import FeaturedJobsData from 'components/FeaturedJobItem/featuredJobs.json';
@@ -7,11 +7,11 @@ import ZipRecruiterJobs from 'components/ZipRecruiterJobs/ZipRecruiterJobs';
const pageTitle = 'Jobs';
+export const metadata: Metadata = { title: pageTitle };
+
function Jobs() {
return (
<>
-
-
{
- prefetch(profileUpdateURL);
- }, []);
+export default function JoinContent({ hasRegistrationError }: { hasRegistrationError: boolean }) {
+ const router = useRouter();
+ const [isErrorModalOpen, setIsErrorModalOpen] = useState(hasRegistrationError);
useEffect(() => {
- if (query.registrationError) {
- setIsErrorModalOpen(true);
- }
- }, [query.registrationError]);
+ router.prefetch(profileUpdateURL);
+ }, [router]);
const handleSuccess = () => {
gtag.conversionEvent({ adId: '9ZvVCOOFmrkBEK-Rnp4D', category: 'sign_up' });
- push(profileUpdateURL);
+ router.push(profileUpdateURL);
};
return (
<>
-
-
@@ -65,12 +58,12 @@ export default function Join() {
Registration Incomplete
- It looks like we're missing information from the first step of registration. Please
+ It looks like we're missing information from the first step of registration. Please
complete the form below to get started.
- If you've already completed this step and were unexpectedly redirected here,
- something may be wrong on our end. Please email us at{' '}
+ If you've already completed this step and were unexpectedly redirected here, something
+ may be wrong on our end. Please email us at{' '}
staff@operationcode.org
{' '}
diff --git a/app/join/form/page.tsx b/app/join/form/page.tsx
new file mode 100644
index 000000000..6c9274d0e
--- /dev/null
+++ b/app/join/form/page.tsx
@@ -0,0 +1,27 @@
+import { cookies } from 'next/headers';
+import { redirect } from 'next/navigation';
+import type { Metadata } from 'next';
+import HeroBanner from 'components/HeroBanner/HeroBanner';
+import Content from 'components/Content/Content';
+import UpdateProfileForm from 'components/Forms/UpdateProfileForm/UpdateProfileForm';
+
+export const metadata: Metadata = { title: 'Update Profile' };
+
+const pageTitle = 'Update Profile';
+
+export default async function UpdateProfile() {
+ const cookieStore = await cookies();
+ const opCodeApplicantEmail = cookieStore.get('opCodeApplicantEmail');
+
+ if (!opCodeApplicantEmail) {
+ redirect('/');
+ }
+
+ return (
+ <>
+
+
+ ]} />
+ >
+ );
+}
diff --git a/app/join/page.tsx b/app/join/page.tsx
new file mode 100644
index 000000000..1f1d01ec6
--- /dev/null
+++ b/app/join/page.tsx
@@ -0,0 +1,14 @@
+import type { Metadata } from 'next';
+import JoinContent from './JoinContent';
+
+export const metadata: Metadata = { title: 'Join' };
+
+export default async function JoinPage({
+ searchParams,
+}: {
+ searchParams: Promise<{ registrationError?: string }>;
+}) {
+ const { registrationError } = await searchParams;
+
+ return ;
+}
diff --git a/app/join/success/page.tsx b/app/join/success/page.tsx
new file mode 100644
index 000000000..18346c160
--- /dev/null
+++ b/app/join/success/page.tsx
@@ -0,0 +1,32 @@
+import { SUCCESS_PAGE_MESSAGE } from 'common/constants/testIDs';
+import type { Metadata } from 'next';
+import LinkButton from 'components/Buttons/LinkButton/LinkButton';
+import HeroBanner from 'components/HeroBanner/HeroBanner';
+import OutboundLink from 'components/OutboundLink/OutboundLink';
+
+export const metadata: Metadata = { title: 'Successful Registration!' };
+
+const pageTitle = `Successful Registration!`;
+
+export default function JoinSuccess() {
+ return (
+
+
+ We will review your application and send an invite to our Slack team as soon as possible. If
+ you do not receive an invite within a week, please email us at{' '}
+
+ staff@operationcode.org
+
+ .
+
+
+
+ Go Home
+
+
+ );
+}
diff --git a/app/layout.tsx b/app/layout.tsx
new file mode 100644
index 000000000..548d0f301
--- /dev/null
+++ b/app/layout.tsx
@@ -0,0 +1,84 @@
+import 'common/styles/globals.css';
+
+import type { Metadata } from 'next';
+import Script from 'next/script';
+import type { PropsWithChildren } from 'react';
+
+import { clientTokens } from 'common/config/environment';
+import { AnalyticsProvider } from 'components/Analytics/AnalyticsProvider';
+import Footer from 'components/Footer/Footer';
+import Nav from 'components/Nav/Nav';
+import { ScrollToTopButton } from 'components/ScrollToTopButton/ScrollToTopButton';
+
+const defaultOgImage = `https://operation-code-assets.s3.us-east-2.amazonaws.com/branding/oc_image.png`;
+
+export const metadata: Metadata = {
+ title: {
+ template: 'Operation Code | %s',
+ default: 'Operation Code',
+ },
+ description:
+ 'Operation Code is a registered 501(c)3 whose mission is to help our military community and SIV allied refugees grow in their tech careers while rebuilding our lives post-conflict.',
+ icons: {
+ icon: '/public/favicon.ico',
+ apple: '/static/apple-icon-180x180.png',
+ },
+ openGraph: {
+ type: 'website',
+ url: 'https://operationcode.org',
+ images: {
+ width: 1200,
+ height: 630,
+ alt: 'Operation Code Logo',
+ url: defaultOgImage,
+ },
+ },
+ twitter: {
+ card: 'summary_large_image',
+ site: 'https://operationcode.org',
+ images: {
+ alt: 'Operation Code Logo',
+ url: defaultOgImage,
+ },
+ },
+};
+
+export default function RootLayout({ children }: PropsWithChildren) {
+ const isProduction = process.env.VERCEL_ENV === 'production';
+
+ return (
+
+
+
+
+ );
+}
diff --git a/pages/404.tsx b/app/not-found.tsx
similarity index 74%
rename from pages/404.tsx
rename to app/not-found.tsx
index 858c68640..58ed17079 100644
--- a/pages/404.tsx
+++ b/app/not-found.tsx
@@ -1,5 +1,5 @@
import ErrorDisplay from 'components/ErrorDisplay/ErrorDisplay';
-export default function Custom404() {
+export default function NotFound() {
return ;
}
diff --git a/pages/index.tsx b/app/page.tsx
similarity index 82%
rename from pages/index.tsx
rename to app/page.tsx
index 571adbf34..ab66e9245 100644
--- a/pages/index.tsx
+++ b/app/page.tsx
@@ -1,4 +1,4 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import JoinSection from 'components/ReusableSections/JoinSection/JoinSection';
@@ -9,11 +9,13 @@ import LinkButton from 'components/Buttons/LinkButton/LinkButton';
import { s3 } from 'common/constants/urls';
import { cx } from 'common/utils/cva';
+export const metadata: Metadata = { title: 'Home' };
+
const successStories = [
{
title: 'Ali Cipolla-Taylor, Talent Acquisition at Microsoft',
quote:
- 'I finished MSTA the last week of February, and then COVID hit. Employment was not going to happen…to anyone. I kept making calls, working on my skills, and throwing myself out there, and I got a role as a vendor at Microsoft. I’m half of the Data Privacy, Compliance, and Controls team for Talent Acquisition now. I had a lot of hard conversations with myself. I learned to lean into a support network, locally and online, through OpCode. I’m notoriously shy on the internet, but I knew that I couldn’t do this alone. Change happens when the discomfort of making the change is less than the life you’re living.',
+ "I finished MSTA the last week of February, and then COVID hit. Employment was not going to happen…to anyone. I kept making calls, working on my skills, and throwing myself out there, and I got a role as a vendor at Microsoft. I'm half of the Data Privacy, Compliance, and Controls team for Talent Acquisition now. I had a lot of hard conversations with myself. I learned to lean into a support network, locally and online, through OpCode. I'm notoriously shy on the internet, but I knew that I couldn't do this alone. Change happens when the discomfort of making the change is less than the life you're living.",
imageSource: `${s3}headshots/ali.jpg`,
},
{
@@ -33,8 +35,6 @@ const successStories = [
function Home() {
return (
-
-
- We're the largest community of military veterans, service members, and spouses
- committed to becoming software developers with the help of mentors, scholarships, and
- our tech partners.
+ We're the largest community of military veterans, service members, and spouses committed
+ to becoming software developers with the help of mentors, scholarships, and our tech
+ partners.
diff --git a/app/podcast/PodcastPlayer.tsx b/app/podcast/PodcastPlayer.tsx
new file mode 100644
index 000000000..3e594c2f6
--- /dev/null
+++ b/app/podcast/PodcastPlayer.tsx
@@ -0,0 +1,27 @@
+'use client';
+
+import dynamic from 'next/dynamic';
+
+const ReactPlayer = dynamic(() => import('react-player/lazy'), { ssr: false });
+
+interface PodcastPlayerProps {
+ url: string;
+}
+
+export default function PodcastPlayer({ url }: PodcastPlayerProps) {
+ return (
+
+ );
+}
diff --git a/pages/podcast.tsx b/app/podcast/page.tsx
similarity index 61%
rename from pages/podcast.tsx
rename to app/podcast/page.tsx
index 2030c1df7..9a2ead3bc 100644
--- a/pages/podcast.tsx
+++ b/app/podcast/page.tsx
@@ -1,14 +1,17 @@
import axios from 'axios';
import get from 'lodash/get';
-import dynamic from 'next/dynamic';
import { parse as parseXml } from 'fast-xml-parser';
-import { ONE_DAY } from 'common/constants/unitsOfTime';
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Card from 'components/Cards/Card/Card';
import Content from 'components/Content/Content';
import Heading from 'components/Heading/Heading';
import Image from 'next/image';
+import PodcastPlayer from './PodcastPlayer';
+
+export const metadata: Metadata = { title: 'Podcast' };
+
+export const revalidate = 86400;
interface RSS {
channel: {
@@ -23,12 +26,7 @@ interface Episode {
story: string;
}
-const pageTitle = 'Podcast';
-
-const ReactPlayer = dynamic(() => import('react-player/lazy'), { ssr: false });
-
-// We have atypical error handling because there exist errors thrown on nearly every request.
-export async function getStaticProps() {
+async function getEpisodes(): Promise {
const { data } = await axios.get('https://operationcode.libsyn.com/rss');
const {
@@ -44,25 +42,24 @@ export async function getStaticProps() {
const numberOfEpisodes = get(rss, 'channel.item.length', 0);
if (numberOfEpisodes > 0) {
- const episodes = rss.channel.item.map(({ image: { href }, link, title, description }) => ({
+ return rss.channel.item.map(({ image: { href }, link, title, description }) => ({
image: href,
name: title[0],
source: link,
story: description.replace(/(
|<\/p>)/g, ''),
}));
-
- return { props: { episodes }, revalidate: ONE_DAY };
}
- // Request failed or RSS Feed is broken... Break the build!
- throw new Error('getStaticProps in /podcast failed.');
+ throw new Error('Failed to fetch podcast episodes.');
}
-function Podcast({ episodes }: { episodes: Episode[] }) {
+const pageTitle = 'Podcast';
+
+export default async function Podcast() {
+ const episodes = await getEpisodes();
+
return (
-
-
Come listen to some inspiring stories of our vets transitioning into tech!
{episodes.map(({ name, image, source, story }, index) => {
- /*
- * Some episodes have multiple parts and are named like "${Name}, part 1".
- * Some episodes are named "${Name} Interview"
- *
- * Parsing them in this manner ensures that the name of the interviewee is
- * available and used for the image alt tag.
- */
const interviewee = name.replace(/ interview/gi, '').split(',')[0];
return (
@@ -97,19 +87,7 @@ function Podcast({ episodes }: { episodes: Episode[] }) {
height={200}
/>
-
+
);
}
-
-export default Podcast;
diff --git a/pages/policy.tsx b/app/policy/page.tsx
similarity index 94%
rename from pages/policy.tsx
rename to app/policy/page.tsx
index fc06b3a57..e794df3ac 100644
--- a/pages/policy.tsx
+++ b/app/policy/page.tsx
@@ -1,12 +1,12 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import { s3 } from 'common/constants/urls';
+export const metadata: Metadata = { title: 'Policy' };
+
function Policy() {
return (
-
-
-
-
- This page is designed to make a journalist's job easy in writing, blogging, or
- documenting Operation Code. Below you will find targeted information corresponding to
- common representative visitors, videos, photos, press releases, and branding details.
+ This page is designed to make a journalist's job easy in writing, blogging, or documenting
+ Operation Code. Below you will find targeted information corresponding to common
+ representative visitors, videos, photos, press releases, and branding details.
@@ -56,11 +56,11 @@ function Press() {
We have long-standing, productive partnerships with some amazing companies, and
yours could be one of them! Organizations that put our members and our open source
work on a pedastal, can look forward to receive social media blasts and the
- appreciation of America's military veterans. If you are thinking about a
+ appreciation of America's military veterans. If you are thinking about a
partnership with Operation Code, but are unsure of what to offer our members,{' '}
- let's talk.
- If you're seeking information to display in announcing the partnership,
- please see below!
+ let's talk. If
+ you're seeking information to display in announcing the partnership, please see
+ below!
@@ -70,10 +70,10 @@ function Press() {
Media Outlets
The staff at Operation Code thank you for taking your time to represent us in your
- work. If your piece has a specific theme or target, and you'd like some
- custom contributions, please join our organization to receive a Slack team invite.
- There you'll likely find many Operation Code members willing and able to
- offer personal anecdotes and first-hand interviews!
+ work. If your piece has a specific theme or target, and you'd like some custom
+ contributions, please join our organization to receive a Slack team invite. There
+ you'll likely find many Operation Code members willing and able to offer personal
+ anecdotes and first-hand interviews!
diff --git a/pages/project_rebuild.tsx b/app/project_rebuild/page.tsx
similarity index 91%
rename from pages/project_rebuild.tsx
rename to app/project_rebuild/page.tsx
index 96008cde1..46d746323 100644
--- a/pages/project_rebuild.tsx
+++ b/app/project_rebuild/page.tsx
@@ -1,5 +1,5 @@
import Image from 'next/image';
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import Content from 'components/Content/Content';
import OutboundLink from 'components/OutboundLink/OutboundLink';
@@ -8,18 +8,11 @@ import { s3 } from 'common/constants/urls';
const pageTitle = 'Project Rebuild';
+export const metadata: Metadata = { title: pageTitle };
+
function ProjectRebuild() {
return (
-
-
In conjunction with Fresh Start Refugee Assistance Center, an Afghan-American led
- non-profit, and Globally.org’s ReUp Refugee Tech Re-Skilling Program, Operation Code
+ non-profit, and Globally.org's ReUp Refugee Tech Re-Skilling Program, Operation Code
is pleased to announce that we are expanding our Project Rebuild Refugee Tech Training
Program. Since March 2022, Operation Code launched the initial pilot cohort, with 8
Afghan refugee participants in attendance. We have expanded the cohorts to include
@@ -76,7 +69,7 @@ function ProjectRebuild() {
Fresh Start provides wraparound services such as:
- initial refugee resettlement efforts, ESL classes, driver’s education and licensing,
+ initial refugee resettlement efforts, ESL classes, driver's education and licensing,
affordable housing, mental health and cultural transition support as well as job
search assistance.
@@ -88,7 +81,7 @@ function ProjectRebuild() {
{' '}
by providing our refugee participants a scholarship to complete one certification
during the six-month cohort. Pairing a refugee with a Veteran or military spouse
- mentor to meet on a regular cadence, our two communities can continue to “rebuild” our
+ mentor to meet on a regular cadence, our two communities can continue to "rebuild" our
parallel experiences, provide a tech-focused workforce development program and help
refugees obtain high paid and meaningful work.
diff --git a/pages/scholarship/code_platoon.tsx b/app/scholarship/code_platoon/CodePlatoonContent.tsx
similarity index 90%
rename from pages/scholarship/code_platoon.tsx
rename to app/scholarship/code_platoon/CodePlatoonContent.tsx
index fab141e65..65d93a4e7 100644
--- a/pages/scholarship/code_platoon.tsx
+++ b/app/scholarship/code_platoon/CodePlatoonContent.tsx
@@ -1,11 +1,12 @@
-import Head from 'components/head';
+'use client';
+
+import { useEffect } from 'react';
import Container from 'components/Container/Container';
import HeroBanner from 'components/HeroBanner/HeroBanner';
-import { useEffect } from 'react';
const pageTitle = 'Code Platoon X Operation Code Bootcamp Scholarship';
-export default function CodePlatoonScholarshipPage() {
+export default function CodePlatoonContent() {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://js.hsforms.net/forms/embed/v2.js';
@@ -33,7 +34,6 @@ export default function CodePlatoonScholarshipPage() {
return (
<>
-
diff --git a/app/scholarship/code_platoon/page.tsx b/app/scholarship/code_platoon/page.tsx
new file mode 100644
index 000000000..675b689c0
--- /dev/null
+++ b/app/scholarship/code_platoon/page.tsx
@@ -0,0 +1,10 @@
+import type { Metadata } from 'next';
+import CodePlatoonContent from './CodePlatoonContent';
+
+export const metadata: Metadata = {
+ title: 'Code Platoon X Operation Code Bootcamp Scholarship',
+};
+
+export default function CodePlatoonScholarshipPage() {
+ return ;
+}
diff --git a/pages/scholarship/index.tsx b/app/scholarship/page.tsx
similarity index 95%
rename from pages/scholarship/index.tsx
rename to app/scholarship/page.tsx
index 869a3a121..7ba1418f3 100644
--- a/pages/scholarship/index.tsx
+++ b/app/scholarship/page.tsx
@@ -1,4 +1,4 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import Container from 'components/Container/Container';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import OutboundLink from 'components/OutboundLink/OutboundLink';
@@ -7,6 +7,8 @@ import Link from 'next/link';
import Card from 'components/Cards/Card/Card';
import Image from 'next/image';
+export const metadata: Metadata = { title: 'Scholarships Program' };
+
const pageTitle = 'Scholarships Program';
interface ScholarshipOption {
@@ -23,7 +25,7 @@ const scholarshipOptions: ScholarshipOption[] = [
{
title: 'Code Platoon X Operation Code',
logoSrc: `${s3}partnerLogos/code_platoon.png`,
- body: 'Apply to attend a full ride scholarship to one of the Code Platoon’s coding bootcamp cohorts.',
+ body: "Apply to attend a full ride scholarship to one of the Code Platoon's coding bootcamp cohorts.",
link: '/scholarship/code_platoon',
},
{
@@ -61,8 +63,6 @@ const scholarshipOptions: ScholarshipOption[] = [
export default function ScholarshipsPage() {
return (
<>
-
-
,
@@ -29,8 +31,6 @@ const mentorItems = [
function Services() {
return (
-
-
- If you're new to coding, learn some basics at{' '}
+ If you're new to coding, learn some basics at{' '}
Ask people who have attended a coding bootcamp or coding bootcamp recruiters by joining
@@ -57,7 +59,7 @@ const questions = {
),
},
{
- title: `I’m a recent bootcamp/college graduate or looking for a job.`,
+ title: `I'm a recent bootcamp/college graduate or looking for a job.`,
content: (
@@ -96,10 +98,10 @@ const questions = {
),
},
{
- title: `I’m an admissions recruiter or representative from a Coding School`,
+ title: `I'm an admissions recruiter or representative from a Coding School`,
content: (
<>
- You’re welcome to post information and answer questions about your Coding School on{' '}
+ You're welcome to post information and answer questions about your Coding School on{' '}
#coding-schools only based on the{' '}
- Explore our numerous channels on a specific subject, or if you don’t see a channel ask on
+ Explore our numerous channels on a specific subject, or if you don't see a channel ask on
the #help or create a new channel.{' '}
Be advised
@@ -181,7 +183,7 @@ const questions = {
<>
Our instructions are focused on seeing-eye users. If you use assistive technology on
- your computer, [Slack's own
+ your computer, [Slack's own
guide](https://slack.com/help/articles/360000411963-Use-Slack-with-a-screen-reader) will
be more helpful.
@@ -195,8 +197,8 @@ const questions = {
/>
- Once you’ve found a channel that interests you, click the green “Join Channel” button.
- You’re ready to go! Have fun, learn, and connect with others!
+ Once you've found a channel that interests you, click the green "Join Channel" button.
+ You're ready to go! Have fun, learn, and connect with others!
-
-
-
Donate Now
@@ -27,10 +27,9 @@ function Sponsorship() {
hasTitleUnderline
columns={[
- Operation Code is pleased to invite America's leading technology companies to
- become shared value sponsors. Please join us and help make our mission a success.
- Together, we will create a new and secure future for today's veterans and military
- spouses.
+ Operation Code is pleased to invite America's leading technology companies to become
+ shared value sponsors. Please join us and help make our mission a success. Together, we
+ will create a new and secure future for today's veterans and military spouses.
,
]}
/>
diff --git a/pages/team.tsx b/app/team/page.tsx
similarity index 90%
rename from pages/team.tsx
rename to app/team/page.tsx
index 99391b268..2f484e1b8 100644
--- a/pages/team.tsx
+++ b/app/team/page.tsx
@@ -1,4 +1,4 @@
-import Head from 'components/head';
+import type { Metadata } from 'next';
import HeroBanner from 'components/HeroBanner/HeroBanner';
import { s3 } from 'common/constants/urls';
import Content from 'components/Content/Content';
@@ -6,6 +6,8 @@ import FlatCard from 'components/Cards/FlatCard/FlatCard';
import cynthiaHeadshot from 'public/static/images/cynthia.jpg';
import glomaniHeadshot from 'public/static/images/glomani.jpg';
+export const metadata: Metadata = { title: 'Team' };
+
const boardMembers = [
{
name: 'Cynthia Kao',
@@ -16,7 +18,7 @@ const boardMembers = [
{
name: 'Ali Taylor-Cipolla',
role: 'Board Vice-Chair',
- description: `Ali Taylor-Cipolla is a compliance specialist by day, activist by night who works with Microsoft and Operation Code, respectively. After spending more than 8 years in the restaurant management field, where it was relatively easy to find work as a constantly moving military spouse, Ali understands the challenges faced by military families, having faced them firsthand. After her partner’s retirement from the United States Air Force, she moved to the Seattle area and completed Microsoft’s Military Spouse Technology Academy. This was where she discovered that she did not want to develop software for a living. However, her love for the technology sector and the people in it brought her to her present role with Global Talent Acquisition at Microsoft. Ali aspires to empower, connect and engage with military families and veterans, helping them achieve stability and realization of their professional goals. She is a current member of the Washington State Military Transition Council where she advocates for the interests of military spouses in her new home state. She lives with her partner and two pet pythons, and when she’s not professionally finding solutions to large problems, she enjoys gardening, PC gaming, and her circus arts practice.`,
+ description: `Ali Taylor-Cipolla is a compliance specialist by day, activist by night who works with Microsoft and Operation Code, respectively. After spending more than 8 years in the restaurant management field, where it was relatively easy to find work as a constantly moving military spouse, Ali understands the challenges faced by military families, having faced them firsthand. After her partner's retirement from the United States Air Force, she moved to the Seattle area and completed Microsoft's Military Spouse Technology Academy. This was where she discovered that she did not want to develop software for a living. However, her love for the technology sector and the people in it brought her to her present role with Global Talent Acquisition at Microsoft. Ali aspires to empower, connect and engage with military families and veterans, helping them achieve stability and realization of their professional goals. She is a current member of the Washington State Military Transition Council where she advocates for the interests of military spouses in her new home state. She lives with her partner and two pet pythons, and when she's not professionally finding solutions to large problems, she enjoys gardening, PC gaming, and her circus arts practice.`,
imageSrc: 'https://operation-code-assets.s3.us-east-2.amazonaws.com/headshots/ali.jpg',
},
{
@@ -61,8 +63,6 @@ const boardMembers = [
export default function Team() {
return (
-
-
+
+ Please read these Terms of Service (“Terms”, “Terms of Service”) carefully before using
+ the https://www.operationcode.org/ website (the “Service”) operated by Operation Code
+ (“us”, “we”, or “our”).
+
+
+ Your access to and use of the Service is conditioned upon your acceptance of and
+ compliance with these Terms. These Terms apply to all visitors, users and others who
+ wish to access or use the Service.
+
+
+ By accessing or using the Service you agree to be bound by these Terms. If you disagree
+ with any part of the terms then you do not have permission to access the Service.
+
+ By creating an Account on our service, you agree to subscribe to newsletters, marketing or
+ promotional materials and other information we may send. However, you may opt out of
+ receiving any, or all, of these communications from us by following the unsubscribe link
+ or instructions provided in any email we send.
+
+ Our Service allows you to post, link, store, share and otherwise make available certain
+ information, text, graphics, videos, or other material (“Content”). You are responsible
+ for the Content that you post on or through the Service, including its legality,
+ reliability, and appropriateness.
+
+
+ By posting Content on or through the Service, You represent and warrant that: (i) the
+ Content is yours (you own it) and/or you have the right to use it and the right to grant
+ us the rights and license as provided in these Terms, and (ii) that the posting of your
+ Content on or through the Service does not violate the privacy rights, publicity rights,
+ copyrights, contract rights or any other rights of any person or entity. We reserve the
+ right to terminate the account of anyone found to be infringing on a copyright.
+
+
+ You retain any and all of your rights to any Content you submit, post or display on or
+ through the Service and you are responsible for protecting those rights. We take no
+ responsibility and assume no liability for Content you or any third party posts on or
+ through the Service. However, by posting Content using the Service you grant us the
+ right and license to use, modify, publicly perform, publicly display, reproduce, and
+ distribute such Content on and through the Service. You agree that this license includes
+ the right for us to make your Content available to other users of the Service, who may
+ also use your Content subject to these Terms.
+
+
+ Operation Code has the right but not the obligation to monitor and edit all Content
+ provided by users.
+
+
+ In addition, Content found on or through this Service are the property of Operation Code
+ or used with permission. You may not distribute, modify, transmit, reuse, download,
+ repost, copy, or use said Content, whether in whole or in part, for commercial purposes
+ or for personal gain, without express advance written permission from us.
+
+ When you create an account with us, you guarantee that you are above the age of 18, and
+ that the information you provide us is accurate, complete, and current at all times.
+ Inaccurate, incomplete, or obsolete information may result in the immediate termination
+ of your account on the Service.
+
+
+ You are responsible for maintaining the confidentiality of your account and password,
+ including but not limited to the restriction of access to your computer and/or account.
+ You agree to accept responsibility for any and all activities or actions that occur
+ under your account and/or password, whether your password is with our Service or a
+ third-party service. You must notify us immediately upon becoming aware of any breach of
+ security or unauthorized use of your account.
+
+
+ You may not use as a username the name of another person or entity or that is not
+ lawfully available for use, a name or trademark that is subject to any rights of another
+ person or entity other than you, without appropriate authorization. You may not use as a
+ username any name that is offensive, vulgar or obscene.
+
+ The Service and its original content (excluding Content provided by users), features and
+ functionality are and will remain the exclusive property of Operation Code and its
+ licensors. The Service is protected by copyright, trademark, and other laws of both the
+ United States and foreign countries. Our trademarks and trade dress may not be used in
+ connection with any product or service without the prior written consent of Operation
+ Code.
+
+ ),
+ },
+ {
+ id: 'links',
+ title: 'Links To Other Web Sites',
+ content: (
+ <>
+
+ Our Service may contain links to third party web sites or services that are not owned or
+ controlled by Operation Code.
+
+
+ Operation Code has no control over, and assumes no responsibility for the content,
+ privacy policies, or practices of any third party web sites or services. We do not
+ warrant the offerings of any of these entities/individuals or their websites.
+
+
+ You acknowledge and agree that Operation Code shall not be responsible or liable,
+ directly or indirectly, for any damage or loss caused or alleged to be caused by or in
+ connection with use of or reliance on any such content, goods or services available on
+ or through any such third party web sites or services.
+
+
+ We strongly advise you to read the terms and conditions and privacy policies of any
+ third party web sites or services that you visit.
+
+ We may terminate or suspend your account and bar access to the Service immediately,
+ without prior notice or liability, under our sole discretion, for any reason whatsoever
+ and without limitation, including but not limited to a breach of the Terms.
+
+
+ If you wish to terminate your account, you may simply discontinue using the Service.
+
+
+ All provisions of the Terms which by their nature should survive termination shall
+ survive termination, including, without limitation, ownership provisions, warranty
+ disclaimers, indemnity and limitations of liability.
+
+ You agree to defend, indemnify and hold harmless Operation Code and its licensee and
+ licensors, and their employees, contractors, agents, officers and directors, from and
+ against any and all claims, damages, obligations, losses, liabilities, costs or debt, and
+ expenses (including but not limited to attorney's fees), resulting from or arising out of
+ a) your use and access of the Service, by you or any person using your account and
+ password; b) a breach of these Terms, or c) Content posted on the Service.
+
+ In no event shall Operation Code, nor its directors, employees, partners, agents,
+ suppliers, or affiliates, be liable for any indirect, incidental, special, consequential
+ or punitive damages, including without limitation, loss of profits, data, use, goodwill,
+ or other intangible losses, resulting from (i) your access to or use of or inability to
+ access or use the Service; (ii) any conduct or content of any third party on the Service;
+ (iii) any content obtained from the Service; and (iv) unauthorized access, use or
+ alteration of your transmissions or content, whether based on warranty, contract, tort
+ (including negligence) or any other legal theory, whether or not we have been informed of
+ the possibility of such damage, and even if a remedy set forth herein is found to have
+ failed of its essential purpose.
+
+ Your use of the Service is at your sole risk. The Service is provided on an “AS IS“ and
+ ”AS AVAILABLE“ basis. The Service is provided without warranties of any kind, whether
+ express or implied, including, but not limited to, implied warranties of
+ merchantability, fitness for a particular purpose, non-infringement or course of
+ performance.
+
+
+ Operation Code its subsidiaries, affiliates, and its licensors do not warrant that a)
+ the Service will function uninterrupted, secure or available at any particular time or
+ location; b) any errors or defects will be corrected; c) the Service is free of viruses
+ or other harmful components; or d) the results of using the Service will meet your
+ requirements.
+
+ Some jurisdictions do not allow the exclusion of certain warranties or the exclusion or
+ limitation of liability for consequential or incidental damages, so the limitations above
+ may not apply to you.
+
+ These Terms shall be governed and construed in accordance with the laws of Oregon,
+ United States, without regard to its conflict of law provisions.
+
+
+ Our failure to enforce any right or provision of these Terms will not be considered a
+ waiver of those rights. If any provision of these Terms is held to be invalid or
+ unenforceable by a court, the remaining provisions of these Terms will remain in effect.
+ These Terms constitute the entire agreement between us regarding our Service, and
+ supersede and replace any prior agreements we might have had between us regarding the
+ Service.
+
+ We reserve the right, at our sole discretion, to modify or replace these Terms at any
+ time. If a revision is material we will provide at least 30 days notice prior to any new
+ terms taking effect. What constitutes a material change will be determined at our sole
+ discretion.
+
+
+ By continuing to access or use our Service after any revisions become effective, you
+ agree to be bound by the revised terms. If you do not agree to the new terms, you are no
+ longer authorized to use the Service.
+
- For use when Operation Code's logo name is between 0-1 inch in height. In most
- cases, use the Original Small Logo. The Stacked Small Logo is to be used where
- graphics needs are larger in vertical height than horizontal width with the Operation
- Code logo name still under 1 inch in height.
+ For use when Operation Code's logo name is between 0-1 inch in height. In most cases,
+ use the Original Small Logo. The Stacked Small Logo is to be used where graphics needs
+ are larger in vertical height than horizontal width with the Operation Code logo name
+ still under 1 inch in height.
diff --git a/components/ErrorDisplay/ErrorDisplay.tsx b/components/ErrorDisplay/ErrorDisplay.tsx
index f08b15978..d95a24fe3 100644
--- a/components/ErrorDisplay/ErrorDisplay.tsx
+++ b/components/ErrorDisplay/ErrorDisplay.tsx
@@ -1,5 +1,3 @@
-import Head from 'components/head';
-
export interface ErrorDisplayPropsType {
/**
* Displasy a status code instead of 'Error'.
@@ -9,20 +7,14 @@ export interface ErrorDisplayPropsType {
function ErrorDisplay({ statusCode }: ErrorDisplayPropsType) {
return (
- <>
-
-
-
-
-
-
-
{statusCode || 'Oh no'}!
-
- We're so ashamed. You definitely weren't supposed to see this...
-
-
+
+
+
{statusCode || 'Oh no'}!
+
+ We're so ashamed. You definitely weren't supposed to see this...
+
- >
+
);
}
diff --git a/components/Form/Checkbox/Checkbox.tsx b/components/Form/Checkbox/Checkbox.tsx
index 5c8a7c19a..5ba3d4316 100644
--- a/components/Form/Checkbox/Checkbox.tsx
+++ b/components/Form/Checkbox/Checkbox.tsx
@@ -46,7 +46,7 @@ function Checkbox({
/>
{/* negative margin here is to align the text with the checkbox bubble while allowing the field to use `flex items start` for if the label wraps lines */}
- {label}
+ {label}
diff --git a/components/Form/Form.tsx b/components/Form/Form.tsx
index 12249317b..6c8b329b3 100644
--- a/components/Form/Form.tsx
+++ b/components/Form/Form.tsx
@@ -1,10 +1,12 @@
-import { connect } from 'formik';
-import type { DetailedHTMLProps, FormHTMLAttributes, FunctionComponent } from 'react';
+'use client';
-const FormikConnectedForm = connect(({ formik: { handleReset, handleSubmit }, ...props }) => (
-
-)) as FunctionComponent, HTMLFormElement>>;
+import { useFormikContext } from 'formik';
+import type { DetailedHTMLProps, FormHTMLAttributes } from 'react';
-FormikConnectedForm.displayName = 'FormikConnectedForm';
+export default function FormikConnectedForm(
+ props: DetailedHTMLProps, HTMLFormElement>,
+) {
+ const { handleReset, handleSubmit } = useFormikContext();
-export default FormikConnectedForm;
+ return ;
+}
diff --git a/components/Form/Input/Input.tsx b/components/Form/Input/Input.tsx
index 1a4de81c6..d251a69a2 100644
--- a/components/Form/Input/Input.tsx
+++ b/components/Form/Input/Input.tsx
@@ -48,7 +48,7 @@ function Input({
{...field}
{...props}
className={cx(
- 'border border-secondary/50 rounded-sm text-lg p-2',
+ 'border border-secondary/50 rounded-sm text-lg p-2 bg-white',
'disabled:opacity-60 hover:disabled:cursor-not-allowed min-w-48',
'focus-visible:border-primary/50 focus-visible:shadow-xs focus-visible:shadow-primary/75 focus-visible:outline-none',
{
diff --git a/components/Form/Input/__stories__/Input.stories.tsx b/components/Form/Input/__stories__/Input.stories.tsx
index 7566a3fc9..5ee1f8a75 100644
--- a/components/Form/Input/__stories__/Input.stories.tsx
+++ b/components/Form/Input/__stories__/Input.stories.tsx
@@ -53,7 +53,7 @@ export default {
(InputStory: () => ReactNode) => (
<>
- NOTE: This component's story has no context outside of Formik and will not function
+ NOTE: This component's story has no context outside of Formik and will not function
properly
diff --git a/components/Form/Input/__tests__/__snapshots__/Input.test.tsx.snap b/components/Form/Input/__tests__/__snapshots__/Input.test.tsx.snap
index d56d41824..e28438acb 100644
--- a/components/Form/Input/__tests__/__snapshots__/Input.test.tsx.snap
+++ b/components/Form/Input/__tests__/__snapshots__/Input.test.tsx.snap
@@ -17,7 +17,7 @@ exports[`Input > should render with required props 1`] = `
data-testid="INPUT_FEEDBACK_GROUPING"
>
({
control: base => {
return {
...base,
- backgroundColor: isDisabled ? 'transparent' : 'white',
+ backgroundColor: isDisabled ? 'transparent' : '#f7f7f7',
borderColor:
isTouched && hasValidationStyling
? outerColor
diff --git a/components/Form/Select/__stories__/SelectMulti.stories.tsx b/components/Form/Select/__stories__/SelectMulti.stories.tsx
index 77fbcb644..86d465aaa 100644
--- a/components/Form/Select/__stories__/SelectMulti.stories.tsx
+++ b/components/Form/Select/__stories__/SelectMulti.stories.tsx
@@ -68,7 +68,7 @@ export default {
(SelectMultiStory: FC) => (
<>
- NOTE: This component's story has no context outside of Formik and will not function
+ NOTE: This component's story has no context outside of Formik and will not function
properly
diff --git a/components/Form/Select/__stories__/SelectSingle.stories.tsx b/components/Form/Select/__stories__/SelectSingle.stories.tsx
index 8ca8ba4bb..cee1ed015 100644
--- a/components/Form/Select/__stories__/SelectSingle.stories.tsx
+++ b/components/Form/Select/__stories__/SelectSingle.stories.tsx
@@ -82,7 +82,7 @@ export default {
(SelectSingleStory: FC) => (
<>
- NOTE: This component's story has no context outside of Formik and will not function
+ NOTE: This component's story has no context outside of Formik and will not function
properly
diff --git a/components/Form/Select/__tests__/__snapshots__/SelectMulti.test.tsx.snap b/components/Form/Select/__tests__/__snapshots__/SelectMulti.test.tsx.snap
index 597dfef82..48ef95254 100644
--- a/components/Form/Select/__tests__/__snapshots__/SelectMulti.test.tsx.snap
+++ b/components/Form/Select/__tests__/__snapshots__/SelectMulti.test.tsx.snap
@@ -30,7 +30,7 @@ exports[`Select > should render with required props 1`] = `
role="log"
/>
should render with required props 1`] = `
role="log"
/>
should render (multi) with required props 1`] = `
role="log"
/>
should render (non-multi) with required props 1`] =
role="log"
/>
- Operation Code's Code of Conduct.
+ Operation Code's Code of Conduct.
{' '}
*
>
@@ -194,7 +194,7 @@ export function RegistrationForm({
href={slackGuidelines}
analyticsEventLabel="Registration Slack Guidelines Checkbox Link"
>
- Operation Code's Slack Community Guidelines.
+ Operation Code's Slack Community Guidelines.
{' '}
*
>
diff --git a/components/Forms/RegistrationForm/__tests__/__snapshots__/RegistrationForm.test.tsx.snap b/components/Forms/RegistrationForm/__tests__/__snapshots__/RegistrationForm.test.tsx.snap
index 081be924e..9f0f4157d 100644
--- a/components/Forms/RegistrationForm/__tests__/__snapshots__/RegistrationForm.test.tsx.snap
+++ b/components/Forms/RegistrationForm/__tests__/__snapshots__/RegistrationForm.test.tsx.snap
@@ -22,7 +22,7 @@ exports[`RegistrationForm > should render with required props 1`] = `
>
should render with required props 1`] = `
>
should render with required props 1`] = `
>
should render with required props 1`] = `
>
should render with required props 1`] = `
>
should render with required props 1`] = `
>
should render with required props 1`] = `
>
should render with required props 1`] = `
value=""
/>
I have read and agree to
@@ -253,7 +253,7 @@ exports[`RegistrationForm > should render with required props 1`] = `
value=""
/>
I have read and agree to
diff --git a/components/Forms/UpdateProfileForm/UpdateProfileForm.tsx b/components/Forms/UpdateProfileForm/UpdateProfileForm.tsx
index 0cff9b692..e45318e2b 100644
--- a/components/Forms/UpdateProfileForm/UpdateProfileForm.tsx
+++ b/components/Forms/UpdateProfileForm/UpdateProfileForm.tsx
@@ -1,5 +1,7 @@
+'use client';
+
import { useState } from 'react';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import type { AxiosError } from 'axios';
import { getServerErrorMessage } from 'common/utils/api-utils';
import { MultiStepForm } from 'components/Form/MultiStepForm';
diff --git a/components/Forms/UpdateProfileForm/__stories__/UpdateProfileForm.stories.tsx b/components/Forms/UpdateProfileForm/__stories__/UpdateProfileForm.stories.tsx
index dacbfafe0..0c06ae1a2 100644
--- a/components/Forms/UpdateProfileForm/__stories__/UpdateProfileForm.stories.tsx
+++ b/components/Forms/UpdateProfileForm/__stories__/UpdateProfileForm.stories.tsx
@@ -8,6 +8,14 @@ import UpdateProfileForm from '../UpdateProfileForm';
export default {
component: UpdateProfileForm,
title: 'Forms/UpdateProfileForm',
+ parameters: {
+ nextjs: {
+ appDirectory: true,
+ navigation: {
+ pathname: '/profile',
+ },
+ },
+ },
};
const Template = (args: UpdateProfileFormProps) => {
diff --git a/components/Forms/UpdateProfileForm/__tests__/UpdateProfileForm.test.tsx b/components/Forms/UpdateProfileForm/__tests__/UpdateProfileForm.test.tsx
index 4702cf74a..74bde8d32 100644
--- a/components/Forms/UpdateProfileForm/__tests__/UpdateProfileForm.test.tsx
+++ b/components/Forms/UpdateProfileForm/__tests__/UpdateProfileForm.test.tsx
@@ -2,10 +2,6 @@ import createSnapshotTest from 'test-utils/createSnapshotTest';
import UpdateProfileForm from '../UpdateProfileForm';
describe('UpdateProfileForm', () => {
- beforeEach(() => {
- vi.mock('next/router', () => require('next-router-mock')); // eslint-disable-line global-require
- });
-
it('should render with required props', () => {
createSnapshotTest();
});
diff --git a/components/Forms/UpdateProfileForm/__tests__/__snapshots__/UpdateProfileForm.test.tsx.snap b/components/Forms/UpdateProfileForm/__tests__/__snapshots__/UpdateProfileForm.test.tsx.snap
index b5efa08ff..262d41749 100644
--- a/components/Forms/UpdateProfileForm/__tests__/__snapshots__/UpdateProfileForm.test.tsx.snap
+++ b/components/Forms/UpdateProfileForm/__tests__/__snapshots__/UpdateProfileForm.test.tsx.snap
@@ -62,7 +62,7 @@ exports[`UpdateProfileForm > should render with required props 1`] = `
role="log"
/>
should render with required props 1`] = `
data-testid="INPUT_FEEDBACK_GROUPING"
>
should render with required props 1`] = `
data-testid="INPUT_FEEDBACK_GROUPING"
>
should render in context of F
role="log"
/>
- Sets the number of "steps" are needed to fill the indicator bar. The
- totalSteps value needs to be more than, or equal to the stepNumber value.
+ Sets the number of “steps” are needed to fill the indicator bar. The totalSteps value
+ needs to be more than, or equal to the stepNumber value.
stepNumber
- Sets the number of "completed" steps and fills the indicator bar relative to
- the totalSteps value. The stepNumber value needs to be less than, or equal to the
- totalSteps value.
+ Sets the number of “completed” steps and fills the indicator bar relative to the
+ totalSteps value. The stepNumber value needs to be less than, or equal to the totalSteps
+ value.
diff --git a/components/Timeline/__tests__/__snapshots__/Timeline.test.tsx.snap b/components/Timeline/__tests__/__snapshots__/Timeline.test.tsx.snap
index b68e6d8df..4d807b946 100644
--- a/components/Timeline/__tests__/__snapshots__/Timeline.test.tsx.snap
+++ b/components/Timeline/__tests__/__snapshots__/Timeline.test.tsx.snap
@@ -78,7 +78,7 @@ exports[`Timeline > should render with no props passed passed 1`] = `
- he was inspired to pursue software engineering as a post-military occupation. He submitted an application to The Flatiron School while on active-duty, only to discover that the program did not accept the "Post-9/11 GI Bill" as payment.
+ he was inspired to pursue software engineering as a post-military occupation. He submitted an application to The Flatiron School while on active-duty, only to discover that the program did not accept the “Post-9/11 GI Bill” as payment.
- We will review your application and send an invite to our Slack team as soon as possible.
- If you do not receive an invite within a week, please email us at{' '}
-
- staff@operationcode.org
-
- .
-
-
-
- Go Home
-
-
- >
- );
-}
diff --git a/pages/terms.tsx b/pages/terms.tsx
deleted file mode 100644
index 817e50d7e..000000000
--- a/pages/terms.tsx
+++ /dev/null
@@ -1,258 +0,0 @@
-import Link from 'next/link';
-import Head from 'components/head';
-import HeroBanner from 'components/HeroBanner/HeroBanner';
-import Content from 'components/Content/Content';
-
-function Terms() {
- return (
- <>
-
-
-
-
-
-
- Last updated: September 8, 2018.
-
-
- Please read these Terms of Service ("Terms", "Terms of Service")
- carefully before using the https://www.operationcode.org/ website (the
- "Service") operated by Operation Code ("us", "we", or
- "our").
-
-
- Your access to and use of the Service is conditioned upon your acceptance of and
- compliance with these Terms. These Terms apply to all visitors, users and others who
- wish to access or use the Service.
-
-
- By accessing or using the Service you agree to be bound by these Terms. If you
- disagree with any part of the terms then you do not have permission to access the
- Service.
-
-
-
-
-
Communications
-
- By creating an Account on our service, you agree to subscribe to newsletters,
- marketing or promotional materials and other information we may send. However, you may
- opt out of receiving any, or all, of these communications from us by following the
- unsubscribe link or instructions provided in any email we send.
-
-
-
-
-
Content
-
- Our Service allows you to post, link, store, share and otherwise make available
- certain information, text, graphics, videos, or other material ("Content").
- You are responsible for the Content that you post on or through the Service, including
- its legality, reliability, and appropriateness.
-
-
- By posting Content on or through the Service, You represent and warrant that: (i) the
- Content is yours (you own it) and/or you have the right to use it and the right to
- grant us the rights and license as provided in these Terms, and (ii) that the posting
- of your Content on or through the Service does not violate the privacy rights,
- publicity rights, copyrights, contract rights or any other rights of any person or
- entity. We reserve the right to terminate the account of anyone found to be infringing
- on a copyright.
-
-
- You retain any and all of your rights to any Content you submit, post or display on or
- through the Service and you are responsible for protecting those rights. We take no
- responsibility and assume no liability for Content you or any third party posts on or
- through the Service. However, by posting Content using the Service you grant us the
- right and license to use, modify, publicly perform, publicly display, reproduce, and
- distribute such Content on and through the Service. You agree that this license
- includes the right for us to make your Content available to other users of the
- Service, who may also use your Content subject to these Terms.
-
-
- Operation Code has the right but not the obligation to monitor and edit all Content
- provided by users.
-
-
- In addition, Content found on or through this Service are the property of Operation
- Code or used with permission. You may not distribute, modify, transmit, reuse,
- download, repost, copy, or use said Content, whether in whole or in part, for
- commercial purposes or for personal gain, without express advance written permission
- from us.
-
-
-
-
-
Accounts
-
- When you create an account with us, you guarantee that you are above the age of 18,
- and that the information you provide us is accurate, complete, and current at all
- times. Inaccurate, incomplete, or obsolete information may result in the immediate
- termination of your account on the Service.
-
-
- You are responsible for maintaining the confidentiality of your account and password,
- including but not limited to the restriction of access to your computer and/or
- account. You agree to accept responsibility for any and all activities or actions that
- occur under your account and/or password, whether your password is with our Service or
- a third-party service. You must notify us immediately upon becoming aware of any
- breach of security or unauthorized use of your account.
-
-
- You may not use as a username the name of another person or entity or that is not
- lawfully available for use, a name or trademark that is subject to any rights of
- another person or entity other than you, without appropriate authorization. You may
- not use as a username any name that is offensive, vulgar or obscene.
-
-
-
-
-
Intellectual Property
-
- The Service and its original content (excluding Content provided by users), features
- and functionality are and will remain the exclusive property of Operation Code and its
- licensors. The Service is protected by copyright, trademark, and other laws of both
- the United States and foreign countries. Our trademarks and trade dress may not be
- used in connection with any product or service without the prior written consent of
- Operation Code.
-
-
-
-
-
Links To Other Web Sites
-
- Our Service may contain links to third party web sites or services that are not owned
- or controlled by Operation Code.
-
-
- Operation Code has no control over, and assumes no responsibility for the content,
- privacy policies, or practices of any third party web sites or services. We do not
- warrant the offerings of any of these entities/individuals or their websites.
-
-
- You acknowledge and agree that Operation Code shall not be responsible or liable,
- directly or indirectly, for any damage or loss caused or alleged to be caused by or in
- connection with use of or reliance on any such content, goods or services available on
- or through any such third party web sites or services.
-
-
- We strongly advise you to read the terms and conditions and privacy policies of any
- third party web sites or services that you visit.
-
-
-
Termination
-
- We may terminate or suspend your account and bar access to the Service immediately,
- without prior notice or liability, under our sole discretion, for any reason
- whatsoever and without limitation, including but not limited to a breach of the Terms.
-
-
- If you wish to terminate your account, you may simply discontinue using the Service.
-
-
- All provisions of the Terms which by their nature should survive termination shall
- survive termination, including, without limitation, ownership provisions, warranty
- disclaimers, indemnity and limitations of liability.
-
-
-
-
-
Indemnification
-
- You agree to defend, indemnify and hold harmless Operation Code and its licensee and
- licensors, and their employees, contractors, agents, officers and directors, from and
- against any and all claims, damages, obligations, losses, liabilities, costs or debt,
- and expenses (including but not limited to attorney's fees), resulting from or
- arising out of a) your use and access of the Service, by you or any person using your
- account and password; b) a breach of these Terms, or c) Content posted on the Service.
-
-
-
Limitation Of Liability
-
- In no event shall Operation Code, nor its directors, employees, partners, agents,
- suppliers, or affiliates, be liable for any indirect, incidental, special,
- consequential or punitive damages, including without limitation, loss of profits,
- data, use, goodwill, or other intangible losses, resulting from (i) your access to or
- use of or inability to access or use the Service; (ii) any conduct or content of any
- third party on the Service; (iii) any content obtained from the Service; and (iv)
- unauthorized access, use or alteration of your transmissions or content, whether based
- on warranty, contract, tort (including negligence) or any other legal theory, whether
- or not we have been informed of the possibility of such damage, and even if a remedy
- set forth herein is found to have failed of its essential purpose.
-
-
-
-
-
Disclaimer
-
- Your use of the Service is at your sole risk. The Service is provided on an "AS
- IS" and "AS AVAILABLE" basis. The Service is provided without
- warranties of any kind, whether express or implied, including, but not limited to,
- implied warranties of merchantability, fitness for a particular purpose,
- non-infringement or course of performance.
-
-
- Operation Code its subsidiaries, affiliates, and its licensors do not warrant that a)
- the Service will function uninterrupted, secure or available at any particular time or
- location; b) any errors or defects will be corrected; c) the Service is free of
- viruses or other harmful components; or d) the results of using the Service will meet
- your requirements.
-
-
-
-
-
Exclusions
-
- Some jurisdictions do not allow the exclusion of certain warranties or the exclusion
- or limitation of liability for consequential or incidental damages, so the limitations
- above may not apply to you.
-
-
-
-
-
Governing Law
-
- These Terms shall be governed and construed in accordance with the laws of Oregon,
- United States, without regard to its conflict of law provisions.
-
-
- Our failure to enforce any right or provision of these Terms will not be considered a
- waiver of those rights. If any provision of these Terms is held to be invalid or
- unenforceable by a court, the remaining provisions of these Terms will remain in
- effect. These Terms constitute the entire agreement between us regarding our Service,
- and supersede and replace any prior agreements we might have had between us regarding
- the Service.
-
-
-
-
-
Changes
-
- We reserve the right, at our sole discretion, to modify or replace these Terms at any
- time. If a revision is material we will provide at least 30 days notice prior to any
- new terms taking effect. What constitutes a material change will be determined at our
- sole discretion.
-
-
- By continuing to access or use our Service after any revisions become effective, you
- agree to be bound by the revised terms. If you do not agree to the new terms, you are
- no longer authorized to use the Service.
-
-
-
-
-
Contact Us
-
- If you have any questions about these Terms, please{' '}
- contact us.
-