Skip to content
Open
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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NEXT_PUBLIC_API_BASE_URL=http://localhost:5000
DOTNET_SERVER_URL=http://localhost:5000
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"feed": "^4.2.2",
"focus-visible": "^5.2.0",
"next": "^13.4.12",
"nextjs": "^0.0.3",
"postcss-focus-visible": "^9.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
16 changes: 8 additions & 8 deletions src/api/axiosConfig.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import axios from "axios"
import { isHyperlink } from '@/lib/isHyperlink'

const BASE_URL = process.env.DOTNET_SERVER_URL
const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:5000'

const AXIOS_BASE = axios.create({
baseURL: BASE_URL,
})
const API = axios.create({
baseURL: BASE_URL,
headers: {
'Content-Type': 'application/json',
},
})

const JSON_CLIENT = isHyperlink(BASE_URL) ? AXIOS_BASE : false

export default JSON_CLIENT
export default API
17 changes: 17 additions & 0 deletions src/api/postsApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,20 @@ export const getPost = (postSlug) => {
return {}
}
}

export const createPost = async ({ title, description }) => {
if (!API) {
throw ['API is not configured correctly.'];
}

try {
const response = await API.post('/posts/', { title, description });
return response.data;
} catch (error) {
if (error.response?.data?.errors) {
throw error.response.data.errors;
}
console.error(error);
throw ['An unexpected error occurred.'];
}
};
2 changes: 1 addition & 1 deletion src/components/ArticleLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function ArticleLayout({ children, meta, isRssFeed = false, previousPathn
return (
<>
<Head>
<title>{`${meta.title} - Spencer Sharp`}</title>
<title>{`${meta.title} - David Kim`}</title>
<meta name="description" content={meta.description} />
</Head>
<Container className="mt-16 lg:mt-32">
Expand Down
2 changes: 2 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function MobileNavigation(props) {
<ul className="-my-2 divide-y divide-zinc-100 text-base text-zinc-800">
<MobileNavItem href="/about">About</MobileNavItem>
<MobileNavItem href="/posts">Posts</MobileNavItem>
<MobileNavItem href="/posts/new">New Post</MobileNavItem>
<MobileNavItem href="/projects">Projects</MobileNavItem>
<MobileNavItem href="/speaking">Speaking</MobileNavItem>
<MobileNavItem href="/uses">Uses</MobileNavItem>
Expand Down Expand Up @@ -134,6 +135,7 @@ function DesktopNavigation(props) {
<ul className="flex rounded-full bg-white/90 px-3 text-sm font-medium text-zinc-800 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur">
<NavItem href="/about">About</NavItem>
<NavItem href="/posts">Posts</NavItem>
<NavItem href="/posts/new">New Post</NavItem>
<NavItem href="/projects">Projects</NavItem>
<NavItem href="/speaking">Speaking</NavItem>
<NavItem href="/uses">Uses</NavItem>
Expand Down
72 changes: 72 additions & 0 deletions src/pages/posts/new.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useState } from 'react';
import { useRouter } from 'next/router';
import { createPost } from '@/api/postsApi';

export default function NewPostPage() {
const router = useRouter();
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [errors, setErrors] = useState([]);

const handleSubmit = async (e) => {
e.preventDefault();
setErrors([]);

try {
const result = await createPost({ title, description });
const slug = result.post?.slug;
if (slug) {
router.push(`/posts/${slug}`);
} else {
throw ['Unexpected response format'];
}
} catch (err) {
setErrors(err);
}
};

return (
<div className="max-w-xl mx-auto mt-10 px-4">
<h1 className="text-2xl font-bold mb-4">Create New Post</h1>

{errors.length > 0 && (
<div className="bg-red-100 text-red-700 px-4 py-3 rounded mb-4">
<ul className="list-disc ml-5">
{errors.map((err, i) => (
<li key={i}>{err}</li>
))}
</ul>
</div>
)}

<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block font-medium mb-1">Title</label>
<input
type="text"
className="w-full border rounded px-3 py-2"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>

<div>
<label className="block font-medium mb-1">Description</label>
<textarea
className="w-full border rounded px-3 py-2"
rows="4"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>

<button
type="submit"
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
>
Create Post
</button>
</form>
</div>
);
}
66 changes: 66 additions & 0 deletions src/pages/posts/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useState } from 'react';
import { useRouter } from 'next/router';
import { createPost } from '../../api/postsApi';

export default function NewPostPage() {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [errors, setErrors] = useState([]);
const router = useRouter();

const handleSubmit = async (e) => {
e.preventDefault();
setErrors([]);

try {
const result = await createPost({ title, description });
const slug = result.post.slug;
router.push(`/posts/${slug}`);
} catch (err) {
setErrors(err);
}
};

return (
<div className="max-w-xl mx-auto mt-12 px-4">
<h1 className="text-2xl font-semibold mb-4">Create New Post</h1>

{errors.length > 0 && (
<ul className="bg-red-100 text-red-800 px-4 py-2 mb-4 rounded">
{errors.map((err, idx) => (
<li key={idx}>• {err}</li>
))}
</ul>
)}

<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium">Title</label>
<input
type="text"
className="w-full border rounded px-3 py-2"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>

<div>
<label className="block text-sm font-medium">Description</label>
<textarea
className="w-full border rounded px-3 py-2"
rows="4"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>

<button
type="submit"
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
>
Create Post
</button>
</form>
</div>
);
}