Skip to content

Commit

Permalink
Add nodejs frontend (#64)
Browse files Browse the repository at this point in the history
* Made api better suited for detached frontend

* Created nodejs frontend

* Fix/add tests

* Add pull request model to admin
  • Loading branch information
mfosterw authored Feb 27, 2024
1 parent ac38740 commit f8447e4
Show file tree
Hide file tree
Showing 28 changed files with 5,715 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[*.{html,css,scss,json,yml,xml}]
[*.{html,css,scss,js,jsx,ts,tsx,json,yml,xml}]
indent_style = space
indent_size = 2

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dist/
downloads/
eggs/
.eggs/
lib/
# lib/ clashes with nextjs app and (probably) shouldn't come up
lib64/
parts/
sdist/
Expand Down
3 changes: 1 addition & 2 deletions config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework.routers import DefaultRouter, SimpleRouter

from democrasite.users.api.views import UserViewSet
from democrasite.webiscite.api.views import BillViewSet, PullRequestViewSet
from democrasite.webiscite.api.views import BillViewSet

router: SimpleRouter | DefaultRouter
if settings.DEBUG:
Expand All @@ -12,7 +12,6 @@

router.register("users", UserViewSet)
router.register("bills", BillViewSet)
router.register("pull-requests", PullRequestViewSet)

# Unfortunately if we want to automatically create links between models we can't use a namespace
# app_name = "api"
Expand Down
24 changes: 22 additions & 2 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# see: https://github.com/typeddjango/django-stubs
django_stubs_ext.monkeypatch()


# TODO: Change to base_dir
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
# democrasite/
APPS_DIR = ROOT_DIR / "democrasite"
Expand Down Expand Up @@ -284,6 +284,26 @@
"root": {"level": "INFO", "handlers": ["console"]},
}

# django-debug-toolbar
# ------------------------------------------------------------------------------
# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-panels
DEBUG_TOOLBAR_PANELS = [
"debug_toolbar.panels.history.HistoryPanel",
"debug_toolbar.panels.versions.VersionsPanel",
"debug_toolbar.panels.timer.TimerPanel",
"debug_toolbar.panels.settings.SettingsPanel",
"debug_toolbar.panels.headers.HeadersPanel",
"debug_toolbar.panels.request.RequestPanel",
"debug_toolbar.panels.sql.SQLPanel",
"debug_toolbar.panels.staticfiles.StaticFilesPanel",
"debug_toolbar.panels.templates.TemplatesPanel",
"debug_toolbar.panels.cache.CachePanel",
"debug_toolbar.panels.signals.SignalsPanel",
"debug_toolbar.panels.redirects.RedirectsPanel",
# 'debug_toolbar.panels.profiling.ProfilingPanel', causes errors with frontend
]


# Celery
# ------------------------------------------------------------------------------
if USE_TZ:
Expand Down Expand Up @@ -384,7 +404,7 @@
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticatedOrReadOnly",),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

Expand Down
1 change: 1 addition & 0 deletions democrasite-frontend/.env.local
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BASE_API_URL=http://localhost:8000/api
3 changes: 3 additions & 0 deletions democrasite-frontend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
36 changes: 36 additions & 0 deletions democrasite-frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
# .env*.local # uncomment if sensitive environment variables added

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions democrasite-frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
39 changes: 39 additions & 0 deletions democrasite-frontend/app/bills/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Container, Center } from "@mantine/core";

import Bill from "@/components/Bill/Bill";
import fetchBills, { fetchBill } from "@/lib/fetch_bills";

export async function generateMetadata({
params,
searchParams,
}: {
params: { id: number };
searchParams: URLSearchParams;
}) {
console.log(params);
return { title: `${(await fetchBill(params.id)).name}` };
}

export default async function BillDetail({
params,
}: {
params: { id: number };
}) {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-32">
<Center h="100%">
<Container size="xs">
<Bill bill={await fetchBill(params.id)} />
</Container>
</Center>
</main>
);
}

export async function generateStaticParams() {
const bills = await fetchBills();

return bills.map((bill: any) => ({
id: bill.id.toString(),
}));
}
Binary file added democrasite-frontend/app/favicon.ico
Binary file not shown.
23 changes: 23 additions & 0 deletions democrasite-frontend/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

@layer utilities {
.text-balance {
text-wrap: balance;
}
}
33 changes: 33 additions & 0 deletions democrasite-frontend/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

import "@mantine/core/styles.css";
import { ColorSchemeScript, MantineProvider } from "@mantine/core";

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

export const metadata: Metadata = {
title: {
default: "Democrasite",
template: "%s - Democrasite",
},
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html>
<head>
<ColorSchemeScript />
</head>
<body className={inter.className}>
<MantineProvider>{children}</MantineProvider>
</body>
</html>
);
}
10 changes: 10 additions & 0 deletions democrasite-frontend/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import BillList from "@/components/BillList/BillList";
import fetchBills from "@/lib/fetch_bills";

export default async function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<BillList bill_list={await fetchBills()} />
</main>
);
}
49 changes: 49 additions & 0 deletions democrasite-frontend/components/Bill/Bill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
Title,
Text,
Anchor,
Divider,
Group,
Stack,
Center,
Container,
} from "@mantine/core";

export default function Bill({ bill }: any) {
return (
<Stack>
<Container ta="center">
<Anchor href={`/bills/${bill.id}`}>
<Title order={3}>
Bill {bill.id}: {bill.name} (PR&nbsp;#{bill.pull_request.number})
</Title>
</Anchor>
{bill.constitutional && <Text c="cyan">Constitutional Amendment</Text>}
{bill.state != "Open" && (
<Text c={bill.state == "Approved" ? "green" : "red"}>
{bill.state}
</Text>
)}
</Container>
<Divider />
<Text lineClamp={3}>{bill.description}</Text>
<Anchor href="#" ta="right" right="0">
{/* href will be bill.diff_url once it is added to the database */}
<Text span c="green">
+{bill.pull_request.additions}
</Text>
<Text span c="red" ml="sm">
-{bill.pull_request.deletions}
</Text>
</Anchor>
<Group justify="space-between">
<Group>
<Text c="green">Yes: {bill.yes_votes.length}</Text>
</Group>
<Group>
<Text c="red">No: {bill.no_votes.length}</Text>
</Group>
</Group>
</Stack>
);
}
38 changes: 38 additions & 0 deletions democrasite-frontend/components/BillList/BillList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Card, Grid, GridCol } from "@mantine/core";
import Bill from "../../components/Bill/Bill";

const bills = [
{
name: "test",
description: "This is a test",
time_created: "2024-02-19T00:05:35.164799-06:00",
author: "http://127.0.0.1:8000/api/users/matthew/",
pull_request: "http://127.0.0.1:8000/api/pull-requests/-1/",
yes_votes: [],
no_votes: ["http://127.0.0.1:8000/api/users/mfosterw/"],
url: "http://127.0.0.1:8000/api/bills/1/",
},
{
name: "hi",
description:
"This bill has an extremely long-winded description. In fact, the description is so long that it will have to be cut off when being displayed. That makes it a pretty long description in my opinion.",
time_created: "2024-02-19T00:05:35.164799-06:00",
author: "http://127.0.0.1:8000/api/users/matthew/",
pull_request: "http://127.0.0.1:8000/api/pull-requests/-2/",
yes_votes: ["http://127.0.0.1:8000/api/users/mfosterw/"],
no_votes: [],
url: "http://127.0.0.1:8000/api/bills/18/",
},
];

export default function BillList(props: any) {
const cards = props.bill_list.map((bill: any) => (
<GridCol key={bill.id} span={{ base: 12, sm: 6, md: 4, lg: 3 }}>
<Card shadow="md" padding="lg" radius="sm" withBorder>
<Bill bill={bill} />
</Card>
</GridCol>
));

return <Grid gutter={{ base: 5, xs: "md", xl: "xl" }}>{cards}</Grid>;
}
11 changes: 11 additions & 0 deletions democrasite-frontend/lib/fetch_bills.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function fetchBills() {
return fetch(`${process.env.BASE_API_URL}/bills/`, {
next: { revalidate: 60 }, // revalidate every minute for testing
}).then((response) => response.json());
}

export function fetchBill(id: number) {
return fetch(`${process.env.BASE_API_URL}/bills/${id}`, {
cache: "no-store", // no caching for testing (probably want to cache info but not vote data)
}).then((response) => response.json());
}
4 changes: 4 additions & 0 deletions democrasite-frontend/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};

export default nextConfig;
Loading

0 comments on commit f8447e4

Please sign in to comment.