All files / core/blong-kukum/primitives model.ts

100% Statements 138/138
100% Branches 4/4
100% Functions 2/2
100% Lines 138/138

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 1391x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 1x 1x 1x  
import {capitalize, checkObject, checkSubject, type PrimitiveDescriptor} from '../engine.ts';
 
/** `model` — the IModelSpec that drives the generated CRUD pages. */
 
const model: PrimitiveDescriptor = {
    id: 'model',
    title: 'Model spec / fixture',
    skill: 'blong-model',
    summary:
        'IModelSpec driving Browse/New/Open/Report pages (meta/model) and mock fixtures (meta/fixture).',
    kinds: ['model', 'fixture'],
    defaultKind: 'model',
    roots: ['meta/model', 'meta/fixture'],
    check(ctx) {
        return [...checkSubject(ctx.subject), ...checkObject(ctx.object)];
    },
    files(ctx) {
        const Entity = capitalize(ctx.object);
        /**
         * Matches the `blong-kopi` template naming (`$subject$ObjectModel.ts`), so
         * regenerating a model replaces the scaffolded one instead of leaving two
         * models claiming the same entity.
         */
        const Name = `${ctx.subject}${Entity}`;
        /**
         * The model system resolves `${...}` placeholders in toolbar params.
         * They are assembled here rather than written literally so the
generated
         * spec keeps them verbatim without template-literal escaping.
         */
        const current = '$' + '{current}';
        const idPlaceholder = '$' + '{' + ctx.object + 'Id}';
        if (ctx.kind === 'fixture') {
            return [
                {
                    path: `meta/fixture/${ctx.subject}Fixture.ts`,
                    content: `import {fixture} from '@feasibleone/blong';
 
export default fixture(() => async function ${ctx.subject}Fixture() {
    return {
        '${ctx.subject}.${ctx.object}': [{${ctx.object}Id: '1', ${ctx.object}Name: 'Example'}],
    };
});
`,
                },
            ];
        }
        return [
            {
                path: `meta/model/${Name}Model.ts`,
                content: `import {model} from '@feasibleone/blong';
 
/**
 * meta/model/${Name}Model.ts — model spec for the \`${ctx.object}\` entity.
 *
 * Drives the auto-generated Browse/New/Open pages. \`public: true\` exposes the
 * standard CRUD operations on the gateway, validated by blong-server's
 * subject.validation — no manual gateway override is needed.
 *
 * The field names here must match meta/type/${ctx.subject}.ts: the Playwright
 * helpers drive this page by name, so a rename has to happen in both places.
 */
export default model(
    () =>
        async function ${Name}Model() {
            return {
                subject: '${ctx.subject}',
                object: '${ctx.object}',
                objectTitle: '${Entity}',
                public: true,
                nameField: '${ctx.object}.${ctx.object}Name',
                schema: {
                    properties: {
                        ${ctx.object}: {
                            properties: {
                                ${ctx.object}Name: {title: 'Name', filter: true, sort: true},
                                ${ctx.object}Status: {
                                    title: 'Status',
                                    widget: {
                                        options: [
                                            {value: 'draft', label: 'Draft'},
                                            {value: 'sent', label: 'Sent'},
                                            {value: 'paid', label: 'Paid'},
                                            {value: 'void', label: 'Void'},
                                        ],
                                    },
                                },
                            },
                            widget: {columns: ['${ctx.object}Name', '${ctx.object}Status']},
                        },
                    },
                },
                cards: {
                    browse: {label: '${Entity}s', widgets: ['${ctx.object}']},
                    edit: {
                        label: '${Entity} Details',
                        className: 'col-12 md:col-8',
                        widgets: ['${ctx.object}.${ctx.object}Name', '${ctx.object}.${ctx.object}Status'],
                    },
                },
                browser: {
                    title: '${Entity}s',
                    icon: 'pi pi-file',
                    toolbar: [
                        {
                            label: 'Create',
                            icon: 'pi pi-plus',
                            action: 'component/${ctx.subject}.${ctx.object}.new',
                            permission: '${ctx.subject}${Entity}Add',
                        },
                        {
                            label: 'Edit',
                            icon: 'pi pi-pencil',
                            enabled: 'current',
                            method: 'component/${ctx.subject}.${ctx.object}.open',
                            params: '${current}',
                        },
                        {
                            label: 'Delete',
                            icon: 'pi pi-trash',
                            enabled: 'selected',
                            confirm: 'Delete selected ${ctx.object}?',
                            method: '${ctx.subject}.${ctx.object}.remove',
                            refresh: true,
                            params: {${ctx.object}Id: '${idPlaceholder}'},
                        },
                    ],
                },
            };
        },
);
`,
            },
        ];
    },
};
 
export default model;