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

Docs #8

Merged
merged 2 commits into from
Dec 3, 2021
Merged
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
173 changes: 25 additions & 148 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@
- [Backend](#backend)
- [Pages](#pages)
- [Data fetching](#data-fetching)
- [i18n configuration](#i18n-configuration)
- [Pre-fetch data in the server-side](#pre-fetch-data-in-the-server-side)
- [Access data from a component](#access-data-from-a-component)
- [Fetch all datasets for listing pages](#fetch-all-datasets-for-listing-pages)
- [Fetch particluar dataset](#fetch-particluar-dataset)
- [Developers](#developers)
- [Boot the local instance](#boot-the-local-instance)
- [Tests](#tests)
- [Architecture](#architecture)
- [Contributing](#contributing)
- [Credits](#credits)

## Features

Expand Down Expand Up @@ -76,164 +74,53 @@ $ export CMS=http://myblog.wordpress.com

### Data fetching

We use Apollo client which allows us to query data with GraphQL. We have setup CKAN API for the demo (it uses demo.ckan.org as DMS):
We use REST API to fetch data from CKAN. Some of the data comes as metadata and other is in CSV files, for which we use different transformers. You can find more about it in `/transoformers` directory.
#### Fetch all datasets for listing pages

Note that we don't have Apollo Server but we connect CKAN API using [`apollo-link-rest`](https://www.apollographql.com/docs/link/links/rest/) module. You can see how it works in [lib/apolloClient.ts](https://github.com/civicdatalab/opubfront-haq/blob/main/lib/apolloClient.ts) and then have a look at [pages/_app.tsx](https://github.com/civicdatalab/opubfront-haq/blob/main/pages/_app.tsx).
When visiting a dataset lisitng page, you may want to fetch the particular type of datasets. To do so, you can use `getServerSideProps` function from NextJS:

For development/debugging purposes, we suggest installing the Chrome extension - https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm.

#### i18n configuration

This is configured by default to support both `English` and `French` subpath for language translation. But for subsequent users, this following steps can be used to configure i18n for other languages;

1. Update `next.config.js`, to add more languages to the i18n locales

```js
i18n: {
locales: ['en', 'fr', 'nl-NL'], // add more language to the list
defaultLocale: 'en', // set the default language to use
},
```

2. Create a folder for the language in `locales` --> `locales/en-Us`

3. In the language folder, different namespace files (json) can be created for each translation. For the `index.js` use-case, I named it `common.json`

```json
// locales/en/common.json
{
"title" : "Portal js in English",
}

// locales/fr/common.json
{
"title" : "Portal js in French",
}
```

4. To use on pages using Server-side Props.

```js
import { loadNamespaces } from './_app';
import useTranslation from 'next-translate/useTranslation';
```javascript
import { GetServerSideProps } from 'next';
import { convertToCkanSearchQuery, fetchDatasets } from 'utils/index';

const Home: React.FC = ()=> {
const { t } = useTranslation();
return (
<div>{t(`common:title`)}</div> // we use common and title base on the common.json data
);
};
export const getServerSideProps: GetServerSideProps = async (context) => {
const variables = convertToCkanSearchQuery(context.query || {});
const data = await fetchDatasets('type_of_dataset', variables);

export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
........ ........
return {
props : {
_ns: await loadNamespaces(['common'], locale),
}
props: {
data,
},
};
};

```
- `convertToCkanSearchQuery` (from [PortalJS](https://github.com/datopian/portal.js)) helps to convert the search query parameters into a object which we can use.
- `fetchDatasets` helps to fetch a list of datasets of particular type.

5. Go to the browser and view the changes using language subpath like this `http://localhost:3000` and `http://localhost:3000/fr`. **Note** The subpath also activate chrome language Translator
Learn more about them [here](utils/README.md).

#### Pre-fetch data in the server-side
#### Fetch particluar dataset

When visiting a dataset page, you may want to fetch the dataset metadata in the server-side. To do so, you can use `getServerSideProps` function from NextJS:
Depending on dataset, they may return metadata in the form of `JSON` or a combination of `JSON` and `CSV` file. We can use `fetchAPI` in this case:

```javascript
import { GetServerSideProps } from 'next';
import { initializeApollo } from '../lib/apolloClient';
import gql from 'graphql-tag';

const QUERY = gql`
query dataset($id: String) {
dataset(id: $id) @rest(type: "Response", path: "package_show?{args}") {
result
}
}
`;

...
import { fetchAPI } from 'utils/index';
import { resourceGetter } from 'utils/resourceParser';

export const getServerSideProps: GetServerSideProps = async (context) => {
const apolloClient = initializeApollo();

await apolloClient.query({
query: QUERY,
variables: {
id: 'my-dataset'
},
});
const data = await fetchAPI(context.query.tender);
const csv = await resourceGetter(data.result.resources, 'CSV');

return {
props: {
initialApolloState: apolloClient.cache.extract(),
data,
csv
},
};
};
```

This would fetch the data from DMS and save it in the Apollo cache so that we can query it again from the components.

#### Access data from a component

Consider situation when rendering a component for org info on the dataset page. We already have pre-fetched dataset metadata that includes `organization` property with attributes such as `name`, `title` etc. We can now query only organization part for our `Org` component:

```javascript
import { useQuery } from '@apollo/react-hooks';
import gql from 'graphql-tag';

export const GET_ORG_QUERY = gql`
query dataset($id: String) {
dataset(id: $id) @rest(type: "Response", path: "package_show?{args}") {
result {
organization {
name
title
image_url
}
}
}
}
`;

export default function Org({ variables }) {
const { loading, error, data } = useQuery(
GET_ORG_QUERY,
{
variables: { id: 'my-dataset' }
}
);

...

const { organization } = data.dataset.result;

return (
<>
{organization ? (
<>
<img
src={
organization.image_url
}
className="h-5 w-5 mr-2 inline-block"
/>
<Link href={`/@${organization.name}`}>
<a className="font-semibold text-primary underline">
{organization.title || organization.name}
</a>
</Link>
</>
) : (
''
)}
</>
);
}
```

## Developers

### Boot the local instance
Expand Down Expand Up @@ -265,18 +152,11 @@ npm run test
npm run test --watch
```

We use Cypress tests as well

```
npm run e2e
```

### Architecture

- Language: Javascript
- Framework: [Next.js](https://nextjs.com/)
- Styling: [SASS](https://sass-lang.com/) with [BEM](http://getbem.com/) and ITCSS
- Data layer API: GraphQL using Apollo.

## Contributing

Expand All @@ -286,6 +166,3 @@ See [CONTRIBUTING.md](https://github.com/CivicDataLab/oci-assam-frontend/blob/ma

Please adhere to [Code of Conduct](https://github.com/CivicDataLab/oci-assam-frontend/blob/main/CODE_OF_CONDUCT.md).

## Credits

This is based on a [PortalJS](https://github.com/datopian/portal.js).
11 changes: 0 additions & 11 deletions data/tempDataBarChart.tsx

This file was deleted.

Binary file added docs/.vuepress/.cache/default-development/0.pack
Binary file not shown.
Binary file added docs/.vuepress/.cache/default-development/1.pack
Binary file not shown.
Binary file not shown.
Binary file not shown.
9 changes: 9 additions & 0 deletions docs/.vuepress/.temp/internal/clientAppEnhances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import clientAppEnhance0 from '/home/ancient/wsl/b4j/node_modules/@vuepress/theme-default/lib/client/clientAppEnhance.js'
import clientAppEnhance1 from '/home/ancient/wsl/b4j/node_modules/@vuepress/plugin-medium-zoom/lib/client/clientAppEnhance.js'
import clientAppEnhance2 from '/home/ancient/wsl/b4j/node_modules/@vuepress/plugin-theme-data/lib/client/clientAppEnhance.js'

export const clientAppEnhances = [
clientAppEnhance0,
clientAppEnhance1,
clientAppEnhance2,
]
5 changes: 5 additions & 0 deletions docs/.vuepress/.temp/internal/clientAppRootComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import clientAppRootComponent0 from '/home/ancient/wsl/b4j/node_modules/@vuepress/plugin-back-to-top/lib/client/components/BackToTop.js'

export const clientAppRootComponents = [
clientAppRootComponent0,
]
9 changes: 9 additions & 0 deletions docs/.vuepress/.temp/internal/clientAppSetups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import clientAppSetup0 from '/home/ancient/wsl/b4j/node_modules/@vuepress/theme-default/lib/client/clientAppSetup.js'
import clientAppSetup1 from '/home/ancient/wsl/b4j/node_modules/@vuepress/plugin-active-header-links/lib/client/clientAppSetup.js'
import clientAppSetup2 from '/home/ancient/wsl/b4j/node_modules/@vuepress/plugin-nprogress/lib/client/clientAppSetup.js'

export const clientAppSetups = [
clientAppSetup0,
clientAppSetup1,
clientAppSetup2,
]
6 changes: 6 additions & 0 deletions docs/.vuepress/.temp/internal/layoutComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineAsyncComponent } from 'vue'

export const layoutComponents = {
"404": defineAsyncComponent(() => import("/home/ancient/wsl/b4j/node_modules/@vuepress/theme-default/lib/client/layouts/404.vue")),
"Layout": defineAsyncComponent(() => import("/home/ancient/wsl/b4j/node_modules/@vuepress/theme-default/lib/client/layouts/Layout.vue")),
}
8 changes: 8 additions & 0 deletions docs/.vuepress/.temp/internal/pagesComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineAsyncComponent } from 'vue'

export const pagesComponents = {
// path: /
"v-8daa1a0e": defineAsyncComponent(() => import(/* webpackChunkName: "v-8daa1a0e" */"/home/ancient/wsl/b4j/docs/.vuepress/.temp/pages/index.html.vue")),
// path: /404.html
"v-3706649a": defineAsyncComponent(() => import(/* webpackChunkName: "v-3706649a" */"/home/ancient/wsl/b4j/docs/.vuepress/.temp/pages/404.html.vue")),
}
6 changes: 6 additions & 0 deletions docs/.vuepress/.temp/internal/pagesData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const pagesData = {
// path: /
"v-8daa1a0e": () => import(/* webpackChunkName: "v-8daa1a0e" */"/home/ancient/wsl/b4j/docs/.vuepress/.temp/pages/index.html.js").then(({ data }) => data),
// path: /404.html
"v-3706649a": () => import(/* webpackChunkName: "v-3706649a" */"/home/ancient/wsl/b4j/docs/.vuepress/.temp/pages/404.html.js").then(({ data }) => data),
}
31 changes: 31 additions & 0 deletions docs/.vuepress/.temp/internal/pagesRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Vuepress } from '@vuepress/client/lib/components/Vuepress'

const routeItems = [
["v-8daa1a0e","/","",["/index.html","/README.md"]],
["v-3706649a","/404.html","",["/404"]],
]

export const pagesRoutes = routeItems.reduce(
(result, [name, path, title, redirects]) => {
result.push(
{
name,
path,
component: Vuepress,
meta: { title },
},
...redirects.map((item) => ({
path: item,
redirect: path,
}))
)
return result
},
[
{
name: "404",
path: "/:catchAll(.*)",
component: Vuepress,
}
]
)
21 changes: 21 additions & 0 deletions docs/.vuepress/.temp/internal/siteData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const siteData = {
"base": "/",
"lang": "en-US",
"title": "",
"description": "",
"head": [],
"locales": {}
}

if (import.meta.webpackHot) {
import.meta.webpackHot.accept()
if (__VUE_HMR_RUNTIME__.updateSiteData) {
__VUE_HMR_RUNTIME__.updateSiteData(siteData)
}
}

if (import.meta.hot) {
import.meta.hot.accept(({ siteData }) => {
__VUE_HMR_RUNTIME__.updateSiteData(siteData)
})
}
44 changes: 44 additions & 0 deletions docs/.vuepress/.temp/internal/themeData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
export const themeData = {
"locales": {
"/": {
"selectLanguageName": "English"
}
},
"navbar": [],
"logo": null,
"darkMode": true,
"repo": null,
"selectLanguageText": "Languages",
"selectLanguageAriaLabel": "Select language",
"sidebar": "auto",
"sidebarDepth": 2,
"editLink": true,
"editLinkText": "Edit this page",
"lastUpdated": true,
"lastUpdatedText": "Last Updated",
"contributors": true,
"contributorsText": "Contributors",
"notFound": [
"There's nothing here.",
"How did we get here?",
"That's a Four-Oh-Four.",
"Looks like we've got some broken links."
],
"backToHome": "Take me home",
"openInNewWindow": "open in new window",
"toggleDarkMode": "toggle dark mode",
"toggleSidebar": "toggle sidebar"
}

if (import.meta.webpackHot) {
import.meta.webpackHot.accept()
if (__VUE_HMR_RUNTIME__.updateThemeData) {
__VUE_HMR_RUNTIME__.updateThemeData(themeData)
}
}

if (import.meta.hot) {
import.meta.hot.accept(({ themeData }) => {
__VUE_HMR_RUNTIME__.updateThemeData(themeData)
})
}
Loading