Update CMS client configuration and refactor content organization
The code changes involve a significant update to the configuration of our CMS client. The nature of retrieving content items has been refactored to be more granular, allowing for the identification and fetching of content from specified collections rather than general categories. These modifications improve the efficiency and specificity of content queries. Furthermore, other changes were made to provide a better alignment of our content structure, including the reorganization of content files and renaming of image paths in various components for consistency.
@@ -1,3 +1,5 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import { notFound } from 'next/navigation';
|
||||
@@ -8,13 +10,18 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { Post } from '../../blog/_components/post';
|
||||
|
||||
const getPostBySlug = cache(async (slug: string) => {
|
||||
const client = await createCmsClient();
|
||||
|
||||
return client.getContentItemBySlug({ slug, collection: 'posts' });
|
||||
});
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: { slug: string };
|
||||
}): Promise<Metadata | undefined> {
|
||||
const cms = await createCmsClient();
|
||||
const post = await cms.getContentItemById(params.slug);
|
||||
const post = await getPostBySlug(params.slug);
|
||||
|
||||
if (!post) {
|
||||
notFound();
|
||||
@@ -29,7 +36,7 @@ export async function generateMetadata({
|
||||
title,
|
||||
description,
|
||||
type: 'article',
|
||||
publishedTime: publishedAt.toDateString(),
|
||||
publishedTime: publishedAt?.toDateString(),
|
||||
url: post.url,
|
||||
images: image
|
||||
? [
|
||||
@@ -49,8 +56,7 @@ export async function generateMetadata({
|
||||
}
|
||||
|
||||
async function BlogPost({ params }: { params: { slug: string } }) {
|
||||
const cms = await createCmsClient();
|
||||
const post = await cms.getContentItemById(params.slug);
|
||||
const post = await getPostBySlug(params.slug);
|
||||
|
||||
if (!post) {
|
||||
notFound();
|
||||
|
||||
@@ -12,7 +12,7 @@ export const Post: React.FC<{
|
||||
<div>
|
||||
<PostHeader post={post} />
|
||||
|
||||
<div className={'mx-auto flex max-w-2xl flex-col space-y-6'}>
|
||||
<div className={'mx-auto flex max-w-2xl flex-col space-y-6 py-8'}>
|
||||
<article className={styles.HTML}>
|
||||
<ContentRenderer content={content} />
|
||||
</article>
|
||||
|
||||
@@ -15,12 +15,18 @@ export const generateMetadata = async () => {
|
||||
};
|
||||
};
|
||||
|
||||
async function BlogPage() {
|
||||
async function BlogPage({ searchParams }: { searchParams: { page: string } }) {
|
||||
const { t } = await createI18nServerInstance();
|
||||
const cms = await createCmsClient();
|
||||
|
||||
const page = searchParams.page ? parseInt(searchParams.page) : 0;
|
||||
const limit = 10;
|
||||
const offset = page * limit;
|
||||
|
||||
const posts = await cms.getContentItems({
|
||||
categories: ['blog'],
|
||||
collection: 'posts',
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,7 @@ import { DocsCards } from '../_components/docs-cards';
|
||||
const getPageBySlug = cache(async (slug: string) => {
|
||||
const client = await createCmsClient();
|
||||
|
||||
return client.getContentItemById(slug);
|
||||
return client.getContentItemBySlug({ slug, collection: 'documentation' });
|
||||
});
|
||||
|
||||
interface PageParams {
|
||||
|
||||
@@ -40,11 +40,14 @@ const Node: React.FC<{
|
||||
level: number;
|
||||
activePath: string;
|
||||
}> = ({ node, level, activePath }) => {
|
||||
const pathPrefix = `/docs`;
|
||||
const url = `${pathPrefix}/${node.url}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocsNavLink
|
||||
label={node.title}
|
||||
url={node.url}
|
||||
url={url}
|
||||
level={level}
|
||||
activePath={activePath}
|
||||
/>
|
||||
|
||||
@@ -6,9 +6,11 @@ async function DocsLayout({ children }: React.PropsWithChildren) {
|
||||
const cms = await createCmsClient();
|
||||
|
||||
const pages = await cms.getContentItems({
|
||||
categories: ['documentation'],
|
||||
collection: 'documentation',
|
||||
});
|
||||
|
||||
console.log(pages);
|
||||
|
||||
return (
|
||||
<div className={'flex'}>
|
||||
<DocsNavigation pages={buildDocumentationTree(pages)} />
|
||||
|
||||
@@ -19,7 +19,7 @@ async function DocsPage() {
|
||||
const { t } = await createI18nServerInstance();
|
||||
|
||||
const docs = await client.getContentItems({
|
||||
categories: ['documentation'],
|
||||
collection: 'documentation',
|
||||
});
|
||||
|
||||
// Filter out any docs that have a parentId, as these are children of other docs
|
||||
|
||||
@@ -76,7 +76,7 @@ function Home() {
|
||||
}
|
||||
width={3069}
|
||||
height={1916}
|
||||
src={`/assets/images/dashboard-demo.webp`}
|
||||
src={`/images/dashboard-demo.webp`}
|
||||
alt={`App Image`}
|
||||
/>
|
||||
</div>
|
||||
@@ -135,7 +135,7 @@ function Home() {
|
||||
<RightFeatureContainer>
|
||||
<Image
|
||||
className="rounded-2xl"
|
||||
src={'/assets/images/sign-in.webp'}
|
||||
src={'/images/sign-in.webp'}
|
||||
width={'626'}
|
||||
height={'683'}
|
||||
alt={'Sign In'}
|
||||
@@ -147,7 +147,7 @@ function Home() {
|
||||
<LeftFeatureContainer>
|
||||
<Image
|
||||
className="rounded-2xl"
|
||||
src={'/assets/images/dashboard.webp'}
|
||||
src={'/images/dashboard.webp'}
|
||||
width={'887'}
|
||||
height={'743'}
|
||||
alt={'Dashboard'}
|
||||
|
||||
@@ -66,9 +66,9 @@ export const metadata = {
|
||||
description: appConfig.description,
|
||||
},
|
||||
icons: {
|
||||
icon: '/assets/images/favicon/favicon.ico',
|
||||
icon: '/images/favicon/favicon.ico',
|
||||
shortcut: '/shortcut-icon.png',
|
||||
apple: '/assets/images/favicon/apple-touch-icon.png',
|
||||
apple: '/images/favicon/apple-touch-icon.png',
|
||||
other: {
|
||||
rel: 'apple-touch-icon-precomposed',
|
||||
url: '/apple-touch-icon-precomposed.png',
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: hello
|
||||
description: hello
|
||||
categories: ['blog']
|
||||
tags: []
|
||||
---
|
||||
|
||||
ddfdsfjsdnfjdks
|
||||
44
apps/web/content/posts/saas-starter-guide.mdoc
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: "A Comprehensive Starter Kit for Indie Hackers"
|
||||
description: "A comprehensive starter kit tailored specifically for SaaS and indie hackers, providing insights and resources to navigate the complexities of SaaS entrepreneurship."
|
||||
categories: ['blog']
|
||||
tags: []
|
||||
image: "/images/posts/saas-starter-blog-post.webp"
|
||||
publishedAt: 2024-04-10
|
||||
---
|
||||
|
||||
In the dynamic world of entrepreneurship, the Software as a Service (SaaS) model has emerged as a beacon of opportunity for indie hackers – individuals or small teams with big dreams of creating impactful software solutions. With the right tools and strategies, launching a successful SaaS startup is within reach for anyone with passion, dedication, and a clear vision. To empower aspiring entrepreneurs on this journey, we've curated a comprehensive starter kit tailored specifically for SaaS and indie hackers.
|
||||
|
||||
## 1. Idea Generation and Validation
|
||||
|
||||
Every successful SaaS startup begins with a compelling idea. To kickstart your journey, leverage idea generation techniques such as problem identification, market research, and brainstorming sessions. Validate your idea by seeking feedback from potential users and analyzing market demand through surveys, interviews, and competitor analysis.
|
||||
|
||||
## 2. MVP Development
|
||||
|
||||
The Minimal Viable Product (MVP) approach is a cornerstone of SaaS startup development. Focus on building a basic version of your product with core features that address the identified problem. Utilize prototyping tools, development frameworks, and outsourcing platforms to expedite MVP development while maintaining quality and cost-effectiveness.
|
||||
|
||||
## 3. Platform Selection
|
||||
|
||||
Choosing the right platform for your SaaS startup is crucial for scalability, flexibility, and long-term success. Evaluate factors such as hosting options, scalability features, security measures, and integration capabilities when selecting a platform. Popular choices include cloud-based solutions like Amazon Web Services (AWS), Microsoft Azure, and Google Cloud Platform (GCP).
|
||||
|
||||
## 4. Product Design and User Experience
|
||||
|
||||
A seamless user experience is paramount for SaaS success. Invest in intuitive product design, responsive UI/UX, and user-centric features to enhance customer satisfaction and retention. Leverage prototyping tools, usability testing, and user feedback loops to iterate and improve your product design continuously.
|
||||
|
||||
## 5. Marketing and Growth Strategies
|
||||
|
||||
Effective marketing and growth strategies are essential for acquiring, retaining, and monetizing customers. Develop a robust marketing plan encompassing content marketing, social media engagement, search engine optimization (SEO), email campaigns, and influencer partnerships. Leverage analytics tools and A/B testing to optimize your marketing efforts and maximize ROI.
|
||||
|
||||
## 6. Customer Support and Feedback Loop
|
||||
|
||||
Providing exceptional customer support is key to building a loyal customer base and fostering long-term relationships. Implement multi-channel support options, such as live chat, email support, and knowledge bases, to address customer inquiries promptly and effectively. Establish a feedback loop to gather insights from users and prioritize product enhancements accordingly.
|
||||
|
||||
## 7. Monetization Strategies
|
||||
|
||||
Explore diverse monetization strategies to generate revenue from your SaaS product. Options include subscription-based models, tiered pricing plans, usage-based billing, and freemium offerings. Conduct pricing experiments, competitor analysis, and customer surveys to determine the optimal pricing strategy for your target market.
|
||||
|
||||
## 8. Legal and Compliance Considerations
|
||||
|
||||
Ensure legal compliance and protect your SaaS startup from potential risks and liabilities. Consult legal experts to draft robust terms of service, privacy policies, and data protection agreements. Familiarize yourself with relevant regulations such as GDPR, CCPA, and PCI DSS to safeguard user data and maintain regulatory compliance.
|
||||
|
||||
Launching a SaaS startup as an indie hacker is a challenging yet rewarding journey. By leveraging the insights and resources provided in this comprehensive starter kit, you can navigate the complexities of SaaS entrepreneurship with confidence and drive sustainable growth for your venture. Embrace experimentation, iteration, and continuous learning as you embark on this exciting entrepreneurial adventure. Remember, the path to success is paved with resilience, determination, and a relentless pursuit of excellence.
|
||||
|
Before Width: | Height: | Size: 365 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -9,22 +9,22 @@
|
||||
"start_url": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/images/favicon/favicon-16x16.png",
|
||||
"src": "/images/favicon/favicon-16x16.png",
|
||||
"sizes": "16x16",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/images/favicon/favicon-32x32.png",
|
||||
"src": "/images/favicon/favicon-32x32.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/images/favicon/android-chrome-192x192.png",
|
||||
"src": "/images/favicon/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/images/favicon/android-chrome-512x512.png",
|
||||
"src": "/images/favicon/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
BIN
apps/web/public/images/posts/saas-starter-blog-post.webp
Normal file
|
After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |