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

feat(blog): trying to add velite #2

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ yarn-error.log*

# turbo
.turbo

# velite
.velite
30 changes: 30 additions & 0 deletions apps/nextjs/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ export default (phase, { defaultConfig }) => {
},
],
},
// Extend webpack configuration to include VeliteWebpackPlugin
webpack: (config, { isServer }) => {
// Initialize VeliteWebpackPlugin with any specific options you might have
// For demonstration, no options are passed
config.plugins.push(new VeliteWebpackPlugin());

// Return the modified configuration
return config;
},

/** We already do linting and typechecking as separate tasks in CI */
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
Expand All @@ -47,3 +57,23 @@ export default (phase, { defaultConfig }) => {

return nextConfig;
};

class VeliteWebpackPlugin {
static started = false;
constructor(/** @type {import('velite').Options} */ options = {}) {
this.options = options;
}
apply(/** @type {import('webpack').Compiler} */ compiler) {
// executed three times in nextjs !!!
// twice for the server (nodejs / edge runtime) and once for the client
compiler.hooks.beforeCompile.tap("VeliteWebpackPlugin", async () => {
if (VeliteWebpackPlugin.started) return;
VeliteWebpackPlugin.started = true;
const dev = compiler.options.mode === "development";
this.options.watch = this.options.watch ?? dev;
this.options.clean = this.options.clean ?? !dev;
const { build } = await import("velite");
await build(this.options); // start velite
});
}
}
8 changes: 6 additions & 2 deletions apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"lint": "dotenv -v SKIP_ENV_VALIDATION=1 next lint",
"start": "pnpm with-env next start",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env --"
"with-env": "dotenv -e ../../.env --",
"content": "velite --clean"
},
"dependencies": {
"@headlessui/tailwindcss": "^0.2.0",
Expand Down Expand Up @@ -62,8 +63,11 @@
"dotenv-cli": "^7.3.0",
"eslint": "^8.56.0",
"prettier": "^3.1.1",
"rehype-pretty-code": "^0.12.6",
"shiki": "1.0.0-beta.6",
"tailwindcss": "^3.4.0",
"typescript": "^5.3.3"
"typescript": "^5.3.3",
"velite": "0.1.0-beta.6"
},
"eslintConfig": {
"root": true,
Expand Down
Binary file added apps/nextjs/public/static/cover-9986b0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/nextjs/public/static/journey-b48700.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export default async function MealPage({
{/* Macros donut chart */}
<MacrosDonut />
{/* Macros progress charts */}
<MacrosProgress />
<Suspense fallback={<div>Loading...</div>}>
<MacrosProgress meal={meal} />
</Suspense>
{/* TODO: Meal Images */}
{/* Foods */}
<Suspense fallback={<div>Loading...</div>}>
Expand Down
51 changes: 51 additions & 0 deletions apps/nextjs/src/app/home/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Metadata } from "next";
import Image from "next/image";
import { notFound } from "next/navigation";
import { posts } from "#site/content";

interface PostProps {
params: {
slug: string;
};
}

function getPostBySlug(slug: string) {
return posts.find((post) => post.slug === slug);
}

export function generateMetadata({ params }: PostProps): Metadata {
const post = getPostBySlug(params.slug);
if (post == null) return {};
return { title: post.title, description: post.description };
}

export function generateStaticParams(): PostProps["params"][] {
return posts.map((post) => ({
slug: post.slug,
}));
}

export default function PostPage({ params }: PostProps) {
const post = getPostBySlug(params.slug);

if (post == null) notFound();

return (
<article className="prose lg:prose-lg dark:prose-invert py-6">
<h1 className="mb-2">{post.title}</h1>
{post.description && (
<p className="mt-0 text-xl text-slate-700 dark:text-slate-200">
{post.description}
</p>
)}
{post.cover && (
<Image src={post.cover} alt={post.title} placeholder="blur" />
)}
<hr className="my-4" />
<div
className="prose"
dangerouslySetInnerHTML={{ __html: post.content }}
></div>
</article>
);
}
21 changes: 21 additions & 0 deletions apps/nextjs/src/app/home/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Link from "next/link";
import { posts } from "#site/content";

export default async function Home() {
return (
<div className="prose dark:prose-invert">
{posts.map((post) => (
<article
key={post.slug}
className={post.featured ? "bg-slate-300" : ""}
>
<Link href={`/blog/${post.slug}`}>
<h2>{post.title}</h2>
</Link>
<p>{post.excerpt}</p>
<p>{post.tags.join(", ")}</p>
</article>
))}
</div>
);
}
3 changes: 3 additions & 0 deletions apps/nextjs/src/app/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Link from "next/link";

import { Button } from "@nourish/ui/button";

import { GetStartedButton } from "~/components/auth/get-started-button";
Expand All @@ -12,6 +14,7 @@ export default function HomePage() {
<GetStartedButton>
<Button>Get Started</Button>
</GetStartedButton>
<Link href="/blog">Blog</Link>
</div>
</main>
);
Expand Down
17 changes: 17 additions & 0 deletions apps/nextjs/src/components/blog/mdx-content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as runtime from "react/jsx-runtime";
import Image from "next/image";

interface MdxProps {
code: string;
components?: Record<string, React.ComponentType>;
}

const useMDXComponent = (code: string) => {
const fn = new Function(code);
return fn({ ...runtime }).default;
};

export function MDXContent({ code, components }: MdxProps) {
const Component = useMDXComponent(code);
return <Component components={{ Image, ...components }} />;
}
100 changes: 65 additions & 35 deletions apps/nextjs/src/components/dashboard/macros-progress.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,69 @@
import type { RouterOutputs } from "@nourish/api";

import { NutrientItem } from "~/components/dashboard/nutrient-item";
import { calculateTotalCalories } from "~/lib/utils";

interface MacrosProgressProps {
meal: Promise<RouterOutputs["meal"]["byId"]>;
}

const MacrosProgress = async (props: MacrosProgressProps) => {
const meal = await props.meal;

if (!meal) {
return null;
}

// Calculate total calories for the meal
let totalCalories = 0;
meal.nutrition.forEach((nutritionItem) => {
const caloriesPer100g = parseFloat(
nutritionItem.foodItem.calories_per_100g ?? "0",
);
const servingSize = nutritionItem.serving_size ?? "";
const quantity = parseFloat(nutritionItem.servings ?? "1");
totalCalories += calculateTotalCalories(
servingSize,
caloriesPer100g,
quantity,
);
});

const MacrosProgress = () => (
<div className="flex w-full flex-col gap-2 px-4 py-4">
{/* Energy */}
<NutrientItem
name={"Energy"}
currentValue={2000.0}
dailyValue={"2500.0"}
unit={"kcal"}
/>
{/* Protein */}
<NutrientItem
name={"Protein"}
currentValue={4.8}
dailyValue={"119.7"}
unit={"g"}
color="lime"
/>
{/* Carbs */}
<NutrientItem
name={"Carbs"}
currentValue={69.2}
dailyValue={"215.4"}
unit={"g"}
color="violet"
/>
{/* Fat */}
<NutrientItem
name={"Fat"}
currentValue={13.3}
dailyValue={"63.8"}
unit={"g"}
color="amber"
/>
</div>
);
return (
<div className="flex w-full flex-col gap-2 px-4 py-4">
{/* Energy */}
<NutrientItem
name={"Energy"}
currentValue={totalCalories}
dailyValue={"2500.0"}
unit={"kcal"}
/>
{/* Protein */}
<NutrientItem
name={"Protein"}
currentValue={4.8}
dailyValue={"119.7"}
unit={"g"}
color="lime"
/>
{/* Carbs */}
<NutrientItem
name={"Carbs"}
currentValue={69.2}
dailyValue={"215.4"}
unit={"g"}
color="violet"
/>
{/* Fat */}
<NutrientItem
name={"Fat"}
currentValue={13.3}
dailyValue={"63.8"}
unit={"g"}
color="amber"
/>
</div>
);
};

export { MacrosProgress };
32 changes: 32 additions & 0 deletions apps/nextjs/src/content/options/index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Nourish
title: Nourish
description: Nourish is an open source nutritional health platform. Optimize your nutrition for better health and performance.
keywords:
- Blog
- Nourish
- nutrition
- tracking
author:
name: Trevor
email: [email protected]
url: https://nourish.run
links:
- text: HOME
link: /
type: navigation
- text: Blog
link: /blog
type: navigation
- text: Privacy Policy
link: /privacy-policy
type: copyright
- text: Terms of Service
link: /terms-of-service
type: copyright
socials:
- name: GitHub
icon: github
link: https://github.com/nourish-run/nourish.run
- name: Twitter
icon: twitter
link: https://twitter.com/nourishrun
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/nextjs/src/content/posts/2024-02-05-test/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Test
slug: test
date: 1992-02-25 13:00:00
cover: cover.jpg
---

hi
8 changes: 8 additions & 0 deletions apps/nextjs/src/content/posts/2024-02-06-hello-world/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Hello world
slug: hello-world
date: 1992-02-25 13:22
cover: journey.png
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/nextjs/src/content/tags/index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- name: Updates
slug: updates

- name: Science
slug: science

- name: Engineering
slug: engineering
25 changes: 25 additions & 0 deletions apps/nextjs/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,28 @@ export function parseServingSizes(servingSizes: string) {
// Split the string on "),"
return servingSizes.split(/(?<=\)),\s?/);
}

export function calculateTotalCalories(
servingSize: string,
caloriesPer100g: number,
quantity: number,
) {
// Extract the weight in grams from the serving size string
const gramsMatch = servingSize.match(/(\d+\.?\d*)g\)/);
let grams = 0;

if (gramsMatch?.[1]) {
// Parse the grams value
grams = parseFloat(gramsMatch[1]);
} else {
// Handle the error case where grams are not found
console.error("Invalid serving size format. Grams not found.");
return 0;
}

// Calculate the total grams consumed
const totalGrams = grams * quantity;

// Calculate the total calories
return (caloriesPer100g / 100) * totalGrams;
}
3 changes: 2 additions & 1 deletion apps/nextjs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"]
"~/*": ["./src/*"],
"#site/content": ["./.velite"]
},
"plugins": [{ "name": "next" }],
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
Expand Down
Loading
Loading