Compare commits

..

21 Commits

Author SHA1 Message Date
Luccas Mateus
0f7d28e02e [flatuitable][m] - add bytes + parsingConfig 2023-09-20 08:12:32 -03:00
Anuar Ustayev (aka Anu)
083d3178cd Merge pull request #999 from igoradamenko/igoradamenko-patch-1
Fix wiki-link regexp to match non-Latin characters
2023-09-12 00:18:23 +06:00
João Demenech
3200dc5ade fix(site,docs): missing mddb command instruction on 'Searching datasets' tutorial 2023-09-11 08:21:35 -03:00
João Demenech
32dce434eb Merge pull request #1017 from datopian/changeset-release/main
Version Packages
2023-08-31 16:13:39 -03:00
github-actions[bot]
37ef29d9a2 Version Packages 2023-08-31 19:09:52 +00:00
João Demenech
98d62532c5 Merge pull request #1018 from datopian/ckan/feat/private-datasets
feat(ckan): makes it possible to search private datasets on the ckan api
2023-08-31 16:06:01 -03:00
João Demenech
50122cd0cb bump: new version of CKAN API 2023-08-30 18:38:35 -03:00
João Demenech
0156e72dd3 feat(ckan): makes it possible to search private datasets on the ckan api 2023-08-30 18:35:40 -03:00
Luccas Mateus
91217f3256 [packanges/ckan][xs] - fix type (#1016) 2023-08-24 16:04:44 -03:00
github-actions[bot]
11f9253709 Version Packages (#1015)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-24 16:03:42 -03:00
João Demenech
c09c78b015 feat(site,seo): disallow the people/* path to be crawled 2023-08-23 16:33:28 -03:00
github-actions[bot]
4a1ccd2f8d Version Packages (#1012)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-21 15:26:25 -03:00
Anuar Ustayev (aka Anu)
728d5b1465 Merge pull request #1014 from datopian/feat/cloud-waitlist-form
feat(site): the newsletter form is now a cloud waitlist form
2023-08-22 00:24:23 +06:00
João Demenech
a43d4a3b86 feat(site): the newsletter form is now a cloud waitlist form
Refs: #432
2023-08-21 14:47:59 -03:00
João Demenech
4bc7ce5ce7 [site,newsletter][s]: fix hero newsletter form (broken after Brevo update) 2023-08-18 09:41:31 -03:00
João Demenech
8c5c6a2112 Merge pull request #1011 from datopian/fix-flatui
Fix flatui
2023-08-15 15:00:04 -03:00
Luccas Mateus
8e896138c6 [@portaljs/components][sm] - fix bug of multiple flatuitable with different urls 2023-08-14 16:46:15 -03:00
github-actions[bot]
b2b4fbdf12 Version Packages (#1010)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-14 12:06:36 -03:00
Luccas Mateus
099f3c5204 [@portaljs/ckan][sm] - fix types (#1009) 2023-08-14 11:16:51 -03:00
Luccas Mateus
88ccee6f0a [@portaljs/ckan][sm] - fix types 2023-08-14 11:02:56 -03:00
Igor Adamenko
3efba6578d Fix wiki-link regexp to match non-Latin characters 2023-08-09 19:02:11 +03:00
29 changed files with 266 additions and 225 deletions

View File

@@ -0,0 +1,5 @@
---
'@portaljs/components': minor
---
FlatUiTables now accepts a bytes param and a parsingConfig param for CSV links

6
package-lock.json generated
View File

@@ -46942,7 +46942,7 @@
}, },
"packages/ckan": { "packages/ckan": {
"name": "@portaljs/ckan", "name": "@portaljs/ckan",
"version": "0.0.3", "version": "0.1.0",
"dependencies": { "dependencies": {
"formik": "^2.2.9", "formik": "^2.2.9",
"swr": "^2.1.5", "swr": "^2.1.5",
@@ -47347,7 +47347,7 @@
}, },
"packages/components": { "packages/components": {
"name": "@portaljs/components", "name": "@portaljs/components",
"version": "0.3.1", "version": "0.3.2",
"dependencies": { "dependencies": {
"@githubocto/flat-ui": "^0.14.1", "@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17", "@heroicons/react": "^2.0.17",
@@ -47828,7 +47828,7 @@
}, },
"packages/remark-wiki-link": { "packages/remark-wiki-link": {
"name": "@portaljs/remark-wiki-link", "name": "@portaljs/remark-wiki-link",
"version": "1.0.4", "version": "1.1.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"mdast-util-to-markdown": "^1.5.0", "mdast-util-to-markdown": "^1.5.0",

View File

@@ -1,5 +1,27 @@
# @portaljs/ckan # @portaljs/ckan
## 0.1.0
### Minor Changes
- [#1018](https://github.com/datopian/portaljs/pull/1018) [`50122cd0`](https://github.com/datopian/portaljs/commit/50122cd0cbbf68bdadc641341279b30b22538cfd) Thanks [@demenech](https://github.com/demenech)! - package_search method now supports custom headers and include_private parameter
### Patch Changes
- [#1016](https://github.com/datopian/portaljs/pull/1016) [`91217f32`](https://github.com/datopian/portaljs/commit/91217f325657e2f298b0e632793ae9bb8b08e870) Thanks [@luccasmmg](https://github.com/luccasmmg)! - remove optional from id in resource interface
## 0.0.5
### Patch Changes
- [#1009](https://github.com/datopian/portaljs/pull/1009) [`88ccee6f`](https://github.com/datopian/portaljs/commit/88ccee6f0aa05decd3efbe7279925340ae817127) Thanks [@luccasmmg](https://github.com/luccasmmg)! - added package_count to organization type
## 0.0.4
### Patch Changes
- [#1009](https://github.com/datopian/portaljs/pull/1009) [`099f3c52`](https://github.com/datopian/portaljs/commit/099f3c520407a7215b5b41f67dc8ea5ac73d07c4) Thanks [@luccasmmg](https://github.com/luccasmmg)! - added package_count to organization type
## 0.0.3 ## 0.0.3
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@portaljs/ckan", "name": "@portaljs/ckan",
"version": "0.0.3", "version": "0.1.0",
"type": "module", "type": "module",
"description": "https://portaljs.org", "description": "https://portaljs.org",
"keywords": [ "keywords": [

View File

@@ -41,7 +41,7 @@ export interface Resource {
description?: string; description?: string;
format?: string; format?: string;
hash?: string; hash?: string;
id?: string; id: string;
last_modified?: string; last_modified?: string;
metadata_modified?: string; metadata_modified?: string;
mimetype?: string; mimetype?: string;
@@ -69,6 +69,7 @@ export interface PackageSearchOptions {
query?: string; query?: string;
resFormat?: Array<string>; resFormat?: Array<string>;
sort?: string; sort?: string;
include_private?: boolean;
} }
export interface Tag { export interface Tag {

View File

@@ -1,6 +1,6 @@
import { Activity } from "./activity.interface"; import { Activity } from './activity.interface';
import { Dataset, Tag } from "./dataset.interface"; import { Dataset, Tag } from './dataset.interface';
import { User } from "./user.interface"; import { User } from './user.interface';
export interface Group { export interface Group {
display_name: string; display_name: string;
@@ -10,9 +10,9 @@ export interface Group {
created: string; created: string;
name: string; name: string;
is_organization: false; is_organization: false;
state: "active" | "deleted" | "inactive"; state: 'active' | 'deleted' | 'inactive';
image_url: string; image_url: string;
type: "group"; type: 'group';
title: string; title: string;
revision_id: string; revision_id: string;
num_followers: number; num_followers: number;

View File

@@ -1,6 +1,6 @@
import { Activity } from "./activity.interface"; import { Activity } from './activity.interface';
import { Dataset, Tag } from "./dataset.interface"; import { Dataset, Tag } from './dataset.interface';
import { User } from "./user.interface"; import { User } from './user.interface';
export interface Organization { export interface Organization {
id: string; id: string;
@@ -13,8 +13,9 @@ export interface Organization {
image_display_url?: string; image_display_url?: string;
created?: string; created?: string;
is_organization: boolean; is_organization: boolean;
approval_status?: "approved"; package_count: number;
state: "active"; approval_status?: 'approved';
state: 'active';
packages?: Array<Dataset>; packages?: Array<Dataset>;
activity_stream?: Array<Activity>; activity_stream?: Array<Activity>;
users?: Array<User>; users?: Array<User>;

View File

@@ -31,11 +31,7 @@ export default class CKAN {
async getDatasetsListWithDetails(options: DatasetListQueryOptions) { async getDatasetsListWithDetails(options: DatasetListQueryOptions) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/current_package_list_with_resources?offset=${options.offset}&limit=${options.limit}`,
this.DMS
}/api/3/action/current_package_list_with_resources?offset=${
options.offset
}&limit=${options.limit}`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -44,7 +40,8 @@ export default class CKAN {
} }
async packageSearch( async packageSearch(
options: PackageSearchOptions options: PackageSearchOptions,
reqOptions: Partial<RequestInit> = {}
): Promise<{ datasets: Dataset[]; count: number }> { ): Promise<{ datasets: Dataset[]; count: number }> {
function buildGroupsQuery(groups: Array<string>) { function buildGroupsQuery(groups: Array<string>) {
if (groups.length > 0) { if (groups.length > 0) {
@@ -99,16 +96,18 @@ export default class CKAN {
options.groups, options.groups,
options?.resFormat options?.resFormat
); );
const response = await fetchRetry(
`${ let url = `${this.DMS}/api/3/action/package_search?`;
this.DMS url += `start=${options.offset}`;
}/api/3/action/package_search?start=${options.offset}&rows=${ url += `&rows=${options.limit}`;
options.limit url += fq ? fq : '';
}${fq ? fq : ''}${options.query ? '&q=' + options.query : ''}${ url += options.query ? '&q=' + options.query : '';
options.sort ? '&sort=' + options.sort : '' url += options.sort ? '&sort=' + options.sort : '';
}`, url += options.include_private
3 ? '&include_private=' + options.include_private
); : '';
const response = await fetchRetry(url, 3, reqOptions);
const responseData = await response.json(); const responseData = await response.json();
const datasets: Array<Dataset> = responseData.result.results; const datasets: Array<Dataset> = responseData.result.results;
return { datasets, count: responseData.result.count }; return { datasets, count: responseData.result.count };
@@ -116,9 +115,7 @@ export default class CKAN {
async getDatasetDetails(datasetName: string) { async getDatasetDetails(datasetName: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/package_show?id=${datasetName}`,
this.DMS
}/api/3/action/package_show?id=${datasetName}`,
1 1
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -131,9 +128,7 @@ export default class CKAN {
async getDatasetActivityStream(datasetName: string) { async getDatasetActivityStream(datasetName: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/package_activity_list?id=${datasetName}`,
this.DMS
}/api/3/action/package_activity_list?id=${datasetName}`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -151,9 +146,7 @@ export default class CKAN {
async getUser(userId: string) { async getUser(userId: string) {
try { try {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/user_show?id=${userId}`,
this.DMS
}/api/3/action/user_show?id=${userId}`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -166,10 +159,7 @@ export default class CKAN {
} }
async getGroupList() { async getGroupList() {
const response = await fetchRetry( const response = await fetchRetry(`${this.DMS}/api/3/action/group_list`, 3);
`${this.DMS}/api/3/action/group_list`,
3
);
const responseData = await response.json(); const responseData = await response.json();
const groups: Array<string> = responseData.result; const groups: Array<string> = responseData.result;
return groups; return groups;
@@ -177,9 +167,7 @@ export default class CKAN {
async getGroupsWithDetails() { async getGroupsWithDetails() {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/group_list?all_fields=True`,
this.DMS
}/api/3/action/group_list?all_fields=True`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -189,9 +177,7 @@ export default class CKAN {
async getGroupDetails(groupName: string) { async getGroupDetails(groupName: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/group_show?id=${groupName}&include_datasets=True`,
this.DMS
}/api/3/action/group_show?id=${groupName}&include_datasets=True`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -201,9 +187,7 @@ export default class CKAN {
async getGroupActivityStream(groupName: string) { async getGroupActivityStream(groupName: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/group_activity_list?id=${groupName}`,
this.DMS
}/api/3/action/group_activity_list?id=${groupName}`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -230,9 +214,7 @@ export default class CKAN {
async getOrgsWithDetails(accrossPages?: boolean) { async getOrgsWithDetails(accrossPages?: boolean) {
if (!accrossPages) { if (!accrossPages) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/organization_list?all_fields=True`,
this.DMS
}/api/3/action/organization_list?all_fields=True`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -251,9 +233,7 @@ export default class CKAN {
for (let i = 0; i < pages; i++) { for (let i = 0; i < pages; i++) {
let allOrgListResponse = await fetchRetry( let allOrgListResponse = await fetchRetry(
`${ `${this.DMS}/api/3/action/organization_list?all_fields=True&offset=${
this.DMS
}/api/3/action/organization_list?all_fields=True&offset=${
i * 25 i * 25
}&limit=25`, }&limit=25`,
3 3
@@ -267,9 +247,7 @@ export default class CKAN {
async getOrgDetails(orgName: string) { async getOrgDetails(orgName: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/organization_show?id=${orgName}&include_datasets=True`,
this.DMS
}/api/3/action/organization_show?id=${orgName}&include_datasets=True`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -279,9 +257,7 @@ export default class CKAN {
async getOrgActivityStream(orgName: string) { async getOrgActivityStream(orgName: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/organization_activity_list?id=${orgName}`,
this.DMS
}/api/3/action/organization_activity_list?id=${orgName}`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -297,9 +273,7 @@ export default class CKAN {
async getAllTags() { async getAllTags() {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/tag_list?all_fields=True`,
this.DMS
}/api/3/action/tag_list?all_fields=True`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -308,9 +282,7 @@ export default class CKAN {
} }
async getResourcesWithAliasList() { async getResourcesWithAliasList() {
const response = await fetch( const response = await fetch(`${this.DMS}/api/3/action/datastore_search`, {
`${this.DMS}/api/3/action/datastore_search`,
{
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -320,17 +292,14 @@ export default class CKAN {
id: '_table_metadata', id: '_table_metadata',
limit: '32000', limit: '32000',
}), }),
} });
);
const responseData = await response.json(); const responseData = await response.json();
const tableMetadata: Array<TableMetadata> = responseData.result.records; const tableMetadata: Array<TableMetadata> = responseData.result.records;
return tableMetadata.filter((item) => item.alias_of); return tableMetadata.filter((item) => item.alias_of);
} }
async datastoreSearch(resourceId: string) { async datastoreSearch(resourceId: string) {
const response = await fetch( const response = await fetch(`${this.DMS}/api/3/action/datastore_search`, {
`${this.DMS}/api/3/action/datastore_search`,
{
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -340,17 +309,14 @@ export default class CKAN {
id: resourceId, id: resourceId,
limit: '32000', limit: '32000',
}), }),
} });
);
const responseData = await response.json(); const responseData = await response.json();
return responseData.result.records; return responseData.result.records;
} }
async getResourceMetadata(resourceId: string) { async getResourceMetadata(resourceId: string) {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/resource_show?id=${resourceId}`,
this.DMS
}/api/3/action/resource_show?id=${resourceId}`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();
@@ -359,17 +325,14 @@ export default class CKAN {
} }
async getResourceInfo(resourceId: string) { async getResourceInfo(resourceId: string) {
const response = await fetch( const response = await fetch(`${this.DMS}/api/3/action/datastore_info`, {
`${this.DMS}/api/3/action/datastore_info`,
{
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ resource_id: resourceId }), body: JSON.stringify({ resource_id: resourceId }),
} });
);
const responseData = await response.json(); const responseData = await response.json();
const resourceInfo: Array<ResourceInfo> = responseData.result; const resourceInfo: Array<ResourceInfo> = responseData.result;
return resourceInfo; return resourceInfo;
@@ -377,9 +340,7 @@ export default class CKAN {
async getFacetFields(field: 'res_format' | 'tags') { async getFacetFields(field: 'res_format' | 'tags') {
const response = await fetchRetry( const response = await fetchRetry(
`${ `${this.DMS}/api/3/action/package_search?facet.field=["${field}"]&rows=0`,
this.DMS
}/api/3/action/package_search?facet.field=["${field}"]&rows=0`,
3 3
); );
const responseData = await response.json(); const responseData = await response.json();

View File

@@ -9,10 +9,14 @@ export function getDaysAgo(date: string) {
return (+today - +createdOn) / msInDay; return (+today - +createdOn) / msInDay;
} }
export default async function fetchRetry(url: string, n: number): Promise<any> { export default async function fetchRetry(
url: string,
n: number,
opts: Partial<RequestInit> = {}
): Promise<any> {
const abortController = new AbortController(); const abortController = new AbortController();
const id = setTimeout(() => abortController.abort(), 30000); const id = setTimeout(() => abortController.abort(), 30000);
const res = await fetch(url, { signal: abortController.signal }); const res = await fetch(url, { signal: abortController.signal, ...opts });
clearTimeout(id); clearTimeout(id);
if (!res.ok && n && n > 0) { if (!res.ok && n && n > 0) {
return await fetchRetry(url, n - 1); return await fetchRetry(url, n - 1);
@@ -21,13 +25,13 @@ export default async function fetchRetry(url: string, n: number): Promise<any> {
} }
export function removeTag(tag?: string) { export function removeTag(tag?: string) {
if (tag === "{{description}}" || !tag) { if (tag === '{{description}}' || !tag) {
return undefined; return undefined;
} }
if (typeof window !== "undefined") { if (typeof window !== 'undefined') {
const div = document.createElement("div"); const div = document.createElement('div');
div.innerHTML = tag; div.innerHTML = tag;
return div.textContent || div.innerText || ""; return div.textContent || div.innerText || '';
} }
return tag; return tag;
} }
@@ -38,10 +42,10 @@ export function convertFieldSchema(
) { ) {
function convertToGraphqlString(fieldName: string) { function convertToGraphqlString(fieldName: string) {
return fieldName return fieldName
.replaceAll(" ", "_") .replaceAll(' ', '_')
.replaceAll("(", "_") .replaceAll('(', '_')
.replaceAll(")", "_") .replaceAll(')', '_')
.replace(/[^\w\s]|(_)\1/gi, "_"); .replace(/[^\w\s]|(_)\1/gi, '_');
} }
const entries = Object.entries(schema); const entries = Object.entries(schema);
return { return {

View File

@@ -1,5 +1,11 @@
# @portaljs/components # @portaljs/components
## 0.3.2
### Patch Changes
- [#1011](https://github.com/datopian/portaljs/pull/1011) [`8e896138`](https://github.com/datopian/portaljs/commit/8e896138c622615d9bd9bd1d4a18de0cf38d85ec) Thanks [@luccasmmg](https://github.com/luccasmmg)! - fix bug when there were multiple flatuitable components at the same time
## 0.3.1 ## 0.3.1
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@portaljs/components", "name": "@portaljs/components",
"version": "0.3.1", "version": "0.3.2",
"type": "module", "type": "module",
"description": "https://portaljs.org", "description": "https://portaljs.org",
"keywords": [ "keywords": [

View File

@@ -5,22 +5,20 @@ import LoadingSpinner from './LoadingSpinner';
const queryClient = new QueryClient(); const queryClient = new QueryClient();
export async function getCsv(url: string, corsProxy?: string) { export async function getCsv(url: string, bytes) {
if (corsProxy) {
url = corsProxy + url;
}
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
Range: 'bytes=0-5132288', Range: `bytes=0-${bytes}`,
}, },
}); });
const data = await response.text(); const data = await response.text();
return data; return data;
} }
export async function parseCsv(file: string): Promise<any> { export async function parseCsv(file: string, parsingConfig): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Papa.parse(file, { Papa.parse(file, {
...parsingConfig,
header: true, header: true,
dynamicTyping: true, dynamicTyping: true,
skipEmptyLines: true, skipEmptyLines: true,
@@ -41,18 +39,29 @@ export interface FlatUiTableProps {
url?: string; url?: string;
data?: { [key: string]: number | string }[]; data?: { [key: string]: number | string }[];
rawCsv?: string; rawCsv?: string;
corsProxy?: string; randomId?: number;
bytes: number;
parsingConfig: any;
} }
export const FlatUiTable: React.FC<FlatUiTableProps> = ({ export const FlatUiTable: React.FC<FlatUiTableProps> = ({
url, url,
data, data,
rawCsv, rawCsv,
corsProxy, bytes = 5132288,
parsingConfig = {},
}) => { }) => {
const randomId = Math.random();
return ( return (
// Provide the client to your App // Provide the client to your App
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<TableInner corsProxy={corsProxy} url={url} data={data} rawCsv={rawCsv} /> <TableInner
bytes={bytes}
url={url}
data={data}
rawCsv={rawCsv}
randomId={randomId}
parsingConfig={parsingConfig}
/>
</QueryClientProvider> </QueryClientProvider>
); );
}; };
@@ -61,7 +70,9 @@ const TableInner: React.FC<FlatUiTableProps> = ({
url, url,
data, data,
rawCsv, rawCsv,
corsProxy, randomId,
bytes,
parsingConfig,
}) => { }) => {
if (data) { if (data) {
return ( return (
@@ -71,13 +82,17 @@ const TableInner: React.FC<FlatUiTableProps> = ({
); );
} }
const { data: csvString, isLoading: isDownloadingCSV } = useQuery( const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
['dataCsv', url], ['dataCsv', url, randomId],
() => getCsv(url as string, corsProxy), () => getCsv(url as string, bytes),
{ enabled: !!url } { enabled: !!url }
); );
const { data: parsedData, isLoading: isParsing } = useQuery( const { data: parsedData, isLoading: isParsing } = useQuery(
['dataPreview', csvString], ['dataPreview', csvString, randomId],
() => parseCsv(rawCsv ? (rawCsv as string) : (csvString as string)), () =>
parseCsv(
rawCsv ? (rawCsv as string) : (csvString as string),
parsingConfig
),
{ enabled: rawCsv ? true : !!csvString } { enabled: rawCsv ? true : !!csvString }
); );
if (isParsing || isDownloadingCSV) if (isParsing || isDownloadingCSV)

View File

@@ -9,17 +9,24 @@ const meta: Meta = {
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { argTypes: {
data: { data: {
description: "Data to be displayed in the table, must be setup as an array of key value pairs" description:
'Data to be displayed in the table, must be setup as an array of key value pairs',
}, },
csv: { csv: {
description: "CSV data as string.", description: 'CSV data as string.',
}, },
url: { url: {
description: "Fetch the data from a CSV file remotely. only the first 5MB of data will be displayed" description:
'Fetch the data from a CSV file remotely. only the first 5MB of data will be displayed',
},
bytes: {
description:
'Fetch the data from a CSV file remotely. only the first <bytes> of data will be displayed',
},
parsingConfig: {
description:
'Configuration for parsing the CSV data. See https://www.papaparse.com/docs#config for more details',
}, },
corsProxy: {
description: "Optionally you cant set a CORS Proxy to which all your requests you be redirected"
}
}, },
}; };
@@ -29,7 +36,7 @@ type Story = StoryObj<FlatUiTableProps>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const FromColumnsAndData: Story = { export const FromColumnsAndData: Story = {
name: "Table data", name: 'Table data',
args: { args: {
data: [ data: [
{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 }, { id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },
@@ -44,20 +51,19 @@ export const FromColumnsAndData: Story = {
}; };
export const FromRawCSV: Story = { export const FromRawCSV: Story = {
name: "Table from raw CSV", name: 'Table from raw CSV',
args: { args: {
rawCsv: ` rawCsv: `
Year,Temp Anomaly Year,Temp Anomaly
1850,-0.418 1850,-0.418
2020,0.923 2020,0.923
` `,
} },
}; };
export const FromURL: Story = { export const FromURL: Story = {
name: "Table from URL", name: 'Table from URL',
args: { args: {
url: "https://raw.githubusercontent.com/datasets/finance-vix/main/data/vix-daily.csv" url: 'https://ckan-dev.sse.datopian.com/datastore/dump/601c9cf0-595e-46d8-88fc-d1ab2904e2db',
} },
}; };

View File

@@ -79,7 +79,7 @@ function fromMarkdown(opts: FromMarkdownOptions = {}) {
data: { isEmbed, target, alias }, data: { isEmbed, target, alias },
} = wikiLink; } = wikiLink;
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const wikiLinkWithHeadingPattern = /([\w\s\/\.-]*)(#.*)?/; const wikiLinkWithHeadingPattern = /([\p{Letter}\d\s\/\.-_]*)(#.*)?/u;
const [, path, heading = ""] = target.match(wikiLinkWithHeadingPattern); const [, path, heading = ""] = target.match(wikiLinkWithHeadingPattern);
const possibleWikiLinkPermalinks = wikiLinkResolver(path); const possibleWikiLinkPermalinks = wikiLinkResolver(path);

View File

@@ -3,31 +3,6 @@ import ButtonLink from './ButtonLink';
import NewsletterForm from './NewsletterForm'; import NewsletterForm from './NewsletterForm';
import Image from 'next/image'; import Image from 'next/image';
import DatahubExampleImg from '@/public/images/showcases/datahub.webp'; import DatahubExampleImg from '@/public/images/showcases/datahub.webp';
const codeLanguage = 'javascript';
const code = `export default {
strategy: 'predictive',
engine: {
cpus: 12,
backups: ['./storage/cache.wtf'],
},
}`;
const tabs = [
{ name: 'cache-advance.config.js', isActive: true },
{ name: 'package.json', isActive: false },
];
function TrafficLightsIcon(props) {
return (
<svg aria-hidden="true" viewBox="0 0 42 10" fill="none" {...props}>
<circle cx="5" cy="5" r="4.5" />
<circle cx="21" cy="5" r="4.5" />
<circle cx="37" cy="5" r="4.5" />
</svg>
);
}
/* eslint jsx-a11y/label-has-associated-control: off */ /* eslint jsx-a11y/label-has-associated-control: off */
export function Hero() { export function Hero() {
const el = useRef(null); const el = useRef(null);
@@ -50,7 +25,7 @@ export function Hero() {
Rapidly build rich data portals using a modern frontend framework. Rapidly build rich data portals using a modern frontend framework.
</p> </p>
<ButtonLink className="mt-8" href="/docs"> <ButtonLink style="secondary" className="mt-8" href="/docs">
Get started Get started
</ButtonLink> </ButtonLink>

View File

@@ -1,14 +1,37 @@
import { loadScripts } from '@/lib/loadNewsletterScripts';
import Script from 'next/script'; import Script from 'next/script';
import { useEffect } from 'react';
import { CloudIcon } from '@heroicons/react/solid';
export default function NewsletterForm() { export default function NewsletterForm() {
useEffect(() => {
/*
* The newsletter scripts MUST be loaded after
* the document is loaded, as they depend on
* the presence of some elements
*
*/
if (document.readyState === 'complete') {
const { resetElements } = loadScripts();
return () => {
resetElements();
};
} else {
window.addEventListener('load', loadScripts);
return () => window.removeEventListener('load', loadScripts);
}
}, []);
return ( return (
<div> <div>
<div <div
id="sib-form-container" id="sib-form-container"
className="mt-8 sm:mx-auto sm:text-center lg:text-left lg:mx-0" className="mt-8 sm:mx-auto sm:text-center lg:text-left lg:mx-0"
> >
<p className="text-base font-medium text-slate-400 dark:text-slate-400"> <p className="text-base font-medium flex items-center">
Sign up to get notified about updates <CloudIcon className="w-6 h-6 mr-1" />
Join the PortalJS Cloud waitlist and get early access!
</p> </p>
<div id="sib-container" className="!bg-transparent !p-0 !pb-5"> <div id="sib-container" className="!bg-transparent !p-0 !pb-5">
<form <form
@@ -47,7 +70,7 @@ export default function NewsletterForm() {
> >
<path d="M460.116 373.846l-20.823-12.022c-5.541-3.199-7.54-10.159-4.663-15.874 30.137-59.886 28.343-131.652-5.386-189.946-33.641-58.394-94.896-95.833-161.827-99.676C261.028 55.961 256 50.751 256 44.352V20.309c0-6.904 5.808-12.337 12.703-11.982 83.556 4.306 160.163 50.864 202.11 123.677 42.063 72.696 44.079 162.316 6.031 236.832-3.14 6.148-10.75 8.461-16.728 5.01z" /> <path d="M460.116 373.846l-20.823-12.022c-5.541-3.199-7.54-10.159-4.663-15.874 30.137-59.886 28.343-131.652-5.386-189.946-33.641-58.394-94.896-95.833-161.827-99.676C261.028 55.961 256 50.751 256 44.352V20.309c0-6.904 5.808-12.337 12.703-11.982 83.556 4.306 160.163 50.864 202.11 123.677 42.063 72.696 44.079 162.316 6.031 236.832-3.14 6.148-10.75 8.461-16.728 5.01z" />
</svg> </svg>
Notify Me Join now
</button> </button>
<input <input
type="text" type="text"
@@ -88,37 +111,6 @@ export default function NewsletterForm() {
</div> </div>
</div> </div>
</div> </div>
<Script
id="newsletter-form-validation-message"
dangerouslySetInnerHTML={{
__html: `
window.REQUIRED_CODE_ERROR_MESSAGE = 'Please choose a country code';
window.LOCALE = 'en';
window.EMAIL_INVALID_MESSAGE = window.SMS_INVALID_MESSAGE = "The information provided is invalid. Please review the field format and try again.";
window.REQUIRED_ERROR_MESSAGE = "This field cannot be left blank. ";
window.GENERIC_INVALID_MESSAGE = "The information provided is invalid. Please review the field format and try again.";
window.translation = {
common: {
selectedList: '{quantity} list selected',
selectedLists: '{quantity} lists selected'
}
};
var AUTOHIDE = Boolean(0);
`,
}}
/>
<Script
strategy="worker"
id="newsletter-submit-form"
src="https://sibforms.com/forms/end-form/build/main.js"
/>
</div> </div>
); );
} }

View File

@@ -101,7 +101,7 @@ List of available datasets:
<Catalog datasets={datasets} facets={['group']}/> <Catalog datasets={datasets} facets={['group']}/>
``` ```
You now have a filter in your page with all possible values automatically added to it. Rerun `npm run mddb`. You now have a filter in your page with all possible values automatically added to it.
![Data catalog with facets built with PortalJS](https://i.imgur.com/p2miSdg.png) ![Data catalog with facets built with PortalJS](https://i.imgur.com/p2miSdg.png)

View File

@@ -3,3 +3,4 @@ id: anuveyatsu
name: Anuar Ustayev name: Anuar Ustayev
avatar: https://avatars.githubusercontent.com/anuveyatsu avatar: https://avatars.githubusercontent.com/anuveyatsu
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -3,3 +3,4 @@ id: joaodemenech
name: João Demenech name: João Demenech
avatar: https://avatars.githubusercontent.com/demenech avatar: https://avatars.githubusercontent.com/demenech
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -3,3 +3,4 @@ id: luccasmateus
name: Luccas Mateus name: Luccas Mateus
avatar: https://avatars.githubusercontent.com/luccasmmg avatar: https://avatars.githubusercontent.com/luccasmmg
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -3,3 +3,4 @@ id: mikanebu
name: Meiran Zhiyenbayev name: Meiran Zhiyenbayev
avatar: https://avatars.githubusercontent.com/mikanebu avatar: https://avatars.githubusercontent.com/mikanebu
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -3,3 +3,4 @@ id: ola-rubaj
name: Ola Rubaj name: Ola Rubaj
avatar: https://avatars.githubusercontent.com/olayway avatar: https://avatars.githubusercontent.com/olayway
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -3,3 +3,4 @@ id: popovayoana
name: Yoana Popova name: Yoana Popova
avatar: https://avatars.githubusercontent.com/popovayoana avatar: https://avatars.githubusercontent.com/popovayoana
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -3,3 +3,4 @@ id: rufuspollock
name: Rufus Pollock name: Rufus Pollock
avatar: https://avatars.githubusercontent.com/rufuspollock avatar: https://avatars.githubusercontent.com/rufuspollock
--- ---
<NextSeo noindex={true} nofollow={true} />

View File

@@ -0,0 +1,45 @@
import { HTMLProps } from "react";
const loadScript = (
props: HTMLProps<HTMLScriptElement> & { textContent?: string }
): (() => void) => {
const script = document.createElement("script");
Object.assign(script, props);
document.head.appendChild(script);
return () => document.head.removeChild(script);
};
export const loadScripts = () => {
const formValidationScript = loadScript({
id: "newsletter-form-validation-message",
textContent: `
window.LOCALE = 'en';
window.EMAIL_INVALID_MESSAGE = "The information provided is invalid. Please review the field format and try again.";
window.REQUIRED_ERROR_MESSAGE = "This field cannot be left blank. ";
window.GENERIC_INVALID_MESSAGE = "The information provided is invalid. Please review the field format and try again.";
window.translation = {
common: {
selectedList: '{quantity} list selected',
selectedLists: '{quantity} lists selected'
}
};
var AUTOHIDE = Boolean(0);
`,
});
const formSubmitScript = loadScript({
id: "newsletter-submit-form",
src: "https://sibforms.com/forms/end-form/build/main.js",
async: true,
defer: true,
});
return {
resetElements: () => {
formSubmitScript();
formValidationScript();
},
};
};

View File

@@ -4,6 +4,7 @@ module.exports = {
generateRobotsTxt: true, generateRobotsTxt: true,
robotsTxtOptions: { robotsTxtOptions: {
policies: [ policies: [
{ userAgent: '*', disallow: '/people/' },
{ userAgent: '*', disallow: '/people' }, { userAgent: '*', disallow: '/people' },
{ userAgent: '*', disallow: '/?amp=1' }, { userAgent: '*', disallow: '/?amp=1' },
], ],

View File

@@ -9,7 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@headlessui/react": "^1.3.0", "@headlessui/react": "^1.3.0",
"@heroicons/react": "^1.0.3", "@heroicons/react": "^1.0.6",
"@mdx-js/loader": "^2.3.0", "@mdx-js/loader": "^2.3.0",
"@portaljs/core": "^1.0.6", "@portaljs/core": "^1.0.6",
"@portaljs/remark-callouts": "^1.0.5", "@portaljs/remark-callouts": "^1.0.5",
@@ -367,9 +367,9 @@
} }
}, },
"node_modules/@heroicons/react": { "node_modules/@heroicons/react": {
"version": "1.0.3", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.6.tgz",
"integrity": "sha512-wdzWbDiFKzeL6xFJsgY2PqvDkx4hFmQDpEFRVj872EA71XOjr8g0DQj5rHWm0y7LwfGOFL0eQmEiMbTyGNOnTQ==", "integrity": "sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==",
"peerDependencies": { "peerDependencies": {
"react": ">= 16" "react": ">= 16"
} }

View File

@@ -12,7 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@headlessui/react": "^1.3.0", "@headlessui/react": "^1.3.0",
"@heroicons/react": "^1.0.3", "@heroicons/react": "^1.0.6",
"@mdx-js/loader": "^2.3.0", "@mdx-js/loader": "^2.3.0",
"@portaljs/core": "^1.0.6", "@portaljs/core": "^1.0.6",
"@portaljs/remark-callouts": "^1.0.5", "@portaljs/remark-callouts": "^1.0.5",