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

Documentation upgrades #7

Merged
merged 1 commit into from
Feb 13, 2024
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
15 changes: 10 additions & 5 deletions .github/workflows/pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,25 @@ jobs:
steps:
- uses: actions/checkout@v3

- name: Use Node.js ${{ matrix.node-version }}
- name: 🟢 Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'

- run: yarn install --frozen-lockfile
- name: 🧰 Install dependencies
run: yarn install --frozen-lockfile

- run: yarn run build
- name: 🔨 Build demo app
run: yarn run build

- name: Prepare Github Pages
- name: 📄 Build types documentation
run: yarn run typedoc

- name: 🌐 Configure Github Pages domain
run: echo "svelte-admin-demo.orbitale.io" > build/CNAME

- name: Deploy
- name: 🚀 Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

This package is an **admin generator** for your JS/TS/Svelte projects. It can consume a distant API, a localStorage, or even an RPC-based data storage (like when using [Tauri](https://tauri.app/)).

| List | View | Edit |
| ---------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- |
| ![SvelteAdmin Backoffice List](./docs/backoffice_list.png) | ![SvelteAdmin Backoffice View](./docs/backoffice_view.png) | ![SvelteAdmin Backoffice Edit](./docs/backoffice_edit.png) |
| List | View | Edit |
|----------------------------------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------|
| ![SvelteAdmin Backoffice List](./docs-src/backoffice_list.png) | ![SvelteAdmin Backoffice View](./docs-src/backoffice_view.png) | ![SvelteAdmin Backoffice Edit](./docs-src/backoffice_edit.png) |

There is a [roadmap](#roadmap) at the end of this documentation to know what features are soon coming!

Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin=prettier-plugin-svelte . --check . && eslint src",
"format": "prettier --plugin=prettier-plugin-svelte --write .",
"test:unit": "vitest"
"test:unit": "vitest",
"typedoc": "typedoc --options typedoc.json --readme none"
},
"exports": {
".": {
Expand Down Expand Up @@ -47,7 +48,8 @@
"carbon-icons-svelte": "^12.0.0",
"luxon": "^3.4.4",
"svelte": "^4.0.0",
"svelte-i18n": "^4.0.0"
"svelte-i18n": "^4.0.0",
"typedoc": "^0.25.8"
},
"devDependencies": {
"@faker-js/faker": "^8.3.1",
Expand All @@ -56,6 +58,7 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.2.3",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@types/node": "^20.11.17",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
Expand All @@ -71,6 +74,7 @@
"sass": "^1.69.5",
"svelte-check": "^3.6.2",
"tslib": "^2.4.1",
"typedoc-plugin-mdn-links": "^3.1.16",
"typescript": "^5.3.3",
"vite": "^5.0.7",
"vitest": "^1.0.4"
Expand Down
90 changes: 75 additions & 15 deletions src/lib/Crud/Operations.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* Operations are a class system that allows you to determine what grids or forms you will display in your {@link Dashboard.DashboardDefinition | Dashboard}
* @module
*/

import type { FieldOptions, FieldInterface } from '$lib/FieldDefinitions/definition';
import type { CrudDefinition } from '$lib/Crud/definition';
import type { DashboardDefinition } from '$lib/Dashboard/definition';
Expand All @@ -6,6 +11,7 @@ import type { CrudTheme } from '$lib/themes/ThemeConfig';
import { defaultPaginationOptions, type PaginationOptions } from '$lib/DataTable/Pagination';
import type { FilterInterface, FilterOptions } from '$lib/Filter';

/** */
export type CrudOperationName =
| 'new'
| 'edit'
Expand All @@ -16,33 +22,57 @@ export type CrudOperationName =
| 'entity_list'
| string;

/** */
export interface CrudOperation {
readonly name: CrudOperationName;
readonly label: string;
readonly displayComponentName: CrudTheme;
readonly fields: Array<FieldInterface<FieldOptions>>;
readonly actions: Array<Action>;
readonly options: Record<string, string | unknown>;

/** */ readonly name: CrudOperationName;
/** */ readonly label: string;
/** */ readonly displayComponentName: CrudTheme;
/** */ readonly fields: Array<FieldInterface<FieldOptions>>;
/** */ readonly actions: Array<Action>;
/** */ readonly options: Record<string, string | unknown>;

/** */
get dashboard(): DashboardDefinition;
set dashboard(dashboard: DashboardDefinition);

/** */
get crud(): CrudDefinition<unknown>;
set crud(crud: CrudDefinition<unknown>);
}

export class BaseCrudOperation implements CrudOperation {
/**
* @abstract
*
* @remark
* This class allows you to create your own classes and extend the base operation
* in case you need something else than the built-in ones.
*
* @example
* export class PreviewOperation extends BaseCrudOperation {
* // Your custom code
* constructor(
* fields: Array<FieldInterface<FieldOptions>>,
* actions: Array<Action> = [],
* options: FormOperationOptions = DEFAULT_FORM_OPERATION_OPTION
* ) {
* super('preview', 'crud.preview.label', 'preview', fields, actions, options);
* }
* }
**/
export abstract class BaseCrudOperation implements CrudOperation {
private _dashboard: DashboardDefinition | null = null;
private _crud: CrudDefinition<unknown> | null = null;

constructor(
public readonly name: CrudOperationName,
public readonly label: string,
public readonly displayComponentName: CrudTheme,
public readonly fields: Array<FieldInterface<FieldOptions>>,
public readonly actions: Array<Action>,
public readonly options: Record<string, string | unknown> = {}
protected constructor(
/** */ public readonly name: CrudOperationName,
/** */ public readonly label: string,
/** */ public readonly displayComponentName: CrudTheme,
/** */ public readonly fields: Array<FieldInterface<FieldOptions>>,
/** */ public readonly actions: Array<Action>,
/** */ public readonly options: Record<string, string | unknown> = {}
) {}

/** */
get dashboard(): DashboardDefinition {
if (!this._dashboard) {
throw new Error('Dashboard is not set in operation: did you try to bypass Crud setup?');
Expand All @@ -60,6 +90,7 @@ export class BaseCrudOperation implements CrudOperation {
this._dashboard = dashboard;
}

/** */
get crud(): CrudDefinition<unknown> {
if (!this._crud) {
throw new Error('Crud is not set in operation: did you try to bypass Crud setup?');
Expand All @@ -75,14 +106,24 @@ export class BaseCrudOperation implements CrudOperation {
}
}

/**
* @see {@link New}
* @see {@link Edit}
**/
export type FormOperationOptions = object & {
preventHttpFormSubmit: boolean;
};
/** */
const DEFAULT_FORM_OPERATION_OPTION: FormOperationOptions = {
preventHttpFormSubmit: true
};

/**
* @group Built-in operations
* @category Built-in operations
*/
export class New extends BaseCrudOperation {
/** */
constructor(
fields: Array<FieldInterface<FieldOptions>>,
actions: Array<Action> = [],
Expand All @@ -92,7 +133,10 @@ export class New extends BaseCrudOperation {
}
}

/**
*/
export class Edit extends BaseCrudOperation {
/** */
constructor(
fields: Array<FieldInterface<FieldOptions>>,
actions: Array<Action> = [],
Expand All @@ -102,13 +146,19 @@ export class Edit extends BaseCrudOperation {
}
}

/**
* @see {@link List}
**/
export type ListOperationOptions = object & {
globalActions?: Array<Action>;
pagination?: Partial<PaginationOptions>;
filters?: FilterInterface<FilterOptions>[];
};

/**
*/
export class List extends BaseCrudOperation {
/** */
constructor(
fields: Array<FieldInterface<FieldOptions>>,
actions: Array<Action> = [],
Expand All @@ -120,22 +170,32 @@ export class List extends BaseCrudOperation {
}
}

/**
*/
export class Delete extends BaseCrudOperation {
/** */
public readonly redirectTo: Action;

/** */
constructor(fields: Array<FieldInterface<FieldOptions>>, redirectTo: Action) {
super('delete', 'crud.delete.label', 'delete', fields, []);
this.redirectTo = redirectTo;
}
}

/**
*/
export class View extends BaseCrudOperation {
/** */
constructor(fields: Array<FieldInterface<FieldOptions>>) {
super('view', 'crud.view.label', 'view', fields, []);
}
}

/**
*/
export class Field extends BaseCrudOperation {
/** */
constructor(name: CrudOperationName = 'field', options: Record<string, string | unknown> = {}) {
super(name, '', 'field', [], [], options);
}
Expand Down
52 changes: 43 additions & 9 deletions src/lib/Crud/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,62 @@ import type { StateProvider } from '$lib/State/Provider';
import type { StateProcessor } from '$lib/State/Processor';
import { type DashboardDefinition } from '$lib/Dashboard/definition';

export type CrudDefinitionOptionsArgument<T> = {
/** */
export type CrudDefinitionOptionsArgument<EntityType> = {
name: string;
label: {
singular: string;
plural: string;
};
defaultOperationName?: string;
identifierFieldName?: 'id' | string;

/**
* Will apply a default minimum timeout (in milliseconds) when running a {@link StateProvider} or {@link StateProcessor}.
* If this option is defined, and the provider or processor call's duration is below this value, it will still wait this amount of time before returning the actual provider/processor value.
*
* The goal of this is to avoid epilepsy-like issues if providers or processors respond too quickly, especially when dealing with List operations and filters.
* This will create a "wait time" for the end user, so that the screen does not blink too much and there eyes (and brain) will not be stressed too much.
*/
minStateLoadingTimeMs?: number;

operations: Array<CrudOperation>;
stateProvider: StateProvider<T>;
stateProcessor: StateProcessor<T>;
stateProvider: StateProvider<EntityType>;
stateProcessor: StateProcessor<EntityType>;
};

export type CrudDefinitionOptions<T> = Required<CrudDefinitionOptionsArgument<T>>;
export type CrudDefinitionOptions<EntityType> = Required<CrudDefinitionOptionsArgument<EntityType>>;

export class CrudDefinition<T> {
public readonly name: string;
public readonly options: CrudDefinitionOptions<T>;
/**
* Crud definition, object used to create an abstract Crud.
*
* @remarks
* Crud objects are related to a single Entity type,
* and contain several Crud Operations, as well as the main
* objects that care about persistence: state providers and processors.
*
* @example
* type Book = {id: number, title: string, description: string};
*
* const BooksCrud = new CrudDefinition<Book>({
* name: 'books',
* label: {singular: 'Book', plural: 'Books'},
* operations: [],
* stateProvider: ...,
* stateProcessor: ...,
* });
*
* @typeParam EntityType - The object type that will be used by providers and processors.
*/
export class CrudDefinition<EntityType> {
/** */ public readonly name: string;
/** */ public readonly options: CrudDefinitionOptions<EntityType>;
private _dashboard: DashboardDefinition | null = null;

constructor(options: CrudDefinitionOptionsArgument<T>) {
/**
* @param {CrudDefinitionOptionsArgument} options
**/
constructor(options: CrudDefinitionOptionsArgument<EntityType>) {
const name = options.name;
this.name = name;

Expand Down Expand Up @@ -57,9 +90,10 @@ export class CrudDefinition<T> {
options.defaultOperationName = defaultOperation.name;
options.identifierFieldName ??= 'id';

this.options = options as CrudDefinitionOptions<T>;
this.options = options as CrudDefinitionOptions<EntityType>;
}

/** */
get dashboard(): DashboardDefinition {
if (!this._dashboard) {
throw new Error('Dashboard is not set in Crud definition: did you try to bypass Crud setup?');
Expand Down
3 changes: 3 additions & 0 deletions src/lib/Crud/form.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export type SubmittedData = Record<string, FormDataEntryValue>;

/**
* Function to get an record of {@link FormDataEntryValue} items from an "onSubmit" form {@link SubmitEvent} object.
*/
export function getSubmittedFormData(event: SubmitEvent): SubmittedData {
const normalizedData: SubmittedData = {};

Expand Down
Loading
Loading