Creating a blog and following the NEXTJS documentation or other article on the web, my articles rendered as plain text on the web.
I have my posts page:
export default function posts({ posts }) {
console.log(posts);
return (
<>
<Head>
<title>Posts</title>
</Head>
{posts.map((post, index) => (
<Article post={post} key={index} />
))}
</>
);
}
export default function getStaticProps(){
// Get list of files from Articles
const files = fs.readdirSync('articles')
// Get frontmatter & slug from each post
const posts = files.map((fileName) => {
const slug = fileName.replace('.md', '')
const readFile = fs.readFileSync(`posts/${fileName}`, 'utf-8')
const {data: frontmatter} = matter(readFile)
return {
slug,
frontmatter
}
})
return {
props: {
posts: posts.sort(sortByDate),
}
}
}
and then I have my [slug].js file:
export default function postPage({ frontmatter, content }) {
const { title, date, cover_image } = frontmatter;
return (
<>
<div className='w-full text-left mb-16 md:mt-16'>
<div className='max-w-[90%] md:max-w-[70%] lg:max-w-[60%] m-auto text-center flex flex-col items-start justify-between'>
<Link href='/posts'>
<a className='italic text-cyan-500'>
←<span className='text-gray-300 ml-2'>Go Back</span>
</a>
</Link>
</div>
</div>
<div className='max-w-[90%] md:max-w-[70%] lg:max-w-[60%] m-auto flex flex-col justify-between gap-4 items-start '>
<h1 className='uppercase font-bold text-gray-300 text-2xl md:text-3xl'>
{title}
</h1>
<p className='text-gray-400'>Posted on {date}</p>
<Image
src={cover_image}
alt='cover image'
style={css}
width={100}
height={100}
unoptimized={true}
className='rounded-md rounded-b-none'
/>
</div>
<div
className='max-w-[90%] md:max-w-[70%] lg:max-w-[60%] m-auto'
dangerouslySetInnerHTML={{ __html: md().render(content) }}
/>
</>
);
}
export async function getStaticPaths() {
const files = fs.readdirSync('articles');
const paths = files.map((filename) => ({
params: {
slug: filename.replace('.md', ''),
},
}));
// console.log(paths);
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params: { slug } }) {
const fileName = fs.readFileSync(
path.join('articles', slug + '.md'),
'utf-8'
);
const { data: frontmatter, content } = matter(fileName);
return {
props: {
frontmatter,
content,
},
};
}
Would love to have some help because i'm stucked for a while and don't understand why. I've tried different things with remark, remar-html, markdown-it, but the same result everytime.
I've tried different things with remark, remar-html, markdown-it, but the same result everytime.
Related
I am building this ecommerce site based on strapi and vue3/pinia.
My products come from ProductStore.js through this function:
async fillArchive() {
this.loading = true;
this.products = await fetch(
apiUrl +
"/api/products?fields=name,featured,description,price,slug&populate=image,category"
)
.then((res) => res.json())
.then((data) => (this.products = data.data))
.catch((err) => (error.value = console.log(err)));
this.loading = false;
},
I get my categories from CategoryStore.js:
async fill() {
this.loading = true;
this.categories = await fetch(apiUrl + "/api/categories?fields=name,slug")
.then((res) => res.json())
.then((data) => (this.categories = data.data))
.catch((err) => (error.value = err));
this.loading = false;
},
So the problem is that I am able to collect the selectedCategories in an array in the ProductFilters.vue child component, its also printed and shows up, but..
<script setup>
import { ref } from "vue";
import { useCategoryStore } from "#/stores/CategoryStore";
const categoryStore = useCategoryStore();
categoryStore.fill();
const checkedCategories = ref([]);
const emit = defineEmits(["changeCheck"]);
const changeCheck = function () {
emit("changeCheck", checkedCategories.value);
};
</script>
<template>
<div>
<label
v-for="category in categoryStore.categories"
:key="category.id"
class="text-sm flex flex-row items-center gap-1.5"
><input
type="checkbox"
:value="category.attributes.name"
v-model="checkedCategories"
#input="changeCheck"
/>{{ category.attributes.name }}</label
>
</div>
{{ checkedCategories }}
</template>
.. i am not getting the checkCategories array in the parent component, whereby I'd like to filter my rendered products. If a category is checked i'd like to render the related products, if all categories are checked render all, none checked also all.
In my parent component - ProductsView.vue - the emited data is not showing up, i'm always getting undefined.
<script setup>
import { computed } from "vue";
import { useProductStore } from "#/stores/ProductStore";
import IconDown from "../components/icons/IconDown.vue";
import IconUp from "../components/icons/IconUp.vue";
import ProductFilters from "../components/ProductFilters.vue";
import ProductArchiveCard from "../components/ProductArchiveCard.vue";
const imageLink = import.meta.env.VITE_STRAPI_URL;
const productStore = useProductStore();
function updateCategories(catName) {
let selected = [];
selected.push(catName);
return console.log(selected);
}
const filteredProducts = computed(() => {
// filter by catName or id and v-for filteredProducts
});
productStore.fillArchive();
</script>
<template>
<section class="px-2 flex flex-col xl:flex-row">
<div class="pt-24 xl:pt-36 xl:w-1/4 relative">
<!-- order by category -->
<ProductFilters #changeCheck="updateCategories(catName)" />
</div>
<div v-if="!productStore.loading">
<div
class="mx-auto grid grid-flow-row gap-10 md:gap-10 lg:gap-16 xl:gap-6 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4"
>
<ProductArchiveCard
class="mx-auto"
v-for="product in productStore.products"
:key="product.name"
:product="product"
:imageLink="imageLink"
/>
</div>
</div>
<div v-else class="flex justify-center py-16 min-h-screen">
<div class="spinner w-8 h-8"></div>
</div>
</section>
</template>
What am I doing wrong in the event handling? Can someone point it out? :) Thanks in advance folks!
I am working on a project with a setup combination of react, tailwind and react icons. In my project multiple sliders are required (some has navigation, some has pagination etc…)
So, I created a slider factory component Carousel and controlling variation through props
// Carousel/index.jsx
import { Children, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { HiOutlineArrowLeft, HiOutlineArrowRight } from "react-icons/hi";
import { Swiper, SwiperSlide } from "swiper/react";
import { Autoplay, Keyboard, Mousewheel, Navigation, Pagination } from "swiper";
import "swiper/css";
import "swiper/css/pagination";
import "swiper/css/navigation";
import NextSlideBtn from "./NextSlideBtn";
import PrevSlideBtn from "./PrevSlideBtn";
const Carousel = ({
children,
loop,
pagination,
navigation,
autoplay,
breakpoints,
}) => {
const childrenArray = Children.toArray(children);
const [swiperRef, setSwiperRef] = useState();
return (
<div className="group relative">
<Swiper
className="rounded overflow-hidden"
onSwiper={setSwiperRef}
modules={[
Autoplay,
Keyboard,
Mousewheel,
Pagination,
Navigation,
]}
mousewheel
keyboard
grabCursor
loop={loop || false}
autoplay={
autoplay && {
delay: 5000,
disableOnInteraction: true,
}
}
spaceBetween={breakpoints && 20}
breakpoints={
breakpoints && {
450: {
slidesPerView: 2,
},
768: {
slidesPerView: 3,
},
1536: {
slidesPerView: 4,
},
}
}
pagination={
pagination && {
clickable: true,
}
}
>
{childrenArray.map((item) => (
<SwiperSlide key={uuidv4()}>{item}</SwiperSlide>
))}
</Swiper>
{navigation && (
<div className="hidden group-hover:block">
<PrevSlideBtn swiperRef={swiperRef}>
<HiOutlineArrowLeft className="text-violet-600" />
</PrevSlideBtn>
<NextSlideBtn swiperRef={swiperRef}>
<HiOutlineArrowRight className="text-violet-600" />
</NextSlideBtn>
</div>
)}
</div>
);
};
export default Carousel;
// Carousel/NextSlideBtn.jsx
const NextSlideBtn = ({ children, swiperRef }) => {
const handleNextSlide = () => swiperRef.slideNext();
return (
<button
onClick={handleNextSlide}
className="absolute z-50 top-1/2 -translate-y-1/2 -right-2 translate-x-2 bg-white shadow-md rounded-full p-3"
>
{children}
</button>
);
};
export default NextSlideBtn;
// Carousel/PrevSlideBtn.jsx
const PrevSlideBtn = ({ children, swiperRef }) => {
const handlePrevSlide = () => swiperRef.slidePrev();
return (
<button
onClick={handlePrevSlide}
className="absolute z-50 top-1/2 -translate-y-1/2 -left-2 -translate-x-2 bg-white shadow-md rounded-full p-3"
>
{children}
</button>
);
};
export default PrevSlideBtn;
One of the requirement was to create custom navigation button, which I managed to accomplish as you can see in the code above, but for more visibility I am marking the flow in short
// Carousel/index.jsx
const [swiperRef, setSwiperRef] = useState();
<div>
<Swiper
onSwiper={setSwiperRef}
...
>
...
</Swiper>
<PrevSlideBtn swiperRef={swiperRef} />
<NextSlideBtn swiperRef={swiperRef} />
</div>
// Carousel/NextSlideBtn.jsx
const NextSlideBtn = ({ children, swiperRef }) => {
const handleNextSlide = () => swiperRef.slideNext();
return (
<button
onClick={handleNextSlide}
...
>
{children}
</button>
);
};
export default NextSlideBtn;
// Carousel/PrevSlideBtn.jsx
const PrevSlideBtn = ({ children, swiperRef }) => {
const handlePrevSlide = () => swiperRef.slidePrev();
return (
<button
onClick={handlePrevSlide}
...
>
{children}
</button>
);
};
export default PrevSlideBtn;
Couple of github issues, stackoverflow questions and swiper docs helped me out to accomplish this, and it was pretty straight forward to understand.
Now my question is how can I customize swiper react pagination (like radio buttons which some websites use, or any other styles…) using tailwind and I want to make it as a separate component like I did for navigation buttons.
I’ve tried many solutions eg:
const paginationBullets = {
type: "custom",
clickable: true,
renderCustom: (_, current, total) => {
return <MyCustomSwiperPaginationComponent current={current} total={total} />;
},
};
But nothing seems to work as expected.
Please help me out.
Component separation is my goal.
export const LoginForm = () => {
const setIsLogin = useSetRecoilState(isLoginAtom);
const [isSubmitting, setIsSubmitting] = useState(false);
const router = useRouter();
const onValid = ({ email, password }: LoginProps) => {
axios
.post(`/api/auth/token`, {
email,
password,
})
.then((res) => {
setIsSubmitting(true);
setIsLogin(true);
toast.success('Success Login!');
router.push('/');
})
.catch((err) => {
console.error(err);
toast.error('Login Error');
});
};
const {
register,
handleSubmit,
formState: { errors },
} = useForm<LoginProps>();
return (
<>
<form
className="flex flex-col mx-auto justify-center items-start mt-10 "
onSubmit={handleSubmit(onValid)}
>
//....
</form>
</>
);
};
// _app.tsx
<div>
<Component {...pageProps} />
<ToastContainer
theme="colored"
autoClose={3000}
position="top-center"
/>
</div>
I'm using nextjs 12 version.
The code above is part of the code for login. When I log in, I get the following error:
After logging out and logging back in, the error does not appear.
I read the related issue https://github.com/fkhadra/react-toastify/issues/858
That doesn't seem to help much.
Is the part declared in _app.tsx the problem?
my console is giving me the non specific error "Uncaught (in promise) AxiosError"
this is my checkout-session, in the terminal it seems to have a problem with the .map in the stripe session , ive verified the object format and made sure it was compliant with stripe standards, i am at a loss and hoping this may be a simple fix
import { useSession, useEffect } from 'next-auth/react'
import React from 'react'
import Header from '../components/Header'
import Image from 'next/image'
import { useSelector } from 'react-redux'
import { selectItems, selectTotal } from '../slices/basketSlice'
import CheckoutProduct from '../components/CheckoutProduct'
import Currency from 'react-currency-formatter'
import { link, loadStripe } from '#stripe/stripe-js'
import axios from 'axios'
const stripePromise = loadStripe(`${process.env.stripe_public_key}`);
function checkout() {
const { data: session } = useSession();
const total = useSelector(selectTotal);
const items = useSelector(selectItems);
const createCheckoutSession = async () => {
const stripe = await stripePromise
const checkoutSession = await axios.post('/api/create-checkout-session', {
items: items,
email: session.user.email
})
const result = await stripe.redirectToCheckout({
sessionId: checkoutSession.data.id
})
if (result.error) alert(result.error.message);
};
return (
<div className='bg-gray-100'>
<Header />
<main className='lg:flex max-w-screen-2xl mx-auto'>
{/* left */}
<div className='flex-grow m-5 shadow-sm'>
<Image
src='https://links.papareact.com/ikj'
width={1020}
height={250}
objectfit='contain'
/>
<div className='flex flex-col p-5 space-y-10 bg-white'>
<h1 className='text-3xl border-b pb-4'>{items.length === 0 ? 'Your Amazon Cart is Empty' : 'Shopping Cart'}</h1>
{items.map((item, i) => (
<CheckoutProduct
key={i}
id={item.id}
title={item.title}
rating={item.rating}
price={item.price}
description={item.description}
category={item.category}
image={item.image}
hasPrime={item.hasPrime}
/>
))}
</div>
</div>
{/* right */}
<div className='flex flex-col bg-white p-10 shadow-md'>
{items.length > 0 && (
<>
<h2 className='whitespace-nowrap'>Subtotal({items.length} items):
<span className='font-bold'>
<Currency quantity={total} currency="USD"/>
</span>
</h2>
<button
onClick={createCheckoutSession}
role={link}
disabled={!session}
className={`button mt-2 ${
!session && 'from-gray-300 to-gray-500 border-gray-200 text-gray-300 cursor-not-allowed'}`}
>
{!session ? 'Sign in to checkout' : 'Proceed to checkout'}
</button>
</>
)}
</div>
</main>
</div>
)
}
export default checkout
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async(req, res) => {
const { items, email } = req.body;
const transformedItems = item.map((item) => ({
quantity: 1,
price_data: {
currency: 'usd',
unit_amount: item.price * 100,
product_data: {
name: item.title,
description: item.description,
images: [item.image]
},
},
}));
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
shipping_options: [`shr_1M2oWjIlRietTXvq5h1c8DDQ`],
shipping_address_collection: {
allowed_countries: ['USA']
},
items: transformedItems,
mode: 'payment',
success_url: `${process.env.HOST}/success`,
cancel_url: `${process.env.HOST}/checkout`,
metadata: {
email,
images: JSON.stringify(items.map((item) => item.image))
}
});
res.status(200).json({ id: session.id })
}
It looks like your request to Stripe is not adhering to the expected structure, and I suspect looking at the logs in your Stripe dashboard will show a list of failed requests with detailed error messages on why they failed.
items is not a parameter when creating Checkout Sessions, line_items is what you're looking for.
shipping_address_collection.allowed_countries is expecting a two-letter country code, so you want to use US instead of USA.
learning how to build a blog with nextjs and graphCMS, been able to get some content using gql queries but for no understandable reason, i can't seem to render the categories.name content onto the site and I am not getting an error. i would like to know what the problem is as I have encountered this prior to this moment with the sanity.io platform for an e-commerce project.
import React, { useState, useEffect } from 'react'
import Link from 'next/link'
import { getCategories } from '../services'
const Categories = () => {
const [categories, setCategories] = useState([]);
useEffect(() => {
getCategories()
.then((newCategories) => setCategories(newCategories))
}, []);
return (
<div className='bg-white shadow-lg rounded-lg p-8 mb-12'>
<h3 className='text-xl mb-8 font-semibold border-b pb-4'>
Categories
</h3>
{categories.map((category) => {
<Link key={category.slug} href={`/category/${category.slug}`}>
<span className='cursor-pointer block pb-3 mb-3'>
{category.name}
</span>
</Link>
})}
</div>
below is the query that I used to set up the process for rendering the content I need to display on the site
export const getCategories = async () => {
const query = gql`
query GetCategories {
categories {
name
slug
}
}
`
const result = await request(graphqlAPI, query);
return result.categories;
}
Assuming you are getting data and the state is correct, you're missing return in your map
{categories.map((category) => {
// notice this return keyword!!
return <Link key={category.slug} href={`/category/${category.slug}`}>
<span className='cursor-pointer block pb-3 mb-3'>
{category.name}
</span>
</Link>
})}
figured it out, had to change {} to () after the .map element and =>