add-chart
Renamed → /portaljs-add-chart. This alias will be removed next minor release.
Wire a scaffolded PortalJS portal to a CKAN backend over its API. Generates a tiny server-side fetch client (no runtime dependency) and feeds the /search catalog and /@<namespace>/<slug> showcases from CKAN instead of datasets.json.
> /plugin marketplace add datopian/portaljs > /plugin install portaljs@datopian-portaljs
How it fires
How this command gets triggered: by you, by Claude, or both.
/portaljs-connect-ckanContext preview
What this command does when you run it.
Wire a scaffolded PortalJS portal to a CKAN backend over its API. Generates a tiny server-side fetch client (no runtime dependency) and feeds the /search catalog and /@<namespace>/<slug> showcases from CKAN instead of datasets.json.
description: Wire a scaffolded PortalJS portal to a CKAN backend over its API. Generates a tiny server-side fetch client (no runtime dependency) and feeds the /search catalog and /@<namespace>/<slug> showcases from CKAN instead of datasets.json. allowed-tools: Read, Write, Edit, Bash, WebFetch
Connect an existing `portaljs-catalog` portal to a live CKAN backend. The portal stops reading the static `datasets.json` manifest (and files in `/public/data/`) and instead feeds its two data surfaces — the **`/search` catalog** and the **`/@<namespace>/<slug>` showcases** — straight from a CKAN instance's REST API (`package_search` / `package_show`) through a tiny generated fetch client. Output is plain, editable Next.js code with **no runtime dependency** — no opaque framework wiring.
> **Why a fetch client, not `@portaljs/ckan`?** That package's bundle wires React UI > components to React 18 internals (`ReactCurrentDispatcher`) and crashes at import under > the template's React 19 — even server-side, where this skill imports it. The CKAN client > class is locked inside that same module, so it can't be used in isolation. A ~40-line > `fetch` wrapper covers the two REST actions this skill needs (`package_search`, > `package_show`), is server-side only, ships zero client bytes, and has no React coupling.
Use this for the "decoupled / any backend" path: the user has a CKAN data management system (their own or a public one) and wants a browseable portal in front of it.
**You can run this right after `/portaljs-new-portal`.** A fresh portal ships with a few sample datasets so it builds and renders immediately — you do NOT need to hand-author a static dataset list first. If the user says up front they want a CKAN backend, scaffold with the sample data, then run `/portaljs-connect-ckan` to swap the source over to CKAN. Only a CKAN base URL is required.
The generated pages fetch CKAN **server-side** in `getStaticProps`/`getStaticPaths`, so the catalog is pre-rendered at build time and the site can be statically deployed.
CKAN instance; the skill appends `/api/3/action/...` itself. Must be publicly reachable.
**If the CKAN base URL is missing, ask for it (and the optional org/group filter) — never dead-end with a missing-input error.**
Extract:
If the CKAN base URL is missing, **ask** and wait for the answer:
To connect a CKAN backend I need: 1. CKAN base URL (e.g. https://demo.dev.datopian.com) — required 2. Optional org filter (Enter for all organizations) 3. Optional group filter (Enter for all groups) 4. Portal directory (Enter for current directory)
The target must be a `portaljs-catalog` portal. Confirm `PORTAL_DIR/package.json` and `PORTAL_DIR/pages` exist (the catalog template also has `datasets.json`, `pages/search.tsx`, and `pages/[owner]/[slug].tsx`, which this skill rewires). If it isn't a portal, tell the user and suggest running `/portaljs-new-portal` first rather than failing silently.
Hit `package_search` with a tiny page to confirm the URL is a working CKAN API:
curl -s -m 20 "CKAN_URL/api/3/action/package_search?rows=1"
The response must be JSON with `"success": true`. If the request fails, times out, or `success` is not `true`, tell the user the URL didn't resolve to a working CKAN API and ask them to confirm it's a CKAN root that's publicly accessible, then retry — don't dead-end.
If `ORG_FILTER` is set, validate each org exists:
curl -s -m 20 "CKAN_URL/api/3/action/organization_show?id=ORG"
If any returns `success: false`, tell the user that org wasn't found, list the valid orgs from `organization_list`, and ask which one they meant (or to drop the filter) before continuing.
Write `PORTAL_DIR/lib/ckan.ts` — a self-contained server-side client over the CKAN REST API. **No package install, no `tsconfig` changes**: it uses the built-in `fetch`, so there is nothing to add to `package.json` and no `paths` entry to maintain. The provided URL becomes the default, overridable at deploy time via the `DMS` env var. Org/group filters and the build-time page cap live here as plain editable constants.
// Minimal server-side CKAN client — plain fetch, no dependency, no React coupling.
// Used ONLY in getStaticProps/getStaticPaths, so it never reaches the browser bundle.
// CKAN backend base URL. Override at deploy time with the DMS env var.
export const DMS = (process.env.DMS || 'CKAN_URL').replace(/\/+$/, '')
// Filters baked in by /portaljs-connect-ckan. Empty array = no filter.
export const ORG_FILTER: string[] = [/* ORG_FILTER */]
export const GROUP_FILTER: string[] = [/* GROUP_FILTER */]
// Max datasets to pre-render at build time (SSG). Raise for larger catalogs;
// note every dataset becomes one statically generated page.
export const MAX_DATASETS = 200
// CKAN REST shapes — only the fields the pages read.
type CkanResource = { id: string; name?: string; format?: string; url?: string }
type CkanOrganization = { name?: string; title?: string }
export type CkanPackage = {
name: string
title?: string
notes?: string
organization?: CkanOrganization
res🌀 AI-native framework for building data portals. Scaffold a full portal from a brief and load datasets in minutes with agentic skills — any backend (CKAN, GitHub, Frictionless).
Repo: datopian/portaljs
Renamed → /portaljs-add-chart. This alias will be removed next minor release.
Renamed → /portaljs-add-dataset. This alias will be removed next minor release.
Renamed → /portaljs-add-map. This alias will be removed next minor release.
Renamed → /portaljs-add-resource. This alias will be removed next minor release.
Migrate a whole ArcGIS Hub site (opendata.arcgis.com or a Hub Premium custom domain) into a PortalJS Arc portal end-to-end. Harvests the Hub /data.json…
Renamed → /portaljs-architect. This alias will be removed next minor release.