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
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ A map-centric website backed by a Core Data project and Typesense index.
## Getting Started

#### Requirements

- Node 20.x
- Netlify CLI
- Core Data Project
Expand All @@ -19,6 +20,7 @@ Copy the `.env.example` file to `.env` and enter required variables. For a local
Add a `/public/config.dev.json` file, which will be ignored by Git, to copy local config settings when starting the application. Use the `config.json` table below to configure your project.

To start, run:

```
npm install && netlify dev
```
Expand All @@ -31,7 +33,7 @@ npm install && netlify dev

#### Create a content repository

On GitHub, create a new content repository. The posts, paths, and pages records you create will be stored here, as well as any i18n, your project configuration, and default user accounts.
On GitHub, create a new content repository. The posts, paths, and pages records you create will be stored here, as well as any i18n, your project configuration, and default user accounts.

###### Directory Structure

Expand Down Expand Up @@ -81,10 +83,10 @@ The `title` attribute will be set as the `<title>` element of the HTML page. Set
Copy the `/public/config.json` file into your content repository to `/content/settings/config.json` and adjust the configuration as desired.

| Key | Type | Description |
|----------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| -------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| content | Object | TinaCMS content collections configuration |
| content.collections | Array | Array of content keys to allow for data entry and routing: "posts", "paths" |
| content.localize_pages | Boolean | If `true` pages content will be pulled from a locale directory (e.g. `/en/My-Awesome-Page.mdx`) |
| content.localize_content | Boolean | If `true` pages and other content will be pulled from a locale directory (e.g. `/en/My-Awesome-Page.mdx`) |
| core_data | Object | Core Data configuration |
| core_data.url | String | URL of the Core Data application |
| core_data.project_ids | Array | Array of Core Data project IDs as strings |
Expand All @@ -99,11 +101,11 @@ Copy the `/public/config.json` file into your content repository to `/content/se
| layers.overlay | Boolean | If `true`, map layer will be rendered as overlay layer. If `false`, map layer will be rendered as base layer |
| result_filtering | Object | Lists of fields and relationships to exclude per model. |
| result_filtering.events.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.instances.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.instances.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.items.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.organizations.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.people.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.places.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.organizations.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.people.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.places.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| result_filtering.works.exclude | Array | List of attributes, user defined fields and associations to be excluded from the search detail panel and detail pages. See [Search detail filtering](search-detail-filtering.md) |
| search | Array | Search UI configuration |
| search.facets | Array | Search facets configuration. |
Expand Down Expand Up @@ -208,4 +210,4 @@ An `x-typesense-api-key` header will need to be added to the request.

## Core Data Fields and Relationships

To find the UUID values for Core Data user-defined fields and relationships, the "Identifier" column can be added to the respective lists within the Core Data application under Project Settings > Configure.
To find the UUID values for Core Data user-defined fields and relationships, the "Identifier" column can be added to the respective lists within the Core Data application under Project Settings > Configure.
2 changes: 1 addition & 1 deletion docs/configuration-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Array<"paths" | "posts">

Required: No

### localize_pages
### localize_content

If set to `true`, pages will be nested within a localized directory (e.g `/pages/en/Home.mdx`). If `false`, pages will not be localized and placed in the root `/pages` directory.

Expand Down
7 changes: 2 additions & 5 deletions src/backend/tina/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import _ from 'underscore';
* @param items
*/
export const filterAll = async (locale: string, items: Array<any>) => {
if (!config.content.localize_pages) {
if (!config.content.localize_content) {
return items;
}

Expand All @@ -35,11 +35,8 @@ export const filterAll = async (locale: string, items: Array<any>) => {

if (localized[locale]) {
filtered.push(localized[locale]);
} else if (localized[config.i18n.default_locale]) {
filtered.push(localized[config.i18n.default_locale])
}
});

return filtered;
};

Expand All @@ -53,7 +50,7 @@ export const filterAll = async (locale: string, items: Array<any>) => {
export const fetchOne = async (locale: string, slug: string, query: any) => {
let response;

if (!config.content.localize_pages) {
if (!config.content.localize_content) {
response = await query({ relativePath: `${slug}.mdx` });
} else {
response = await query({ relativePath: `${locale}/${slug}.mdx` });
Expand Down
20 changes: 12 additions & 8 deletions src/backend/tina/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,26 @@ export const fetchPages = async (locale: string, params?: any) => {
return filterAll(locale, pages);
};

export const fetchPath = async (slug: string) => {
const response = await client.queries.path({ relativePath: `${slug}.mdx`});
export const fetchPath = async (locale: string, slug: string) => {
const response = await fetchOne(locale, slug, client.queries.path);
return response.data?.path;
};

export const fetchPaths = async () => {
export const fetchPaths = async (locale: string) => {
const response = await client.queries.pathConnection();
return response.data?.pathConnection?.edges?.map((item) => item?.node);
const paths = response.data?.pathConnection?.edges?.map((item) => item?.node);

return filterAll(locale, paths)
};

export const fetchPost = async (slug: string) => {
const response = await client.queries.post({ relativePath: `${slug}.mdx`});
export const fetchPost = async (locale: string, slug: string) => {
const response = await fetchOne(locale, slug, client.queries.post);
return response.data?.post;
};

export const fetchPosts = async () => {
export const fetchPosts = async (locale: string) => {
const response = await client.queries.postConnection();
return response.data?.postConnection?.edges?.map((item) => item?.node);
const posts = response.data?.postConnection?.edges?.map((item) => item?.node);

return filterAll(locale, posts)
};
2 changes: 1 addition & 1 deletion src/layouts/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const {
<LanguagePicker
locales={locales}
currentLocale={currentLocale}
currentUrl={currentUrl}
currentUrl={currentUrl.pathname}
/>
</MobileHeader>
</div>
42 changes: 14 additions & 28 deletions src/pages/[lang]/paths/[slug].astro
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
---
import PathViewer from "@apps/paths/PathViewer";
import PathViewer from '@apps/paths/PathViewer';
import { fetchPath, fetchPaths } from '@backend/tina';
import config from '@config';
import { getTranslations } from '@backend/i18n';
import Layout from "@layouts/Layout.astro";
import Layout from '@layouts/Layout.astro';
import { hasPathsContent } from '@utils/config';
import _ from 'underscore';

const { slug } = Astro.params;
const path = await fetchPath(slug);
const locale = Astro.currentLocale;
const path = await fetchPath(locale, slug);

const { t } = await getTranslations(Astro.currentLocale);
const { t } = await getTranslations(locale);
const title = path?.title || t('paths');

export const getStaticPaths = async () => {
Expand All @@ -20,33 +21,18 @@ export const getStaticPaths = async () => {

const staticPaths = [];

const locales = config.i18n.locales;
const paths = await fetchPaths();
for (const lang of config.i18n.locales) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like re-formatting? Any reason for this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how _.each plays with async functions so I just changed it to match how we're getting the static paths here, since we need to fetch paths for each locale separately. (Or is the re-formatting you're referencing something smaller? Definitely possible that some auto-formatting slipped through my notice; VSCode likes to forget that I told it not to auto-format on save...)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I see. fetchPaths now requires the locale to be passed as an argument. That makes sense. Yes, good call switching to for, _.each does not handle async requests. 👍

const paths = await fetchPaths(lang);

_.each(locales, (lang) => {
_.each(paths, (path) => {
staticPaths.push({ params: {
lang,
slug: path?._sys.filename
}})
});
});
for (const path of paths) {
staticPaths.push({ params: { lang, slug: path._sys.filename } });
}
}

return staticPaths;
};

---

<Layout
fullscreen
t={t}
tab='paths'
title={title}
>
{ path && (
<PathViewer
path={path}
client:only='react'
/>
)}
</Layout>
<Layout fullscreen t={t} tab='paths' title={title}>
{path && <PathViewer path={path} client:only='react' />}
</Layout>
7 changes: 4 additions & 3 deletions src/pages/[lang]/paths/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getRelativeLocaleUrl } from 'astro:i18n';
import _ from 'underscore';

const { t } = await getTranslations(Astro.currentLocale);
const paths = await fetchPaths();
const paths = await fetchPaths(Astro.currentLocale);

export const getStaticPaths = () => {
if (!hasPathsContent(config)) {
Expand All @@ -38,7 +38,7 @@ export const getStaticPaths = () => {
{ t('paths') }
</h1>
<Cards>
{ _.map(paths, (path) => (
{ !_.isEmpty(paths) && _.map(paths, (path) => (
<Card
imageUrl={path?.image}
alt={path?.imageAlt}
Expand All @@ -48,7 +48,8 @@ export const getStaticPaths = () => {
author={path?.author}
date={path?.date}
/>
))}
)) }
{ _.isEmpty(paths) && <p class="italic text-sm">({ t('none') })</p> }
</Cards>
</Container>
</Layout>
37 changes: 14 additions & 23 deletions src/pages/[lang]/posts/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,33 @@ import { hasPostsContent } from '@utils/config';
import _ from 'underscore';

const { slug } = Astro.params;
const post = await fetchPost(slug);

const { t } = await getTranslations(Astro.currentLocale);
const locale = Astro.currentLocale;

const post = await fetchPost(locale, slug);
const { t } = await getTranslations(locale);

export const getStaticPaths = async () => {
if (!hasPostsContent(config)) {
return [];
}

const staticPaths = [];
const paths = [];

const locales = config.i18n.locales;
const posts = await fetchPosts();
for (const lang of config.i18n.locales) {
const posts = await fetchPosts(lang);

_.each(locales, (lang) => {
_.each(posts, (post) => {
staticPaths.push({ params: {
lang,
slug: post?._sys.filename
}});
});
});
for (const post of posts) {
paths.push({ params: { lang, slug: post._sys.filename } });
}
}

return staticPaths;
return paths;
};
---

<Layout
footer
t={t}
tab='posts'
title={slug}
>
<Container
className='pb-16 w-full'
>
<Layout footer t={t} tab='posts' title={post?.title}>
<Container className='pb-16 w-full'>
<PostContent
content={post?.body}
title={post?.title}
Expand Down
7 changes: 4 additions & 3 deletions src/pages/[lang]/posts/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getRelativeLocaleUrl } from 'astro:i18n';
import _ from 'underscore';

const { t } = await getTranslations(Astro.currentLocale);
const posts = await fetchPosts();
const posts = await fetchPosts(Astro.currentLocale);

export const getStaticPaths = () => {
if (!hasPostsContent(config)) {
Expand All @@ -38,7 +38,7 @@ export const getStaticPaths = () => {
{ t('posts') }
</h1>
<Cards>
{ _.map(posts, (post) => (
{ !_.isEmpty(posts) && _.map(posts, (post) => (
<Card
imageUrl={post?.cardImage}
alt={post?.imageAlt}
Expand All @@ -48,7 +48,8 @@ export const getStaticPaths = () => {
author={post.author}
date={post.date}
/>
))}
)) }
{ _.isEmpty(posts) && <p class="italic text-sm">({ t('none') })</p> }
</Cards>
</Container>
</Layout>
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface SearchConfig {
export interface Configuration {
content?: {
collections?: Array<String>,
localize_pages?: boolean
localize_content?: boolean
};

core_data: {
Expand Down
13 changes: 11 additions & 2 deletions src/utils/nav.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { fetchPages } from '@backend/tina';
import config from '@config';
import _ from 'underscore';

/**
* Returns the first page flagged as "home_page".
* Returns the first page flagged as "home_page". If none exists for the current locale, will default to the configured `default_locale` if possible.
*
* @param locale
*/
export const getHomepage = async (locale: string) => {
const pages = await fetchPages(locale, { filter: { home_page: { eq: true } } });
const [page, ] = pages;
let page: any;
if (pages && pages.length) {
page = pages[0];
} else if (config.i18n.default_locale) {
const defaultPages = await fetchPages(config.i18n.default_locale, { filter: { home_page: { eq: true } } });
if (defaultPages && defaultPages.length) {
page = defaultPages[0];
}
}

return page;
};
Expand Down
4 changes: 2 additions & 2 deletions test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ describe('content', () => {
expect(config.content?.collections).toBeArrayOfValues(collections);
});

test('localize_pages matches allowed values', () => {
expect(config.content?.localize_pages).toBeBoolean();
test('localize_content matches allowed values', () => {
expect(config.content?.localize_content).toBeBoolean();
});
});

Expand Down
5 changes: 5 additions & 0 deletions tina/content/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const Pages: Collection = {
label: 'Pages',
path: 'content/pages',
format: 'mdx',
ui: {
allowedActions: {
createNestedFolder: true
}
},
fields: [{
name: 'title',
label: 'Title',
Expand Down
5 changes: 5 additions & 0 deletions tina/content/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ const Paths: Collection = {
label: 'Paths',
path: 'content/paths',
format: 'mdx',
ui: {
allowedActions: {
createNestedFolder: true
}
},
fields: [
{
name: 'title',
Expand Down
Loading