diff --git a/docs/app/(home)/page.tsx b/docs/app/(home)/page.tsx index f0cdf12..832f112 100644 --- a/docs/app/(home)/page.tsx +++ b/docs/app/(home)/page.tsx @@ -2,7 +2,6 @@ /** biome-ignore-all lint/suspicious/noArrayIndexKey: */ "use client"; -import { cva } from "class-variance-authority"; import { cn } from "fumadocs-ui/utils/cn"; import Image from "next/image"; import Link from "next/link"; diff --git a/docs/bun.lock b/docs/bun.lock index a029737..ff5cdb4 100644 --- a/docs/bun.lock +++ b/docs/bun.lock @@ -4,6 +4,9 @@ "": { "name": "docs", "dependencies": { + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-collapsible": "^1.1.12", + "class-variance-authority": "^0.7.1", "fumadocs-core": "15.8.1", "fumadocs-mdx": "12.0.1", "fumadocs-ui": "15.8.1", @@ -11,6 +14,7 @@ "next": "15.5.4", "react": "^19.1.1", "react-dom": "^19.1.1", + "tailwind-merge": "^3.3.1", }, "devDependencies": { "@biomejs/biome": "^2.2.4", diff --git a/docs/cli.json b/docs/cli.json new file mode 100644 index 0000000..1584439 --- /dev/null +++ b/docs/cli.json @@ -0,0 +1,11 @@ +{ + "aliases": { + "uiDir": "./components/ui", + "componentsDir": "./components", + "blockDir": "./components", + "cssDir": "./styles", + "libDir": "./lib" + }, + "baseDir": "", + "commands": {} +} \ No newline at end of file diff --git a/docs/components/accordion.tsx b/docs/components/accordion.tsx new file mode 100644 index 0000000..9b2fd84 --- /dev/null +++ b/docs/components/accordion.tsx @@ -0,0 +1,136 @@ +'use client'; + +import type { + AccordionMultipleProps, + AccordionSingleProps, +} from '@radix-ui/react-accordion'; +import * as AccordionPrimitive from '@radix-ui/react-accordion'; +import { Check, ChevronRight, Link as LinkIcon } from 'lucide-react'; +import { + type ComponentPropsWithoutRef, + forwardRef, + type ReactNode, + useEffect, + useRef, + useState, +} from 'react'; +import { cn } from '../lib/cn'; +import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'; +import { buttonVariants } from './ui/button'; +import { mergeRefs } from '../lib/merge-refs'; + +export const Accordions = forwardRef< + HTMLDivElement, + | Omit + | Omit +>(({ type = 'single', className, defaultValue, ...props }, ref) => { + const rootRef = useRef(null); + const composedRef = mergeRefs(ref, rootRef); + const [value, setValue] = useState(() => + type === 'single' ? (defaultValue ?? '') : (defaultValue ?? []), + ); + + useEffect(() => { + const id = window.location.hash.substring(1); + const element = rootRef.current; + if (!element || id.length === 0) return; + + const selected = document.getElementById(id); + if (!selected || !element.contains(selected)) return; + const value = selected.getAttribute('data-accordion-value'); + + if (value) + setValue((prev) => (typeof prev === 'string' ? value : [value, ...prev])); + }, []); + + return ( + // @ts-expect-error -- Multiple types + + ); +}); + +Accordions.displayName = 'Accordions'; + +export const Accordion = forwardRef< + HTMLDivElement, + Omit< + ComponentPropsWithoutRef, + 'value' | 'title' + > & { + title: string | ReactNode; + value?: string; + } +>( + ( + { title, className, id, value = String(title), children, ...props }, + ref, + ) => { + return ( + + + + + {title} + + {id ? : null} + + +
+ {children} +
+
+
+ ); + }, +); + +function CopyButton({ id }: { id: string }) { + const [checked, onClick] = useCopyButton(() => { + const url = new URL(window.location.href); + url.hash = id; + + return navigator.clipboard.writeText(url.toString()); + }); + + return ( + + ); +} + +Accordion.displayName = 'Accordion'; diff --git a/docs/components/files.tsx b/docs/components/files.tsx new file mode 100644 index 0000000..ee3f73f --- /dev/null +++ b/docs/components/files.tsx @@ -0,0 +1,85 @@ +'use client'; + +import { cva } from 'class-variance-authority'; +import { + File as FileIcon, + Folder as FolderIcon, + FolderOpen, +} from 'lucide-react'; +import { type HTMLAttributes, type ReactNode, useState } from 'react'; +import { cn } from '../lib/cn'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from './ui/collapsible'; + +const itemVariants = cva( + 'flex flex-row items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground [&_svg]:size-4', +); + +export function Files({ + className, + ...props +}: HTMLAttributes): React.ReactElement { + return ( +
+ {props.children} +
+ ); +} + +export interface FileProps extends HTMLAttributes { + name: string; + icon?: ReactNode; +} + +export interface FolderProps extends HTMLAttributes { + name: string; + + disabled?: boolean; + + /** + * Open folder by default + * + * @defaultValue false + */ + defaultOpen?: boolean; +} + +export function File({ + name, + icon = , + className, + ...rest +}: FileProps): React.ReactElement { + return ( +
+ {icon} + {name} +
+ ); +} + +export function Folder({ + name, + defaultOpen = false, + ...props +}: FolderProps): React.ReactElement { + const [open, setOpen] = useState(defaultOpen); + + return ( + + + {open ? : } + {name} + + +
{props.children}
+
+
+ ); +} diff --git a/docs/components/ui/collapsible.tsx b/docs/components/ui/collapsible.tsx new file mode 100644 index 0000000..dbcf3f0 --- /dev/null +++ b/docs/components/ui/collapsible.tsx @@ -0,0 +1,39 @@ +'use client'; +import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'; +import { forwardRef, useEffect, useState } from 'react'; +import { cn } from '../../lib/cn'; + +const Collapsible = CollapsiblePrimitive.Root; + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; + +const CollapsibleContent = forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef +>(({ children, ...props }, ref) => { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + return ( + + {children} + + ); +}); + +CollapsibleContent.displayName = + CollapsiblePrimitive.CollapsibleContent.displayName; + +export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx index eb7e005..73f597c 100644 --- a/docs/content/docs/index.mdx +++ b/docs/content/docs/index.mdx @@ -3,4 +3,64 @@ title: Quick Start description: Getting Started with ckan-devstaller --- -This site is a work in progress. You may [view the ckan-devstaller README and source code](https://github.com/dathere/ckan-devstaller) in the meantime. +ckan-devstaller attempts to install a CKAN instance from source along with [ckan-compose](https://github.com/tino097/ckan-compose) and other optional features, intended for development use in a new Ubuntu 22.04 instance. + +Make sure `ckan-devstaller` is run in a **new** Ubuntu 22.04 instanceof. Do NOT run `ckan-devstaller` in an existing instance that is important for your usage. + +import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; + + + If you are using Ubuntu 22.04 on VirtualBox, you may need to add your user to the sudoers file before running the ckan-devstaller install script. Open a terminal in your virtual machine (VM), run `su -` and log in as the root user with the password you used to set up the VM, then type `sudo adduser sudo` where `` is your username then restart your VM and run the ckan-devstaller installer script. + Currently `ckan-devstaller` supports `x86_64` architecture. `ARM64` support is planned. + + +Currently you have two options to choose from for installation. Paste one of the following scripts into your new Ubuntu 22.04 instance's terminal: + +## (Option 1/2) Install with interactive mode + +```bash +wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.2.1/install.bash | bash -s default +``` + +## (Option 2/2) Install with non-interactive mode with a specific config + +The following script will install the following: + +- CKAN 2.11.3 +- [ckan-compose](https://github.com/tino097/ckan-compose/tree/ckan-devstaller) +- [DataStore extension](https://docs.ckan.org/en/2.11/maintaining/datastore.html) +- [ckanext-scheming extension](https://github.com/ckan/ckanext-scheming) +- [DataPusher+ extension](https://github.com/dathere/datapusher-plus) + +[DRUF mode](https://github.com/dathere/datapusher-plus?tab=readme-ov-file#druf-dataset-resource-upload-first-workflow) for DataPusher+ is available but disabled by default. + +```bash +wget -O - https://github.com/dathere/ckan-devstaller/releases/download/0.2.1/install.bash | bash -s default +``` + +## Learn more + +import { BlocksIcon, GitMergeIcon, Trash2Icon } from 'lucide-react'; + + + } + href="https://github.com/dathere/ckan-devstaller" + title="Source code"> + View the source code of ckan-devstaller on GitHub + + } + href="/docs/reference/installation-architecture" + title="Installation architecture" + > + Learn about where files are installed after running ckan-devstaller + + } + href="/docs/tutorials/uninstall-ckan" + title="Uninstall CKAN" + > + Learn how to uninstall CKAN after running ckan-devstaller + + diff --git a/docs/content/docs/reference/installation-architecture.mdx b/docs/content/docs/reference/installation-architecture.mdx new file mode 100644 index 0000000..87c255e --- /dev/null +++ b/docs/content/docs/reference/installation-architecture.mdx @@ -0,0 +1,76 @@ +--- +title: Installation architecture +description: View a brief overview of what the installation from ckan-devstaller looks like +--- + +import { File, Folder, Files } from 'fumadocs-ui/components/files'; + +## CKAN and extensions + +The CKAN repository selected from ckan-devstaller is installed to `/usr/lib/ckan/default/src/ckan`. Extensions are also installed as sibling folders. For example if `ckanext-scheming` is also installed: + +```files +/usr/lib/ckan/default/src +├── ckan +│ ├── ... +├── ckanext-scheming +│ ├── ... +``` + +The configuration file for CKAN is installed at `/etc/ckan/default/ckan.ini`: + +```files +/etc/ckan/default +├── ckan.ini +├── who.ini +``` + +## ckan-compose + +We install certain first-time install files and `ckan-compose` as a directory in the user's home (`~`) directory. For example for the user `adam`: + +```files +/home/adam +├── ahoy +├── dpp_default_config.ini +├── get-docker.sh +├── permissions.sql +├── ckan-compose +│ ├── ... +``` + +After running ckan-devstaller you may also see many files starting with `qsv` and `README`. There are various files you can remove after running ckan-devstaller including: + +- `dpp_default_config.ini` +- `get-docker.sh` +- `permissions.sql` +- `README` +- The various `qsv` files + +Here's a script you can run for cleanup after running ckan-devstaller: + +```bash +cd ~/ +rm -rf dpp_default_config.ini get-docker.sh permissions.sql README qsv* +``` + +## DataPusher+ + +We install a compatible version of qsv with the DataPusher+ variant named `qsvdp` and move it to `/usr/local/bin`: + +```files +/usr/local/bin +├── qsvdp +``` + +The ckanext-scheming and DataPusher+ extensions are installed in the same location as other CKAN extensions: + +```files +/usr/lib/ckan/default/src +├── ckan +| ├── ... +├── ckanext-scheming +| ├── ... +├── datapusher_plus +| ├── ... +``` diff --git a/docs/lib/cn.ts b/docs/lib/cn.ts new file mode 100644 index 0000000..ba66fd2 --- /dev/null +++ b/docs/lib/cn.ts @@ -0,0 +1 @@ +export { twMerge as cn } from 'tailwind-merge'; diff --git a/docs/lib/merge-refs.ts b/docs/lib/merge-refs.ts new file mode 100644 index 0000000..7d05f74 --- /dev/null +++ b/docs/lib/merge-refs.ts @@ -0,0 +1,15 @@ +import type * as React from 'react'; + +export function mergeRefs( + ...refs: (React.Ref | undefined)[] +): React.RefCallback { + return (value) => { + refs.forEach((ref) => { + if (typeof ref === 'function') { + ref(value); + } else if (ref) { + ref.current = value; + } + }); + }; +} diff --git a/docs/package.json b/docs/package.json index 9b9d27c..7d3909d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,13 +11,17 @@ "format": "biome format --write" }, "dependencies": { + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-collapsible": "^1.1.12", + "class-variance-authority": "^0.7.1", "fumadocs-core": "15.8.1", "fumadocs-mdx": "12.0.1", "fumadocs-ui": "15.8.1", "lucide-react": "^0.544.0", "next": "15.5.4", "react": "^19.1.1", - "react-dom": "^19.1.1" + "react-dom": "^19.1.1", + "tailwind-merge": "^3.3.1" }, "devDependencies": { "@types/node": "24.5.2", diff --git a/docs/source.config.ts b/docs/source.config.ts index d2e968b..e49b84a 100644 --- a/docs/source.config.ts +++ b/docs/source.config.ts @@ -1,3 +1,4 @@ +import { remarkMdxFiles } from 'fumadocs-core/mdx-plugins'; import { defineConfig, defineDocs, @@ -21,6 +22,6 @@ export const docs = defineDocs({ export default defineConfig({ mdxOptions: { - // MDX options + remarkPlugins: [remarkMdxFiles], }, });