apostrophe 3.31.0 → 3.32.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/.eslintrc +3 -0
- package/CHANGELOG.md +21 -1
- package/modules/@apostrophecms/asset/index.js +65 -7
- package/modules/@apostrophecms/asset/lib/webpack/utils.js +242 -28
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +26 -16
- package/modules/@apostrophecms/i18n/i18n/en.json +17 -3
- package/modules/@apostrophecms/i18n/i18n/es.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/fr.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +2 -1
- package/modules/@apostrophecms/i18n/i18n/sk.json +1 -0
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +41 -20
- package/modules/@apostrophecms/login/index.js +123 -26
- package/modules/@apostrophecms/login/ui/apos/components/AposForgotPasswordForm.vue +124 -0
- package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +339 -0
- package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +163 -0
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +135 -293
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +65 -14
- package/modules/@apostrophecms/login/ui/apos/mixins/AposLoginFormMixin.js +45 -0
- package/modules/@apostrophecms/login/views/passwordResetEmail.html +9 -0
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +17 -6
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalBody.vue +4 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +7 -1
- package/modules/@apostrophecms/piece-type/index.js +1 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +4 -3
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +11 -5
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +6 -2
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Link.js +7 -4
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +4 -0
- package/modules/@apostrophecms/template/index.js +11 -12
- package/modules/@apostrophecms/template/lib/bundlesLoader.js +20 -5
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposAdvisoryLockMixin.js +2 -1
- package/modules/@apostrophecms/widget-type/index.js +17 -3
- package/package.json +1 -1
- package/test/assets.js +338 -25
- package/test/extra_node_modules/@company/bundle/index.js +8 -0
- package/test/extra_node_modules/@company/bundle/ui/src/company.js +3 -0
- package/test/extra_node_modules/@company/bundle/ui/src/company.scss +3 -0
- package/test/login.js +427 -12
- package/test/modules/@company/bundle/index.js +10 -0
- package/test/modules/@company/bundle/ui/src/company.js +3 -0
- package/test/modules/@company/bundle/ui/src/company.scss +3 -0
- package/test/modules/bundle-edge/index.js +10 -0
- package/test/modules/bundle-edge/ui/src/edge.js +3 -0
- package/test/modules/bundle-edge/ui/src/edge.scss +3 -0
- package/test/modules/bundle-page/index.js +2 -1
- package/test/modules/bundle-page/ui/src/extra.js +3 -1
- package/test/modules/bundle-page/ui/src/extra.scss +3 -0
- package/test/modules/bundle-page/ui/src/main.js +3 -0
- package/test/modules/bundle-page/ui/src/main.scss +3 -0
- package/test/modules/bundle-page-type/index.js +12 -0
- package/test/modules/bundle-page-type/ui/src/another.js +3 -0
- package/test/modules/bundle-page-type/ui/src/index.js +3 -0
- package/test/modules/bundle-page-type/ui/src/index.scss +3 -0
- package/test/modules/bundle-page-type/ui/src/main.js +3 -0
- package/test/modules/bundle-page-type/ui/src/main.scss +3 -0
- package/test/modules/bundle-widget/ui/src/extra2.js +3 -1
- package/test-lib/test.js +9 -1
|
@@ -6,75 +6,46 @@
|
|
|
6
6
|
v-show="loaded"
|
|
7
7
|
:class="themeClass"
|
|
8
8
|
>
|
|
9
|
+
<transition name="fade-outer">
|
|
10
|
+
<div v-if="showNav" class="apos-login__nav">
|
|
11
|
+
<a
|
|
12
|
+
href="#"
|
|
13
|
+
class="apos-login__link apos-login--arrow-left"
|
|
14
|
+
@click.prevent="setStage('login')"
|
|
15
|
+
>{{ $t('apostrophe:loginBack') }}</a>
|
|
16
|
+
<a
|
|
17
|
+
:href="homeUrl"
|
|
18
|
+
class="apos-login__link apos-login--arrow-right"
|
|
19
|
+
>{{ $t('apostrophe:loginHome') }}</a>
|
|
20
|
+
</div>
|
|
21
|
+
</transition>
|
|
9
22
|
<div class="apos-login__wrapper">
|
|
10
23
|
<transition name="fade-body" mode="out-in">
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@done="requirementDone(requirement, $event)"
|
|
34
|
-
@block="requirementBlock(requirement)"
|
|
35
|
-
/>
|
|
36
|
-
<!-- TODO -->
|
|
37
|
-
<!-- <a href="#" class="apos-login__link">Forgot Password</a> -->
|
|
38
|
-
<AposButton
|
|
39
|
-
data-apos-test="loginSubmit"
|
|
40
|
-
:busy="busy"
|
|
41
|
-
:disabled="disabled"
|
|
42
|
-
type="primary"
|
|
43
|
-
label="apostrophe:login"
|
|
44
|
-
button-type="submit"
|
|
45
|
-
class="apos-login__submit"
|
|
46
|
-
:modifiers="['gradient-on-hover', 'block']"
|
|
47
|
-
@click="submit"
|
|
48
|
-
/>
|
|
49
|
-
</form>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
<div
|
|
53
|
-
key="2"
|
|
54
|
-
class="apos-login__upper"
|
|
55
|
-
v-else-if="activeSoloRequirement"
|
|
56
|
-
>
|
|
57
|
-
<TheAposLoginHeader
|
|
58
|
-
:env="context.env"
|
|
59
|
-
:name="context.name"
|
|
60
|
-
:error="$t(error)"
|
|
61
|
-
:tiny="true"
|
|
62
|
-
/>
|
|
63
|
-
<div class="apos-login__body">
|
|
64
|
-
<Component
|
|
65
|
-
v-if="!fetchingRequirementProps"
|
|
66
|
-
v-bind="getRequirementProps(activeSoloRequirement.name)"
|
|
67
|
-
:is="activeSoloRequirement.component"
|
|
68
|
-
:success="activeSoloRequirement.success"
|
|
69
|
-
:error="activeSoloRequirement.error"
|
|
70
|
-
@done="requirementDone(activeSoloRequirement, $event)"
|
|
71
|
-
@confirm="requirementConfirmed(activeSoloRequirement)"
|
|
72
|
-
/>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
24
|
+
<AposForgotPasswordForm
|
|
25
|
+
v-if="loaded && stage === 'forgotPassword'"
|
|
26
|
+
:context="context"
|
|
27
|
+
:context-error="error"
|
|
28
|
+
@redirect="onRedirect"
|
|
29
|
+
@set-stage="setStage"
|
|
30
|
+
/>
|
|
31
|
+
<AposResetPasswordForm
|
|
32
|
+
v-else-if="loaded && stage === 'resetPassword'"
|
|
33
|
+
:context="context"
|
|
34
|
+
:data="passwordResetData"
|
|
35
|
+
:context-error="error"
|
|
36
|
+
@redirect="onRedirect"
|
|
37
|
+
@set-stage="setStage"
|
|
38
|
+
/>
|
|
39
|
+
<AposLoginForm
|
|
40
|
+
v-else-if="loaded"
|
|
41
|
+
:context="context"
|
|
42
|
+
:context-error="error"
|
|
43
|
+
@redirect="onRedirect"
|
|
44
|
+
@set-stage="setStage"
|
|
45
|
+
/>
|
|
75
46
|
</transition>
|
|
76
47
|
</div>
|
|
77
|
-
<transition name="fade-
|
|
48
|
+
<transition name="fade-outer">
|
|
78
49
|
<div class="apos-login__footer" v-show="loaded">
|
|
79
50
|
<AposLogo class="apos-login__logo" />
|
|
80
51
|
<label class="apos-login__project-version">
|
|
@@ -89,94 +60,38 @@
|
|
|
89
60
|
<script>
|
|
90
61
|
import AposThemeMixin from 'Modules/@apostrophecms/ui/mixins/AposThemeMixin';
|
|
91
62
|
|
|
63
|
+
const STAGES = [
|
|
64
|
+
'login',
|
|
65
|
+
'forgotPassword',
|
|
66
|
+
'resetPassword'
|
|
67
|
+
];
|
|
68
|
+
|
|
92
69
|
export default {
|
|
93
70
|
name: 'TheAposLogin',
|
|
94
71
|
mixins: [ AposThemeMixin ],
|
|
95
72
|
data() {
|
|
96
73
|
return {
|
|
97
|
-
|
|
74
|
+
stage: STAGES[0],
|
|
98
75
|
mounted: false,
|
|
99
76
|
beforeCreateFinished: false,
|
|
100
77
|
error: '',
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
data: {},
|
|
104
|
-
hasErrors: false
|
|
105
|
-
},
|
|
106
|
-
schema: [
|
|
107
|
-
{
|
|
108
|
-
name: 'username',
|
|
109
|
-
label: 'Username',
|
|
110
|
-
placeholder: 'Enter username',
|
|
111
|
-
type: 'string',
|
|
112
|
-
required: true
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: 'password',
|
|
116
|
-
label: 'Password',
|
|
117
|
-
placeholder: 'Enter password',
|
|
118
|
-
type: 'password',
|
|
119
|
-
required: true
|
|
120
|
-
}
|
|
121
|
-
],
|
|
122
|
-
requirements: getRequirements(),
|
|
123
|
-
context: {},
|
|
124
|
-
requirementProps: {},
|
|
125
|
-
fetchingRequirementProps: false
|
|
78
|
+
passwordResetData: {},
|
|
79
|
+
context: {}
|
|
126
80
|
};
|
|
127
81
|
},
|
|
128
82
|
computed: {
|
|
129
83
|
loaded() {
|
|
130
84
|
return this.mounted && this.beforeCreateFinished;
|
|
131
85
|
},
|
|
132
|
-
|
|
133
|
-
return this.
|
|
134
|
-
!!this.beforeSubmitRequirements.find(requirement => !requirement.done);
|
|
135
|
-
},
|
|
136
|
-
beforeSubmitRequirements() {
|
|
137
|
-
return this.requirements.filter(requirement => requirement.phase === 'beforeSubmit');
|
|
86
|
+
showNav() {
|
|
87
|
+
return this.stage !== STAGES[0];
|
|
138
88
|
},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// beforeSubmit requirements are not presented solo.
|
|
142
|
-
activeSoloRequirement() {
|
|
143
|
-
return (this.phase === 'afterPasswordVerified') &&
|
|
144
|
-
this.requirements.find(requirement =>
|
|
145
|
-
(requirement.phase === 'afterPasswordVerified') && !requirement.done
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
watch: {
|
|
150
|
-
async activeSoloRequirement(newVal) {
|
|
151
|
-
if (
|
|
152
|
-
(this.phase === 'afterPasswordVerified') &&
|
|
153
|
-
(newVal?.phase === 'afterPasswordVerified') &&
|
|
154
|
-
newVal.propsRequired &&
|
|
155
|
-
!(newVal.success || newVal.error)
|
|
156
|
-
) {
|
|
157
|
-
try {
|
|
158
|
-
this.fetchingRequirementProps = true;
|
|
159
|
-
const data = await apos.http.post(`${apos.login.action}/requirement-props`, {
|
|
160
|
-
busy: true,
|
|
161
|
-
body: {
|
|
162
|
-
name: newVal.name,
|
|
163
|
-
incompleteToken: this.incompleteToken
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
this.requirementProps = {
|
|
167
|
-
...this.requirementProps,
|
|
168
|
-
[newVal.name]: data
|
|
169
|
-
};
|
|
170
|
-
} catch (e) {
|
|
171
|
-
this.error = e.message || 'apostrophe:loginErrorGeneric';
|
|
172
|
-
} finally {
|
|
173
|
-
this.fetchingRequirementProps = false;
|
|
174
|
-
}
|
|
175
|
-
} else {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
89
|
+
homeUrl() {
|
|
90
|
+
return `${apos.prefix}/`;
|
|
178
91
|
}
|
|
179
92
|
},
|
|
93
|
+
// We need it here and not in the login form because the version used in the footer.
|
|
94
|
+
// The context will be passed to every form, might be a good thing in the future.
|
|
180
95
|
async beforeCreate() {
|
|
181
96
|
const stateChange = parseInt(window.sessionStorage.getItem('aposStateChange'));
|
|
182
97
|
const seen = JSON.parse(window.sessionStorage.getItem('aposStateChangeSeen') || '{}');
|
|
@@ -193,167 +108,59 @@ export default {
|
|
|
193
108
|
this.context = await apos.http.post(`${apos.login.action}/context`, {
|
|
194
109
|
busy: true
|
|
195
110
|
});
|
|
196
|
-
this.requirementProps = this.context.requirementProps;
|
|
197
111
|
} catch (e) {
|
|
112
|
+
this.context = {};
|
|
198
113
|
this.error = e.message || 'apostrophe:loginErrorGeneric';
|
|
199
114
|
} finally {
|
|
200
115
|
this.beforeCreateFinished = true;
|
|
201
116
|
}
|
|
202
117
|
},
|
|
118
|
+
created() {
|
|
119
|
+
const url = new URL(document.location);
|
|
120
|
+
const data = {
|
|
121
|
+
email: url.searchParams.get('email'),
|
|
122
|
+
reset: url.searchParams.get('reset')
|
|
123
|
+
};
|
|
124
|
+
if (data.email && data.reset) {
|
|
125
|
+
this.passwordResetData = data;
|
|
126
|
+
this.setStage('resetPassword');
|
|
127
|
+
}
|
|
128
|
+
},
|
|
203
129
|
mounted() {
|
|
204
130
|
this.mounted = true;
|
|
205
131
|
},
|
|
206
132
|
methods: {
|
|
207
|
-
|
|
208
|
-
|
|
133
|
+
setStage(name) {
|
|
134
|
+
// 1. Enabled status per stage. A bit cryptic but effective.
|
|
135
|
+
// Search for a method composed of the `name` + `Enabled`
|
|
136
|
+
// (e.g. `forgotPasswordEnabled` and execute it (should return boolean).
|
|
137
|
+
// If no method is found it is enabled. Fallback to the default stage.
|
|
138
|
+
const enabled = this[`${name}Enabled`]?.() ?? true;
|
|
139
|
+
if (!enabled) {
|
|
140
|
+
this.stage = STAGES[0];
|
|
209
141
|
return;
|
|
210
142
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
},
|
|
216
|
-
async invokeInitialLoginApi() {
|
|
217
|
-
try {
|
|
218
|
-
const response = await apos.http.post(`${apos.login.action}/login`, {
|
|
219
|
-
busy: true,
|
|
220
|
-
body: {
|
|
221
|
-
...this.doc.data,
|
|
222
|
-
requirements: this.getInitialSubmitRequirementsData(),
|
|
223
|
-
session: true
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
if (response && response.incompleteToken) {
|
|
227
|
-
this.incompleteToken = response.incompleteToken;
|
|
228
|
-
this.phase = 'afterPasswordVerified';
|
|
229
|
-
} else {
|
|
230
|
-
this.redirectAfterLogin();
|
|
231
|
-
}
|
|
232
|
-
} catch (e) {
|
|
233
|
-
this.error = e.message || 'An error occurred. Please try again.';
|
|
234
|
-
this.phase = 'beforeSubmit';
|
|
235
|
-
} finally {
|
|
236
|
-
this.busy = false;
|
|
143
|
+
// 2. Set it only if it's a known stage
|
|
144
|
+
if (STAGES.includes(name)) {
|
|
145
|
+
this.stage = name;
|
|
146
|
+
return;
|
|
237
147
|
}
|
|
148
|
+
// 3. Fallback to the default stage
|
|
149
|
+
this.stage = STAGES[0];
|
|
238
150
|
},
|
|
239
|
-
|
|
240
|
-
return
|
|
241
|
-
.filter(r => r.phase !== 'afterPasswordVerified' || !r.done)
|
|
242
|
-
.map(r => ([
|
|
243
|
-
r.name,
|
|
244
|
-
r.value
|
|
245
|
-
])));
|
|
246
|
-
},
|
|
247
|
-
async invokeFinalLoginApi() {
|
|
248
|
-
try {
|
|
249
|
-
await apos.http.post(`${apos.login.action}/login`, {
|
|
250
|
-
busy: true,
|
|
251
|
-
body: {
|
|
252
|
-
...this.doc.data,
|
|
253
|
-
incompleteToken: this.incompleteToken,
|
|
254
|
-
requirements: this.getFinalSubmitRequirementsData(),
|
|
255
|
-
session: true
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
this.redirectAfterLogin();
|
|
259
|
-
} catch (e) {
|
|
260
|
-
this.error = e.message || 'An error occurred. Please try again.';
|
|
261
|
-
this.phase = 'beforeSubmit';
|
|
262
|
-
} finally {
|
|
263
|
-
this.busy = false;
|
|
264
|
-
}
|
|
151
|
+
forgotPasswordEnabled() {
|
|
152
|
+
return apos.login.passwordResetEnabled;
|
|
265
153
|
},
|
|
266
|
-
|
|
267
|
-
return
|
|
268
|
-
r.name,
|
|
269
|
-
r.value
|
|
270
|
-
])));
|
|
154
|
+
resetPasswordEnabled() {
|
|
155
|
+
return apos.login.passwordResetEnabled;
|
|
271
156
|
},
|
|
272
|
-
|
|
157
|
+
onRedirect(loc) {
|
|
273
158
|
window.sessionStorage.setItem('aposStateChange', Date.now());
|
|
274
159
|
window.sessionStorage.setItem('aposStateChangeSeen', '{}');
|
|
275
|
-
|
|
276
|
-
// Redisplay homepage with editing interface
|
|
277
|
-
location.assign(`${apos.prefix}/`);
|
|
278
|
-
},
|
|
279
|
-
async requirementBlock(requirementBlock) {
|
|
280
|
-
const requirement = this.requirements
|
|
281
|
-
.find(requirement => requirement.name === requirementBlock.name);
|
|
282
|
-
requirement.done = false;
|
|
283
|
-
requirement.value = undefined;
|
|
284
|
-
},
|
|
285
|
-
async requirementDone(requirementDone, value) {
|
|
286
|
-
const requirement = this.requirements
|
|
287
|
-
.find(requirement => requirement.name === requirementDone.name);
|
|
288
|
-
|
|
289
|
-
if (requirement.phase === 'beforeSubmit') {
|
|
290
|
-
requirement.done = true;
|
|
291
|
-
requirement.value = value;
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
requirement.error = null;
|
|
296
|
-
|
|
297
|
-
try {
|
|
298
|
-
await apos.http.post(`${apos.login.action}/requirement-verify`, {
|
|
299
|
-
busy: true,
|
|
300
|
-
body: {
|
|
301
|
-
name: requirement.name,
|
|
302
|
-
value,
|
|
303
|
-
incompleteToken: this.incompleteToken
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
requirement.success = true;
|
|
308
|
-
} catch (err) {
|
|
309
|
-
requirement.error = err;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Avoids the need for a deep watch
|
|
313
|
-
this.requirements = [ ...this.requirements ];
|
|
314
|
-
|
|
315
|
-
if (requirement.success && !requirement.askForConfirmation) {
|
|
316
|
-
requirement.done = true;
|
|
317
|
-
|
|
318
|
-
if (!this.activeSoloRequirement) {
|
|
319
|
-
await this.invokeFinalLoginApi();
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
},
|
|
323
|
-
|
|
324
|
-
async requirementConfirmed (requirementConfirmed) {
|
|
325
|
-
const requirement = this.requirements
|
|
326
|
-
.find(requirement => requirement.name === requirementConfirmed.name);
|
|
327
|
-
|
|
328
|
-
requirement.done = true;
|
|
329
|
-
|
|
330
|
-
if (!this.activeSoloRequirement) {
|
|
331
|
-
await this.invokeFinalLoginApi();
|
|
332
|
-
}
|
|
333
|
-
},
|
|
334
|
-
getRequirementProps(name) {
|
|
335
|
-
return this.requirementProps[name] || {};
|
|
160
|
+
location.assign(loc);
|
|
336
161
|
}
|
|
337
162
|
}
|
|
338
163
|
};
|
|
339
|
-
|
|
340
|
-
function getRequirements() {
|
|
341
|
-
const requirements = Object.entries(apos.login.requirements).map(([ name, requirement ]) => {
|
|
342
|
-
return {
|
|
343
|
-
name,
|
|
344
|
-
component: requirement.component || name,
|
|
345
|
-
...requirement,
|
|
346
|
-
done: false,
|
|
347
|
-
value: null,
|
|
348
|
-
success: null,
|
|
349
|
-
error: null
|
|
350
|
-
};
|
|
351
|
-
});
|
|
352
|
-
return [
|
|
353
|
-
...requirements.filter(r => r.phase === 'beforeSubmit'),
|
|
354
|
-
...requirements.filter(r => r.phase === 'afterPasswordVerified')
|
|
355
|
-
];
|
|
356
|
-
}
|
|
357
164
|
</script>
|
|
358
165
|
|
|
359
166
|
<style lang="scss">
|
|
@@ -377,14 +184,14 @@ function getRequirements() {
|
|
|
377
184
|
|
|
378
185
|
.fade-stage-enter-to,
|
|
379
186
|
.fade-body-enter-to,
|
|
380
|
-
.fade-
|
|
187
|
+
.fade-outer-enter-to,
|
|
381
188
|
.fade-body-leave {
|
|
382
189
|
opacity: 1;
|
|
383
190
|
}
|
|
384
191
|
|
|
385
192
|
.fade-stage-enter,
|
|
386
193
|
.fade-body-enter,
|
|
387
|
-
.fade-
|
|
194
|
+
.fade-outer-enter,
|
|
388
195
|
.fade-body-leave-to {
|
|
389
196
|
opacity: 0;
|
|
390
197
|
}
|
|
@@ -406,7 +213,7 @@ function getRequirements() {
|
|
|
406
213
|
transform: translateY(4px);
|
|
407
214
|
}
|
|
408
215
|
|
|
409
|
-
.fade-
|
|
216
|
+
.fade-outer-enter-active {
|
|
410
217
|
transition: opacity 0.4s linear;
|
|
411
218
|
transition-delay: 1s;
|
|
412
219
|
}
|
|
@@ -418,22 +225,61 @@ function getRequirements() {
|
|
|
418
225
|
height: 100vh;
|
|
419
226
|
background-color: var(--a-background-primary);
|
|
420
227
|
|
|
421
|
-
&
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
228
|
+
&__nav {
|
|
229
|
+
position: absolute;
|
|
230
|
+
top: 0;
|
|
231
|
+
right: 0;
|
|
232
|
+
left: 0;
|
|
233
|
+
display: flex;
|
|
234
|
+
justify-content: space-between;
|
|
235
|
+
align-items: center;
|
|
236
|
+
padding: $spacing-triple;
|
|
425
237
|
}
|
|
426
238
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
display:
|
|
430
|
-
|
|
239
|
+
&__link {
|
|
240
|
+
@include type-large;
|
|
241
|
+
display: inline-block;
|
|
242
|
+
text-decoration: underline;
|
|
243
|
+
text-underline-offset: 2px;
|
|
431
244
|
|
|
432
|
-
|
|
433
|
-
|
|
245
|
+
&:hover,
|
|
246
|
+
&:focus,
|
|
247
|
+
&:active {
|
|
248
|
+
color: var(--a-text-primary);
|
|
434
249
|
}
|
|
435
250
|
}
|
|
436
251
|
|
|
252
|
+
&--arrow-left,
|
|
253
|
+
&--arrow-right {
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: center;
|
|
257
|
+
gap: $spacing-half;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
&--arrow-left::before,
|
|
261
|
+
&--arrow-right::after {
|
|
262
|
+
content: '';
|
|
263
|
+
width: 3px;
|
|
264
|
+
height: 3px;
|
|
265
|
+
border: solid var(--a-text-primary);
|
|
266
|
+
border-width: 3px 3px 0 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
&--arrow-right::after {
|
|
270
|
+
transform: rotate(45deg);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
&--arrow-left::before {
|
|
274
|
+
transform: rotate(-135deg);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
&__wrapper {
|
|
278
|
+
width: 100%;
|
|
279
|
+
max-width: $login-container;
|
|
280
|
+
margin: 0 auto;
|
|
281
|
+
}
|
|
282
|
+
|
|
437
283
|
&__loader {
|
|
438
284
|
display: flex;
|
|
439
285
|
flex-direction: column;
|
|
@@ -472,8 +318,4 @@ function getRequirements() {
|
|
|
472
318
|
margin-left: auto;
|
|
473
319
|
}
|
|
474
320
|
}
|
|
475
|
-
|
|
476
|
-
.apos-login__submit ::v-deep .apos-button {
|
|
477
|
-
height: 47px;
|
|
478
|
-
}
|
|
479
321
|
</style>
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="apos-login__header"
|
|
4
|
-
:class="{'apos-login__header--tiny': tiny}"
|
|
4
|
+
:class="{'apos-login__header--tiny': tiny, 'apos-login__header--center': !!subtitle}"
|
|
5
5
|
>
|
|
6
|
+
<div class="apos-login__project-header">
|
|
7
|
+
<label
|
|
8
|
+
class="apos-login__project apos-login__project-env"
|
|
9
|
+
:class="[`apos-login__project-env--${env}`]"
|
|
10
|
+
>
|
|
11
|
+
{{ env }}
|
|
12
|
+
</label>
|
|
13
|
+
<label
|
|
14
|
+
v-if="subtitle"
|
|
15
|
+
class="apos-login__project apos-login__project-subtitle"
|
|
16
|
+
>
|
|
17
|
+
{{ subtitle }}
|
|
18
|
+
</label>
|
|
19
|
+
</div>
|
|
20
|
+
<label class="apos-login__project apos-login__project-name">
|
|
21
|
+
{{ title }}
|
|
22
|
+
</label>
|
|
6
23
|
<label
|
|
7
|
-
|
|
8
|
-
|
|
24
|
+
v-if="help"
|
|
25
|
+
class="apos-login--help"
|
|
9
26
|
>
|
|
10
|
-
{{
|
|
11
|
-
</label>
|
|
12
|
-
<label class="apos-login__project apos-login__project-name">
|
|
13
|
-
{{ name }}
|
|
27
|
+
{{ help }}
|
|
14
28
|
</label>
|
|
15
29
|
<label class="apos-login--error">
|
|
16
30
|
{{ error }}
|
|
@@ -26,24 +40,32 @@ export default {
|
|
|
26
40
|
type: String,
|
|
27
41
|
default: ''
|
|
28
42
|
},
|
|
29
|
-
|
|
43
|
+
title: {
|
|
30
44
|
type: String,
|
|
31
45
|
default: ''
|
|
32
46
|
},
|
|
33
|
-
|
|
34
|
-
type:
|
|
35
|
-
default:
|
|
47
|
+
subtitle: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: ''
|
|
50
|
+
},
|
|
51
|
+
help: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: ''
|
|
36
54
|
},
|
|
37
55
|
error: {
|
|
38
56
|
type: String,
|
|
39
57
|
default: ''
|
|
58
|
+
},
|
|
59
|
+
tiny: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
default: false
|
|
40
62
|
}
|
|
41
63
|
}
|
|
42
64
|
};
|
|
43
65
|
</script>
|
|
44
66
|
<style scoped lang='scss'>
|
|
45
|
-
|
|
46
67
|
.apos-login {
|
|
68
|
+
$this: &;
|
|
47
69
|
|
|
48
70
|
&__header {
|
|
49
71
|
z-index: $z-index-manager-display;
|
|
@@ -51,7 +73,22 @@ export default {
|
|
|
51
73
|
flex-direction: column;
|
|
52
74
|
justify-content: center;
|
|
53
75
|
align-items: flex-start;
|
|
54
|
-
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&__project-header {
|
|
79
|
+
display: flex;
|
|
80
|
+
gap: $spacing-base;
|
|
81
|
+
flex-wrap: nowrap;
|
|
82
|
+
margin-bottom: 15px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&__project-subtitle {
|
|
86
|
+
@include type-title;
|
|
87
|
+
margin: 0;
|
|
88
|
+
opacity: 0.6;
|
|
89
|
+
line-height: 1;
|
|
90
|
+
text-transform: capitalize;
|
|
91
|
+
white-space: nowrap;
|
|
55
92
|
}
|
|
56
93
|
|
|
57
94
|
&__project-name {
|
|
@@ -68,7 +105,6 @@ export default {
|
|
|
68
105
|
color: var(--a-white);
|
|
69
106
|
background: var(--a-success);
|
|
70
107
|
border-radius: 5px;
|
|
71
|
-
margin-bottom: 15px;
|
|
72
108
|
|
|
73
109
|
&--development {
|
|
74
110
|
background: var(--a-danger);
|
|
@@ -79,6 +115,13 @@ export default {
|
|
|
79
115
|
}
|
|
80
116
|
}
|
|
81
117
|
|
|
118
|
+
&--help {
|
|
119
|
+
@include type-label;
|
|
120
|
+
margin-top: $spacing-double;
|
|
121
|
+
text-align: center;
|
|
122
|
+
white-space: pre-line;
|
|
123
|
+
}
|
|
124
|
+
|
|
82
125
|
&--error {
|
|
83
126
|
@include type-help;
|
|
84
127
|
color: var(--a-danger);
|
|
@@ -87,6 +130,14 @@ export default {
|
|
|
87
130
|
margin-bottom: 15px;
|
|
88
131
|
}
|
|
89
132
|
|
|
133
|
+
&__header--center {
|
|
134
|
+
align-items: center;
|
|
135
|
+
|
|
136
|
+
#{$this}__project-header {
|
|
137
|
+
margin-bottom: $spacing-triple;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
90
141
|
&__header--tiny {
|
|
91
142
|
flex-direction: row;
|
|
92
143
|
// stylelint-disable-next-line scale-unlimited/declaration-strict-value
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Mixin for login form related common behavior.
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
props: {
|
|
5
|
+
contextError: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: ''
|
|
8
|
+
},
|
|
9
|
+
context: {
|
|
10
|
+
type: Object,
|
|
11
|
+
default: function() {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
data() {
|
|
17
|
+
return {
|
|
18
|
+
error: '',
|
|
19
|
+
doc: {
|
|
20
|
+
data: {},
|
|
21
|
+
hasErrors: false
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
computed: {
|
|
26
|
+
passwordResetEnabled() {
|
|
27
|
+
return apos.login.passwordResetEnabled;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
watch: {
|
|
31
|
+
contextError(newVal) {
|
|
32
|
+
// Copy it only once
|
|
33
|
+
if (!this.contextErrorReceived && newVal && !this.error) {
|
|
34
|
+
this.error = newVal;
|
|
35
|
+
this.contextErrorReceived = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
mounted() {
|
|
40
|
+
this.error = this.contextError;
|
|
41
|
+
if (this.contextError) {
|
|
42
|
+
this.contextErrorReceived = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|