5htp-core 0.5.9 → 0.6.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/client/app/component.tsx +1 -0
- package/client/assets/css/colors.less +244 -0
- package/client/assets/css/components/button.less +27 -9
- package/client/assets/css/components/card.less +24 -0
- package/client/assets/css/components/input.less +0 -4
- package/client/assets/css/components/mantine.less +11 -0
- package/client/assets/css/components/other.less +1 -0
- package/client/assets/css/components/progressbar.less +45 -0
- package/client/assets/css/components/table.less +8 -12
- package/client/assets/css/components.less +1 -0
- package/client/assets/css/core.less +6 -4
- package/client/assets/css/text/text.less +0 -9
- package/client/assets/css/theme.less +10 -0
- package/client/assets/css/utils/layouts.less +8 -4
- package/client/assets/css/utils/sizing.less +2 -0
- package/client/components/Card/index.tsx +1 -2
- package/client/components/Checkbox.tsx +2 -1
- package/client/components/DropDown.tsx +11 -5
- package/client/components/Input.tsx +1 -0
- package/client/components/Rte/ToolbarPlugin/BlockFormat.tsx +1 -1
- package/client/components/Rte/ToolbarPlugin/ElementFormat.tsx +5 -3
- package/client/components/Rte/ToolbarPlugin/index.tsx +4 -4
- package/client/components/Select.tsx +95 -69
- package/client/components/Table/index.tsx +34 -23
- package/client/components/containers/Popover/index.tsx +1 -1
- package/client/components/containers/Popover/popover.less +1 -1
- package/client/components/index.ts +3 -2
- package/client/components/utils.tsx +5 -5
- package/client/index.ts +1 -0
- package/client/services/router/components/router.tsx +0 -1
- package/client/services/router/index.tsx +1 -2
- package/client/services/router/request/api.ts +4 -4
- package/common/errors/index.tsx +6 -0
- package/common/router/index.ts +8 -2
- package/package.json +1 -2
- package/server/app/commands.ts +2 -21
- package/server/app/container/console/index.ts +1 -6
- package/server/app/container/index.ts +0 -2
- package/server/app/index.ts +88 -22
- package/server/app/service/index.ts +30 -35
- package/server/services/auth/index.ts +15 -17
- package/server/services/auth/old.ts +1 -1
- package/server/services/auth/router/index.ts +24 -12
- package/server/services/cache/index.ts +5 -16
- package/server/services/cron/index.ts +2 -9
- package/server/services/database/index.ts +5 -10
- package/server/services/database/stats.ts +0 -2
- package/server/services/disks/driver.ts +1 -1
- package/server/services/disks/drivers/s3/index.ts +4 -8
- package/server/services/disks/index.ts +10 -9
- package/server/services/email/index.ts +5 -2
- package/server/services/email/transporter.ts +1 -21
- package/server/services/fetch/index.ts +9 -11
- package/server/services/fetch/service.json +2 -1
- package/server/services/prisma/index.ts +1 -14
- package/server/services/router/index.ts +28 -53
- package/server/services/router/request/api.ts +2 -7
- package/server/services/router/service.ts +5 -17
- package/server/services/security/encrypt/aes/index.ts +1 -1
- package/server/services/socket/index.ts +11 -19
- package/types/global/utils.d.ts +44 -1
- package/types/icons.d.ts +1 -1
- package/server/app/container/patch.ts +0 -15
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
|
|
15
15
|
// Core
|
|
16
16
|
import Button from '@client/components/Button';
|
|
17
|
-
import DropDown from '@client/components/
|
|
17
|
+
import DropDown from '@client/components/DropDown';
|
|
18
18
|
|
|
19
19
|
/*----------------------------------
|
|
20
20
|
- TYPES
|
|
@@ -85,8 +85,10 @@ export default function ElementFormatDropdown({
|
|
|
85
85
|
const formatOption = FormatOptions.find((option) => option.value === currentValue) || FormatOptions[0];
|
|
86
86
|
|
|
87
87
|
return (
|
|
88
|
-
<DropDown disabled={disabled}
|
|
89
|
-
|
|
88
|
+
<DropDown disabled={disabled}
|
|
89
|
+
icon={isRTL ? formatOption.iconRTL : formatOption.icon}
|
|
90
|
+
size="s"
|
|
91
|
+
hint={formatOption.label}
|
|
90
92
|
popover={{ tag: 'li' }}
|
|
91
93
|
>
|
|
92
94
|
{FormatOptions.map((option) => (
|
|
@@ -510,7 +510,7 @@ export default function ToolbarPlugin({
|
|
|
510
510
|
)}
|
|
511
511
|
|
|
512
512
|
{blockType === 'code' ? (
|
|
513
|
-
<DropDown label={getLanguageFriendlyName(codeLanguage)}
|
|
513
|
+
<DropDown size="s" label={getLanguageFriendlyName(codeLanguage)}
|
|
514
514
|
disabled={!isEditable}
|
|
515
515
|
icon="code"
|
|
516
516
|
>
|
|
@@ -591,7 +591,7 @@ export default function ToolbarPlugin({
|
|
|
591
591
|
|
|
592
592
|
<DropDown popover={{ tag: 'li' }} icon="font" size="s"
|
|
593
593
|
disabled={!isEditable}
|
|
594
|
-
|
|
594
|
+
hint="Formatting options for additional text styles"
|
|
595
595
|
>
|
|
596
596
|
|
|
597
597
|
<Button icon="strikethrough" size="s"
|
|
@@ -640,8 +640,8 @@ export default function ToolbarPlugin({
|
|
|
640
640
|
<DropDown popover={{ tag: 'li' }}
|
|
641
641
|
disabled={!isEditable}
|
|
642
642
|
size="s"
|
|
643
|
-
|
|
644
|
-
|
|
643
|
+
icon="plus-circle"
|
|
644
|
+
hint="Insert specialized editor node"
|
|
645
645
|
>
|
|
646
646
|
|
|
647
647
|
<Button icon="horizontal-rule" size="s" onClick={() => {
|
|
@@ -5,41 +5,44 @@
|
|
|
5
5
|
// Npm
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import {
|
|
8
|
-
Select as MantineSelect,
|
|
9
8
|
SelectProps,
|
|
10
9
|
MultiSelect as MantineMultiSelect,
|
|
11
10
|
ComboboxItem,
|
|
12
|
-
|
|
13
|
-
Menu,
|
|
14
|
-
Button
|
|
11
|
+
Menu
|
|
15
12
|
} from '@mantine/core';
|
|
16
13
|
import Input from '@client/components/Input';
|
|
17
14
|
|
|
18
15
|
// Core
|
|
19
|
-
import { Props as DropdownProps } from '@client/components/dropdown';
|
|
20
16
|
import { useMantineInput, InputBaseProps } from '@client/components/utils';
|
|
17
|
+
import Button, { Props as ButtonProps } from '@client/components/Button';
|
|
18
|
+
import Popover, { Props as PopoverProps } from '@client/components/containers/Popover';
|
|
21
19
|
|
|
22
20
|
/*----------------------------------
|
|
23
21
|
- TYPES
|
|
24
22
|
----------------------------------*/
|
|
25
23
|
|
|
26
24
|
export type Props = SelectProps & InputBaseProps<ComboboxItem> & {
|
|
27
|
-
|
|
25
|
+
popoverProps?: PopoverProps
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
export type Choice = ComboboxItem;
|
|
31
29
|
|
|
32
|
-
const ensureChoice = (
|
|
30
|
+
const ensureChoice = (
|
|
31
|
+
choice: ComboboxItem | string,
|
|
32
|
+
choices: ComboboxItem[],
|
|
33
|
+
current: ComboboxItem[]
|
|
34
|
+
): ComboboxItem => {
|
|
33
35
|
|
|
34
36
|
// Allready a choice
|
|
35
|
-
if (typeof choice === 'object'
|
|
37
|
+
if (typeof choice === 'object')
|
|
36
38
|
return choice;
|
|
37
|
-
|
|
39
|
+
|
|
40
|
+
// Ensure current is an array of choices
|
|
41
|
+
const allChoices = [...choices, ...current];
|
|
38
42
|
|
|
39
43
|
// Find the choice
|
|
40
|
-
const found =
|
|
41
|
-
if (found)
|
|
42
|
-
return found;
|
|
44
|
+
const found = allChoices.find( c => c.value === choice );
|
|
45
|
+
if (found) return found;
|
|
43
46
|
|
|
44
47
|
// Create a new choice
|
|
45
48
|
return {
|
|
@@ -60,28 +63,23 @@ export default (initProps: Props) => {
|
|
|
60
63
|
onChange, value: current,
|
|
61
64
|
required
|
|
62
65
|
}, {
|
|
63
|
-
multiple, choices: initChoices, enableSearch,
|
|
66
|
+
multiple, choices: initChoices, enableSearch, popoverProps,
|
|
64
67
|
...props
|
|
65
68
|
}] = useMantineInput<Props, string|number>(initProps);
|
|
66
69
|
|
|
67
|
-
const
|
|
70
|
+
const currentArray = (Array.isArray(current)
|
|
71
|
+
? current
|
|
72
|
+
: current ? [current] : []
|
|
73
|
+
).map(c => ensureChoice(c, [], []));
|
|
68
74
|
|
|
69
75
|
const choicesViaFunc = typeof initChoices === 'function';
|
|
70
76
|
if (choicesViaFunc)
|
|
71
77
|
enableSearch = true;
|
|
72
78
|
else
|
|
73
|
-
initChoices = initChoices?.map( c => ensureChoice(c, []) ) || [];
|
|
74
|
-
|
|
75
|
-
if (enableSearch)
|
|
76
|
-
props.searchable = true;
|
|
79
|
+
initChoices = initChoices?.map( c => ensureChoice(c, [], currentArray) ) || [];
|
|
77
80
|
|
|
78
81
|
let [choices, setChoices] = React.useState<ComboboxItem[]>( choicesViaFunc
|
|
79
|
-
?
|
|
80
|
-
? current.map( c => ensureChoice(c, []) )
|
|
81
|
-
: current
|
|
82
|
-
? [ensureChoice(current, [])]
|
|
83
|
-
: []
|
|
84
|
-
) || []
|
|
82
|
+
? currentArray.map( c => ensureChoice(c, [], currentArray) )
|
|
85
83
|
: initChoices
|
|
86
84
|
);
|
|
87
85
|
|
|
@@ -98,71 +96,70 @@ export default (initProps: Props) => {
|
|
|
98
96
|
- ACTIONS
|
|
99
97
|
----------------------------------*/
|
|
100
98
|
|
|
101
|
-
const valueToChoice = (value: string) => choices.find(c => c.value === value);
|
|
102
|
-
|
|
103
99
|
React.useEffect(() => {
|
|
104
100
|
|
|
105
101
|
if (choicesViaFunc && opened) {
|
|
102
|
+
|
|
103
|
+
const keywords = search.keywords === current?.label
|
|
104
|
+
? undefined
|
|
105
|
+
: search.keywords;
|
|
106
|
+
|
|
106
107
|
//setSearch(s => ({ ...s, loading: true }));
|
|
107
|
-
initChoices(
|
|
108
|
+
initChoices(keywords).then((searchResults) => {
|
|
108
109
|
//setSearch(s => ({ ...s, loading: false }))
|
|
109
110
|
setChoices(searchResults);
|
|
110
111
|
})
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
initRef.current = true;
|
|
114
|
-
|
|
115
114
|
}, [
|
|
116
115
|
opened,
|
|
117
|
-
|
|
116
|
+
search.keywords,
|
|
118
117
|
// When initChoices is a function, React considers it's always different
|
|
119
118
|
// It avoids the choices are fetched everytimle the parent component is re-rendered
|
|
120
119
|
typeof initChoices === 'function' ? true : initChoices
|
|
121
120
|
]);
|
|
122
121
|
|
|
123
|
-
|
|
122
|
+
/*----------------------------------
|
|
123
|
+
- RENDER
|
|
124
|
+
----------------------------------*/
|
|
125
|
+
|
|
124
126
|
if (multiple) {
|
|
125
|
-
|
|
126
|
-
props.value = current
|
|
127
|
-
|
|
127
|
+
|
|
128
|
+
props.value = current
|
|
129
|
+
? current.map( c => ensureChoice(c, choices, currentArray).value )
|
|
130
|
+
: [];
|
|
131
|
+
|
|
132
|
+
props.onChange = (value: string[]) => {
|
|
133
|
+
onChange( value.map(value => ensureChoice(value, choices, currentArray)) )
|
|
134
|
+
};
|
|
135
|
+
|
|
128
136
|
} else {
|
|
129
|
-
|
|
130
|
-
props.value = current
|
|
131
|
-
|
|
137
|
+
|
|
138
|
+
props.value = current
|
|
139
|
+
? [ensureChoice(current, choices, currentArray).value]
|
|
140
|
+
: [];
|
|
141
|
+
|
|
142
|
+
props.onChange = (value: string[]) => {
|
|
143
|
+
|
|
144
|
+
setOpened(false);
|
|
145
|
+
|
|
146
|
+
onChange( value.length > 0
|
|
147
|
+
? ensureChoice(value[value.length - 1], choices, currentArray)
|
|
148
|
+
: undefined
|
|
149
|
+
)
|
|
150
|
+
};
|
|
132
151
|
}
|
|
133
152
|
|
|
134
|
-
/*----------------------------------
|
|
135
|
-
- RENDER
|
|
136
|
-
----------------------------------*/
|
|
137
153
|
if (minimal) {
|
|
138
154
|
return (
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
closeOnItemClick={!multiple}>
|
|
142
|
-
<Menu.Target>
|
|
143
|
-
<Button variant="subtle"
|
|
144
|
-
leftSection={(
|
|
145
|
-
(current && multiple) ? (
|
|
146
|
-
<span class="badge bg info s">
|
|
147
|
-
{current.length}
|
|
148
|
-
</span>
|
|
149
|
-
) : null
|
|
150
|
-
)}
|
|
151
|
-
rightSection={<i src="angle-down" />}
|
|
152
|
-
onClick={() => setOpened((o) => !o)} >
|
|
153
|
-
|
|
154
|
-
{props.label || props.placeholder}
|
|
155
|
-
|
|
156
|
-
</Button>
|
|
157
|
-
</Menu.Target>
|
|
158
|
-
<Menu.Dropdown>
|
|
159
|
-
|
|
155
|
+
<Popover {...(popoverProps || {})} state={[opened, setOpened]} content={(
|
|
156
|
+
<div class="card col menu floating">
|
|
160
157
|
{enableSearch && <>
|
|
158
|
+
|
|
161
159
|
<Input title="Search" value={search.keywords}
|
|
162
160
|
wrapper={false} minimal icon="search"
|
|
163
161
|
onChange={v => setSearch(s => ({ ...s, keywords: v }))} />
|
|
164
162
|
|
|
165
|
-
<Menu.Divider />
|
|
166
163
|
</>}
|
|
167
164
|
|
|
168
165
|
{choices.map(choice => {
|
|
@@ -172,8 +169,9 @@ export default (initProps: Props) => {
|
|
|
172
169
|
: props.value === choice.value;
|
|
173
170
|
|
|
174
171
|
return (
|
|
175
|
-
<
|
|
176
|
-
|
|
172
|
+
<Button key={choice.value}
|
|
173
|
+
size="s"
|
|
174
|
+
suffix={isSelected ? <i src="check" /> : null}
|
|
177
175
|
onClick={() => onChange( multiple
|
|
178
176
|
? (isSelected
|
|
179
177
|
? current.filter(c => c.value !== choice.value)
|
|
@@ -185,28 +183,56 @@ export default (initProps: Props) => {
|
|
|
185
183
|
)
|
|
186
184
|
)}>
|
|
187
185
|
{choice.label}
|
|
188
|
-
</
|
|
186
|
+
</Button>
|
|
189
187
|
)
|
|
190
188
|
})}
|
|
191
|
-
</
|
|
192
|
-
|
|
189
|
+
</div>
|
|
190
|
+
)}>
|
|
191
|
+
<Button
|
|
192
|
+
prefix={(
|
|
193
|
+
(multiple && current?.length) ? (
|
|
194
|
+
<span class="badge bg info s">
|
|
195
|
+
{current.length}
|
|
196
|
+
</span>
|
|
197
|
+
) : icon ? <i src={icon} /> : null
|
|
198
|
+
)}
|
|
199
|
+
suffix={iconR ? <i src={iconR} /> : <i src="angle-down" />}
|
|
200
|
+
onClick={() => setOpened((o) => !o)}>
|
|
201
|
+
|
|
202
|
+
{props.label || props.placeholder}
|
|
203
|
+
|
|
204
|
+
</Button>
|
|
205
|
+
</Popover>
|
|
193
206
|
)
|
|
194
207
|
|
|
195
208
|
} else {
|
|
196
209
|
return (
|
|
197
|
-
<
|
|
210
|
+
<MantineMultiSelect
|
|
198
211
|
{...props}
|
|
199
212
|
|
|
200
|
-
data={
|
|
213
|
+
data={[
|
|
214
|
+
{
|
|
215
|
+
group: 'Search results',
|
|
216
|
+
// Exclude the choices that are already selected
|
|
217
|
+
items: choices.filter(c => !props.value.includes(c.value))
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
group: 'Selected',
|
|
221
|
+
items: currentArray
|
|
222
|
+
}
|
|
223
|
+
]}
|
|
201
224
|
nothingFound={search.loading ? 'Loading...' : props || 'No options found'}
|
|
202
225
|
comboboxProps={{
|
|
203
226
|
withArrow: false
|
|
204
227
|
}}
|
|
205
228
|
|
|
229
|
+
searchable={enableSearch}
|
|
206
230
|
clearable={!required}
|
|
207
231
|
required={required}
|
|
208
232
|
allowDeselect={!required}
|
|
233
|
+
checkIconPosition="right"
|
|
209
234
|
|
|
235
|
+
dropdownOpened={opened}
|
|
210
236
|
onDropdownOpen={() => setOpened(true)}
|
|
211
237
|
onDropdownClose={() => setOpened(false)}
|
|
212
238
|
onSearchChange={(keywords) => setSearch(s => ({ ...s, keywords }))}
|
|
@@ -34,6 +34,7 @@ export type Props<TRow> = {
|
|
|
34
34
|
// Interactions
|
|
35
35
|
sort?: TSortOptions,
|
|
36
36
|
onSort?: (columnId: string | null, order: TSortOptions["order"]) => void,
|
|
37
|
+
onCellClick?: (row: TRow) => void,
|
|
37
38
|
|
|
38
39
|
selection?: [TRow[], React.SetStateAction<TRow[]>],
|
|
39
40
|
maxSelection?: number,
|
|
@@ -58,6 +59,7 @@ type TSortOptions = {
|
|
|
58
59
|
export default function Liste<TRow extends TDonneeInconnue>({
|
|
59
60
|
stickyHeader, onSort, sort: sorted,
|
|
60
61
|
data: rows, setData, empty,
|
|
62
|
+
onCellClick,
|
|
61
63
|
selection: selectionState, maxSelection,
|
|
62
64
|
columns, ...props
|
|
63
65
|
}: Props<TRow>) {
|
|
@@ -136,32 +138,41 @@ export default function Liste<TRow extends TDonneeInconnue>({
|
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
if (iDonnee === 0) {
|
|
142
|
+
|
|
143
|
+
const headerProps = { className: '', ...cellProps };
|
|
144
|
+
const isCurrentlySorted = sort && sorted && sorted.id === sort.id;
|
|
145
|
+
const isSortable = sort && onSort;
|
|
146
|
+
if (isSortable) {
|
|
147
|
+
headerProps.className += ' clickable';
|
|
148
|
+
headerProps.onClick = () => {
|
|
149
|
+
if (isCurrentlySorted)
|
|
150
|
+
onSort(null, sort.order);
|
|
151
|
+
else
|
|
152
|
+
onSort(sort.id, sort.order);
|
|
153
|
+
}
|
|
148
154
|
}
|
|
155
|
+
|
|
156
|
+
renduColonnes.push(
|
|
157
|
+
<th {...headerProps}>
|
|
158
|
+
<div class="row sp-btw">
|
|
159
|
+
|
|
160
|
+
{isSortable ? (
|
|
161
|
+
<a>{label}</a>
|
|
162
|
+
) : label}
|
|
163
|
+
|
|
164
|
+
{isCurrentlySorted && (
|
|
165
|
+
<i src={sort.order === "asc" ? "caret-up" : "caret-down"} />
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
</th>
|
|
169
|
+
);
|
|
149
170
|
}
|
|
150
171
|
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
{isSortable ? (
|
|
156
|
-
<a>{label}</a>
|
|
157
|
-
) : label}
|
|
158
|
-
|
|
159
|
-
{isCurrentlySorted && (
|
|
160
|
-
<i src={sort.order === "asc" ? "caret-up" : "caret-down"} />
|
|
161
|
-
)}
|
|
162
|
-
</div>
|
|
163
|
-
</th>
|
|
164
|
-
);
|
|
172
|
+
if (onCellClick) {
|
|
173
|
+
cellProps.onClick = () => onCellClick(row);
|
|
174
|
+
classe += ' clickable';
|
|
175
|
+
}
|
|
165
176
|
|
|
166
177
|
let render: ComponentChild;
|
|
167
178
|
if (Array.isArray(cell)) {
|
|
@@ -113,7 +113,7 @@ export default (props: Props) => {
|
|
|
113
113
|
renderedContent = React.cloneElement(
|
|
114
114
|
content,
|
|
115
115
|
{
|
|
116
|
-
className: 'card popover
|
|
116
|
+
className: 'card popover'
|
|
117
117
|
+ (position ? ' pos_' + position.cote : '')
|
|
118
118
|
+ ' ' + (content.props.className || ''),
|
|
119
119
|
|
|
@@ -31,7 +31,7 @@ export {
|
|
|
31
31
|
UnstyledButton,
|
|
32
32
|
VisuallyHidden,
|
|
33
33
|
Paper,
|
|
34
|
-
|
|
34
|
+
Popover as PopoverMantine,
|
|
35
35
|
ActionIcon,
|
|
36
36
|
CloseButton,
|
|
37
37
|
Group,
|
|
@@ -103,9 +103,10 @@ export {
|
|
|
103
103
|
Progress,
|
|
104
104
|
Radio,
|
|
105
105
|
Rating,
|
|
106
|
+
RangeSlider,
|
|
106
107
|
RingProgress,
|
|
107
108
|
SegmentedControl,
|
|
108
|
-
|
|
109
|
+
Select as SelectMantine,
|
|
109
110
|
SemiCircleProgress,
|
|
110
111
|
SimpleGrid,
|
|
111
112
|
Skeleton,
|
|
@@ -57,7 +57,7 @@ const sizeAdapter = {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export function useMantineInput<TProps extends __BaseInputProps & InputBaseProps<any>, TValue>({
|
|
60
|
-
title, wrapper, hint, errors, icon, iconR, minimal, onChange, value, ...props
|
|
60
|
+
title, wrapper, hint, errors, icon, iconR, prefix, suffix, minimal, onChange, value, ...props
|
|
61
61
|
}: InputBaseProps<TValue> & TProps): [
|
|
62
62
|
InputBaseProps<any>,
|
|
63
63
|
TProps
|
|
@@ -74,11 +74,11 @@ export function useMantineInput<TProps extends __BaseInputProps & InputBaseProps
|
|
|
74
74
|
props.description = hint;
|
|
75
75
|
}
|
|
76
76
|
// Prefix
|
|
77
|
-
if (props.leftSection === undefined
|
|
78
|
-
props.leftSection = <i src={icon}
|
|
77
|
+
if (props.leftSection === undefined)
|
|
78
|
+
props.leftSection = icon !== undefined ? <i src={icon} /> : prefix;
|
|
79
79
|
// Suffix
|
|
80
|
-
if (props.rightSection === undefined
|
|
81
|
-
props.rightSection = <i src={iconR}
|
|
80
|
+
if (props.rightSection === undefined)
|
|
81
|
+
props.rightSection = iconR !== undefined ? <i src={iconR} /> : suffix;
|
|
82
82
|
|
|
83
83
|
// Errors
|
|
84
84
|
if (errors?.length)
|
package/client/index.ts
CHANGED
|
@@ -191,7 +191,6 @@ export default ({ service: clientRouter, loaderComponent }: TProps) => {
|
|
|
191
191
|
// Reset scroll
|
|
192
192
|
window.scrollTo(0, 0);
|
|
193
193
|
// Should be called AFTER rendering the page (so after the state change)
|
|
194
|
-
console.log("CHANGE PAGE", currentPage);
|
|
195
194
|
currentPage?.updateClient();
|
|
196
195
|
// Scroll to the selected content via url hash
|
|
197
196
|
restoreScroll(currentPage);
|
|
@@ -55,7 +55,6 @@ const LogPrefix = '[router]'
|
|
|
55
55
|
|
|
56
56
|
// Client router can handle Client requests AND Server requests (for pages only)
|
|
57
57
|
export type { default as ClientResponse, TRouterContext } from "./response";
|
|
58
|
-
export { Link } from './components/Link';
|
|
59
58
|
|
|
60
59
|
export type Router = ClientRouter | ServerRouter;
|
|
61
60
|
|
|
@@ -128,7 +127,7 @@ type THookName = 'page.change' | 'page.changed' | 'page.rendered'
|
|
|
128
127
|
|
|
129
128
|
type Config<TAdditionnalContext extends {} = {}> = {
|
|
130
129
|
preload: string[], // List of globs
|
|
131
|
-
context: (context:
|
|
130
|
+
context: (context: {}, router: ClientRouter) => TAdditionnalContext,
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
/*----------------------------------
|
|
@@ -21,7 +21,7 @@ import FileToUpload from '@client/components/File/FileToUpload';
|
|
|
21
21
|
- TYPES
|
|
22
22
|
----------------------------------*/
|
|
23
23
|
|
|
24
|
-
const debug =
|
|
24
|
+
const debug = false;
|
|
25
25
|
|
|
26
26
|
export type Config = {
|
|
27
27
|
|
|
@@ -86,7 +86,7 @@ export default class ApiClient implements ApiClientService {
|
|
|
86
86
|
else if (typeof ids === 'string')
|
|
87
87
|
ids = [ids];
|
|
88
88
|
|
|
89
|
-
console.log("[api] Reload data", ids, params, page.fetchers);
|
|
89
|
+
debug && console.log("[api] Reload data", ids, params, page.fetchers);
|
|
90
90
|
|
|
91
91
|
for (const id of ids) {
|
|
92
92
|
|
|
@@ -97,7 +97,7 @@ export default class ApiClient implements ApiClientService {
|
|
|
97
97
|
if (params !== undefined)
|
|
98
98
|
fetcher.data = { ...(fetcher.data || {}), ...params };
|
|
99
99
|
|
|
100
|
-
console.log("[api][reload]", id, fetcher.method, fetcher.path, fetcher.data);
|
|
100
|
+
debug && console.log("[api][reload]", id, fetcher.method, fetcher.path, fetcher.data);
|
|
101
101
|
|
|
102
102
|
this.fetchAsync(fetcher.method, fetcher.path, fetcher.data).then((data) => {
|
|
103
103
|
|
|
@@ -227,7 +227,7 @@ export default class ApiClient implements ApiClientService {
|
|
|
227
227
|
|
|
228
228
|
} else if (options.encoding === 'multipart') {
|
|
229
229
|
|
|
230
|
-
console.log("[api] Multipart request", data);
|
|
230
|
+
debug && console.log("[api] Multipart request", data);
|
|
231
231
|
// Browser will automatically choose the right headers
|
|
232
232
|
config.body = toMultipart(data);
|
|
233
233
|
|
package/common/errors/index.tsx
CHANGED
|
@@ -187,6 +187,12 @@ export class NotFound extends CoreError {
|
|
|
187
187
|
public static msgDefaut = "The resource you asked for was not found.";
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
export class Gone extends CoreError {
|
|
191
|
+
public http = 410;
|
|
192
|
+
public title = "Gone";
|
|
193
|
+
public static msgDefaut = "The resource you asked for has been removed.";
|
|
194
|
+
}
|
|
195
|
+
|
|
190
196
|
export class RateLimit extends CoreError {
|
|
191
197
|
public http = 429;
|
|
192
198
|
public title = "You're going too fast";
|
package/common/router/index.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
- DEPENDANCES
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
|
+
// Npm
|
|
6
|
+
import zod from 'zod';
|
|
7
|
+
|
|
5
8
|
// types
|
|
6
9
|
import type {
|
|
7
10
|
default as ClientRouter,
|
|
@@ -42,6 +45,7 @@ export type TRoute<RouterContext extends TClientOrServerContextForPage = TClient
|
|
|
42
45
|
path: string,
|
|
43
46
|
|
|
44
47
|
// Execute
|
|
48
|
+
schema?: zod.ZodSchema,
|
|
45
49
|
controller: TRouteController<RouterContext>,
|
|
46
50
|
options: TRouteOptions
|
|
47
51
|
} & (
|
|
@@ -67,7 +71,8 @@ export type TErrorRoute<RouterContext extends TClientOrServerContextForPage = TC
|
|
|
67
71
|
export type TAnyRoute<RouterContext extends TClientOrServerContextForPage = TClientOrServerContextForPage> =
|
|
68
72
|
TRoute<RouterContext> | TErrorRoute<RouterContext>
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
// ClientRouterContext already includes server context
|
|
75
|
+
export type TClientOrServerContext = ClientRouterContext;// | ServerRouterContext;
|
|
71
76
|
|
|
72
77
|
export type TClientOrServerContextForPage = With<TClientOrServerContext, 'page'>
|
|
73
78
|
|
|
@@ -90,10 +95,11 @@ export type TRouteOptions = {
|
|
|
90
95
|
accept?: string,
|
|
91
96
|
raw?: boolean, // true to return raw data
|
|
92
97
|
auth?: TUserRole | boolean,
|
|
93
|
-
|
|
98
|
+
redirectLogged?: string, // Redirect to this route if auth: false and user is logged
|
|
94
99
|
|
|
95
100
|
// Rendering
|
|
96
101
|
static?: boolean,
|
|
102
|
+
canonicalParams?: string[], // For SEO + unique ID for static cache
|
|
97
103
|
layout?: false | string, // The nale of the layout
|
|
98
104
|
|
|
99
105
|
// To cleanup
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "Convenient TypeScript framework designed for Performance and Productivity.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -73,7 +73,6 @@
|
|
|
73
73
|
"prettier": "^3.3.3",
|
|
74
74
|
"react-scrollbars-custom": "^4.0.27",
|
|
75
75
|
"react-slider": "^2.0.1",
|
|
76
|
-
"react-textarea-autosize": "^8.3.3",
|
|
77
76
|
"regenerator-runtime": "^0.13.9",
|
|
78
77
|
"request": "^2.88.2",
|
|
79
78
|
"slugify": "^1.6.6",
|
package/server/app/commands.ts
CHANGED
|
@@ -48,31 +48,12 @@ export type Services = {
|
|
|
48
48
|
/*----------------------------------
|
|
49
49
|
- SERVICE
|
|
50
50
|
----------------------------------*/
|
|
51
|
-
export default class CommandsManager extends Service<Config, Hooks, Application
|
|
51
|
+
export default class CommandsManager extends Service<Config, Hooks, Application> {
|
|
52
52
|
|
|
53
53
|
public priority = 2 as 2;
|
|
54
54
|
|
|
55
55
|
public commandsIndex: CommandsList = {}
|
|
56
|
-
|
|
57
|
-
/*----------------------------------
|
|
58
|
-
- LIFECYCLE
|
|
59
|
-
----------------------------------*/
|
|
60
|
-
|
|
61
|
-
protected async start() {
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
protected async ready() {
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
protected async shutdown() {
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/*----------------------------------
|
|
74
|
-
- DEFINITIONS
|
|
75
|
-
----------------------------------*/
|
|
56
|
+
|
|
76
57
|
public command<TArgs extends any[]>(
|
|
77
58
|
...args: (
|
|
78
59
|
[name: string, description: string, childrens: Command[]]
|