Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F9582923
form.ts
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
4 KB
Referenced Files
None
Subscribers
None
form.ts
View Options
import
{
Context
}
from
"koa"
;
import
Router
from
"@koa/router"
;
import
{
FlatTemplatable
,
Templatable
,
tempstream
}
from
"tempstream"
;
import
{
hasFieldOfType
,
hasShape
,
is
,
predicates
,
}
from
"@sealcode/ts-predicates"
;
import
{
FormField
}
from
"./fields/field"
;
import
{
Fields
,
Mountable
,
PageErrorMessage
}
from
"../page/mountable"
;
export
type
FormDataPrimitive
=
string
|
string
[]
|
undefined
;
export
type
FormDataValue
=
|
FormDataPrimitive
|
Record
<
string
,
FormDataPrimitive
>
;
export
type
FieldValueType
<
F
extends
FormField
>
=
F
extends
FormField
<
infer
R
>
?
R
:
never
;
export
type
FormFieldsToValues
<
F
extends
Record
<
string
,
FormField
<
keyof
F
>>>
=
{
[
Property
in
keyof
F
]
:
FieldValueType
<
F
[
Property
]
>
;
};
export
type
FormMessage
=
{
type
:
"info"
|
"success"
|
"error"
;
text
:
string
};
export
type
FormData
<
Fieldnames
extends
string
=
string
>
=
{
raw_values
:
Record
<
Fieldnames
,
FormDataValue
>
;
messages
:
FormMessage
[];
};
export
abstract
class
Form
<
F
extends
Fields
>
extends
Mountable
<
F
>
{
defaultSuccessMessage
=
"Done"
;
submitButtonText
=
"Wyślij"
;
async
canAccess
(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_
:
Context
)
:
Promise
<
{
canAccess
:
boolean
;
message
:
string
}
>
{
return
{
canAccess
:
true
,
message
:
""
};
}
async
renderError
(
_
:
Context
,
error
:
PageErrorMessage
)
:
Promise
<
FlatTemplatable
>
{
return
tempstream
/* HTML */
`<div>
${
error
.
message
}
</div>`
;
}
async
render
(
ctx
:
Context
,
data
:
FormData
)
:
Promise
<
FlatTemplatable
>
{
return
tempstream
/* HTML */
`
${
this
.
makeFormTag
()
}
${
!
this
.
controls
.
some
((
control
)
=>
control
.
role
==
"messages"
)
?
this
.
renderMessages
(
ctx
,
data
)
:
""
}
${
this
.
renderControls
(
ctx
,
data
)
}
<input type="submit" value="
${
this
.
submitButtonText
}
"/></form>`
;
}
public
makeFormTag
()
:
FlatTemplatable
{
return
`<form method="POST" action="./">`
;
}
public
async
onValuesInvalid
(
ctx
:
Context
,
form_messages
:
FormMessage
[]
)
:
Promise
<
void
>
{
ctx
.
status
=
422
;
ctx
.
body
=
await
this
.
render
(
ctx
,
{
raw_values
:
ctx
.
$body
,
messages
:
form_messages
.
length
?
form_messages
:
[{
type
:
"error"
,
text
:
"Some fields are invalid"
}],
});
}
public
async
onError
(
ctx
:
Context
,
data
:
FormData
,
error
:
unknown
)
:
Promise
<
void
>
{
ctx
.
status
=
422
;
let
error_message
=
"Unknown error has occured"
;
if
(
is
(
error
,
predicates
.
object
)
&&
hasShape
({
message
:
predicates
.
string
},
error
)
)
{
error_message
=
error
.
message
;
}
ctx
.
body
=
await
this
.
render
(
ctx
,
{
raw_values
:
data
.
raw_values
,
messages
:
[{
type
:
"error"
,
text
:
error_message
}],
});
}
public
abstract
onSubmit
(
ctx
:
Context
,
data
:
FormData
)
:
void
|
Promise
<
void
>
;
public
async
onSuccess
(
ctx
:
Context
,
data
:
FormData
)
:
Promise
<
void
>
{
ctx
.
body
=
await
this
.
render
(
ctx
,
{
raw_values
:
data
.
raw_values
,
messages
:
[{
type
:
"success"
,
text
:
this
.
defaultSuccessMessage
}],
});
ctx
.
status
=
422
;
}
public
mount
(
router
:
Router
,
path
:
string
)
:
void
{
router
.
use
(
path
,
async
(
ctx
,
next
)
=>
{
const
result
=
await
this
.
canAccess
(
ctx
);
if
(
!
result
.
canAccess
)
{
ctx
.
body
=
this
.
renderError
(
ctx
,
{
type
:
"access"
,
message
:
result
.
message
,
});
ctx
.
status
=
403
;
return
;
}
await
next
();
});
router
.
get
(
path
,
async
(
ctx
)
=>
{
ctx
.
type
=
"html"
;
ctx
.
body
=
await
this
.
render
(
ctx
,
{
raw_values
:
{},
messages
:
[]
});
});
router
.
post
(
path
,
async
(
ctx
)
=>
{
const
{
valid
,
form_messages
}
=
await
this
.
validate
(
ctx
,
ctx
.
$body
);
if
(
!
valid
)
{
await
this
.
onValuesInvalid
(
ctx
,
form_messages
);
return
;
}
try
{
ctx
.
status
=
303
;
await
this
.
onSubmit
(
ctx
,
{
raw_values
:
ctx
.
$body
,
messages
:
[],
});
await
this
.
onSuccess
(
ctx
,
{
raw_values
:
ctx
.
$body
,
messages
:
[],
});
}
catch
(
e
:
unknown
)
{
// eslint-disable-next-line no-console
console
.
dir
(
e
,
{
depth
:
5
});
const
message
=
is
(
e
,
predicates
.
object
)
&&
hasFieldOfType
(
e
,
"message"
,
predicates
.
string
)
?
e
?
.
message
:
is
(
e
,
predicates
.
string
)
?
e
:
"Wystąpił błąd"
;
ctx
.
status
=
422
;
await
this
.
onError
(
ctx
,
{
raw_values
:
ctx
.
$body
,
messages
:
[
{
type
:
"error"
,
text
:
message
,
},
],
},
e
);
}
});
}
extractRawValues
(
ctx
:
Context
)
:
Record
<
string
,
FormDataValue
>
{
return
ctx
.
$body
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-java
Expires
Sat, Oct 11, 08:11 (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
977817
Default Alt Text
form.ts (4 KB)
Attached To
Mode
rSGEN sealgen
Attached
Detach File
Event Timeline
Log In to Comment