5htp-core 0.2.1 → 0.2.2-1
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/package.json +10 -3
- package/src/client/app/index.ts +2 -2
- package/src/client/assets/css/components/card.less +0 -3
- package/src/client/assets/css/components/other.less +2 -4
- package/src/client/assets/css/components/table.less +1 -2
- package/src/client/assets/css/components.less +4 -0
- package/src/client/assets/css/core.less +0 -1
- package/src/client/assets/css/theme.less +2 -2
- package/src/client/assets/css/utils/medias.less +21 -0
- package/src/client/components/Card/index.tsx +8 -5
- package/src/client/components/Dialog/index.less +3 -3
- package/src/client/components/Row/index.less +2 -0
- package/src/client/components/Row/index.tsx +44 -10
- package/src/client/components/Video/index.less +39 -0
- package/src/client/components/Video/index.tsx +69 -0
- package/src/client/components/containers/Popover/index.tsx +2 -2
- package/src/client/components/data/Time.tsx +1 -1
- package/src/client/components/data/progressbar/circular/index.tsx +1 -1
- package/src/client/components/index.ts +24 -8
- package/src/client/components/input/BaseV2/index.tsx +0 -1
- package/src/client/components/{input/BaseV2/index.less → inputv3/base.less} +1 -1
- package/src/client/components/inputv3/base.tsx +73 -0
- package/src/client/components/{input/UploadImage → inputv3/file}/Bouton.tsx +0 -0
- package/src/client/components/inputv3/file/FileToUpload.ts +34 -0
- package/src/client/components/inputv3/file/index.less +59 -0
- package/src/client/components/inputv3/file/index.tsx +157 -0
- package/src/client/components/{input → inputv3/string}/index.tsx +41 -27
- package/src/client/pages/bug.tsx +3 -4
- package/src/client/services/router/index.tsx +0 -1
- package/src/client/services/router/request/api.ts +20 -12
- package/src/client/services/router/request/multipart.ts +27 -0
- package/src/common/data/chaines/greetings.ts +1 -1
- package/src/common/data/dates.ts +1 -1
- package/src/common/data/input/validate.ts +0 -9
- package/src/common/data/markdown.ts +1 -1
- package/src/common/errors/index.ts +16 -12
- package/src/common/router/request/api.ts +11 -3
- package/src/common/validation/schema.ts +21 -20
- package/src/common/validation/validators.ts +3 -6
- package/src/server/app/commands.ts +149 -0
- package/src/server/app/index.ts +25 -5
- package/src/server/app/service.ts +4 -0
- package/src/server/services/cache/commands.ts +41 -0
- package/src/server/services/cache/index.ts +102 -34
- package/src/server/services/console/index.ts +1 -1
- package/src/server/services/database/connection.ts +38 -22
- package/src/server/services/database/datatypes.ts +51 -12
- package/src/server/services/database/index.ts +133 -40
- package/src/server/services/database/metas.ts +63 -37
- package/src/server/services/database/repository.ts +26 -0
- package/src/server/services/email/index.ts +102 -42
- package/src/server/services/fetch/index.ts +110 -0
- package/src/server/services/router/http/multipart.ts +70 -41
- package/src/server/services/router/index.ts +35 -4
- package/src/server/services/router/request/index.ts +8 -6
- package/src/server/services/schema/index.ts +4 -11
- package/src/server/services/schema/request.ts +16 -7
- package/src/server/services/schema/router.ts +6 -2
- package/src/server/{services_old → services/security/encrypt}/aes.ts +33 -14
- package/src/server/services/users/index.ts +3 -3
- package/src/server/services/users/router/index.ts +0 -2
- package/src/types/global/utils.d.ts +11 -1
- package/tsconfig.common.json +3 -0
- package/src/client/components/input/Textarea.tsx +0 -57
- package/src/client/components/input/Upload.tsx +0 -5
- package/src/client/components/input/UploadImage/index.less +0 -93
- package/src/client/components/input/UploadImage/index.tsx +0 -220
- package/src/common/data/file.ts +0 -25
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.input.upload {
|
|
2
|
+
|
|
3
|
+
position: relative;
|
|
4
|
+
overflow: hidden;
|
|
5
|
+
|
|
6
|
+
border-radius: @radius;
|
|
7
|
+
border: dashed 3px var(--cLine);
|
|
8
|
+
background: @cBgPage + #090909;
|
|
9
|
+
|
|
10
|
+
padding: @spacing;
|
|
11
|
+
width: 100%;
|
|
12
|
+
min-height: 200px;
|
|
13
|
+
height: 14.15rem;
|
|
14
|
+
|
|
15
|
+
.preview,
|
|
16
|
+
.indication,
|
|
17
|
+
input[type="file"] {
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 0; left: 0;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
input[type="file"] {
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
opacity: 0;
|
|
27
|
+
z-index: 2;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.indication {
|
|
31
|
+
transition: all .1s linear;
|
|
32
|
+
background: inherit;
|
|
33
|
+
opacity: 0;
|
|
34
|
+
z-index: 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.actions {
|
|
38
|
+
position: absolute;
|
|
39
|
+
right: @spacing;
|
|
40
|
+
bottom: @spacing;
|
|
41
|
+
z-index: 3;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// When no file selected, or when hover
|
|
45
|
+
.indication:first-child,
|
|
46
|
+
&:hover .indication {
|
|
47
|
+
opacity: 0.9;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Image preview
|
|
51
|
+
> img {
|
|
52
|
+
border-radius: @radius;
|
|
53
|
+
max-width: 100%;
|
|
54
|
+
height: 100%;
|
|
55
|
+
|
|
56
|
+
display: block;
|
|
57
|
+
margin: 0 auto;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
// Npm
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { ComponentChild } from 'preact';
|
|
7
|
+
|
|
8
|
+
// Composants généraux
|
|
9
|
+
import Bouton from '@client/components/button';
|
|
10
|
+
|
|
11
|
+
// Libs
|
|
12
|
+
import useContext from '@/client/context';
|
|
13
|
+
|
|
14
|
+
// specific
|
|
15
|
+
import FileToUpload from './FileToUpload';
|
|
16
|
+
|
|
17
|
+
// Ressources
|
|
18
|
+
import './index.less';
|
|
19
|
+
|
|
20
|
+
/*----------------------------------
|
|
21
|
+
- OUTILS
|
|
22
|
+
----------------------------------*/
|
|
23
|
+
export { default as FileToUpload } from './FileToUpload';
|
|
24
|
+
|
|
25
|
+
export const createImagePreview = (file: Blob) => new Promise((resolve, reject) => {
|
|
26
|
+
|
|
27
|
+
// Conversion de l'image en base24 pour l'afficher
|
|
28
|
+
const reader = new FileReader();
|
|
29
|
+
let imageBase64: string | null = null;
|
|
30
|
+
reader.addEventListener('load', () => {
|
|
31
|
+
|
|
32
|
+
var enc = new TextDecoder("utf-8");
|
|
33
|
+
|
|
34
|
+
// reader.result pout être une chaine ou un arraybuffer
|
|
35
|
+
imageBase64 = reader.result instanceof ArrayBuffer
|
|
36
|
+
? enc.decode(reader.result) // ArrayBuffer => Chaine
|
|
37
|
+
: reader.result;
|
|
38
|
+
|
|
39
|
+
// Affichage
|
|
40
|
+
resolve(imageBase64);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
reader.readAsDataURL(file);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Instanciate FileToUpload from browser side File
|
|
47
|
+
const normalizeFile = (file: File) => new FileToUpload({
|
|
48
|
+
name: file.name,
|
|
49
|
+
type: file.type,
|
|
50
|
+
size: file.size,
|
|
51
|
+
data: file,
|
|
52
|
+
//original: file
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/*----------------------------------
|
|
56
|
+
- TYPES
|
|
57
|
+
----------------------------------*/
|
|
58
|
+
|
|
59
|
+
export type Props = {
|
|
60
|
+
|
|
61
|
+
// Input
|
|
62
|
+
title: ComponentChild,
|
|
63
|
+
value?: FileToUpload,
|
|
64
|
+
|
|
65
|
+
// Display
|
|
66
|
+
emptyText?: ComponentChild,
|
|
67
|
+
className?: string,
|
|
68
|
+
previewUrl?: string,
|
|
69
|
+
|
|
70
|
+
// Actions
|
|
71
|
+
onChange: (file: FileToUpload | undefined) => void
|
|
72
|
+
remove?: () => Promise<void>,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*----------------------------------
|
|
76
|
+
- COMPOSANT
|
|
77
|
+
----------------------------------*/
|
|
78
|
+
export default ({
|
|
79
|
+
|
|
80
|
+
// Input
|
|
81
|
+
title,
|
|
82
|
+
value: file,
|
|
83
|
+
className,
|
|
84
|
+
|
|
85
|
+
// Display
|
|
86
|
+
emptyText = 'Click here to select a File',
|
|
87
|
+
previewUrl: previewUrlInit,
|
|
88
|
+
|
|
89
|
+
// Actions
|
|
90
|
+
onChange
|
|
91
|
+
}: Props) => {
|
|
92
|
+
|
|
93
|
+
/*----------------------------------
|
|
94
|
+
- INITIALIZE
|
|
95
|
+
----------------------------------*/
|
|
96
|
+
|
|
97
|
+
const [previewUrl, setPreviewUrl] = React.useState<string | undefined>(previewUrlInit);
|
|
98
|
+
|
|
99
|
+
className = 'input upload ' + (className === undefined ? '' : ' ' + className)
|
|
100
|
+
|
|
101
|
+
/*----------------------------------
|
|
102
|
+
- ACTIONS
|
|
103
|
+
----------------------------------*/
|
|
104
|
+
|
|
105
|
+
const selectFile = (fileSelectEvent: any) => {
|
|
106
|
+
const selectedfile = fileSelectEvent.target.files[0] as File;
|
|
107
|
+
if (selectedfile) {
|
|
108
|
+
|
|
109
|
+
const fileToUpload = normalizeFile(selectedfile);
|
|
110
|
+
|
|
111
|
+
onChange(fileToUpload);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
React.useEffect(() => {
|
|
116
|
+
|
|
117
|
+
// Image = decode & display preview
|
|
118
|
+
if (file !== undefined && file.type.startsWith('image/'))
|
|
119
|
+
createImagePreview(file.data).then(setPreviewUrl);
|
|
120
|
+
else
|
|
121
|
+
setPreviewUrl(undefined);
|
|
122
|
+
|
|
123
|
+
}, [file]);
|
|
124
|
+
|
|
125
|
+
/*----------------------------------
|
|
126
|
+
- RENDER
|
|
127
|
+
----------------------------------*/
|
|
128
|
+
return <>
|
|
129
|
+
<label>{title}</label>
|
|
130
|
+
|
|
131
|
+
<div class={className}>
|
|
132
|
+
|
|
133
|
+
{file && <>
|
|
134
|
+
<div class="preview">
|
|
135
|
+
|
|
136
|
+
{previewUrl ? (
|
|
137
|
+
<img src={previewUrl} />
|
|
138
|
+
) : file ? <>
|
|
139
|
+
<strong>{file.name}</strong>
|
|
140
|
+
</> : null}
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="row actions sp-05">
|
|
144
|
+
|
|
145
|
+
<Bouton class='fg error' icon="trash" shape="pill" size="s"
|
|
146
|
+
async onClick={() => onChange(undefined)} />
|
|
147
|
+
</div>
|
|
148
|
+
</>}
|
|
149
|
+
|
|
150
|
+
<div class="indication col al-center">
|
|
151
|
+
{emptyText}
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<input type="file" onChange={selectFile} />
|
|
155
|
+
</div>
|
|
156
|
+
</>
|
|
157
|
+
}
|
|
@@ -8,41 +8,43 @@ import { ComponentChild, JSX } from 'preact';
|
|
|
8
8
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
9
9
|
|
|
10
10
|
// Core libs
|
|
11
|
-
import { useInput, InputBaseProps } from '
|
|
11
|
+
import { useInput, InputBaseProps } from '../base';
|
|
12
12
|
|
|
13
13
|
/*----------------------------------
|
|
14
14
|
- TYPES
|
|
15
15
|
----------------------------------*/
|
|
16
16
|
export type Props = {
|
|
17
17
|
|
|
18
|
-
type?: 'email' | 'password',
|
|
19
|
-
multiline?: boolean, // true = textarea
|
|
20
|
-
onPressEnter?: (value: string) => void,
|
|
21
|
-
|
|
22
18
|
// Decoration
|
|
23
19
|
icon?: string,
|
|
24
|
-
prefix?:
|
|
25
|
-
suffix?:
|
|
20
|
+
prefix?: React.VNode,
|
|
21
|
+
suffix?: React.VNode,
|
|
26
22
|
iconR?: string,
|
|
27
23
|
|
|
24
|
+
// Behavior
|
|
25
|
+
type?: 'email' | 'password' | 'longtext',
|
|
26
|
+
choice?: string[] | ((input: string) => Promise<string[]>),
|
|
27
|
+
multiple?: boolean,
|
|
28
|
+
|
|
29
|
+
// Actions
|
|
30
|
+
onPressEnter?: (value: string) => void,
|
|
28
31
|
inputRef?: React.Ref<HTMLInputElement>
|
|
29
|
-
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/*----------------------------------
|
|
33
35
|
- COMPOSANT
|
|
34
36
|
----------------------------------*/
|
|
35
37
|
export default ({
|
|
38
|
+
// Behavoir
|
|
36
39
|
type,
|
|
37
40
|
icon, prefix, suffix, iconR,
|
|
38
|
-
multiline,
|
|
39
41
|
onPressEnter,
|
|
40
42
|
inputRef,
|
|
41
43
|
...props
|
|
42
44
|
}: Props & InputBaseProps<string> & Omit<JSX.HTMLAttributes<HTMLInputElement>, 'onChange'>) => {
|
|
43
45
|
|
|
44
46
|
/*----------------------------------
|
|
45
|
-
-
|
|
47
|
+
- INIT
|
|
46
48
|
----------------------------------*/
|
|
47
49
|
|
|
48
50
|
const [{ value, focus, fieldProps }, setValue, commitValue, setState] = useInput(props, '');
|
|
@@ -61,26 +63,37 @@ export default ({
|
|
|
61
63
|
const refInput = inputRef || React.useRef<HTMLInputElement>();
|
|
62
64
|
|
|
63
65
|
/*----------------------------------
|
|
64
|
-
-
|
|
66
|
+
- ATTRIBUTES
|
|
65
67
|
----------------------------------*/
|
|
66
68
|
|
|
69
|
+
let className: string = 'input text';
|
|
70
|
+
|
|
67
71
|
// Auto prefix
|
|
68
|
-
if (prefix === undefined)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
if (prefix === undefined && icon !== undefined)
|
|
73
|
+
prefix = <i src={icon} />
|
|
74
|
+
|
|
75
|
+
// Type
|
|
76
|
+
let Tag = 'input';
|
|
77
|
+
if (type === 'password') {
|
|
78
|
+
|
|
79
|
+
prefix = prefix || <i src="key" />;
|
|
80
|
+
fieldProps.type = 'password';
|
|
81
|
+
|
|
82
|
+
} else if (type === 'email') {
|
|
83
|
+
|
|
84
|
+
prefix = prefix || <i src="at" />;
|
|
85
|
+
fieldProps.type = 'email';
|
|
86
|
+
|
|
87
|
+
} else if (type === 'longtext') {
|
|
88
|
+
|
|
89
|
+
prefix = prefix || <i src="text" />;
|
|
90
|
+
Tag = TextareaAutosize;
|
|
76
91
|
|
|
77
|
-
// Auto suffix
|
|
78
|
-
if (suffix === undefined) {
|
|
79
|
-
if (iconR !== undefined)
|
|
80
|
-
suffix = <i src={iconR} />
|
|
81
92
|
}
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
// Auto suffix
|
|
95
|
+
if (suffix === undefined && iconR !== undefined)
|
|
96
|
+
suffix = <i src={iconR} />
|
|
84
97
|
|
|
85
98
|
// When no value, show the lable as a placeholder
|
|
86
99
|
if (value === '')
|
|
@@ -91,8 +104,9 @@ export default ({
|
|
|
91
104
|
if (props.className !== undefined)
|
|
92
105
|
className += ' ' + props.className;
|
|
93
106
|
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
/*----------------------------------
|
|
108
|
+
- RENDER
|
|
109
|
+
----------------------------------*/
|
|
96
110
|
return (
|
|
97
111
|
<div class={className} onClick={() => refInput.current?.focus()}>
|
|
98
112
|
|
|
@@ -101,8 +115,8 @@ export default ({
|
|
|
101
115
|
<div class="contValue">
|
|
102
116
|
|
|
103
117
|
<Tag {...fieldProps}
|
|
118
|
+
// @ts-ignore: Property 'ref' does not exist on type 'IntrinsicAttributes'
|
|
104
119
|
ref={refInput}
|
|
105
|
-
type={type}
|
|
106
120
|
value={value}
|
|
107
121
|
|
|
108
122
|
onFocus={() => setState({ focus: true })}
|
package/src/client/pages/bug.tsx
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
// Core components
|
|
8
|
-
import Button from '@client/components
|
|
9
|
-
import Textarea from '@client/components/input/Textarea';
|
|
8
|
+
import { Button, String } from '@client/components';
|
|
10
9
|
import Card, { Props as CardProps } from '@client/components/Dialog/card';
|
|
11
10
|
|
|
12
11
|
// Core libs
|
|
@@ -50,11 +49,11 @@ export default ({ ...self }: {} & CardProps) => {
|
|
|
50
49
|
|
|
51
50
|
<p>What's the problem ?</p>
|
|
52
51
|
|
|
53
|
-
<
|
|
52
|
+
<String type="longtext" title="Description of the problem" value={observation} onChange={setObservation} />
|
|
54
53
|
|
|
55
54
|
<p>What did you do just before the problem occurs ?</p>
|
|
56
55
|
|
|
57
|
-
<
|
|
56
|
+
<String type="longtext" title="How the problem occured ?" value={before} onChange={setBefore} />
|
|
58
57
|
|
|
59
58
|
</Card>
|
|
60
59
|
)
|
|
@@ -14,7 +14,6 @@ import type {
|
|
|
14
14
|
} from '@server/services/router';
|
|
15
15
|
import type { TBasicSSrData } from '@server/services/router/response';
|
|
16
16
|
|
|
17
|
-
import { Erreur } from '@common/errors';
|
|
18
17
|
import BaseRouter, {
|
|
19
18
|
defaultOptions, TRoute, TErrorRoute, TClientOrServerContext, TRouteModule
|
|
20
19
|
} from '@common/router'
|
|
@@ -8,11 +8,14 @@ import axios, { AxiosResponse, AxiosError, AxiosRequestConfig } from 'axios';
|
|
|
8
8
|
// Core
|
|
9
9
|
import type { TApiResponseData } from '@server/services/router';
|
|
10
10
|
import ApiClientService, {
|
|
11
|
+
TPostData,
|
|
11
12
|
TApiFetchOptions, TFetcherList, TFetcherArgs, TFetcher
|
|
12
13
|
} from '@common/router/request/api';
|
|
13
14
|
import { instancierViaCode, NetworkError } from '@common/errors';
|
|
14
15
|
import type ClientApplication from '@client/app';
|
|
15
16
|
|
|
17
|
+
import { toMultipart } from './multipart';
|
|
18
|
+
|
|
16
19
|
// Specific
|
|
17
20
|
import type { default as Router, Request } from '..';
|
|
18
21
|
|
|
@@ -34,7 +37,8 @@ export default class ApiClient implements ApiClientService {
|
|
|
34
37
|
// APO Client needs to know the current request so we can monitor which api request is made from which page
|
|
35
38
|
public constructor(
|
|
36
39
|
public app: ClientApplication,
|
|
37
|
-
public request: Request
|
|
40
|
+
public request: Request,
|
|
41
|
+
public router = request.router
|
|
38
42
|
) {
|
|
39
43
|
|
|
40
44
|
}
|
|
@@ -42,16 +46,16 @@ export default class ApiClient implements ApiClientService {
|
|
|
42
46
|
/*----------------------------------
|
|
43
47
|
- HIGH LEVEL
|
|
44
48
|
----------------------------------*/
|
|
45
|
-
public get = <TData extends unknown = unknown>(path: string, data?:
|
|
49
|
+
public get = <TData extends unknown = unknown>(path: string, data?: TPostData, opts?: TApiFetchOptions) =>
|
|
46
50
|
this.createFetcher<TData>('GET', path, data, opts);
|
|
47
51
|
|
|
48
|
-
public post = <TData extends unknown = unknown>(path: string, data?:
|
|
52
|
+
public post = <TData extends unknown = unknown>(path: string, data?: TPostData, opts?: TApiFetchOptions) =>
|
|
49
53
|
this.createFetcher<TData>('POST', path, data, opts);
|
|
50
54
|
|
|
51
|
-
public put = <TData extends unknown = unknown>(path: string, data?:
|
|
55
|
+
public put = <TData extends unknown = unknown>(path: string, data?: TPostData, opts?: TApiFetchOptions) =>
|
|
52
56
|
this.createFetcher<TData>('PUT', path, data, opts);
|
|
53
57
|
|
|
54
|
-
public delete = <TData extends unknown = unknown>(path: string, data?:
|
|
58
|
+
public delete = <TData extends unknown = unknown>(path: string, data?: TPostData, opts?: TApiFetchOptions) =>
|
|
55
59
|
this.createFetcher<TData>('DELETE', path, data, opts);
|
|
56
60
|
|
|
57
61
|
|
|
@@ -108,9 +112,9 @@ export default class ApiClient implements ApiClientService {
|
|
|
108
112
|
method, path, data, options,
|
|
109
113
|
// For async calls: api.post(...).then((data) => ...)
|
|
110
114
|
then: (callback: (data: any) => void) => this.fetchAsync<TData>(...args).then(callback),
|
|
111
|
-
catch: (callback: (data: any) => void) => this.fetchAsync(...args).catch(callback),
|
|
112
|
-
finally: (callback: () => void) => this.fetchAsync(...args).finally(callback),
|
|
113
|
-
run: () => this.fetchAsync(...args)
|
|
115
|
+
catch: (callback: (data: any) => void) => this.fetchAsync<TData>(...args).catch(callback),
|
|
116
|
+
finally: (callback: () => void) => this.fetchAsync<TData>(...args).finally(callback),
|
|
117
|
+
run: () => this.fetchAsync<TData>(...args)
|
|
114
118
|
};
|
|
115
119
|
}
|
|
116
120
|
|
|
@@ -183,12 +187,16 @@ export default class ApiClient implements ApiClientService {
|
|
|
183
187
|
};
|
|
184
188
|
|
|
185
189
|
if (data) {
|
|
186
|
-
|
|
190
|
+
// URL params
|
|
191
|
+
if (method === "GET") {
|
|
187
192
|
config.params = data;
|
|
188
|
-
|
|
193
|
+
// Post form data
|
|
194
|
+
} else if (options?.encoding === 'multipart') {
|
|
195
|
+
config.headers["Content-Type"] = 'multipart/form-data';
|
|
196
|
+
config.data = toMultipart(data);
|
|
197
|
+
// Post JSON
|
|
198
|
+
} else {
|
|
189
199
|
config.data = data;
|
|
190
|
-
if (data instanceof FormData)
|
|
191
|
-
config.headers["Content-Type"] = 'multipart/form-data';
|
|
192
200
|
}
|
|
193
201
|
}
|
|
194
202
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import { TPostData } from '@common/router/request/api';
|
|
7
|
+
import { FileToUpload } from '@client/components/inputv3/file';
|
|
8
|
+
|
|
9
|
+
/*----------------------------------
|
|
10
|
+
- TYPES
|
|
11
|
+
----------------------------------*/
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- UTILS
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
export const toMultipart = (postData: TPostData) => {
|
|
17
|
+
|
|
18
|
+
const formData = new FormData();
|
|
19
|
+
for (const key in postData) {
|
|
20
|
+
let data = postData[key];
|
|
21
|
+
if (typeof data === 'object' && (data instanceof FileToUpload))
|
|
22
|
+
data = data.data;
|
|
23
|
+
formData.append(key, data);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return formData;
|
|
27
|
+
}
|
|
@@ -24,4 +24,4 @@ const howareyou = () => {
|
|
|
24
24
|
return howareyou_list[ index ];
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export default (username: string) => hello() + ', ' + username + '. ' + howareyou();
|
|
27
|
+
export default (username: string, withHowAreYou: boolean = true) => hello() + ', ' + username + '. ' + (withHowAreYou ? howareyou() : '');
|
package/src/common/data/dates.ts
CHANGED
|
@@ -5,7 +5,7 @@ const timeAgo = new TimeAgo('en-US')
|
|
|
5
5
|
|
|
6
6
|
import dayjs from 'dayjs';
|
|
7
7
|
|
|
8
|
-
export const timeSince = (date: Date | number | string
|
|
8
|
+
export const timeSince = (date: Date | number | string) => {
|
|
9
9
|
|
|
10
10
|
if (date === undefined)
|
|
11
11
|
return 'Inconnu';
|
|
@@ -3,15 +3,6 @@
|
|
|
3
3
|
/*----------------------------------
|
|
4
4
|
- DEPENDANCES
|
|
5
5
|
----------------------------------*/
|
|
6
|
-
|
|
7
|
-
// Npm
|
|
8
|
-
import { ComponentChild } from 'preact'
|
|
9
|
-
|
|
10
|
-
// Libs
|
|
11
|
-
import { Erreur, TListeErreursSaisie, InputErrorSchema } from '@common/errors';
|
|
12
|
-
|
|
13
|
-
import { EXCLURE_VALEUR } from './validators/build';
|
|
14
|
-
|
|
15
6
|
/*----------------------------------
|
|
16
7
|
- CONSTANTES
|
|
17
8
|
----------------------------------*/
|
|
@@ -19,7 +19,7 @@ type TDetailsErreur = {
|
|
|
19
19
|
/*----------------------------------
|
|
20
20
|
- ERREURS
|
|
21
21
|
----------------------------------*/
|
|
22
|
-
export abstract class
|
|
22
|
+
export abstract class CoreError extends Error {
|
|
23
23
|
|
|
24
24
|
public static msgDefaut: string;
|
|
25
25
|
|
|
@@ -38,7 +38,7 @@ export abstract class Erreur extends Error {
|
|
|
38
38
|
|
|
39
39
|
super(message);
|
|
40
40
|
|
|
41
|
-
this.message = message || (this.constructor as typeof
|
|
41
|
+
this.message = message || (this.constructor as typeof CoreError).msgDefaut;
|
|
42
42
|
|
|
43
43
|
if (details !== undefined) {
|
|
44
44
|
this.idRapport = details.idRapport;
|
|
@@ -66,13 +66,13 @@ export abstract class Erreur extends Error {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export class InputError extends
|
|
69
|
+
export class InputError extends CoreError {
|
|
70
70
|
public http = 400;
|
|
71
71
|
public title = "Bad Request";
|
|
72
72
|
public static msgDefaut = "Bad Request.";
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export class InputErrorSchema extends
|
|
75
|
+
export class InputErrorSchema extends CoreError {
|
|
76
76
|
|
|
77
77
|
public http = 400;
|
|
78
78
|
public title = "Bad Request";
|
|
@@ -103,36 +103,40 @@ export class InputErrorSchema extends Erreur {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
export class AuthRequired extends
|
|
106
|
+
export class AuthRequired extends CoreError {
|
|
107
107
|
public http = 401;
|
|
108
108
|
public title = "Authentication Required";
|
|
109
109
|
public static msgDefaut = "Please Login to Continue.";
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
export class Forbidden extends
|
|
112
|
+
export class Forbidden extends CoreError {
|
|
113
113
|
public http = 403;
|
|
114
114
|
public title = "Access Denied";
|
|
115
115
|
public static msgDefaut = "You're not allowed to access to this resource.";
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export class NotFound extends
|
|
118
|
+
export class NotFound extends CoreError {
|
|
119
119
|
public http = 404;
|
|
120
120
|
public title = "Not Found";
|
|
121
121
|
public static msgDefaut = "The resource you asked for was not found.";
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export class Anomaly extends
|
|
124
|
+
export class Anomaly extends CoreError {
|
|
125
125
|
|
|
126
126
|
public http = 500;
|
|
127
127
|
public title = "Technical Error";
|
|
128
128
|
public static msgDefaut = "A technical error has occurred. A notification has just been sent to the admin.";
|
|
129
129
|
|
|
130
|
-
public constructor(
|
|
130
|
+
public constructor(
|
|
131
|
+
message: string,
|
|
132
|
+
public dataForDebugging?: object,
|
|
133
|
+
public originalError?: Error,
|
|
134
|
+
) {
|
|
131
135
|
super(message);
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
export class NotAvailable extends
|
|
139
|
+
export class NotAvailable extends CoreError {
|
|
136
140
|
|
|
137
141
|
// TODO: page erreur pour code 503
|
|
138
142
|
public http = 404;
|
|
@@ -149,7 +153,7 @@ export const instancierViaCode = (
|
|
|
149
153
|
code: number,
|
|
150
154
|
message?: string | TListeErreursSaisie,
|
|
151
155
|
details?: TDetailsErreur
|
|
152
|
-
):
|
|
156
|
+
): CoreError => {
|
|
153
157
|
|
|
154
158
|
if (typeof message === 'object')
|
|
155
159
|
return new InputErrorSchema(message, details);
|
|
@@ -163,4 +167,4 @@ export const instancierViaCode = (
|
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
export default
|
|
170
|
+
export default CoreError;
|
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
import type { HttpMethod } from '@server/services/router';
|
|
6
6
|
|
|
7
|
+
import type { FileToUpload } from '@client/components/inputv3/file';
|
|
8
|
+
|
|
7
9
|
/*----------------------------------
|
|
8
10
|
- TYPES
|
|
9
11
|
----------------------------------*/
|
|
10
12
|
|
|
11
13
|
export type TFetcherList = { [id: string]: TFetcher }
|
|
12
14
|
|
|
15
|
+
export type TPostData = {[key: string]: PrimitiveValue}
|
|
16
|
+
|
|
17
|
+
export type TPostDataWithFile = {[key: string]: PrimitiveValue | FileToUpload}
|
|
18
|
+
|
|
13
19
|
export type TFetcher<TData extends unknown = unknown> = {
|
|
14
20
|
|
|
15
21
|
// For async calls: api.post(...).then((data) => ...)
|
|
@@ -18,20 +24,22 @@ export type TFetcher<TData extends unknown = unknown> = {
|
|
|
18
24
|
|
|
19
25
|
method: HttpMethod,
|
|
20
26
|
path: string,
|
|
21
|
-
data?:
|
|
27
|
+
data?: TPostData,
|
|
22
28
|
options?: TApiFetchOptions
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export type TFetcherArgs = [
|
|
26
32
|
method: HttpMethod,
|
|
27
33
|
path: string,
|
|
28
|
-
data?:
|
|
34
|
+
data?: TPostData,
|
|
29
35
|
options?: TApiFetchOptions
|
|
30
36
|
]
|
|
31
37
|
|
|
32
38
|
export type TApiFetchOptions = {
|
|
33
39
|
captcha?: string, // Action id (required by recaptcha)
|
|
34
|
-
onProgress?: (percent: number) => void
|
|
40
|
+
onProgress?: (percent: number) => void,
|
|
41
|
+
// Default: json
|
|
42
|
+
encoding?: 'json' | 'multipart'
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
// https://stackoverflow.com/questions/44851268/typescript-how-to-extract-the-generic-parameter-from-a-type
|