Been stuck for a while now trying to figure this out and pulling my hair out because it should be simple but I can't seem to get my head around how to properly implement it.
I'm using GatsbyJS with Contentful as a CMS.
I want to have a simple way for admins using Contentful to add new documents and for those documents to show up on the site under the sections (also added by the admin).
So essentially admins will see a section such as "Minutes Documents" and under that, they'd be able to add first a new Year section such as 2018 then under that section a corresponding document (link to PDF file) under the title of the Month that document was released such as January.
On the site, I'd like that to display like so for instance:
Minutes Documents
2018
Jan
Feb
etc...
2017
Jan
Feb
etc...
So the admin can add as many Year Sections as they need and within that 12 files titled by Month.
The furthest I've managed to get so far is being able to display an array of all my documents added, but I can't yet get my head around how I can best split divide them up under section headers for each year.
My current code is like so:
GrahpQL document query:
export const query = graphql`
query NMODocs {
allContentfulDocument {
edges {
node {
id
publishDate(formatString: "MMMM, YYYY")
title
file {
file {
url
}
}
}
}
}
}
`;
I have an array:
<ul>
{data.allContentfulDocument.edges.map(({ node }) => (
<DocListing key={node.id} doc={node} />
))}
</ul>
And a DocLising component:
import React from 'react';
const DocListing = ({ doc }) => (
<li>
<a title={doc.title} href={doc.file.file.url}>
{doc.publishDate}
</a>
</li>
);
export default DocListing;
This currently results in a list of dates that link to corresponding document files. So to reiterate my question I'm wondering the best way to display this data under the relevant year section headings?
Apologies if this is poorly explained as I'm still learning and trying to get to grips with a lot of stuff that's new to me on this project. So I'm open to any suggestions or advice on how to approach this.
Assuming that you have an array with accessible dates you could group them with a little helper function.
const list = ['2018-08-12', '2018-07-12', '2017-08-12'];
const groupedList = list.reduce((acc, item) => {
const year = item.split('-')[0];
if (!acc[year]) {
acc[year] = []
}
acc[year].push(item);
return acc;
}, {});
Which would result in an object including keys for every year.
I'm not sure this possible with a GraphQL query.
Also in case you're relying on the publish date in contentful that's a little risky because I think it changes when content is updated to the last publish date (please double check). It might make sense to split it into its own content type so that you can set the date yourself and be sure.
Hope that helps. :)
Related
I'm working on a forum and using a form that the user fills out I'm storing data as an object inside an array. The data I'm storing includes the title of a topic, the message, the author and the date. That data is stored inside a topic array which I'm mapping on screen so the user can see all current topics, who created them and the date in which they were created. I also started to use localStorage so I can save my data and test to make sure everything looks good after my page refreshes.
const [topic, setTopic] = useState(() => {
const topicJson = localStorage.getItem("topic");
return topicJson ? JSON.parse(topicJson) : [];
});
const updatedTopic = [
...topic,
{
title: title,
message,
author: "Dagger",
date: new Date(),
},
];
setTopic(updatedTopic);
};
That's the code that I'm using which works as intended however when I map through the array to post the data on screen, I'm having trouble with showing the date. I'm using date-fns because it displays the date exactly how I want it.
Example: 2/19 9:39PM. That's how I want the date to look on screen and using date-fns has been the only thing I've found that makes it look that way.
{topic
.sort((a, b) => b.date - a.date)
.map(({ date }, index) => (
<tr>
{<td>{format(date, "M/dd h:mma")}</td>}
</tr>
))}
That's the code I'm using to map through the array to show the date. Before adding localStorage it worked fine. When I remove the format and show the code as date it works but including format gives me this error:
throw new RangeError('Invalid time value');
// Convert the date in system timezone to the same date in UTC+00:00 timezone.
// This ensures that when UTC functions will be implemented, locales will be compatible with them.
I think your are using Date object to store date and JSON.stringify cant handle Date object. You should to use Date method.
Easy way just save not Date object but ISO string.
date.toISOString()
And in table:
{<td>{format(parseISO(date), "M/dd h:mma")}</td>}
I want a scalable solution, using React, to list some pages and automatically using the last one as my main page.
I have my 'legals mention' page, which will be regularly updated with new content.
import { useTranslation } from "react-i18next";
function Legals() {
const { t } = useTranslation('common');
return (
<div>
<h1>Legals</h1>
<span>Version 2.1 - 4th revision</span>
<span>ยท Effective September 30, 2020</span>
<button>Archived versions</button>
{...}<Content>{...}
</div>
)}
export default Legals;
So this is supposed to display the most recent version, and contains a button with a list to older versions.
I was thinking of using i18n and check in a folder for the last version, is it the best way to do it?
How to accomplish it, at least in pseudocode?
I am currently working on a blogging app, in which users can create their own blogs and each blog has blogposts within that. I'm ideating about architecting a database that is scalable when each blog has a lot of blogposts.
So is it better to structure my database as this:
blog1 : {
blogname : 'blog1',
blogposts: [array of blogposts]
},
blog2 : {
blogname : 'blog2',
blogposts: [array of blogposts]
}
Or should I create a separate collection with all the blogposts, something like this:
blogpost1: {
id: 'blogpost1',
content: {blogpost content in json format}
},
blogpost2: {
id: 'blogpost2',
content: {blogpost content in json format}
}
and reference them in the blog collection.
I want to know which choice would be superior when there are a lot of blogposts. Because I remember reading somewhere in MongoDB docs that it's not recommended to have arrays within document that can grow beyond bounds, so approach #1 is not ideal, right?
When creating databases, I find it useful to think about the requests I would be making.
A blogging app user would want to search all blogs or find a blogger by some criteria.
In this case separate collections for bloggers and blogs would work best. Then structure your documents so that the bloggers link to their blogs and vice versa.
This can be done with Mongoose Schemas (https://mongoosejs.com/docs/index.html).
// models/blogger.js
const mongoose = require('mongoose')
const bloggerSchema = mongoose.Schema({
blogs: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Blog'
}
],
name: String
})
bloggerSchema.set('toJSON', {
transform: (document, returnedObject) => {
const blogger = returnedObject
blogger.id = blogger._id.toString()
delete blogger._id
delete blogger.__v
}
})
module.exports = mongoose.model('Blogger', bloggerSchema)
Then use populate with your request:
// controllers/bloggers.js
const bloggersRouter = require('express').Router()
const Blogger = require('../models/blogger')
bloggersRouter.get('/', async (request, response) => {
const bloggers = await Blogger.find({}).populate(
'blogs', {
title: 1
}
)
response.json(bloggers.map(blogger => blogger.toJSON()))
})
module.exports = bloggersRouter
This way you don't have to add the blogs in their entirety to the blogger document, you can just include the title or anything else that you need on the bloggers initial view.
You could also think about limiting the length of a blog, so you can have more control over the data and then think about the options Joe suggested.
Why does it have to be one or the other?
Storing the blog posts in the same document as the blog is great as long as the individual posts are not very large, and there aren't very many of them.
Storing the posts in a separate collection is good for bigger posts and busy blogs but adds an additional query or lookup to retrieve.
I would think it is expected that your users' output will run the gamut from sparse to prolific, and individual posts will range from a few dozen bytes to many megabytes.
For small posts on not very active blogs, store the posts in the blog document for efficient retrieval.
For busy blogs, store them in an archive collection. Perhaps store the most recent couple of posts, or the most popular posts, in the blog document so you don't have to refer to the other collection every time.
You will also need to figure out how to split a post between documents. MongoDB has a 16MB limit on a single document, so if any of your users make huge posts, you'll need to be able to store them somewhere.
Your question as written seems to be asking whether it is better to follow a relation model or a strict document model. I think in reality neither is a perfect fit for this and a hybridized and flexible approach would work out better.
I've got an app using mobx-state-tree that currently has a few simple stores:
Article represents an article, either sourced through a 3rd party API or written in-house
ArticleStore holds references to articles: { articles: {}, isLoading: bool }
Simple scenario
This setup works well for simple use-cases, such as fetching articles based on ID. E.g.
User navigates to /article/{articleUri}
articleStoreInstance.fetch([articleUri]) returns the article in question
The ID is picked up in render function, and is rendered using articleStoreInstance.articles.get(articleUri)
Complex scenario
For a more complex scenario, if I wanted to fetch a set of articles based on a complex query, e.g. { offset: 100, limit: 100, freeTextQuery: 'Trump' }, should I then:
Have a global SearchResult store that simply links to the articles that the user has searched for
Instantiate a one-time SearchResult store that I pass around for as long as I need it?
Keep queries and general UI state out of stores altogether?
I should add that I'd like to keep articles in the stores between page-loads to avoid re-fetching the same content over and over.
Is there a somewhat standardized way of addressing this problem? Any examples to look at?
What you need might be a Search store which keeps track of following information:
Query params (offset, limit, etc.)
Query results (results of the last search)
(Optional) Query state (isLoading)
Then to avoid storing articles in 2 places, the query results should not use Article model, but reference to Article model. Anytime you query, the actual result will be saved in existing store ArticleStore, and Search only holds references:
import { types, getParent, flow } from 'mobx-state-tree'
const Search = types.model({
params: // your own params info
results: types.array(types.reference(Article))
}).views(self => ({
get parent() {
return getParent(self) // get root node to visit ArticleStore
}
})).actions(self => ({
search: flow(function*(params) {
this.params = params // save query params
const result = yield searchByQuery(query) // your query here
this.parent.articleStore.saveArticles(result) // save result to ArticleStore
this.results = getArticleIds(result) // extract ids here for references
})
}))
Hope it's what you are looking for.
I'm using gatsby-source-contentful where I have a type called ContentfulBlogPost, whose schema looks something like this:
{
id
title
body
date
}
Date is a string (in YYYY-MM-DD format), auto-magically handled by Gatsby's data layer as a Moment object; I can call fromNow and formatString on it and all that.
What I want is to use GraphQL to query for a list of posts from a given year, based on that date field.
If I do this:
{
allContentfulBlogPost(
filter: { date: { regex: "/^2017/" } }
) {
...
}
}
then I get this error: "Cannot read property 'fromNow' of undefined"
What I really want is to be able to filter the allContentfulBlogPost data set by date, either by transforming the date into an integer/string year and finding on that, or by asking for all items whose date falls between Jan 1 and Dec 31 of the given year.
Right now, I have a query running in gatsby-node that pulls in all blog posts, then groups them by year in JS and then passes the array of posts into a template. This works, but it really seems like there should be a GraphQL way of doing this?