firstly 0.2.0 → 0.3.0
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 +23 -0
- package/esm/bin/cmd.js +3 -158
- package/esm/changeLog/index.d.ts +1 -6
- package/esm/feedback/FeedbackController.d.ts +6 -2
- package/esm/feedback/FeedbackController.js +145 -143
- package/esm/feedback/server/index.d.ts +2 -2
- package/esm/feedback/server/index.js +3 -3
- package/esm/feedback/types.d.ts +5 -0
- package/esm/feedback/ui/DialogIssue.svelte +5 -5
- package/esm/feedback/ui/DialogIssues.svelte +5 -5
- package/esm/feedback/ui/DialogMilestones.svelte +1 -1
- package/esm/internals/BaseEnum.d.ts +2 -1
- package/esm/internals/FF_Entity.js +1 -17
- package/esm/internals/FF_Fields.d.ts +4 -3
- package/esm/internals/FF_Fields.js +14 -55
- package/esm/internals/cellsBuildor.d.ts +2 -1
- package/esm/internals/cellsBuildor.js +5 -4
- package/esm/internals/index.d.ts +7 -12
- package/esm/internals/storeItem.d.ts +12 -20
- package/esm/internals/storeItem.js +20 -6
- package/esm/mail/server/index.d.ts +8 -2
- package/esm/mail/server/index.js +35 -7
- package/esm/server/index.d.ts +1 -1
- package/esm/svelte/FF_Cell.svelte +3 -5
- package/esm/svelte/FF_Cell.svelte.d.ts +4 -2
- package/esm/svelte/FF_Form.svelte +4 -5
- package/esm/svelte/FF_Grid.svelte +2 -2
- package/esm/svelte/FF_Layout.svelte +3 -3
- package/esm/svelte/FF_Repo.svelte.d.ts +9 -0
- package/esm/svelte/FF_Repo.svelte.js +39 -0
- package/esm/svelte/class/SP.svelte.js +14 -2
- package/esm/svelte/dialog/DialogManagement.svelte +2 -5
- package/esm/svelte/dialog/DialogPrimitive.svelte +1 -2
- package/esm/svelte/dialog/dialog.js +2 -2
- package/esm/svelte/ff_Config.svelte.js +2 -2
- package/esm/svelte/index.d.ts +2 -7
- package/esm/svelte/index.js +2 -7
- package/esm/ui/Button.svelte +34 -66
- package/esm/ui/Button.svelte.d.ts +9 -35
- package/esm/ui/Clipboardable.svelte +13 -17
- package/esm/ui/Clipboardable.svelte.d.ts +9 -33
- package/esm/ui/Field.svelte +48 -9
- package/esm/ui/FieldGroup.svelte.d.ts +1 -1
- package/esm/ui/Grid.svelte +13 -87
- package/esm/ui/Grid.svelte.d.ts +0 -1
- package/esm/ui/Grid2.svelte +26 -90
- package/esm/ui/Grid2.svelte.d.ts +1 -2
- package/esm/ui/GridPaginate.svelte +1 -1
- package/esm/ui/GridPaginate2.svelte +2 -2
- package/esm/ui/Icon.svelte +2 -18
- package/esm/ui/Icon.svelte.d.ts +0 -2
- package/esm/ui/LibIcon.js +2 -2
- package/esm/ui/Loading.svelte +1 -1
- package/esm/ui/dialog/DialogManagement.svelte +14 -5
- package/esm/ui/dialog/DialogPrimitive.svelte +3 -3
- package/esm/ui/dialog/FormEditAction.svelte +4 -4
- package/esm/ui/dialog/dialog.d.ts +5 -2
- package/esm/ui/dialog/dialog.js +2 -2
- package/esm/ui/index.d.ts +1 -0
- package/esm/ui/index.js +1 -0
- package/esm/ui/internals/FieldContainer.svelte +25 -14
- package/esm/ui/internals/FieldContainer.svelte.d.ts +9 -30
- package/esm/ui/internals/Input.svelte.d.ts +1 -1
- package/esm/ui/internals/Textarea.svelte +2 -5
- package/esm/ui/internals/select/MultiSelectMelt.svelte +10 -8
- package/esm/ui/internals/select/MultiSelectMelt.svelte.d.ts +1 -1
- package/esm/ui/internals/select/Select2.svelte +88 -0
- package/esm/ui/internals/select/Select2.svelte.d.ts +12 -0
- package/esm/ui/internals/select/SelectMelt.svelte +33 -24
- package/esm/ui/internals/select/SelectMelt.svelte.d.ts +1 -1
- package/esm/ui/internals/select/SelectRadio.svelte +2 -2
- package/esm/ui/internals/select/SelectRadio.svelte.d.ts +1 -1
- package/esm/ui/link/Link.svelte +1 -1
- package/esm/ui/link/LinkPlus.svelte +9 -5
- package/esm/ui/link/LinkPlus.svelte.d.ts +5 -19
- package/esm/virtual/Customer.js +1 -2
- package/esm/virtual/UIEntity.js +9 -5
- package/package.json +11 -25
- package/esm/auth/AuthController.d.ts +0 -58
- package/esm/auth/AuthController.js +0 -114
- package/esm/auth/Entities.d.ts +0 -47
- package/esm/auth/Entities.js +0 -182
- package/esm/auth/README.md +0 -3
- package/esm/auth/index.d.ts +0 -5
- package/esm/auth/index.js +0 -5
- package/esm/auth/server/AuthController.server.d.ts +0 -58
- package/esm/auth/server/AuthController.server.js +0 -498
- package/esm/auth/server/handleAuth.d.ts +0 -4
- package/esm/auth/server/handleAuth.js +0 -142
- package/esm/auth/server/handleGuard.d.ts +0 -22
- package/esm/auth/server/handleGuard.js +0 -34
- package/esm/auth/server/helperDb.d.ts +0 -10
- package/esm/auth/server/helperDb.js +0 -56
- package/esm/auth/server/helperFirstly.d.ts +0 -1
- package/esm/auth/server/helperFirstly.js +0 -9
- package/esm/auth/server/helperOslo.d.ts +0 -7
- package/esm/auth/server/helperOslo.js +0 -24
- package/esm/auth/server/helperRemultServer.d.ts +0 -5
- package/esm/auth/server/helperRemultServer.js +0 -44
- package/esm/auth/server/helperRole.d.ts +0 -19
- package/esm/auth/server/helperRole.js +0 -57
- package/esm/auth/server/index.d.ts +0 -8
- package/esm/auth/server/index.js +0 -8
- package/esm/auth/server/module.d.ts +0 -300
- package/esm/auth/server/module.js +0 -230
- package/esm/auth/server/providers/github.d.ts +0 -33
- package/esm/auth/server/providers/github.js +0 -87
- package/esm/auth/server/providers/helperProvider.d.ts +0 -1
- package/esm/auth/server/providers/helperProvider.js +0 -25
- package/esm/auth/static/assets/Page-9Ytj29NS.d.ts +0 -2
- package/esm/auth/static/assets/Page-9Ytj29NS.js +0 -1
- package/esm/auth/static/assets/Page-BHW08QWz.css +0 -1
- package/esm/auth/static/assets/Page-C1pM-UDt.d.ts +0 -2
- package/esm/auth/static/assets/Page-C1pM-UDt.js +0 -20
- package/esm/auth/static/assets/Page-CPz6KCw_.d.ts +0 -2
- package/esm/auth/static/assets/Page-CPz6KCw_.js +0 -1
- package/esm/auth/static/assets/index-AoBb9Ds5.d.ts +0 -232
- package/esm/auth/static/assets/index-AoBb9Ds5.js +0 -2
- package/esm/auth/static/assets/index-DKWpA6v7.css +0 -4
- package/esm/auth/static/favicon.svg +0 -79
- package/esm/auth/static/index.html +0 -13
- package/esm/auth/types.d.ts +0 -73
- package/esm/auth/types.js +0 -1
- package/esm/svelte/FF_Display.svelte +0 -51
- package/esm/svelte/FF_Display.svelte.d.ts +0 -29
- package/esm/svelte/FF_Edit.svelte +0 -104
- package/esm/svelte/FF_Edit.svelte.d.ts +0 -32
- package/esm/svelte/FF_Error.svelte +0 -23
- package/esm/svelte/FF_Error.svelte.d.ts +0 -29
- package/esm/svelte/FF_Field.svelte +0 -62
- package/esm/svelte/FF_Field.svelte.d.ts +0 -29
- package/esm/svelte/FF_Hint.svelte +0 -21
- package/esm/svelte/FF_Hint.svelte.d.ts +0 -29
- package/esm/svelte/FF_Label.svelte +0 -23
- package/esm/svelte/FF_Label.svelte.d.ts +0 -29
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SvelteComponent } from 'svelte';
|
|
1
|
+
import type { Component, SvelteComponent } from 'svelte';
|
|
2
2
|
import { type ClassType, type EntityFilter, type FieldMetadata, type Repository } from 'remult';
|
|
3
3
|
import type { UnArray } from '../utils/types.js';
|
|
4
4
|
export type VisibilityMode = 'view' | 'edit' | 'hide';
|
|
@@ -13,6 +13,7 @@ type CellInternal<Entity> = {
|
|
|
13
13
|
clipboardable?: boolean;
|
|
14
14
|
clearable?: boolean;
|
|
15
15
|
component?: new (...args: any[]) => SvelteComponent;
|
|
16
|
+
componentS5?: Component;
|
|
16
17
|
props?: any;
|
|
17
18
|
rowToProps?: (row: any) => any;
|
|
18
19
|
};
|
|
@@ -98,9 +98,10 @@ export const buildWhere = (entity, defaultWhere, fields_filter, fields_search, o
|
|
|
98
98
|
and.push(...buildSearchWhere(entity, fields_search, obj.search));
|
|
99
99
|
}
|
|
100
100
|
for (const field of fields_filter) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
const rfi = getRelationFieldInfo(field);
|
|
102
|
+
// For relation fields, allow null as valid filter value (filters for NULL in DB)
|
|
103
|
+
const hasValue = rfi?.type === 'toOne' ? obj && obj[field.key] !== undefined : obj && obj[field.key];
|
|
104
|
+
if (hasValue) {
|
|
104
105
|
if (field.inputType === 'checkbox') {
|
|
105
106
|
// @ts-ignore
|
|
106
107
|
and.push({ [field.key]: obj[field.key] });
|
|
@@ -127,7 +128,7 @@ export const buildWhere = (entity, defaultWhere, fields_filter, fields_search, o
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
else if (rfi?.type === 'toOne') {
|
|
130
|
-
// @ts-ignore (setting the id of the relation)
|
|
131
|
+
// @ts-ignore (setting the id of the relation, null = filter for NULL)
|
|
131
132
|
and.push({ [field.key]: obj[field.key] });
|
|
132
133
|
}
|
|
133
134
|
else {
|
package/esm/internals/index.d.ts
CHANGED
|
@@ -18,20 +18,19 @@ import { default as Link } from '../ui/link/Link.svelte';
|
|
|
18
18
|
import { default as LinkPlus } from '../ui/link/LinkPlus.svelte';
|
|
19
19
|
import { default as Loading } from '../ui/Loading.svelte';
|
|
20
20
|
import { default as Tooltip } from '../ui/Tooltip.svelte';
|
|
21
|
-
import type {
|
|
22
|
-
import type { CellsInput
|
|
21
|
+
import type { BaseItem, BaseItemLight, FF_Icon } from './BaseEnum.js';
|
|
22
|
+
import type { CellsInput } from './cellsBuildor.js';
|
|
23
23
|
import { FF_Role } from './common.js';
|
|
24
|
-
import { storeItem } from './storeItem.js';
|
|
24
|
+
import { storeItem, type StoreItem } from './storeItem.js';
|
|
25
25
|
import { storeList } from './storeList.js';
|
|
26
26
|
export { Field, FormEditAction, Grid, Grid2, GridPaginate, GridPaginate2, FieldGroup, Icon, Link, LinkPlus, Loading, Button, Tooltip, DialogManagement, FieldContainer, SelectMelt, Clipboardable, };
|
|
27
27
|
export type { BaseEnumOptions } from './BaseEnum.js';
|
|
28
|
-
export type { BaseItem };
|
|
29
|
-
export type BaseItemLight = Partial<BaseItem>;
|
|
28
|
+
export type { BaseItem, BaseItemLight };
|
|
30
29
|
export type { DialogMetaDataInternal } from '../ui/dialog/dialog.js';
|
|
31
|
-
export type CellsInput
|
|
30
|
+
export type { CellsInput };
|
|
32
31
|
export type { Cell, VisibilityMode } from './cellsBuildor.js';
|
|
33
32
|
export type { FF_FindOptions } from './storeList.js';
|
|
34
|
-
export type StoreItem
|
|
33
|
+
export type { StoreItem };
|
|
35
34
|
export type StoreList<T> = ReturnType<typeof storeList<T>>;
|
|
36
35
|
export type { ResolvedType, UnArray, RecursivePartial } from '../utils/types.js';
|
|
37
36
|
export { FF_Fields } from './FF_Fields.js';
|
|
@@ -68,16 +67,12 @@ declare module 'remult' {
|
|
|
68
67
|
};
|
|
69
68
|
default_select_if_one_item?: boolean;
|
|
70
69
|
multiSelect?: boolean;
|
|
70
|
+
filterNobodyLabel?: string;
|
|
71
71
|
skipForDefaultField?: boolean;
|
|
72
72
|
}
|
|
73
73
|
interface EntityOptions<entityType> {
|
|
74
74
|
searchableFind?: (str: string) => FindOptionsBase<entityType>;
|
|
75
75
|
displayValue?: (item: entityType) => BaseItem;
|
|
76
|
-
permissionApiCrud?: BaseEnum[] | BaseEnum;
|
|
77
|
-
permissionApiDelete?: BaseEnum[] | BaseEnum;
|
|
78
|
-
permissionApiInsert?: BaseEnum[] | BaseEnum;
|
|
79
|
-
permissionApiRead?: BaseEnum[] | BaseEnum;
|
|
80
|
-
permissionApiUpdate?: BaseEnum[] | BaseEnum;
|
|
81
76
|
changeLog?: false | ColumnDeciderArgs<entityType>;
|
|
82
77
|
}
|
|
83
78
|
}
|
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import type { ErrorInfo, FindOptions, Repository } from 'remult';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
loading?: boolean;
|
|
5
|
-
errors: ErrorInfo<T> | undefined;
|
|
6
|
-
globalError?: string | undefined;
|
|
7
|
-
};
|
|
8
|
-
export declare const storeItem: <T>(repo: Repository<T>, initValues?: TheStoreItem<T>) => {
|
|
9
|
-
subscribe: (this: void, run: import("svelte/store").Subscriber<TheStoreItem<T>>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
1
|
+
import type { EntityFilter, ErrorInfo, FindOptions, Repository } from 'remult';
|
|
2
|
+
export interface StoreItem<T> {
|
|
3
|
+
subscribe: (run: (value: TheStoreItem<T>) => void) => () => void;
|
|
10
4
|
create: (item: Partial<T>) => void;
|
|
11
5
|
set: (newItem: TheStoreItem<T>) => void;
|
|
12
|
-
|
|
13
|
-
* if you have keys, build the id with
|
|
14
|
-
* ```ts
|
|
15
|
-
* const id = repo.metadata.idMetadata.getId({a:1,b:2})
|
|
16
|
-
* store.fetch(id)
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
fetch: (id: Parameters<Repository<T>["findId"]>[0], options?: FindOptions<T>, onNewData?: (item: T) => void) => Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* `.save()` will `update` or `insert` the current item.
|
|
22
|
-
*/
|
|
6
|
+
fetch: (idOrWhere: string | number | EntityFilter<T>, options?: FindOptions<T>, onNewData?: (item: T | undefined) => void) => Promise<void>;
|
|
23
7
|
save: () => Promise<T | undefined>;
|
|
24
8
|
delete: () => Promise<void>;
|
|
25
9
|
onChange: (prop: keyof T, callback: (newValue: any, oldValue: any) => void) => void;
|
|
10
|
+
}
|
|
11
|
+
type TheStoreItem<T> = {
|
|
12
|
+
item: T | undefined;
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
initLoading?: boolean;
|
|
15
|
+
errors: ErrorInfo<T> | undefined;
|
|
16
|
+
globalError?: string | undefined;
|
|
26
17
|
};
|
|
18
|
+
export declare const storeItem: <T>(repo: Repository<T>, initValues?: TheStoreItem<T>) => StoreItem<T>;
|
|
27
19
|
export {};
|
|
@@ -5,6 +5,7 @@ import { isError } from './helper.js';
|
|
|
5
5
|
export const storeItem = (repo, initValues = {
|
|
6
6
|
item: undefined,
|
|
7
7
|
loading: true,
|
|
8
|
+
initLoading: true,
|
|
8
9
|
errors: undefined,
|
|
9
10
|
globalError: undefined,
|
|
10
11
|
}) => {
|
|
@@ -30,38 +31,50 @@ export const storeItem = (repo, initValues = {
|
|
|
30
31
|
internalStore.set({
|
|
31
32
|
item: repo.create(item),
|
|
32
33
|
loading: false,
|
|
34
|
+
initLoading: false,
|
|
33
35
|
errors: {},
|
|
34
36
|
globalError: undefined,
|
|
35
37
|
});
|
|
36
38
|
},
|
|
37
39
|
// set: internalStore.set,
|
|
38
40
|
set: (newItem) => {
|
|
39
|
-
internalStore.update((
|
|
41
|
+
internalStore.update(() => {
|
|
40
42
|
return { ...newItem };
|
|
41
43
|
});
|
|
42
44
|
},
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
46
|
+
* Fetch by ID or WHERE clause
|
|
45
47
|
* ```ts
|
|
48
|
+
* // By ID (string or number)
|
|
49
|
+
* store.fetch(123)
|
|
50
|
+
* store.fetch('abc')
|
|
51
|
+
*
|
|
52
|
+
* // By WHERE clause (object)
|
|
53
|
+
* store.fetch({ siteId: 123 })
|
|
54
|
+
*
|
|
55
|
+
* // With composite keys, build the id with
|
|
46
56
|
* const id = repo.metadata.idMetadata.getId({a:1,b:2})
|
|
47
57
|
* store.fetch(id)
|
|
48
58
|
* ```
|
|
49
59
|
*/
|
|
50
|
-
fetch: async (
|
|
60
|
+
fetch: async (idOrWhere, options, onNewData) => {
|
|
51
61
|
if (BROWSER) {
|
|
52
62
|
internalStore.update((s) => ({ ...s, loading: true }));
|
|
53
63
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
64
|
+
const isId = typeof idOrWhere === 'string' || typeof idOrWhere === 'number';
|
|
65
|
+
const item = isId
|
|
66
|
+
? await repo.findId(idOrWhere, options)
|
|
67
|
+
: await repo.findFirst(idOrWhere, options);
|
|
56
68
|
internalStore.update((s) => ({
|
|
57
69
|
...s,
|
|
58
70
|
loading: false,
|
|
71
|
+
initLoading: false,
|
|
59
72
|
item: item ?? {},
|
|
60
73
|
errors: undefined,
|
|
61
74
|
globalError: undefined,
|
|
62
75
|
}));
|
|
63
76
|
if (onNewData) {
|
|
64
|
-
onNewData(item ??
|
|
77
|
+
onNewData(item ?? undefined);
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
80
|
catch (error) {
|
|
@@ -69,6 +82,7 @@ export const storeItem = (repo, initValues = {
|
|
|
69
82
|
internalStore.update((s) => ({
|
|
70
83
|
...s,
|
|
71
84
|
loading: false,
|
|
85
|
+
initLoading: false,
|
|
72
86
|
item: {},
|
|
73
87
|
errors: {},
|
|
74
88
|
// @ts-ignore
|
|
@@ -29,8 +29,14 @@ export type MailOptions = GlobalEasyOptions & {
|
|
|
29
29
|
defaults?: DefaultOptions;
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
|
-
declare let transporter: ReturnType<typeof typeNodemailer.createTransport>;
|
|
33
32
|
export type SendMail = typeof sendMail;
|
|
33
|
+
export type SendMailResult = {
|
|
34
|
+
data: SMTPTransport.SentMessageInfo;
|
|
35
|
+
error?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
error: any;
|
|
38
|
+
data?: undefined;
|
|
39
|
+
};
|
|
34
40
|
export declare const sendMail: (
|
|
35
41
|
/** usefull for logs, it has NO impact on the mail itself */
|
|
36
42
|
topic: string, easyOptions: GlobalEasyOptions & {
|
|
@@ -46,6 +52,6 @@ topic: string, easyOptions: GlobalEasyOptions & {
|
|
|
46
52
|
}[];
|
|
47
53
|
}, options?: {
|
|
48
54
|
nodemailer?: MailOptions['nodemailer'];
|
|
49
|
-
}) =>
|
|
55
|
+
}) => Promise<SendMailResult>;
|
|
50
56
|
export declare const mail: (o?: MailOptions) => Module<unknown>;
|
|
51
57
|
export {};
|
package/esm/mail/server/index.js
CHANGED
|
@@ -99,10 +99,10 @@ export const sendMail = async (topic, easyOptions, options) => {
|
|
|
99
99
|
}
|
|
100
100
|
try {
|
|
101
101
|
if (!globalOptions?.nodemailer?.transport) {
|
|
102
|
-
const
|
|
102
|
+
const data = await transporter.sendMail({ ...nodemailerOptions.defaults });
|
|
103
103
|
log.error(`${magenta(`[${topic}]`)} - ⚠️ ${red(`mail not configured`)} ⚠️
|
|
104
104
|
We are still nice and generated you an email preview link (the mail we not really sent):
|
|
105
|
-
👉 ${cyan(String(nodemailer.getTestMessageUrl(
|
|
105
|
+
👉 ${cyan(String(nodemailer.getTestMessageUrl(data)))}
|
|
106
106
|
|
|
107
107
|
To really send mails, check out the doc ${white(`https://firstly.fun/modules/mail`)}.
|
|
108
108
|
`);
|
|
@@ -113,10 +113,10 @@ export const sendMail = async (topic, easyOptions, options) => {
|
|
|
113
113
|
topic,
|
|
114
114
|
metadata,
|
|
115
115
|
});
|
|
116
|
-
return
|
|
116
|
+
return { data };
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
|
-
const
|
|
119
|
+
const data = await transporter.sendMail({ ...nodemailerOptions.defaults });
|
|
120
120
|
log.success(`${magenta(`[${topic}]`)} - Sent to ${typeof nodemailerOptions.defaults?.to === 'string' ? green(nodemailerOptions.defaults?.to) : nodemailerOptions.defaults?.to}`);
|
|
121
121
|
await repo(mailEntities.Mail).insert({
|
|
122
122
|
status: 'sent',
|
|
@@ -125,7 +125,7 @@ export const sendMail = async (topic, easyOptions, options) => {
|
|
|
125
125
|
topic,
|
|
126
126
|
metadata,
|
|
127
127
|
});
|
|
128
|
-
return
|
|
128
|
+
return { data };
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
catch (error) {
|
|
@@ -138,15 +138,43 @@ ${cyan(JSON.stringify(globalOptions?.nodemailer?.transport, null, 2))}
|
|
|
138
138
|
else {
|
|
139
139
|
log.error(`${magenta(`[${topic}]`)} - Error`, error);
|
|
140
140
|
}
|
|
141
|
+
// TODO
|
|
142
|
+
// Build comprehensive error info for JSON storage
|
|
143
|
+
const errorInfoJSON = {
|
|
144
|
+
message: error instanceof Error ? error.message : String(error),
|
|
145
|
+
name: error instanceof Error ? error.name : 'Unknown',
|
|
146
|
+
// stack: error instanceof Error ? error.stack : undefined,
|
|
147
|
+
code: error?.code,
|
|
148
|
+
errno: error?.errno,
|
|
149
|
+
syscall: error?.syscall,
|
|
150
|
+
hostname: error?.hostname,
|
|
151
|
+
port: error?.port,
|
|
152
|
+
address: error?.address,
|
|
153
|
+
response: error?.response,
|
|
154
|
+
responseCode: error?.responseCode,
|
|
155
|
+
command: error?.command,
|
|
156
|
+
// Capture any other enumerable properties
|
|
157
|
+
...Object.getOwnPropertyNames(error).reduce((acc, key) => {
|
|
158
|
+
if (!['message', 'name', 'stack'].includes(key)) {
|
|
159
|
+
try {
|
|
160
|
+
acc[key] = error[key];
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
// Ignore properties that can't be accessed
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return acc;
|
|
167
|
+
}, {}),
|
|
168
|
+
};
|
|
141
169
|
await repo(mailEntities.Mail).insert({
|
|
142
170
|
status: 'error',
|
|
143
|
-
errorInfo: JSON.stringify(
|
|
171
|
+
errorInfo: JSON.stringify(errorInfoJSON),
|
|
144
172
|
to: JSON.stringify(to),
|
|
145
173
|
html: easyOptionsToUse.saveHtml ? html : '',
|
|
146
174
|
topic,
|
|
147
175
|
metadata,
|
|
148
176
|
});
|
|
149
|
-
|
|
177
|
+
return { error };
|
|
150
178
|
}
|
|
151
179
|
};
|
|
152
180
|
const mailModule = new Module({
|
package/esm/server/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare class ModuleFF {
|
|
|
27
27
|
modulesFF?: ModuleFF[];
|
|
28
28
|
constructor(input: ModuleInput);
|
|
29
29
|
}
|
|
30
|
-
type Options = RemultServerOptions<RequestEvent<Partial<Record<string, string>>,
|
|
30
|
+
type Options = RemultServerOptions<RequestEvent<Partial<Record<string, string>>, any>> & {
|
|
31
31
|
/** @deprecated use `remult` modules instead */
|
|
32
32
|
modulesFF?: ModuleFF[] | undefined;
|
|
33
33
|
};
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
cell: CellMetadata<valueType, entityType>
|
|
14
14
|
r?: FF_Repo<entityType>
|
|
15
15
|
class?: string
|
|
16
|
+
value?: valueType
|
|
17
|
+
error?: string
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
let props: Props<valueType, entityType> = $props()
|
|
20
|
+
let { value = $bindable(), error, ...props }: Props<valueType, entityType> = $props()
|
|
20
21
|
|
|
21
22
|
// let classes = $derived(getClasses('field', props.classes))
|
|
22
23
|
|
|
@@ -25,9 +26,6 @@
|
|
|
25
26
|
let hint = $derived(props.cell.field?.options.ui?.hint ?? props.cell.ui?.hint)
|
|
26
27
|
// @ts-ignore
|
|
27
28
|
let ui = $derived(deepMerge(props.cell.field?.options.ui ?? {}, props.cell.ui ?? {}))
|
|
28
|
-
|
|
29
|
-
let error = ''
|
|
30
|
-
let value: any = $state('')
|
|
31
29
|
</script>
|
|
32
30
|
|
|
33
31
|
<!-- Snippets sections -->
|
|
@@ -4,11 +4,13 @@ interface Props<valueType = unknown, entityType = unknown> {
|
|
|
4
4
|
cell: CellMetadata<valueType, entityType>;
|
|
5
5
|
r?: FF_Repo<entityType>;
|
|
6
6
|
class?: string;
|
|
7
|
+
value?: valueType;
|
|
8
|
+
error?: string;
|
|
7
9
|
}
|
|
8
10
|
declare function $$render<valueType = unknown, entityType = unknown>(): {
|
|
9
11
|
props: Props<valueType, entityType>;
|
|
10
12
|
exports: {};
|
|
11
|
-
bindings: "";
|
|
13
|
+
bindings: "value";
|
|
12
14
|
slots: {};
|
|
13
15
|
events: {};
|
|
14
16
|
};
|
|
@@ -16,7 +18,7 @@ declare class __sveltets_Render<valueType = unknown, entityType = unknown> {
|
|
|
16
18
|
props(): ReturnType<typeof $$render<valueType, entityType>>['props'];
|
|
17
19
|
events(): ReturnType<typeof $$render<valueType, entityType>>['events'];
|
|
18
20
|
slots(): ReturnType<typeof $$render<valueType, entityType>>['slots'];
|
|
19
|
-
bindings(): "";
|
|
21
|
+
bindings(): "value";
|
|
20
22
|
exports(): {};
|
|
21
23
|
}
|
|
22
24
|
interface $$IsomorphicComponent {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" generics="entityType = unknown">
|
|
2
2
|
import { EntityError, getEntityRef } from 'remult'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { FF_Cell, getClasses } from './'
|
|
5
5
|
import type { FF_Repo, FieldGroup, FormTheme } from './'
|
|
6
6
|
|
|
7
7
|
const default_uid = $props.id()
|
|
@@ -91,11 +91,10 @@
|
|
|
91
91
|
{/if}
|
|
92
92
|
<div data-ff-form-fields class="{classes?.fields} {group.class}">
|
|
93
93
|
{#each group.fields as field}
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
{field}
|
|
94
|
+
<FF_Cell
|
|
95
|
+
cell={{ field, mode: 'edit' }}
|
|
97
96
|
bind:value={valuesToUse[field.key as keyof entityType]}
|
|
98
|
-
error={errors[field.key]}
|
|
97
|
+
error={errors[field.key ?? '']}
|
|
99
98
|
/>
|
|
100
99
|
{/each}
|
|
101
100
|
</div>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import Icon from '../ui/Icon.svelte'
|
|
5
5
|
import { LibIcon_Add, LibIcon_Delete, LibIcon_Edit } from '../ui/LibIcon'
|
|
6
|
-
import { dialog,
|
|
6
|
+
import { dialog, FF_Cell_Display, FF_Repo, getClasses, type GridTheme } from './'
|
|
7
7
|
|
|
8
8
|
interface Props<entityType> {
|
|
9
9
|
// uid?: string
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
<tr data-ff-grid-row class={classes?.row}>
|
|
101
101
|
{#each fields as f (f.key)}
|
|
102
102
|
<td data-ff-grid-row-cell class="{classes?.rowCell} td-{f.inputType}">
|
|
103
|
-
<
|
|
103
|
+
<FF_Cell_Display field={f} value={item[f.key as keyof entityType]}></FF_Cell_Display>
|
|
104
104
|
</td>
|
|
105
105
|
{/each}
|
|
106
106
|
{#if showActions}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
{:else if layoutToUse.type === 'grid'}
|
|
29
29
|
<FF_Grid {r}></FF_Grid>
|
|
30
30
|
{:else if layoutToUse.type === 'tab'}
|
|
31
|
-
<div role="tablist" class="tabs tabs
|
|
31
|
+
<div role="tablist" class="tabs-lift tabs">
|
|
32
32
|
{#each layoutToUse.groups ?? [] as group (group.key)}
|
|
33
33
|
<input
|
|
34
34
|
type="radio"
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
onchange={() => (selectedThing = group.key)}
|
|
40
40
|
aria-label={group.caption}
|
|
41
41
|
/>
|
|
42
|
-
<div role="tabpanel" class="tab-content
|
|
42
|
+
<div role="tabpanel" class="tab-content border-base-300 bg-base-100 p-6">
|
|
43
43
|
<FF_Form {r} groups={[group]} show={{ title: false }}></FF_Form>
|
|
44
44
|
</div>
|
|
45
45
|
{/each}
|
|
46
46
|
</div>
|
|
47
47
|
{:else if layoutToUse.type === 'accordion'}
|
|
48
48
|
{#each layoutToUse.groups ?? [] as group (group.key)}
|
|
49
|
-
<div class="collapse bg-base-100">
|
|
49
|
+
<div class="collapse-arrow collapse bg-base-100">
|
|
50
50
|
<input
|
|
51
51
|
type="radio"
|
|
52
52
|
name="my-accordion"
|
|
@@ -62,6 +62,15 @@ export declare class FF_Repo<Entity, QueryOptions extends QueryOptionsHelper<Ent
|
|
|
62
62
|
aggregates: ExtractAggregateResult<Entity, QueryOptions>;
|
|
63
63
|
hasNextPage: boolean;
|
|
64
64
|
} | undefined>;
|
|
65
|
+
/**
|
|
66
|
+
* Refresh query keeping current items count (BIG refresh)
|
|
67
|
+
* Useful after edit/delete to stay at current scroll position
|
|
68
|
+
*/
|
|
69
|
+
queryRefresh(options: Pick<QueryOptionsHelper<Entity>, 'where' | 'orderBy'>): Promise<{
|
|
70
|
+
items: Entity[];
|
|
71
|
+
aggregates: ExtractAggregateResult<Entity, QueryOptions>;
|
|
72
|
+
hasNextPage: boolean;
|
|
73
|
+
} | undefined>;
|
|
65
74
|
create(...args: Parameters<Repository<Entity>['create']>): Entity;
|
|
66
75
|
delete(...args: Parameters<Repository<Entity>['delete']>): Promise<undefined>;
|
|
67
76
|
getLayout: getLayoutStrict<Entity>;
|
|
@@ -132,6 +132,45 @@ export class FF_Repo {
|
|
|
132
132
|
hasNextPage: this.hasNextPage,
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Refresh query keeping current items count (BIG refresh)
|
|
137
|
+
* Useful after edit/delete to stay at current scroll position
|
|
138
|
+
*/
|
|
139
|
+
async queryRefresh(options) {
|
|
140
|
+
const currentCount = this.items?.length ?? this.#queryOptions?.pageSize ?? 25;
|
|
141
|
+
this.loading = {
|
|
142
|
+
...this.loading,
|
|
143
|
+
fetching: true,
|
|
144
|
+
init: this.items === undefined,
|
|
145
|
+
};
|
|
146
|
+
const { data: queryResult, error: queryResultError } = tryCatchSync(() => this.#repo.query({
|
|
147
|
+
...this.#queryOptions,
|
|
148
|
+
...options,
|
|
149
|
+
pageSize: currentCount,
|
|
150
|
+
aggregate: {
|
|
151
|
+
...this.#queryOptions?.aggregate,
|
|
152
|
+
},
|
|
153
|
+
}));
|
|
154
|
+
if (queryResultError) {
|
|
155
|
+
this.globalError = queryResultError.message;
|
|
156
|
+
return this.loadingEnd();
|
|
157
|
+
}
|
|
158
|
+
const { data: paginator, error: paginatorError } = await tryCatch(queryResult.paginator());
|
|
159
|
+
if (paginatorError) {
|
|
160
|
+
this.globalError = paginatorError.message;
|
|
161
|
+
return this.loadingEnd();
|
|
162
|
+
}
|
|
163
|
+
this.#paginator = paginator;
|
|
164
|
+
this.items = this.#paginator.items;
|
|
165
|
+
// @ts-expect-error - We know the structure will match due to how we define the types
|
|
166
|
+
this.aggregates = this.#paginator.aggregates;
|
|
167
|
+
this.hasNextPage = this.#paginator.hasNextPage && this.aggregates.$count > this.items.length;
|
|
168
|
+
return this.loadingEnd({
|
|
169
|
+
items: this.items,
|
|
170
|
+
aggregates: this.aggregates,
|
|
171
|
+
hasNextPage: this.hasNextPage,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
135
174
|
create(...args) {
|
|
136
175
|
this.item = this.#repo.create(...args);
|
|
137
176
|
return this.item;
|
|
@@ -7,6 +7,7 @@ import { goto } from '$app/navigation';
|
|
|
7
7
|
import { page } from '$app/state';
|
|
8
8
|
import { debounce } from '../helpers/debounce.js';
|
|
9
9
|
const CONFIG_DELIMITER = ';';
|
|
10
|
+
const NULL_URL_VALUE = '__null__';
|
|
10
11
|
/**
|
|
11
12
|
* SearchParams class for handling URL search parameters with Svelte 5 runes
|
|
12
13
|
* Provides automatic binding and URL updates
|
|
@@ -257,6 +258,12 @@ export class SP {
|
|
|
257
258
|
const urlKey = this.keyMap[propKey]; // Get the URL parameter key
|
|
258
259
|
const paramValue = params.get(urlKey);
|
|
259
260
|
if (paramValue !== null) {
|
|
261
|
+
// Decode null sentinel from URL
|
|
262
|
+
if (paramValue === NULL_URL_VALUE) {
|
|
263
|
+
this.paramValues[propKey] = null;
|
|
264
|
+
this.debouncedValues[propKey] = null;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
260
267
|
// If there is a decode function, always use it to get the proper object
|
|
261
268
|
if (def.decode) {
|
|
262
269
|
const decodedValue = def.decode(paramValue);
|
|
@@ -314,11 +321,16 @@ export class SP {
|
|
|
314
321
|
}
|
|
315
322
|
const params = new URLSearchParams(window.location.search);
|
|
316
323
|
for (const [propKey, value] of Object.entries(this.debouncedValues)) {
|
|
317
|
-
// Skip undefined
|
|
318
|
-
if (value === undefined
|
|
324
|
+
// Skip undefined values
|
|
325
|
+
if (value === undefined) {
|
|
319
326
|
params.delete(this.keyMap[propKey]);
|
|
320
327
|
continue;
|
|
321
328
|
}
|
|
329
|
+
// Encode null as special URL value
|
|
330
|
+
if (value === null) {
|
|
331
|
+
params.set(this.keyMap[propKey], NULL_URL_VALUE);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
322
334
|
// Get the definition and URL key
|
|
323
335
|
const def = this.config[propKey];
|
|
324
336
|
if (!def)
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
>
|
|
17
17
|
{@html toShow.children}
|
|
18
18
|
<svelte:fragment slot="actions">
|
|
19
|
-
<Button class="text-white"
|
|
19
|
+
<Button class="text-white" onclick={() => dialog.close(toShow.id, { success: true })}
|
|
20
20
|
>Confirmer</Button
|
|
21
21
|
>
|
|
22
22
|
</svelte:fragment>
|
|
@@ -29,10 +29,7 @@
|
|
|
29
29
|
>
|
|
30
30
|
{@html toShow.children}
|
|
31
31
|
<svelte:fragment slot="actions">
|
|
32
|
-
<Button
|
|
33
|
-
class="btn-outline btn-error"
|
|
34
|
-
on:click={() => dialog.close(toShow.id, { success: true })}
|
|
35
|
-
>
|
|
32
|
+
<Button class="btn-error" onclick={() => dialog.close(toShow.id, { success: true })}>
|
|
36
33
|
Confirmer
|
|
37
34
|
</Button>
|
|
38
35
|
</svelte:fragment>
|
|
@@ -18,7 +18,7 @@ const createDialogManagement = () => {
|
|
|
18
18
|
confirm: (topic, text, icon) => {
|
|
19
19
|
const detail = {
|
|
20
20
|
detail: {
|
|
21
|
-
caption: '
|
|
21
|
+
caption: 'A Confirmer',
|
|
22
22
|
icon: { data: icon },
|
|
23
23
|
},
|
|
24
24
|
children: `
|
|
@@ -34,7 +34,7 @@ const createDialogManagement = () => {
|
|
|
34
34
|
confirmDelete: (topic) => {
|
|
35
35
|
const detail = {
|
|
36
36
|
detail: {
|
|
37
|
-
caption: 'Supprimer',
|
|
37
|
+
caption: 'A Supprimer',
|
|
38
38
|
icon: { data: LibIcon_Delete },
|
|
39
39
|
},
|
|
40
40
|
children: topic
|
package/esm/svelte/index.d.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import type { CellMetadata, getLayout } from './customField';
|
|
2
|
+
import { default as FF_Cell_Display } from './FF_Cell_Display.svelte';
|
|
2
3
|
import { default as FF_Cell } from './FF_Cell.svelte';
|
|
3
4
|
import { default as FF_Config } from './FF_Config.svelte';
|
|
4
|
-
import { default as FF_Display } from './FF_Display.svelte';
|
|
5
|
-
import { default as FF_Edit } from './FF_Edit.svelte';
|
|
6
|
-
import { default as FF_Error } from './FF_Error.svelte';
|
|
7
|
-
import { default as FF_Field } from './FF_Field.svelte';
|
|
8
5
|
import { default as FF_Form } from './FF_Form.svelte';
|
|
9
6
|
import { default as FF_Grid } from './FF_Grid.svelte';
|
|
10
|
-
import { default as FF_Hint } from './FF_Hint.svelte';
|
|
11
|
-
import { default as FF_Label } from './FF_Label.svelte';
|
|
12
7
|
import { default as FF_Layout } from './FF_Layout.svelte';
|
|
13
8
|
export type { FieldTheme, FormTheme, GridTheme, Theme, EditTheme, DisplayTheme, } from './ff_Config.svelte.js';
|
|
14
9
|
export { getDynamicCustomField, getTheme, setDynamicCustomField, setTheme, getClasses, daisyTheme, defaultTheme, emptyTheme, FF_Theme, } from './ff_Config.svelte.js';
|
|
15
|
-
export { FF_Grid, FF_Form,
|
|
10
|
+
export { FF_Grid, FF_Form, FF_Config, FF_Layout, FF_Cell, FF_Cell_Display };
|
|
16
11
|
export type { DynamicCustomField, FieldGroup } from './customField';
|
|
17
12
|
export { FF_Repo } from './FF_Repo.svelte.js';
|
|
18
13
|
export { tryCatch, tryCatchSync } from './tryCatch';
|
package/esm/svelte/index.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
+
import { default as FF_Cell_Display } from './FF_Cell_Display.svelte';
|
|
1
2
|
import { default as FF_Cell } from './FF_Cell.svelte';
|
|
2
3
|
import { default as FF_Config } from './FF_Config.svelte';
|
|
3
|
-
import { default as FF_Display } from './FF_Display.svelte';
|
|
4
|
-
import { default as FF_Edit } from './FF_Edit.svelte';
|
|
5
|
-
import { default as FF_Error } from './FF_Error.svelte';
|
|
6
|
-
import { default as FF_Field } from './FF_Field.svelte';
|
|
7
4
|
import { default as FF_Form } from './FF_Form.svelte';
|
|
8
5
|
import { default as FF_Grid } from './FF_Grid.svelte';
|
|
9
|
-
import { default as FF_Hint } from './FF_Hint.svelte';
|
|
10
|
-
import { default as FF_Label } from './FF_Label.svelte';
|
|
11
6
|
import { default as FF_Layout } from './FF_Layout.svelte';
|
|
12
7
|
export { getDynamicCustomField, getTheme, setDynamicCustomField, setTheme, getClasses, daisyTheme, defaultTheme, emptyTheme, FF_Theme, } from './ff_Config.svelte.js';
|
|
13
|
-
export { FF_Grid, FF_Form,
|
|
8
|
+
export { FF_Grid, FF_Form, FF_Config, FF_Layout, FF_Cell, FF_Cell_Display };
|
|
14
9
|
export { FF_Repo } from './FF_Repo.svelte.js';
|
|
15
10
|
export { tryCatch, tryCatchSync } from './tryCatch';
|
|
16
11
|
export { overwriteOptions, deepMerge, isOfType } from './helpers';
|