firstly 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/LICENSE +18 -0
- package/README.md +12 -0
- package/esm/KitBaseEnum.d.ts +35 -0
- package/esm/KitBaseEnum.js +32 -0
- package/esm/KitEntity.d.ts +2 -0
- package/esm/KitEntity.js +24 -0
- package/esm/KitFields.d.ts +10 -0
- package/esm/KitFields.js +196 -0
- package/esm/ROUTES.d.ts +88 -0
- package/esm/ROUTES.js +98 -0
- package/esm/SqlDatabase/LogToConsoleCustom.d.ts +1 -0
- package/esm/SqlDatabase/LogToConsoleCustom.js +102 -0
- package/esm/api/index.d.ts +42 -0
- package/esm/api/index.js +97 -0
- package/esm/auth/Adapter.d.ts +10 -0
- package/esm/auth/Adapter.js +54 -0
- package/esm/auth/AuthController.d.ts +59 -0
- package/esm/auth/AuthController.js +434 -0
- package/esm/auth/Entities.d.ts +39 -0
- package/esm/auth/Entities.js +154 -0
- package/esm/auth/RoleController.d.ts +14 -0
- package/esm/auth/RoleController.js +57 -0
- package/esm/auth/helper.d.ts +1 -0
- package/esm/auth/helper.js +7 -0
- package/esm/auth/index.d.ts +153 -0
- package/esm/auth/index.js +279 -0
- package/esm/auth/providers/github.d.ts +25 -0
- package/esm/auth/providers/github.js +51 -0
- package/esm/auth/providers/index.d.ts +3 -0
- package/esm/auth/providers/index.js +26 -0
- package/esm/auth/providers/strava.d.ts +25 -0
- package/esm/auth/providers/strava.js +51 -0
- package/esm/auth/static/assets/Page-BMFREPjF.d.ts +5 -0
- package/esm/auth/static/assets/Page-BMFREPjF.js +18 -0
- package/esm/auth/static/assets/Page-BMOLAIFx.d.ts +5 -0
- package/esm/auth/static/assets/Page-BMOLAIFx.js +1 -0
- package/esm/auth/static/assets/Page-BwHye0GW.d.ts +5 -0
- package/esm/auth/static/assets/Page-BwHye0GW.js +1 -0
- package/esm/auth/static/assets/Page-gV58jf2r.css +1 -0
- package/esm/auth/static/assets/index-CKmKKRRL.d.ts +53 -0
- package/esm/auth/static/assets/index-CKmKKRRL.js +2 -0
- package/esm/auth/static/assets/index-R27C_TlP.css +4 -0
- package/esm/auth/static/favicon.svg +79 -0
- package/esm/auth/static/index.html +14 -0
- package/esm/auth/types.d.ts +33 -0
- package/esm/auth/types.js +1 -0
- package/esm/bin/cmd.d.ts +1 -0
- package/esm/bin/cmd.js +408 -0
- package/esm/changeLog/index.d.ts +55 -0
- package/esm/changeLog/index.js +179 -0
- package/esm/cron/index.d.ts +60 -0
- package/esm/cron/index.js +102 -0
- package/esm/feedback/FeedbackController.d.ts +30 -0
- package/esm/feedback/FeedbackController.js +313 -0
- package/esm/feedback/index.d.ts +18 -0
- package/esm/feedback/index.js +14 -0
- package/esm/feedback/ui/DialogIssue.svelte +102 -0
- package/esm/feedback/ui/DialogIssue.svelte.d.ts +20 -0
- package/esm/feedback/ui/DialogIssues.svelte +91 -0
- package/esm/feedback/ui/DialogIssues.svelte.d.ts +20 -0
- package/esm/feedback/ui/DialogMilestones.svelte +38 -0
- package/esm/feedback/ui/DialogMilestones.svelte.d.ts +18 -0
- package/esm/feedback/ui/Feedback.svelte +12 -0
- package/esm/feedback/ui/Feedback.svelte.d.ts +16 -0
- package/esm/formats/dates.d.ts +18 -0
- package/esm/formats/dates.js +35 -0
- package/esm/formats/index.d.ts +4 -0
- package/esm/formats/index.js +3 -0
- package/esm/formats/numbers.d.ts +4 -0
- package/esm/formats/numbers.js +34 -0
- package/esm/formats/strings.d.ts +11 -0
- package/esm/formats/strings.js +109 -0
- package/esm/handle/index.d.ts +7 -0
- package/esm/handle/index.js +40 -0
- package/esm/helper.d.ts +50 -0
- package/esm/helper.js +118 -0
- package/esm/index.d.ts +103 -0
- package/esm/index.js +42 -0
- package/esm/kitCellsBuildor.d.ts +45 -0
- package/esm/kitCellsBuildor.js +105 -0
- package/esm/kitStoreItem.d.ts +28 -0
- package/esm/kitStoreItem.js +170 -0
- package/esm/kitStoreList.d.ts +33 -0
- package/esm/kitStoreList.js +98 -0
- package/esm/mail/index.d.ts +11 -0
- package/esm/mail/index.js +51 -0
- package/esm/theme.d.ts +4 -0
- package/esm/theme.js +4 -0
- package/esm/ui/Button.svelte +102 -0
- package/esm/ui/Button.svelte.d.ts +27 -0
- package/esm/ui/Clipboardable.svelte +19 -0
- package/esm/ui/Clipboardable.svelte.d.ts +25 -0
- package/esm/ui/Field.svelte +288 -0
- package/esm/ui/Field.svelte.d.ts +29 -0
- package/esm/ui/FieldGroup.svelte +91 -0
- package/esm/ui/FieldGroup.svelte.d.ts +30 -0
- package/esm/ui/Grid.svelte +246 -0
- package/esm/ui/Grid.svelte.d.ts +46 -0
- package/esm/ui/GridLoading.svelte +32 -0
- package/esm/ui/GridLoading.svelte.d.ts +20 -0
- package/esm/ui/GridPaginate.svelte +66 -0
- package/esm/ui/GridPaginate.svelte.d.ts +22 -0
- package/esm/ui/Icon.svelte +86 -0
- package/esm/ui/Icon.svelte.d.ts +46 -0
- package/esm/ui/LibIcon.d.ts +23 -0
- package/esm/ui/LibIcon.js +28 -0
- package/esm/ui/Loading.svelte +11 -0
- package/esm/ui/Loading.svelte.d.ts +20 -0
- package/esm/ui/Tooltip.svelte +42 -0
- package/esm/ui/Tooltip.svelte.d.ts +22 -0
- package/esm/ui/dialog/DialogForm.svelte +70 -0
- package/esm/ui/dialog/DialogForm.svelte.d.ts +19 -0
- package/esm/ui/dialog/DialogManagement.svelte +87 -0
- package/esm/ui/dialog/DialogManagement.svelte.d.ts +25 -0
- package/esm/ui/dialog/DialogPrimitive.svelte +89 -0
- package/esm/ui/dialog/DialogPrimitive.svelte.d.ts +28 -0
- package/esm/ui/dialog/FormEditAction.svelte +54 -0
- package/esm/ui/dialog/FormEditAction.svelte.d.ts +24 -0
- package/esm/ui/dialog/dialog.d.ts +51 -0
- package/esm/ui/dialog/dialog.js +98 -0
- package/esm/ui/index.d.ts +5 -0
- package/esm/ui/index.js +19 -0
- package/esm/ui/internals/FieldContainer.svelte +22 -0
- package/esm/ui/internals/FieldContainer.svelte.d.ts +30 -0
- package/esm/ui/internals/Input.svelte +98 -0
- package/esm/ui/internals/Input.svelte.d.ts +35 -0
- package/esm/ui/internals/Textarea.svelte +61 -0
- package/esm/ui/internals/Textarea.svelte.d.ts +30 -0
- package/esm/ui/internals/select/MultiSelectMelt.svelte +217 -0
- package/esm/ui/internals/select/MultiSelectMelt.svelte.d.ts +30 -0
- package/esm/ui/internals/select/SelectMelt.svelte +238 -0
- package/esm/ui/internals/select/SelectMelt.svelte.d.ts +35 -0
- package/esm/ui/internals/select/SelectRadio.svelte +37 -0
- package/esm/ui/internals/select/SelectRadio.svelte.d.ts +25 -0
- package/esm/ui/link/Link.svelte +28 -0
- package/esm/ui/link/Link.svelte.d.ts +25 -0
- package/esm/ui/link/LinkPlus.svelte +44 -0
- package/esm/ui/link/LinkPlus.svelte.d.ts +21 -0
- package/esm/utils/tailwind.d.ts +2 -0
- package/esm/utils/tailwind.js +3 -0
- package/esm/utils/transition.d.ts +10 -0
- package/esm/utils/transition.js +33 -0
- package/esm/utils/types.d.ts +17 -0
- package/esm/utils/types.js +17 -0
- package/esm/virtual/Customer.d.ts +4 -0
- package/esm/virtual/Customer.js +24 -0
- package/esm/virtual/FilterEntity.d.ts +7 -0
- package/esm/virtual/FilterEntity.js +34 -0
- package/esm/virtual/StateDemoEnum.d.ts +9 -0
- package/esm/virtual/StateDemoEnum.js +42 -0
- package/esm/virtual/UIEntity.d.ts +16 -0
- package/esm/virtual/UIEntity.js +84 -0
- package/esm/vite/index.d.ts +8 -0
- package/esm/vite/index.js +47 -0
- package/package.json +94 -10
package/esm/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import 'remult';
|
|
2
|
+
import { Log } from '@kitql/helpers';
|
|
3
|
+
import { kitStoreItem } from './kitStoreItem.js';
|
|
4
|
+
import { kitStoreList } from './kitStoreList.js';
|
|
5
|
+
import { default as Button } from './ui/Button.svelte';
|
|
6
|
+
import { default as Clipboardable } from './ui/Clipboardable.svelte';
|
|
7
|
+
import { default as DialogManagement } from './ui/dialog/DialogManagement.svelte';
|
|
8
|
+
import { default as FormEditAction } from './ui/dialog/FormEditAction.svelte';
|
|
9
|
+
import { default as Field } from './ui/Field.svelte';
|
|
10
|
+
import { default as FieldGroup } from './ui/FieldGroup.svelte';
|
|
11
|
+
import { default as Grid } from './ui/Grid.svelte';
|
|
12
|
+
import { default as GridPaginate } from './ui/GridPaginate.svelte';
|
|
13
|
+
import { default as Icon } from './ui/Icon.svelte';
|
|
14
|
+
import { default as FieldContainer } from './ui/internals/FieldContainer.svelte';
|
|
15
|
+
import { default as SelectMelt } from './ui/internals/select/SelectMelt.svelte';
|
|
16
|
+
import { default as Link } from './ui/link/Link.svelte';
|
|
17
|
+
import { default as LinkPlus } from './ui/link/LinkPlus.svelte';
|
|
18
|
+
import { default as Loading } from './ui/Loading.svelte';
|
|
19
|
+
import { default as Tooltip } from './ui/Tooltip.svelte';
|
|
20
|
+
export const logFirstly = new Log('firstly');
|
|
21
|
+
export const KitRole = {
|
|
22
|
+
Admin: 'KitAdmin',
|
|
23
|
+
};
|
|
24
|
+
export { Field, FormEditAction, Grid, GridPaginate, FieldGroup, Icon, Link, LinkPlus, Loading, Button, Tooltip, DialogManagement, FieldContainer, SelectMelt, Clipboardable, };
|
|
25
|
+
export { dialog } from './ui/dialog/dialog.js';
|
|
26
|
+
export { KitBaseEnum, getEnum, getEnums } from './KitBaseEnum.js';
|
|
27
|
+
export { KitFields } from './KitFields.js';
|
|
28
|
+
export { KitEntity } from './KitEntity.js';
|
|
29
|
+
export { LogToConsoleCustom } from './SqlDatabase/LogToConsoleCustom.js';
|
|
30
|
+
export { getEntityDisplayValue, isError, kitDbNamesOf, getFieldLinkDisplayValue } from './helper.js';
|
|
31
|
+
export { buildWhere, getPlaceholder, buildSearchWhere, kitCellsBuildor, kitCellBuildor, fieldsOf, } from './kitCellsBuildor.js';
|
|
32
|
+
export { kitStoreItem };
|
|
33
|
+
export { kitStoreList };
|
|
34
|
+
export { FilterEntity } from './virtual/FilterEntity.js';
|
|
35
|
+
export { UIEntity } from './virtual/UIEntity.js';
|
|
36
|
+
// Icons
|
|
37
|
+
export { LibIcon_Empty, LibIcon_Forbidden, LibIcon_ChevronDown, LibIcon_ChevronUp, LibIcon_ChevronLeft, LibIcon_ChevronRight, LibIcon_Search, LibIcon_Check, LibIcon_MultiCheck, LibIcon_Add, LibIcon_MultiAdd, LibIcon_Edit, LibIcon_Delete, LibIcon_Cross, LibIcon_Save, LibIcon_Man, LibIcon_Woman, LibIcon_Send, LibIcon_Load, LibIcon_Settings, LibIcon_Sort, LibIcon_SortAsc, LibIcon_SortDesc, } from './ui/LibIcon.js';
|
|
38
|
+
// Formats & Utils
|
|
39
|
+
export { displayPhone, arrToStr } from './formats/strings.js';
|
|
40
|
+
export { displayCurrency } from './formats/numbers.js';
|
|
41
|
+
export { tw } from './utils/tailwind.js';
|
|
42
|
+
export { litOrStr } from './utils/types.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { SvelteComponent } from 'svelte';
|
|
2
|
+
import { type EntityFilter, type FieldMetadata, type Repository } from 'remult';
|
|
3
|
+
import type { UnArray } from './utils/types.js';
|
|
4
|
+
export type VisibilityMode = 'view' | 'edit' | 'hide';
|
|
5
|
+
type KitCellInternal<Entity> = {
|
|
6
|
+
col?: keyof Entity;
|
|
7
|
+
kind?: 'field' | 'field_link' | 'entity_link' | 'slot' | 'header' | 'component';
|
|
8
|
+
class?: string;
|
|
9
|
+
header?: string;
|
|
10
|
+
headerSlot?: boolean;
|
|
11
|
+
modeEdit?: VisibilityMode;
|
|
12
|
+
modeView?: VisibilityMode;
|
|
13
|
+
clipboardable?: boolean;
|
|
14
|
+
clearable?: boolean;
|
|
15
|
+
component?: new (...args: any[]) => SvelteComponent;
|
|
16
|
+
props?: any;
|
|
17
|
+
rowToProps?: (row: any) => any;
|
|
18
|
+
};
|
|
19
|
+
export type KitCell<Entity> = KitCellInternal<Entity> & {
|
|
20
|
+
field?: FieldMetadata<any, Entity>;
|
|
21
|
+
};
|
|
22
|
+
export type KitCellsInput<Entity> = (keyof Entity | KitCellInternal<Entity>)[];
|
|
23
|
+
/**
|
|
24
|
+
* kitCellsBuildor is a function to build cells for a <Grid /> or <FieldGroup /> component.
|
|
25
|
+
*
|
|
26
|
+
* ```html
|
|
27
|
+
* <script lang="ts">
|
|
28
|
+
* import { repo } from 'remult'
|
|
29
|
+
*
|
|
30
|
+
* const cells = kitCellsBuildor(repo(Site), ['name', 'description'])
|
|
31
|
+
* const store = kitStoreList( repo(Site) )
|
|
32
|
+
* $: store.fetch()
|
|
33
|
+
* </script>
|
|
34
|
+
*
|
|
35
|
+
* <Grid {cells} {store} />
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
*/
|
|
39
|
+
export declare function kitCellsBuildor<Entity>(repo: Repository<Entity>, inputBuildor: KitCellsInput<Entity>): KitCell<Entity>[];
|
|
40
|
+
export declare function kitCellBuildor<Entity>(repo: Repository<Entity>, inputBuildor: UnArray<KitCellsInput<Entity>>): KitCell<Entity>;
|
|
41
|
+
export declare const fieldsOf: <Entity>(b: KitCell<Entity>[]) => FieldMetadata<any, Entity>[];
|
|
42
|
+
export declare const getPlaceholder: <Entity>(fields: FieldMetadata<any, Entity>[]) => string;
|
|
43
|
+
export declare const buildSearchWhere: <Entity>(fields: FieldMetadata<any, Entity>[], search?: string | null) => EntityFilter<Entity>[];
|
|
44
|
+
export declare const buildWhere: <Entity>(defaultWhere: EntityFilter<Entity> | undefined, fields_filter: FieldMetadata<any, Entity>[], fields_search: FieldMetadata<any, Entity>[], obj: Record<string, string>) => EntityFilter<Entity>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {} from 'remult';
|
|
2
|
+
import { getRelationFieldInfo } from 'remult/internals';
|
|
3
|
+
import { getEnum, KitBaseEnum } from './KitBaseEnum.js';
|
|
4
|
+
/**
|
|
5
|
+
* kitCellsBuildor is a function to build cells for a <Grid /> or <FieldGroup /> component.
|
|
6
|
+
*
|
|
7
|
+
* ```html
|
|
8
|
+
* <script lang="ts">
|
|
9
|
+
* import { repo } from 'remult'
|
|
10
|
+
*
|
|
11
|
+
* const cells = kitCellsBuildor(repo(Site), ['name', 'description'])
|
|
12
|
+
* const store = kitStoreList( repo(Site) )
|
|
13
|
+
* $: store.fetch()
|
|
14
|
+
* </script>
|
|
15
|
+
*
|
|
16
|
+
* <Grid {cells} {store} />
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
export function kitCellsBuildor(repo, inputBuildor) {
|
|
21
|
+
const buildor = [];
|
|
22
|
+
for (let i = 0; i < inputBuildor.length; i++) {
|
|
23
|
+
const item = inputBuildor[i];
|
|
24
|
+
let b;
|
|
25
|
+
if (item instanceof Object) {
|
|
26
|
+
b = { ...item, field: repo.fields[item.col] };
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
b = { col: item, field: repo.fields[item] };
|
|
30
|
+
}
|
|
31
|
+
// Let's tweak defaults...
|
|
32
|
+
if (b.kind === undefined) {
|
|
33
|
+
if (b.field?.options.href) {
|
|
34
|
+
b.kind = 'field_link';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
buildor.push(b);
|
|
38
|
+
}
|
|
39
|
+
return buildor;
|
|
40
|
+
}
|
|
41
|
+
export function kitCellBuildor(repo, inputBuildor) {
|
|
42
|
+
return kitCellsBuildor(repo, [inputBuildor])[0];
|
|
43
|
+
}
|
|
44
|
+
export const fieldsOf = (b) => {
|
|
45
|
+
return b.filter((c) => c.field).map((c) => c.field) ?? [];
|
|
46
|
+
};
|
|
47
|
+
export const getPlaceholder = (fields) => {
|
|
48
|
+
return fields.map((c) => c.caption).join(', ');
|
|
49
|
+
};
|
|
50
|
+
export const buildSearchWhere = (fields, search) => {
|
|
51
|
+
if (!search) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const f = [
|
|
55
|
+
{
|
|
56
|
+
$or: fields.map((f) => {
|
|
57
|
+
if (f.inputType === 'number') {
|
|
58
|
+
return { [f.key]: search };
|
|
59
|
+
}
|
|
60
|
+
const sSplitted = search.split(' ');
|
|
61
|
+
return {
|
|
62
|
+
$and: sSplitted.map((s) => ({ [f.key]: { $contains: s } })),
|
|
63
|
+
};
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
return f;
|
|
68
|
+
};
|
|
69
|
+
export const buildWhere = (defaultWhere, fields_filter, fields_search, obj) => {
|
|
70
|
+
const and = [];
|
|
71
|
+
if (defaultWhere) {
|
|
72
|
+
and.push(defaultWhere);
|
|
73
|
+
}
|
|
74
|
+
if (obj.search) {
|
|
75
|
+
and.push(...buildSearchWhere(fields_search, obj.search));
|
|
76
|
+
}
|
|
77
|
+
for (const field of fields_filter) {
|
|
78
|
+
const rfi = getRelationFieldInfo(field);
|
|
79
|
+
// if there is a value
|
|
80
|
+
if (obj[field.key]) {
|
|
81
|
+
if (field.inputType === 'checkbox') {
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
and.push({ [field.key]: obj[field.key] });
|
|
84
|
+
}
|
|
85
|
+
else if (field.inputType === 'selectEnum') {
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
const theEnum = getEnum(field, obj[field.key]);
|
|
88
|
+
// Take the where of the enum if it exists, or it's using this selection as a filter
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
const wheretoUse = theEnum?.where ?? new KitBaseEnum(obj[field.key]);
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
and.push({ [field.key]: wheretoUse });
|
|
93
|
+
}
|
|
94
|
+
else if (rfi.type === 'toOne') {
|
|
95
|
+
// @ts-ignore (stting the id of the relation)
|
|
96
|
+
and.push({ [field.key]: obj[field.key] });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.info(`Not handled filter field ${field.key} ${field.inputType}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
return { $and: and };
|
|
105
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
2
|
+
import type { ErrorInfo, FindOptions, Repository } from 'remult';
|
|
3
|
+
type TheStoreItem<T> = {
|
|
4
|
+
item: T | undefined;
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
errors: ErrorInfo<T> | undefined;
|
|
7
|
+
globalError?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
export declare const kitStoreItem: <T>(repo: Repository<T>, initValues?: TheStoreItem<T>) => {
|
|
10
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<TheStoreItem<T>>, invalidate?: import("svelte/store").Invalidator<TheStoreItem<T>> | undefined) => import("svelte/store").Unsubscriber;
|
|
11
|
+
create: (item: Partial<T>) => void;
|
|
12
|
+
set: (newItem: TheStoreItem<T>) => void;
|
|
13
|
+
/**
|
|
14
|
+
* if you have keys, build the id with
|
|
15
|
+
* ```ts
|
|
16
|
+
* const id = repo.metadata.idMetadata.getId({a:1,b:2})
|
|
17
|
+
* store.fetch(id)
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
fetch: (id: Parameters<Repository<T>['findId']>[0], options?: FindOptions<T>, onNewData?: ((item: T) => void) | undefined) => Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* `.save()` will `update` or `insert` the current item.
|
|
23
|
+
*/
|
|
24
|
+
save: () => Promise<T | undefined>;
|
|
25
|
+
delete: () => Promise<void>;
|
|
26
|
+
onChange: (prop: keyof T, callback: (newValue: any, oldValue: any) => void) => void;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { BROWSER } from 'esm-env';
|
|
2
|
+
import { derived, get, writable } from 'svelte/store';
|
|
3
|
+
import { Log } from '@kitql/helpers';
|
|
4
|
+
import { isError } from './helper';
|
|
5
|
+
export const kitStoreItem = (repo, initValues = {
|
|
6
|
+
item: undefined,
|
|
7
|
+
loading: true,
|
|
8
|
+
errors: undefined,
|
|
9
|
+
globalError: undefined,
|
|
10
|
+
}) => {
|
|
11
|
+
const internalStore = writable(initValues);
|
|
12
|
+
// Function to watch changes on a specific property of `item`
|
|
13
|
+
const onChange = (prop, callback) => {
|
|
14
|
+
const itemProperty = derived(internalStore, ($s) => $s.item && $s.item[prop]);
|
|
15
|
+
let oldValue;
|
|
16
|
+
// Subscribe to the derived store to monitor changes
|
|
17
|
+
itemProperty.subscribe((newValue) => {
|
|
18
|
+
if (newValue !== oldValue) {
|
|
19
|
+
if (oldValue !== undefined) {
|
|
20
|
+
// to avoid running on initial undefined state
|
|
21
|
+
callback(newValue, oldValue);
|
|
22
|
+
}
|
|
23
|
+
oldValue = newValue;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
subscribe: internalStore.subscribe,
|
|
29
|
+
create: (item) => {
|
|
30
|
+
internalStore.set({
|
|
31
|
+
item: repo.create(item),
|
|
32
|
+
loading: false,
|
|
33
|
+
errors: {},
|
|
34
|
+
globalError: undefined,
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
// set: internalStore.set,
|
|
38
|
+
set: (newItem) => {
|
|
39
|
+
internalStore.update((s) => {
|
|
40
|
+
return { ...newItem };
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
/**
|
|
44
|
+
* if you have keys, build the id with
|
|
45
|
+
* ```ts
|
|
46
|
+
* const id = repo.metadata.idMetadata.getId({a:1,b:2})
|
|
47
|
+
* store.fetch(id)
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
fetch: async (id, options, onNewData) => {
|
|
51
|
+
if (BROWSER) {
|
|
52
|
+
internalStore.update((s) => ({ ...s, loading: true }));
|
|
53
|
+
try {
|
|
54
|
+
const item = await repo.findId(id, options);
|
|
55
|
+
// lastOptions = options
|
|
56
|
+
internalStore.update((s) => ({
|
|
57
|
+
...s,
|
|
58
|
+
loading: false,
|
|
59
|
+
item,
|
|
60
|
+
errors: undefined,
|
|
61
|
+
globalError: undefined,
|
|
62
|
+
}));
|
|
63
|
+
if (onNewData) {
|
|
64
|
+
onNewData(item);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (isError(error)) {
|
|
69
|
+
internalStore.update((s) => ({
|
|
70
|
+
...s,
|
|
71
|
+
loading: false,
|
|
72
|
+
item: {},
|
|
73
|
+
errors: {},
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
globalError: error.message,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* `.save()` will `update` or `insert` the current item.
|
|
83
|
+
*/
|
|
84
|
+
save: async () => {
|
|
85
|
+
const s = get(internalStore);
|
|
86
|
+
try {
|
|
87
|
+
if (!s.item) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const item = await repo.save(s.item);
|
|
91
|
+
internalStore.update((s) => ({
|
|
92
|
+
...s,
|
|
93
|
+
loading: false,
|
|
94
|
+
item,
|
|
95
|
+
errors: undefined,
|
|
96
|
+
globalError: undefined,
|
|
97
|
+
}));
|
|
98
|
+
return item;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (isError(error)) {
|
|
102
|
+
if (!error.modelState) {
|
|
103
|
+
internalStore.update((s) => ({
|
|
104
|
+
...s,
|
|
105
|
+
loading: false,
|
|
106
|
+
item: s.item,
|
|
107
|
+
errors: undefined,
|
|
108
|
+
globalError: error.message,
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const errors = {};
|
|
113
|
+
for (const key in error.modelState) {
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
errors[key] = error.modelState[key];
|
|
116
|
+
}
|
|
117
|
+
internalStore.update((s) => ({
|
|
118
|
+
...s,
|
|
119
|
+
loading: false,
|
|
120
|
+
item: s.item,
|
|
121
|
+
errors,
|
|
122
|
+
globalError: undefined,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// After we updated everything, let's throw the error
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
delete: async () => {
|
|
131
|
+
const s = get(internalStore);
|
|
132
|
+
if (!s.item) {
|
|
133
|
+
new Log('firstly').error(`To delete an item, you need set it first.`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
await repo.delete(s.item);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (isError(error)) {
|
|
141
|
+
if (!error.modelState) {
|
|
142
|
+
internalStore.update((s) => ({
|
|
143
|
+
...s,
|
|
144
|
+
loading: false,
|
|
145
|
+
item: s.item,
|
|
146
|
+
errors: undefined,
|
|
147
|
+
globalError: error.message,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const errors = {};
|
|
152
|
+
for (const key in error.modelState) {
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
errors[key] = error.modelState[key];
|
|
155
|
+
}
|
|
156
|
+
internalStore.update((s) => ({
|
|
157
|
+
...s,
|
|
158
|
+
loading: false,
|
|
159
|
+
item: s.item,
|
|
160
|
+
errors,
|
|
161
|
+
globalError: undefined,
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
onChange,
|
|
169
|
+
};
|
|
170
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
2
|
+
import type { FindOptions, Repository } from 'remult';
|
|
3
|
+
type TheStoreList<T> = {
|
|
4
|
+
items: T[];
|
|
5
|
+
loading: boolean;
|
|
6
|
+
totalCount: number | undefined;
|
|
7
|
+
};
|
|
8
|
+
export type FindOptionsPlus<T> = FindOptions<T> & {
|
|
9
|
+
withCount?: boolean;
|
|
10
|
+
withItems?: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* @param repo remult repository to listen to
|
|
14
|
+
* @param initValues usually the data coming from SSR
|
|
15
|
+
* @returns a store with the initial values and a listen() method to subscribe to changes
|
|
16
|
+
*
|
|
17
|
+
* Example
|
|
18
|
+
* ```ts
|
|
19
|
+
* // get the repo
|
|
20
|
+
* const taskRepo = remult.repo(Task)
|
|
21
|
+
*
|
|
22
|
+
* const tasks = kitStore(taskRepo, data.tasks)
|
|
23
|
+
* $: browser && tasks.listen(data.options)
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare const kitStoreList: <T>(repo: Repository<T>, initValues?: TheStoreList<T>) => {
|
|
27
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<TheStoreList<T>>, invalidate?: import("svelte/store").Invalidator<TheStoreList<T>> | undefined) => import("svelte/store").Unsubscriber;
|
|
28
|
+
manualSet: (info: TheStoreList<T>) => void;
|
|
29
|
+
fetch: (options?: FindOptionsPlus<T>, onNewData?: ((items?: T[], totalCount?: number) => void) | undefined) => Promise<void>;
|
|
30
|
+
listen: (options?: FindOptionsPlus<T>) => Promise<void>;
|
|
31
|
+
getRepo: () => Repository<T>;
|
|
32
|
+
};
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { BROWSER } from 'esm-env';
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import { writable } from 'svelte/store';
|
|
4
|
+
/**
|
|
5
|
+
* @param repo remult repository to listen to
|
|
6
|
+
* @param initValues usually the data coming from SSR
|
|
7
|
+
* @returns a store with the initial values and a listen() method to subscribe to changes
|
|
8
|
+
*
|
|
9
|
+
* Example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // get the repo
|
|
12
|
+
* const taskRepo = remult.repo(Task)
|
|
13
|
+
*
|
|
14
|
+
* const tasks = kitStore(taskRepo, data.tasks)
|
|
15
|
+
* $: browser && tasks.listen(data.options)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export const kitStoreList = (repo, initValues = { items: [], loading: true, totalCount: undefined }) => {
|
|
19
|
+
const { subscribe, set, update } = writable(initValues);
|
|
20
|
+
let unSub = null;
|
|
21
|
+
onDestroy(async () => {
|
|
22
|
+
await plzUnSub();
|
|
23
|
+
});
|
|
24
|
+
// if we already have a subscription, unsubscribe (on option update for example)
|
|
25
|
+
const plzUnSub = async () => {
|
|
26
|
+
if (unSub) {
|
|
27
|
+
await unSub();
|
|
28
|
+
unSub = null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
subscribe,
|
|
33
|
+
// set,
|
|
34
|
+
manualSet: (info) => {
|
|
35
|
+
set(info);
|
|
36
|
+
},
|
|
37
|
+
fetch: async (options, onNewData) => {
|
|
38
|
+
if (BROWSER) {
|
|
39
|
+
update((s) => ({ ...s, loading: true }));
|
|
40
|
+
const withCount = options?.withCount ?? false;
|
|
41
|
+
const withItems = options?.withItems ?? true;
|
|
42
|
+
if (!withItems && !withCount) {
|
|
43
|
+
throw new Error(`xxx.fetch() withItems and withCount can't be both false!`);
|
|
44
|
+
}
|
|
45
|
+
else if (!withItems && withCount) {
|
|
46
|
+
const totalCount = await repo.count(options?.where);
|
|
47
|
+
set({ loading: false, items: [], totalCount });
|
|
48
|
+
if (onNewData) {
|
|
49
|
+
onNewData(undefined, totalCount);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (withItems && !withCount) {
|
|
53
|
+
const items = await repo.find(options);
|
|
54
|
+
set({ loading: false, items, totalCount: undefined });
|
|
55
|
+
if (onNewData) {
|
|
56
|
+
onNewData(items, undefined);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const [items, totalCount] = await Promise.all([
|
|
61
|
+
repo.find({ ...options }),
|
|
62
|
+
repo.count(options?.where),
|
|
63
|
+
]);
|
|
64
|
+
set({ loading: false, items, totalCount });
|
|
65
|
+
if (onNewData) {
|
|
66
|
+
onNewData(items, totalCount);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
listen: async (options) => {
|
|
72
|
+
if (BROWSER) {
|
|
73
|
+
await plzUnSub();
|
|
74
|
+
unSub = repo.liveQuery({ ...options }).subscribe(async (info) => {
|
|
75
|
+
const withCount = options?.withCount ?? false;
|
|
76
|
+
let totalCount = undefined;
|
|
77
|
+
if (withCount) {
|
|
78
|
+
totalCount = await repo.count(options?.where);
|
|
79
|
+
}
|
|
80
|
+
update((c) => {
|
|
81
|
+
return { ...c, items: info.items, loading: false, ...(withCount ? { totalCount } : {}) };
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
throw new Error(`xxx.listen() Too early!
|
|
87
|
+
|
|
88
|
+
You should do like:
|
|
89
|
+
let tasks = tasksStore<Task>(taskRepo, data.tasks)
|
|
90
|
+
$: browser && tasks.listen()
|
|
91
|
+
`);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
getRepo: () => {
|
|
95
|
+
return repo;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type * as typeNodemailer from 'nodemailer';
|
|
2
|
+
import type Mail from 'nodemailer/lib/mailer';
|
|
3
|
+
export type MailOptions = {
|
|
4
|
+
from?: Mail.Options['from'];
|
|
5
|
+
transport?: Parameters<typeof typeNodemailer.createTransport>[0];
|
|
6
|
+
apiUrl?: Parameters<typeof typeNodemailer.createTestAccount>[0];
|
|
7
|
+
};
|
|
8
|
+
declare let transporter: ReturnType<typeof typeNodemailer.createTransport>;
|
|
9
|
+
export declare const mailInit: (nodemailer: typeof typeNodemailer, o?: MailOptions) => void;
|
|
10
|
+
export declare const sendMail: (topic: string, mailOptions: Parameters<typeof transporter.sendMail>[0]) => ReturnType<typeof transporter.sendMail>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Log, magenta } from '@kitql/helpers';
|
|
2
|
+
let transporter;
|
|
3
|
+
let options;
|
|
4
|
+
export const mailInit = (nodemailer, o) => {
|
|
5
|
+
options = o;
|
|
6
|
+
if (o?.transport) {
|
|
7
|
+
transporter = nodemailer.createTransport(o?.transport);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
nodemailerHolder = nodemailer;
|
|
11
|
+
try {
|
|
12
|
+
nodemailer.createTestAccount(options?.apiUrl ?? '', (err, account) => {
|
|
13
|
+
options = { ...options, from: account.user };
|
|
14
|
+
transporter = nodemailer.createTransport({
|
|
15
|
+
host: account.smtp.host,
|
|
16
|
+
port: account.smtp.port,
|
|
17
|
+
secure: account.smtp.secure,
|
|
18
|
+
auth: {
|
|
19
|
+
user: account.user,
|
|
20
|
+
pass: account.pass,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
log.error("Error nodemailer.createTestAccount() can't be done.");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const log = new Log('firstly | mail');
|
|
31
|
+
let nodemailerHolder;
|
|
32
|
+
export const sendMail = async (topic, mailOptions) => {
|
|
33
|
+
try {
|
|
34
|
+
const info = await transporter.sendMail({
|
|
35
|
+
...mailOptions,
|
|
36
|
+
...{ from: mailOptions.from ?? options?.from },
|
|
37
|
+
});
|
|
38
|
+
if (!options?.transport) {
|
|
39
|
+
log.info(
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
`${magenta(`[${topic}]`)} - Preview URL: ${nodemailerHolder.getTestMessageUrl(info)}`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
log.success(`${magenta(`[${topic}]`)} - Sent to ${mailOptions.to}`);
|
|
45
|
+
}
|
|
46
|
+
return info;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
log.error(`${magenta(`[${topic}]`)} - Error`, error);
|
|
50
|
+
}
|
|
51
|
+
};
|
package/esm/theme.d.ts
ADDED
package/esm/theme.js
ADDED