Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jurisdiction to canonical form URLs #69

Merged
merged 19 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/app/[path]/[topic]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Example: courtformsonline.org/ma/housing
import { legalTopics } from '../../../config/topics.config';
import { fetchInterviews } from '../../../data/fetchInterviewData';
import { formSources } from '../../../config/formSources.config';
import { toUrlFriendlyString } from '../../utils/helpers';
import Button from 'react-bootstrap/Button';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
Expand Down Expand Up @@ -40,6 +42,9 @@ const Page = async ({ params }: PageProps) => {
key={index}
title={interview.title}
metadata={interview.metadata}
landingPageURL={
'/' + path + '/forms/' + toUrlFriendlyString(interview.title)
}
link={interview.link}
serverUrl={interview.serverUrl}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { fetchInterviews } from '../../../data/fetchInterviewData';
// Example: courtformsonline.org/ma/forms/[form-slug]
import { fetchInterviews } from '../../../../data/fetchInterviewData';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import Button from 'react-bootstrap/Button';
import { toUrlFriendlyString } from '../../utils/helpers';
import { toUrlFriendlyString } from '../../../utils/helpers';

interface PageProps {
params: {
Expand Down
77 changes: 77 additions & 0 deletions src/app/[path]/forms/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Example: courtformsonline.org/ma/forms
import { Form } from '../../interfaces/Form';
import InteractiveForm from '../../components/InteractiveForm';
import {
pathToServerConfig,
formSources,
} from '../../../config/formSources.config';
import { toUrlFriendlyString } from '../../utils/helpers';

async function getData() {
let allData: Form[] = [];

// Iterating over an array of server objects
for (const server of formSources.docassembleServers) {
const url = new URL(server.url); // Access the URL directly from the server object
url.pathname = '/list';
url.search = 'json=1';

const res = await fetch(url.toString());

// Handle errors
if (!res.ok) {
console.error(`Failed to fetch data from ${server.url}`);
continue; // Skip this server and continue with the next one
}

const data = await res.json();

if (!data.hasOwnProperty('interviews')) {
console.error(
`Data from ${server.url} does not contain "interviews" key`
);
continue; // Skip this server and continue with the next one
}

// Include the server name and server URL in the data
const interviews = data['interviews'].map((interview: Form) => ({
...interview,
serverName: server.name,
serverUrl: server.url,
}));

allData = allData.concat(interviews);
}

return allData;
}

interface PageProps {
params: {
path: string;
};
}

export default async function Page({ params }: PageProps) {
const forms = await getData();
const { path } = params;
const server = pathToServerConfig[path].name;

return (
<div className="container">
<h1 className="form-heading">All {server} Forms</h1>
{forms.map((form, index) => (
<InteractiveForm
key={index}
title={form.title}
metadata={form.metadata}
landingPageURL={
'/' + path + '/forms/' + toUrlFriendlyString(form.title)
}
link={form.link}
serverUrl={form.serverUrl}
/>
))}
</div>
);
}
5 changes: 3 additions & 2 deletions src/app/[path]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { pathToServerConfig } from '../../config/formSources.config';

// This is only necessary because there is a bug within next that doesnt allow static params to be passed from page to page properly. This layout only exists to assist in passing said props

// This is only necessary because there is a bug within next that doesnt allow
// static params to be passed from page to page properly. This layout only exists
// to assist in passing those params.
export async function generateStaticParams() {
return Object.keys(pathToServerConfig).map((key) => ({
path: key.toLowerCase(),
Expand Down
1 change: 1 addition & 0 deletions src/app/[path]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Example: courtformsonline.org/ma
import AffiliatesSection from '../components/AffiliatesSection';
import HeroSection from '../components/HeroSection';
import HowItWorksSection from '../components/HowItWorksSection';
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/InteractiveForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ import Link from 'next/link';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import Button from 'react-bootstrap/Button';
import { toUrlFriendlyString } from '../utils/helpers';

interface InteractiveFormProps {
title: string;
metadata: any;
landingPageURL: string;
link: string;
serverUrl: string;
}

const InteractiveForm: React.FC<InteractiveFormProps> = ({
title,
metadata,
landingPageURL,
link,
serverUrl,
}) => {
const fullUrl = `${serverUrl}${link}`;
const formPageUrl = `/forms/${toUrlFriendlyString(title)}`;

return (
<div>
<div className="form-content">
<div className="form-text-section">
<Link className="form-link" href={formPageUrl} passHref>
<Link className="form-link" href={landingPageURL} passHref>
<h2 className="form-subheading">{title}</h2>
</Link>
<ReactMarkdown remarkPlugins={[remarkGfm]}>
Expand Down
20 changes: 17 additions & 3 deletions src/app/components/NavigationBar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
'use client';
import Image from 'next/image';
import Link from 'next/link';
import { useParams } from 'next/navigation';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons';
import { faLanguage } from '@fortawesome/free-solid-svg-icons';
import {
faArrowUpRightFromSquare,
faLanguage,
} from '@fortawesome/free-solid-svg-icons';
import { prefix } from '../../../prefix';
import styles from '../css/NavigationBar.module.css';
import nextConfig from '../../../next.config';

export default function NavigationBar() {
const params = useParams();
const path = params && Object.hasOwn(params, 'path') ? params.path : '';
let pathSegment = '';
if (path && path.length > 0) pathSegment = '/' + path;
return (
<nav
role="navigation"
Expand Down Expand Up @@ -68,7 +76,13 @@ export default function NavigationBar() {
</ul>
</li> */}
<li className="nav-item">
<Link href="/forms" className={styles.NavLink}>
<Link href={pathSegment + '/forms'} className={styles.NavLink}>
All{' '}
{path && path.length > 0 ? (
<span className={styles.AllFormsPath}>{path}</span>
) : (
' '
)}{' '}
Forms
</Link>
</li>
Expand Down
7 changes: 6 additions & 1 deletion src/app/components/TopicCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@ const TopicCard = ({
index
}
className="form-tag text-decoration-none"
href={`/forms/${toUrlFriendlyString(interview.metadata.title)}`}
href={
'/' +
path +
'/forms/' +
toUrlFriendlyString(interview.metadata.title)
}
>
{interview.metadata.title}
</Link>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/TopicsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const TopicsSection = async ({ path, interviews, isError }) => {
return (
<section id="topics">
<div className="container">
<h2>Browse court forms by category</h2>
<h2>Browse Court Forms by Category</h2>
{filteredTopics.length > 9 && <ShowAllToggle />}
<div className="row row-cols-1 row-cols-md-3 g-5 card-container">
{filteredTopics.map((topic, index) => (
Expand Down
5 changes: 5 additions & 0 deletions src/app/css/NavigationBar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
&:not(:hover) {
text-decoration: none;
}

}

.AllFormsPath {
text-transform: uppercase;
}

.ExternalLinkIcon {
Expand Down
22 changes: 17 additions & 5 deletions src/app/forms/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Example: courtformsonline.org/forms
import { Form } from '../interfaces/Form';
import InteractiveForm from '../components/InteractiveForm';
import { formSources } from '../../config/formSources.config';
import {
pathToServerConfig,
formSources,
} from '../../config/formSources.config';
import { toUrlFriendlyString } from '../utils/helpers';

interface LegalFormsPageProps {
forms: Form[];
}
const serverProps = pathToServerConfig;

async function getData() {
let allData: Form[] = [];
Expand Down Expand Up @@ -32,11 +35,17 @@ async function getData() {
continue; // Skip this server and continue with the next one
}

let path = '';
for (let key in serverProps) {
if (serverProps[key].servers.indexOf(server.name) > -1) path = '/' + key;
}

// Include the server name and server URL in the data
const interviews = data['interviews'].map((interview: Form) => ({
...interview,
serverName: server.name,
serverUrl: server.url,
serverPath: path ? path : '',
}));

allData = allData.concat(interviews);
Expand All @@ -45,7 +54,7 @@ async function getData() {
return allData;
}

export default async function Page() {
export default async function Page(path) {
const forms = await getData();

return (
Expand All @@ -56,6 +65,9 @@ export default async function Page() {
key={index}
title={form.title}
metadata={form.metadata}
landingPageURL={
form.serverPath + '/forms/' + toUrlFriendlyString(form.title)
}
link={form.link}
serverUrl={form.serverUrl}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/app/interfaces/Form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export interface Form {
metadata: {
description: string;
};
landingPageURL: string;
link: string;
serverUrl: string;
serverPath: string;
}
15 changes: 12 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import './globals.css';
import { Inter } from 'next/font/google';
import 'bootstrap/dist/css/bootstrap.css';
import Script from 'next/script';

import NavigationBar from './components/NavigationBar';
import Footer from './components/Footer';

import 'bootstrap/dist/css/bootstrap.css';
import '@fortawesome/fontawesome-free/css/all.min.css';
import '@fontsource/inter/700.css'; // Bold weight
import Script from 'next/script';
import './globals.css';

const inter = Inter({ subsets: ['latin'] });

Expand All @@ -15,11 +17,18 @@ export const metadata = {
'Free online interactive court forms from Suffolk University Law School',
};

interface LayoutParams {
params: {
path: string;
};
}

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// console.log('params: ' + JSON.stringify(params));
return (
<html lang="en">
<body className={inter.className}>
Expand Down
1 change: 1 addition & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Example: courtformsonline.org
import AffiliatesSection from './components/AffiliatesSection';
import HeroSection from './components/HeroSection';
import HowItWorksSection from './components/HowItWorksSection';
Expand Down
3 changes: 2 additions & 1 deletion src/config/formSources.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ export const pathToServerConfig = {
ma: {
path: 'ma',
servers: ['Suffolk LIT Lab', 'Greater Boston Legal Services'],
name: 'Massachusetts',
},
};

export const formSources = {
docassembleServers: [
{
key: 'suffolkListLab',
key: 'suffolkLITLab',
url: 'https://apps.suffolklitlab.org',
name: 'Suffolk LIT Lab',
},
Expand Down