create-foldkit-app 0.30.1 → 0.31.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/dist/templates/base/FOLDKIT.md +3 -1
- package/dist/templates/examples/auth/src/main.ts +3 -3
- package/dist/templates/examples/auth/src/page/loggedIn/model.ts +2 -2
- package/dist/templates/examples/auth/src/page/loggedOut/model.ts +2 -2
- package/dist/templates/examples/auth/src/route.ts +19 -29
- package/dist/templates/examples/auth/src/scene.test.ts +2 -2
- package/dist/templates/examples/auth/src/update.ts +3 -4
- package/dist/templates/examples/form/src/main.ts +12 -21
- package/dist/templates/examples/interrupting-commands/src/scene.test.ts +2 -2
- package/dist/templates/examples/interrupting-commands/src/story.test.ts +7 -7
- package/dist/templates/examples/job-application/src/main.ts +2 -2
- package/dist/templates/examples/job-application/src/model.ts +7 -12
- package/dist/templates/examples/job-application/src/scene.test.ts +5 -11
- package/dist/templates/examples/job-application/src/story.test.ts +4 -4
- package/dist/templates/examples/job-application/src/update.ts +8 -4
- package/dist/templates/examples/job-application/src/view/review.ts +92 -94
- package/dist/templates/examples/managed-resource-layer/src/main.ts +23 -27
- package/dist/templates/examples/managed-resource-layer/src/scene.test.ts +3 -4
- package/dist/templates/examples/managed-resource-layer/src/story.test.ts +11 -20
- package/dist/templates/examples/map/src/main.fixtures.ts +2 -2
- package/dist/templates/examples/map/src/main.ts +23 -21
- package/dist/templates/examples/map/src/scene.test.ts +4 -2
- package/dist/templates/examples/map/src/story.test.ts +9 -10
- package/dist/templates/examples/query-sync/src/main.ts +30 -29
- package/dist/templates/examples/query-sync/src/scene.test.ts +8 -15
- package/dist/templates/examples/query-sync/src/story.test.ts +21 -8
- package/dist/templates/examples/route-transitions/src/main.ts +15 -25
- package/dist/templates/examples/route-transitions/src/route.ts +21 -26
- package/dist/templates/examples/route-transitions/src/story.test.ts +20 -26
- package/dist/templates/examples/routing/src/fileTree.ts +3 -3
- package/dist/templates/examples/routing/src/main.ts +2 -12
- package/dist/templates/examples/routing/src/page/people.ts +18 -16
- package/dist/templates/examples/routing/src/route.ts +18 -32
- package/dist/templates/examples/routing/src/scene.test.ts +12 -20
- package/dist/templates/examples/routing/src/story.test.ts +9 -6
- package/dist/templates/examples/shopping-cart/src/route.ts +11 -19
- package/dist/templates/examples/shopping-cart/src/scene.test.ts +10 -8
- package/dist/templates/examples/shopping-cart/src/story.test.ts +2 -2
- package/dist/templates/examples/ssg/src/route.ts +9 -8
- package/dist/templates/examples/state-machine/src/main.ts +68 -63
- package/dist/templates/examples/state-machine/src/story.test.ts +8 -7
- package/dist/templates/examples/state-machine/src/view.ts +7 -13
- package/dist/templates/examples/todo/src/main.ts +11 -17
- package/dist/templates/examples/todo/src/scene.test.ts +2 -2
- package/dist/templates/examples/todo/src/story.test.ts +11 -12
- package/dist/templates/examples/ui-showcase/src/main.ts +64 -85
- package/dist/templates/examples/ui-showcase/src/scene.test.ts +14 -30
- package/dist/templates/examples/ui-showcase/src/story.test.ts +2 -2
- package/dist/templates/examples/ui-showcase/src/ui/message.ts +0 -70
- package/dist/templates/examples/view-transitions/src/main.ts +1 -1
- package/dist/templates/examples/view-transitions/src/route.ts +9 -12
- package/dist/templates/examples/view-transitions/src/scene.test.ts +5 -14
- package/dist/templates/examples/websocket-chat/src/main.ts +29 -37
- package/dist/templates/examples/websocket-chat/src/scene.test.ts +18 -10
- package/dist/templates/examples/websocket-chat/src/story.test.ts +19 -15
- package/dist/templates/release.json +13 -13
- package/dist/templates/rendering/ssg/src/route.ts +9 -8
- package/dist/templates/rendering/ssg/src/scene.test.ts +2 -2
- package/package.json +1 -1
|
@@ -31,7 +31,9 @@ Replace it whenever the project upgrades its Foldkit packages. If `./repos/foldk
|
|
|
31
31
|
- Foldkit is tightly coupled to the Effect ecosystem. Do not suggest solutions outside of Effect-TS.
|
|
32
32
|
- Model fields must be Schema types (the model is a schema). Plain TypeScript types are fine elsewhere (function return types, local variables, etc.).
|
|
33
33
|
- Use full names like `Message` (not `Msg`), and `withReturnType` (not `as const` or type casting).
|
|
34
|
-
-
|
|
34
|
+
- Declare every variant together: `defineMessageUnion()` for Messages, `defineTaggedUnion()` for other domain unions, and `defineRouteUnion()` for Routes.
|
|
35
|
+
- Keep constructors on their union: `Message.ClickedSubmit()`, `FetchState.Ok({ data })`, and `AppRoute.Home()`.
|
|
36
|
+
- Use `taggedStruct()` only when the variants cannot be declared together, such as a recursive union.
|
|
35
37
|
- Push back on any direction that violates Elm Architecture principles: unidirectional data flow, messages as facts (not commands), model as single source of truth, side effects confined to commands. If a prompt suggests mutating state, imperative event handlers, or two-way bindings, flag the issue and propose the idiomatic Foldkit approach.
|
|
36
38
|
- Never use `NoOp`. Every message must describe what happened. A command's result message is named from the command, not from the fact it reports, whether or not it carries a payload: `LockScroll` → `CompletedLockScroll`, `DetermineStartTime` → `CompletedDetermineStartTime` (never `DeterminedStartTime`).
|
|
37
39
|
|
|
@@ -9,7 +9,7 @@ import { SESSION_STORAGE_KEY } from './constant'
|
|
|
9
9
|
import { Session, SessionJsonString } from './domain/session'
|
|
10
10
|
import { Message } from './message'
|
|
11
11
|
import { LoggedIn, LoggedOut, Model } from './model'
|
|
12
|
-
import {
|
|
12
|
+
import { AppRoute, urlToAppRoute } from './route'
|
|
13
13
|
import { RedirectToDashboard, RedirectToLogin } from './update'
|
|
14
14
|
|
|
15
15
|
// FLAGS
|
|
@@ -56,7 +56,7 @@ export const init: Runtime.RoutingApplicationInit<Model, Message, Flags> = (
|
|
|
56
56
|
model: LoggedOut.init(route),
|
|
57
57
|
})),
|
|
58
58
|
M.orElse(() => ({
|
|
59
|
-
model: LoggedOut.init(
|
|
59
|
+
model: LoggedOut.init(AppRoute.Login()),
|
|
60
60
|
commands: [RedirectToLogin()],
|
|
61
61
|
})),
|
|
62
62
|
),
|
|
@@ -68,7 +68,7 @@ export const init: Runtime.RoutingApplicationInit<Model, Message, Flags> = (
|
|
|
68
68
|
model: LoggedIn.init(route, session),
|
|
69
69
|
})),
|
|
70
70
|
M.orElse(() => ({
|
|
71
|
-
model: LoggedIn.init(
|
|
71
|
+
model: LoggedIn.init(AppRoute.Dashboard(), session),
|
|
72
72
|
commands: [RedirectToDashboard()],
|
|
73
73
|
})),
|
|
74
74
|
),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { taggedStruct } from 'foldkit/schema'
|
|
2
2
|
|
|
3
3
|
import { Session } from '../../domain/session'
|
|
4
4
|
import { LoggedInRoute } from '../../route'
|
|
5
5
|
|
|
6
6
|
// MODEL
|
|
7
7
|
|
|
8
|
-
export const Model =
|
|
8
|
+
export const Model = taggedStruct('LoggedIn', {
|
|
9
9
|
route: LoggedInRoute,
|
|
10
10
|
session: Session,
|
|
11
11
|
})
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { taggedStruct } from 'foldkit/schema'
|
|
2
2
|
|
|
3
3
|
import { LoggedOutRoute } from '../../route'
|
|
4
4
|
import * as Login from './page/login'
|
|
5
5
|
|
|
6
6
|
// MODEL
|
|
7
7
|
|
|
8
|
-
export const Model =
|
|
8
|
+
export const Model = taggedStruct('LoggedOut', {
|
|
9
9
|
route: LoggedOutRoute,
|
|
10
10
|
loginModel: Login.Model,
|
|
11
11
|
})
|
|
@@ -1,46 +1,36 @@
|
|
|
1
1
|
import { Schema as S, pipe } from 'effect'
|
|
2
2
|
import { Route } from 'foldkit'
|
|
3
|
-
import {
|
|
3
|
+
import { defineRouteUnion, literal } from 'foldkit/route'
|
|
4
4
|
|
|
5
|
-
export const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export const AppRoute = defineRouteUnion({
|
|
6
|
+
Home: {},
|
|
7
|
+
Login: {},
|
|
8
|
+
Dashboard: {},
|
|
9
|
+
Settings: {},
|
|
10
|
+
NotFound: { path: S.String },
|
|
11
|
+
})
|
|
10
12
|
|
|
11
|
-
export type
|
|
12
|
-
export type LoginRoute = typeof LoginRoute.Type
|
|
13
|
-
export type DashboardRoute = typeof DashboardRoute.Type
|
|
14
|
-
export type SettingsRoute = typeof SettingsRoute.Type
|
|
15
|
-
export type NotFoundRoute = typeof NotFoundRoute.Type
|
|
13
|
+
export type AppRoute = typeof AppRoute.Type
|
|
16
14
|
|
|
17
|
-
export const LoggedOutRoute =
|
|
18
|
-
export const LoggedInRoute =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
])
|
|
23
|
-
export const AppRoute = S.Union([
|
|
24
|
-
HomeRoute,
|
|
25
|
-
LoginRoute,
|
|
26
|
-
DashboardRoute,
|
|
27
|
-
SettingsRoute,
|
|
28
|
-
NotFoundRoute,
|
|
15
|
+
export const LoggedOutRoute = AppRoute.subset(['Home', 'Login', 'NotFound'])
|
|
16
|
+
export const LoggedInRoute = AppRoute.subset([
|
|
17
|
+
'Dashboard',
|
|
18
|
+
'Settings',
|
|
19
|
+
'NotFound',
|
|
29
20
|
])
|
|
30
21
|
|
|
31
22
|
export type LoggedOutRoute = typeof LoggedOutRoute.Type
|
|
32
23
|
export type LoggedInRoute = typeof LoggedInRoute.Type
|
|
33
|
-
export type AppRoute = typeof AppRoute.Type
|
|
34
24
|
|
|
35
|
-
export const homeRouter = pipe(Route.root, Route.mapTo(
|
|
36
|
-
export const loginRouter = pipe(literal('login'), Route.mapTo(
|
|
25
|
+
export const homeRouter = pipe(Route.root, Route.mapTo(AppRoute.Home))
|
|
26
|
+
export const loginRouter = pipe(literal('login'), Route.mapTo(AppRoute.Login))
|
|
37
27
|
export const dashboardRouter = pipe(
|
|
38
28
|
literal('dashboard'),
|
|
39
|
-
Route.mapTo(
|
|
29
|
+
Route.mapTo(AppRoute.Dashboard),
|
|
40
30
|
)
|
|
41
31
|
export const settingsRouter = pipe(
|
|
42
32
|
literal('settings'),
|
|
43
|
-
Route.mapTo(
|
|
33
|
+
Route.mapTo(AppRoute.Settings),
|
|
44
34
|
)
|
|
45
35
|
|
|
46
36
|
const routeParser = Route.oneOf(
|
|
@@ -52,5 +42,5 @@ const routeParser = Route.oneOf(
|
|
|
52
42
|
|
|
53
43
|
export const urlToAppRoute = Route.parseUrlWithFallback(
|
|
54
44
|
routeParser,
|
|
55
|
-
|
|
45
|
+
AppRoute.NotFound,
|
|
56
46
|
)
|
|
@@ -20,12 +20,12 @@ import {
|
|
|
20
20
|
SimulateAuthRequest,
|
|
21
21
|
initModel as initLoginModel,
|
|
22
22
|
} from './page/loggedOut/page/login'
|
|
23
|
-
import {
|
|
23
|
+
import { AppRoute } from './route'
|
|
24
24
|
import { RedirectToDashboard, update } from './update'
|
|
25
25
|
import { view } from './view'
|
|
26
26
|
|
|
27
27
|
const validModel = LoggedOut.Model({
|
|
28
|
-
route:
|
|
28
|
+
route: AppRoute.Login(),
|
|
29
29
|
loginModel: LoginModel.make({
|
|
30
30
|
...initLoginModel(),
|
|
31
31
|
email: Valid({ value: 'alice@example.com' }),
|
|
@@ -8,8 +8,7 @@ import { ClearSession, LogError, SaveSession } from './command'
|
|
|
8
8
|
import { Message } from './message'
|
|
9
9
|
import { LoggedIn, LoggedOut, Model } from './model'
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
HomeRoute,
|
|
11
|
+
AppRoute,
|
|
13
12
|
dashboardRouter,
|
|
14
13
|
homeRouter,
|
|
15
14
|
loginRouter,
|
|
@@ -62,7 +61,7 @@ const foldLoggedOutOutMessage: (
|
|
|
62
61
|
SucceededLogin:
|
|
63
62
|
({ session }) =>
|
|
64
63
|
() => ({
|
|
65
|
-
model: LoggedIn.init(
|
|
64
|
+
model: LoggedIn.init(AppRoute.Dashboard(), session),
|
|
66
65
|
commands: [SaveSession({ session }), RedirectToDashboard()],
|
|
67
66
|
}),
|
|
68
67
|
}),
|
|
@@ -88,7 +87,7 @@ const foldLoggedInOutMessage: (
|
|
|
88
87
|
M.withReturnType<Update.Step<Model, Message>>(),
|
|
89
88
|
M.tagsExhaustive({
|
|
90
89
|
RequestedLogout: () => () => ({
|
|
91
|
-
model: LoggedOut.init(
|
|
90
|
+
model: LoggedOut.init(AppRoute.Home()),
|
|
92
91
|
commands: [ClearSession(), RedirectToHome()],
|
|
93
92
|
}),
|
|
94
93
|
}),
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from 'foldkit/fieldValidation'
|
|
22
22
|
import { type Attribute, Document, Html, HtmlBuilder } from 'foldkit/html'
|
|
23
23
|
import { defineMessageUnion } from 'foldkit/message'
|
|
24
|
-
import {
|
|
24
|
+
import { defineTaggedUnion } from 'foldkit/schema'
|
|
25
25
|
import { evo } from 'foldkit/struct'
|
|
26
26
|
|
|
27
27
|
import { Button, Input, Textarea } from '@foldkit/ui'
|
|
@@ -37,22 +37,13 @@ const emailRules = makeRules({
|
|
|
37
37
|
|
|
38
38
|
// MODEL
|
|
39
39
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Submitting,
|
|
48
|
-
SubmitSuccess,
|
|
49
|
-
SubmitError,
|
|
50
|
-
])
|
|
51
|
-
|
|
52
|
-
type NotSubmitted = typeof NotSubmitted.Type
|
|
53
|
-
type Submitting = typeof Submitting.Type
|
|
54
|
-
type SubmitSuccess = typeof SubmitSuccess.Type
|
|
55
|
-
type SubmitError = typeof SubmitError.Type
|
|
40
|
+
const Submission = defineTaggedUnion({
|
|
41
|
+
NotSubmitted: {},
|
|
42
|
+
Submitting: {},
|
|
43
|
+
SubmitSuccess: { confirmationText: S.String },
|
|
44
|
+
SubmitError: { error: S.String },
|
|
45
|
+
})
|
|
46
|
+
|
|
56
47
|
type Submission = typeof Submission.Type
|
|
57
48
|
|
|
58
49
|
export const Model = S.Struct({
|
|
@@ -83,7 +74,7 @@ export const initialModel: Model = {
|
|
|
83
74
|
name: NotValidated({ value: '' }),
|
|
84
75
|
email: NotValidated({ value: '' }),
|
|
85
76
|
messageText: NotValidated({ value: '' }),
|
|
86
|
-
submission: NotSubmitted(),
|
|
77
|
+
submission: Submission.NotSubmitted(),
|
|
87
78
|
}
|
|
88
79
|
|
|
89
80
|
export const init: Runtime.ApplicationInit<Model, Message> = () => ({
|
|
@@ -191,7 +182,7 @@ export const update = (model: Model, message: Message) =>
|
|
|
191
182
|
|
|
192
183
|
return {
|
|
193
184
|
model: evo(model, {
|
|
194
|
-
submission: () => Submitting(),
|
|
185
|
+
submission: () => Submission.Submitting(),
|
|
195
186
|
}),
|
|
196
187
|
commands: [
|
|
197
188
|
SubmitForm({
|
|
@@ -206,7 +197,7 @@ export const update = (model: Model, message: Message) =>
|
|
|
206
197
|
SucceededSubmitForm: ({ name }) => ({
|
|
207
198
|
model: evo(model, {
|
|
208
199
|
submission: () =>
|
|
209
|
-
SubmitSuccess({
|
|
200
|
+
Submission.SubmitSuccess({
|
|
210
201
|
confirmationText: `Welcome to the waitlist, ${name}! We'll be in touch soon.`,
|
|
211
202
|
}),
|
|
212
203
|
}),
|
|
@@ -215,7 +206,7 @@ export const update = (model: Model, message: Message) =>
|
|
|
215
206
|
FailedSubmitForm: () => ({
|
|
216
207
|
model: evo(model, {
|
|
217
208
|
submission: () =>
|
|
218
|
-
SubmitError({
|
|
209
|
+
Submission.SubmitError({
|
|
219
210
|
error:
|
|
220
211
|
'Sorry, there was an error adding you to the waitlist. Please try again.',
|
|
221
212
|
}),
|
|
@@ -52,7 +52,7 @@ describe('view', () => {
|
|
|
52
52
|
CancelUploadFile({ uploadId: 0 }),
|
|
53
53
|
Message.CompletedCancelUploadFile({
|
|
54
54
|
uploadId: 0,
|
|
55
|
-
outcome: Interruptible.Interrupted(),
|
|
55
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
56
56
|
}),
|
|
57
57
|
),
|
|
58
58
|
expect(text('Cancelled')).toExist(),
|
|
@@ -79,7 +79,7 @@ describe('view', () => {
|
|
|
79
79
|
CancelUploadFile({ uploadId: 0 }),
|
|
80
80
|
Message.CompletedCancelUploadFile({
|
|
81
81
|
uploadId: 0,
|
|
82
|
-
outcome: Interruptible.Interrupted(),
|
|
82
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
83
83
|
}),
|
|
84
84
|
),
|
|
85
85
|
expect(role('button', { name: 'Cancel all' })).toBeAbsent(),
|
|
@@ -57,7 +57,7 @@ describe('update', () => {
|
|
|
57
57
|
CancelUploadFile({ uploadId: 0 }),
|
|
58
58
|
Message.CompletedCancelUploadFile({
|
|
59
59
|
uploadId: 0,
|
|
60
|
-
outcome: Interruptible.Interrupted(),
|
|
60
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
61
61
|
}),
|
|
62
62
|
),
|
|
63
63
|
Command.expectNone(),
|
|
@@ -83,7 +83,7 @@ describe('update', () => {
|
|
|
83
83
|
CancelUploadFile({ uploadId: 0 }),
|
|
84
84
|
Message.CompletedCancelUploadFile({
|
|
85
85
|
uploadId: 0,
|
|
86
|
-
outcome: Interruptible.NotFound(),
|
|
86
|
+
outcome: Interruptible.Outcome.NotFound(),
|
|
87
87
|
}),
|
|
88
88
|
),
|
|
89
89
|
model(model => {
|
|
@@ -105,7 +105,7 @@ describe('update', () => {
|
|
|
105
105
|
CancelUploadFile({ uploadId: 0 }),
|
|
106
106
|
Message.CompletedCancelUploadFile({
|
|
107
107
|
uploadId: 0,
|
|
108
|
-
outcome: Interruptible.Interrupted(),
|
|
108
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
109
109
|
}),
|
|
110
110
|
),
|
|
111
111
|
Command.expectExact(
|
|
@@ -135,7 +135,7 @@ describe('update', () => {
|
|
|
135
135
|
CancelUploadFile({ uploadId: 0 }),
|
|
136
136
|
Message.CompletedCancelUploadFile({
|
|
137
137
|
uploadId: 0,
|
|
138
|
-
outcome: Interruptible.Interrupted(),
|
|
138
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
139
139
|
}),
|
|
140
140
|
),
|
|
141
141
|
Command.resolve(
|
|
@@ -161,7 +161,7 @@ describe('update', () => {
|
|
|
161
161
|
CancelUploadFile({ uploadId: 0 }),
|
|
162
162
|
Message.CompletedCancelUploadFile({
|
|
163
163
|
uploadId: 0,
|
|
164
|
-
outcome: Interruptible.Interrupted(),
|
|
164
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
165
165
|
}),
|
|
166
166
|
),
|
|
167
167
|
message(Message.ClickedRestartUpload({ uploadId: 0 })),
|
|
@@ -201,14 +201,14 @@ describe('update', () => {
|
|
|
201
201
|
CancelUploadFile({ uploadId: 0 }),
|
|
202
202
|
Message.CompletedCancelUploadFile({
|
|
203
203
|
uploadId: 0,
|
|
204
|
-
outcome: Interruptible.Interrupted(),
|
|
204
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
205
205
|
}),
|
|
206
206
|
),
|
|
207
207
|
Command.resolve(
|
|
208
208
|
CancelUploadFile({ uploadId: 2 }),
|
|
209
209
|
Message.CompletedCancelUploadFile({
|
|
210
210
|
uploadId: 2,
|
|
211
|
-
outcome: Interruptible.Interrupted(),
|
|
211
|
+
outcome: Interruptible.Outcome.Interrupted(),
|
|
212
212
|
}),
|
|
213
213
|
),
|
|
214
214
|
Command.expectNone(),
|
|
@@ -5,7 +5,7 @@ import { BrowserCrypto } from '@effect/platform-browser'
|
|
|
5
5
|
import { Menu, Tabs } from '@foldkit/ui'
|
|
6
6
|
|
|
7
7
|
import { Message } from './message'
|
|
8
|
-
import { Model,
|
|
8
|
+
import { Model, Submission } from './model'
|
|
9
9
|
import {
|
|
10
10
|
Attachments,
|
|
11
11
|
CoverLetter,
|
|
@@ -58,7 +58,7 @@ export const init: Runtime.ApplicationInit<Model, Message, Flags> = ({
|
|
|
58
58
|
coverLetter: CoverLetter.init(),
|
|
59
59
|
attachments: Attachments.init(),
|
|
60
60
|
isPreviewVisible: false,
|
|
61
|
-
submission: NotSubmitted(),
|
|
61
|
+
submission: Submission.NotSubmitted(),
|
|
62
62
|
stepMenu: Menu.init({ id: 'step-menu' }),
|
|
63
63
|
stepTabs: Tabs.init({ id: 'step-tabs' }),
|
|
64
64
|
isSubmitAttempted: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema as S } from 'effect'
|
|
2
|
-
import {
|
|
2
|
+
import { defineTaggedUnion } from 'foldkit/schema'
|
|
3
3
|
|
|
4
4
|
import { Menu, Tabs } from '@foldkit/ui'
|
|
5
5
|
|
|
@@ -15,17 +15,12 @@ import {
|
|
|
15
15
|
|
|
16
16
|
// SUBMISSION
|
|
17
17
|
|
|
18
|
-
export const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
NotSubmitted,
|
|
25
|
-
Submitting,
|
|
26
|
-
SubmitSuccess,
|
|
27
|
-
SubmitError,
|
|
28
|
-
])
|
|
18
|
+
export const Submission = defineTaggedUnion({
|
|
19
|
+
NotSubmitted: {},
|
|
20
|
+
Submitting: {},
|
|
21
|
+
SubmitSuccess: {},
|
|
22
|
+
SubmitError: { error: S.String },
|
|
23
|
+
})
|
|
29
24
|
export type Submission = typeof Submission.Type
|
|
30
25
|
|
|
31
26
|
// MODEL
|
|
@@ -15,13 +15,7 @@ import { describe, test } from 'vitest'
|
|
|
15
15
|
import { Menu, Tabs } from '@foldkit/ui'
|
|
16
16
|
import { Message as TabsMessage } from '@foldkit/ui/tabs'
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
type Model,
|
|
20
|
-
NotSubmitted,
|
|
21
|
-
SubmitError,
|
|
22
|
-
SubmitSuccess,
|
|
23
|
-
Submitting,
|
|
24
|
-
} from './model'
|
|
18
|
+
import { type Model, Submission } from './model'
|
|
25
19
|
import {
|
|
26
20
|
Attachments,
|
|
27
21
|
CoverLetter,
|
|
@@ -44,7 +38,7 @@ const initialModel: Model = {
|
|
|
44
38
|
coverLetter: CoverLetter.init(),
|
|
45
39
|
attachments: Attachments.init(),
|
|
46
40
|
isPreviewVisible: false,
|
|
47
|
-
submission: NotSubmitted(),
|
|
41
|
+
submission: Submission.NotSubmitted(),
|
|
48
42
|
stepMenu: Menu.init({ id: 'step-menu' }),
|
|
49
43
|
stepTabs: Tabs.init({ id: 'step-tabs' }),
|
|
50
44
|
isSubmitAttempted: false,
|
|
@@ -236,7 +230,7 @@ describe('view', () => {
|
|
|
236
230
|
given({
|
|
237
231
|
...initialModel,
|
|
238
232
|
currentStep: 'Review',
|
|
239
|
-
submission: Submitting(),
|
|
233
|
+
submission: Submission.Submitting(),
|
|
240
234
|
}),
|
|
241
235
|
expect(role('button', { name: 'Submitting...' })).toExist(),
|
|
242
236
|
)
|
|
@@ -248,7 +242,7 @@ describe('view', () => {
|
|
|
248
242
|
given({
|
|
249
243
|
...initialModel,
|
|
250
244
|
currentStep: 'Review',
|
|
251
|
-
submission: SubmitSuccess(),
|
|
245
|
+
submission: Submission.SubmitSuccess(),
|
|
252
246
|
}),
|
|
253
247
|
expect(text('Application Submitted', { exact: false })).toExist(),
|
|
254
248
|
)
|
|
@@ -260,7 +254,7 @@ describe('view', () => {
|
|
|
260
254
|
given({
|
|
261
255
|
...initialModel,
|
|
262
256
|
currentStep: 'Review',
|
|
263
|
-
submission: SubmitError({ error: 'Network down' }),
|
|
257
|
+
submission: Submission.SubmitError({ error: 'Network down' }),
|
|
264
258
|
}),
|
|
265
259
|
expect(text('Network down')).toExist(),
|
|
266
260
|
expect(role('button', { name: 'Try Again' })).toExist(),
|
|
@@ -11,7 +11,7 @@ import { Message as TabsMessage } from '@foldkit/ui/tabs'
|
|
|
11
11
|
|
|
12
12
|
import { SubmitApplication } from './command'
|
|
13
13
|
import { Message } from './message'
|
|
14
|
-
import { type Model,
|
|
14
|
+
import { type Model, Submission } from './model'
|
|
15
15
|
import {
|
|
16
16
|
Attachments,
|
|
17
17
|
CoverLetter,
|
|
@@ -39,7 +39,7 @@ const initialModel: Model = {
|
|
|
39
39
|
coverLetter: CoverLetter.init(),
|
|
40
40
|
attachments: Attachments.init(),
|
|
41
41
|
isPreviewVisible: false,
|
|
42
|
-
submission: NotSubmitted(),
|
|
42
|
+
submission: Submission.NotSubmitted(),
|
|
43
43
|
stepMenu: Menu.init({ id: 'step-menu' }),
|
|
44
44
|
stepTabs: Tabs.init({ id: 'step-tabs' }),
|
|
45
45
|
isSubmitAttempted: false,
|
|
@@ -748,7 +748,7 @@ describe('update', () => {
|
|
|
748
748
|
given({
|
|
749
749
|
...initialModel,
|
|
750
750
|
currentStep: 'Review',
|
|
751
|
-
submission: Submitting(),
|
|
751
|
+
submission: Submission.Submitting(),
|
|
752
752
|
}),
|
|
753
753
|
message(Message.SucceededSubmitApplication()),
|
|
754
754
|
model(model => {
|
|
@@ -763,7 +763,7 @@ describe('update', () => {
|
|
|
763
763
|
given({
|
|
764
764
|
...initialModel,
|
|
765
765
|
currentStep: 'Review',
|
|
766
|
-
submission: Submitting(),
|
|
766
|
+
submission: Submission.Submitting(),
|
|
767
767
|
}),
|
|
768
768
|
message(Message.FailedSubmitApplication({ error: 'Server down' })),
|
|
769
769
|
model(model => {
|
|
@@ -7,7 +7,7 @@ import { Menu, Tabs } from '@foldkit/ui'
|
|
|
7
7
|
import { SubmitApplication } from './command'
|
|
8
8
|
import { Step } from './domain'
|
|
9
9
|
import { Message } from './message'
|
|
10
|
-
import { type Model,
|
|
10
|
+
import { type Model, Submission } from './model'
|
|
11
11
|
import {
|
|
12
12
|
Attachments,
|
|
13
13
|
CoverLetter,
|
|
@@ -163,7 +163,9 @@ export const update = (model: Model, message: Message) =>
|
|
|
163
163
|
})
|
|
164
164
|
if (isApplicationComplete(revealedModel)) {
|
|
165
165
|
return {
|
|
166
|
-
model: evo(revealedModel, {
|
|
166
|
+
model: evo(revealedModel, {
|
|
167
|
+
submission: () => Submission.Submitting(),
|
|
168
|
+
}),
|
|
167
169
|
commands: [SubmitApplication()],
|
|
168
170
|
}
|
|
169
171
|
}
|
|
@@ -171,10 +173,12 @@ export const update = (model: Model, message: Message) =>
|
|
|
171
173
|
},
|
|
172
174
|
|
|
173
175
|
SucceededSubmitApplication: () => ({
|
|
174
|
-
model: evo(model, { submission: () => SubmitSuccess() }),
|
|
176
|
+
model: evo(model, { submission: () => Submission.SubmitSuccess() }),
|
|
175
177
|
}),
|
|
176
178
|
|
|
177
179
|
FailedSubmitApplication: ({ error }) => ({
|
|
178
|
-
model: evo(model, {
|
|
180
|
+
model: evo(model, {
|
|
181
|
+
submission: () => Submission.SubmitError({ error }),
|
|
182
|
+
}),
|
|
179
183
|
}),
|
|
180
184
|
})
|