pict-section-login 0.0.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/example_applications/custom_login/Custom-Login-Application.js +75 -0
- package/example_applications/custom_login/html/index.html +110 -0
- package/example_applications/custom_login/package.json +27 -0
- package/example_applications/harness_app/Harness-App-Application.js +167 -0
- package/example_applications/harness_app/Harness-App-Configuration.json +4 -0
- package/example_applications/harness_app/html/index.html +90 -0
- package/example_applications/harness_app/package.json +28 -0
- package/example_applications/harness_app/providers/PictRouter-HarnessApp.json +24 -0
- package/example_applications/harness_app/views/PictView-HarnessApp-Books.js +172 -0
- package/example_applications/harness_app/views/PictView-HarnessApp-Dashboard.js +158 -0
- package/example_applications/harness_app/views/PictView-HarnessApp-Layout.js +86 -0
- package/example_applications/harness_app/views/PictView-HarnessApp-Login.js +58 -0
- package/example_applications/harness_app/views/PictView-HarnessApp-TopBar.js +157 -0
- package/example_applications/harness_app/views/PictView-HarnessApp-Users.js +188 -0
- package/example_applications/oauth_login/OAuth-Login-Application.js +78 -0
- package/example_applications/oauth_login/html/index.html +57 -0
- package/example_applications/oauth_login/package.json +27 -0
- package/example_applications/orator_login/Orator-Login-Application.js +61 -0
- package/example_applications/orator_login/html/index.html +51 -0
- package/example_applications/orator_login/package.json +27 -0
- package/package.json +53 -0
- package/source/Pict-Section-Login-DefaultConfiguration.js +265 -0
- package/source/Pict-Section-Login.js +533 -0
- package/test/Browser_Integration_tests.js +588 -0
- package/test/Pict-Section-Login_tests.js +593 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const libPictApplication = require('pict-application');
|
|
2
|
+
const libPictSectionLogin = require('../../source/Pict-Section-Login.js');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A custom login view that demonstrates pointing the section at an
|
|
6
|
+
* arbitrary endpoint. The HTML page mocks window.fetch so this works
|
|
7
|
+
* entirely client-side with no server.
|
|
8
|
+
*/
|
|
9
|
+
class CustomLoginView extends libPictSectionLogin
|
|
10
|
+
{
|
|
11
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
12
|
+
{
|
|
13
|
+
super(pFable, pOptions, pServiceHash);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
onLoginSuccess(pSessionData)
|
|
17
|
+
{
|
|
18
|
+
this.log.info('Custom login succeeded!', pSessionData);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onLoginFailed(pError)
|
|
22
|
+
{
|
|
23
|
+
this.log.warn('Custom login failed: ' + pError);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onLogout()
|
|
27
|
+
{
|
|
28
|
+
this.log.info('Custom logout complete.');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const _CustomLoginViewConfiguration = (
|
|
33
|
+
{
|
|
34
|
+
"ViewIdentifier": "CustomLogin",
|
|
35
|
+
"TargetElementAddress": "#Pict-Login-Container",
|
|
36
|
+
"LoginEndpoint": "/api/custom-auth",
|
|
37
|
+
"LoginMethod": "POST",
|
|
38
|
+
"LogoutEndpoint": "/api/custom-logout",
|
|
39
|
+
"CheckSessionEndpoint": "/api/custom-session",
|
|
40
|
+
"CheckSessionOnLoad": false,
|
|
41
|
+
"ShowOAuthProviders": false
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
class CustomLoginApplication extends libPictApplication
|
|
45
|
+
{
|
|
46
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
47
|
+
{
|
|
48
|
+
super(pFable, pOptions, pServiceHash);
|
|
49
|
+
|
|
50
|
+
this.pict.addView('CustomLogin', _CustomLoginViewConfiguration, CustomLoginView);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onAfterInitialize()
|
|
54
|
+
{
|
|
55
|
+
super.onAfterInitialize();
|
|
56
|
+
let tmpView = this.pict.views.CustomLogin;
|
|
57
|
+
if (tmpView)
|
|
58
|
+
{
|
|
59
|
+
tmpView.render();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = CustomLoginApplication;
|
|
65
|
+
|
|
66
|
+
module.exports.default_configuration = (
|
|
67
|
+
{
|
|
68
|
+
"Name": "Custom Login Example",
|
|
69
|
+
"Hash": "CustomLoginExample",
|
|
70
|
+
"MainViewportViewIdentifier": "CustomLogin",
|
|
71
|
+
"pict_configuration":
|
|
72
|
+
{
|
|
73
|
+
"Product": "CustomLogin-Example"
|
|
74
|
+
}
|
|
75
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Custom Login - Pict Example</title>
|
|
7
|
+
<!-- PICT Dynamic View CSS Container -->
|
|
8
|
+
<style id="PICT-CSS"></style>
|
|
9
|
+
<!-- Red Rock Mesa Theme — Pict Example App -->
|
|
10
|
+
<style>
|
|
11
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
12
|
+
body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: #FAEDCD; color: #264653; }
|
|
13
|
+
|
|
14
|
+
/* --- Header Bar --- */
|
|
15
|
+
.pict-example-header { display: flex; align-items: stretch; background: #264653; border-bottom: 3px solid #E76F51; }
|
|
16
|
+
.pict-example-badge { background: #E76F51; color: #fff; padding: 0.6rem 1rem; font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; display: flex; align-items: center; gap: 0.5rem; }
|
|
17
|
+
.pict-example-badge svg { width: 14px; height: 14px; fill: #fff; flex-shrink: 0; }
|
|
18
|
+
.pict-example-app-name { padding: 0.6rem 1rem; color: #FAEDCD; font-size: 1.1rem; font-weight: 600; display: flex; align-items: center; }
|
|
19
|
+
.pict-example-module { margin-left: auto; padding: 0.6rem 1rem; color: #D4A373; font-size: 0.75rem; display: flex; align-items: center; letter-spacing: 0.03em; }
|
|
20
|
+
|
|
21
|
+
/* --- Content Area --- */
|
|
22
|
+
.pict-example-content { padding: 1.5rem; }
|
|
23
|
+
|
|
24
|
+
/* --- Info Banner --- */
|
|
25
|
+
.pict-example-info { background: #264653; color: #FAEDCD; border-radius: 6px; padding: 1rem 1.25rem; margin-bottom: 1.5rem; font-size: 0.85rem; line-height: 1.5; }
|
|
26
|
+
.pict-example-info code { background: rgba(250,237,205,0.15); padding: 0.15rem 0.4rem; border-radius: 3px; font-size: 0.8rem; }
|
|
27
|
+
</style>
|
|
28
|
+
<script>
|
|
29
|
+
// =============================================================
|
|
30
|
+
// MOCK FETCH — Simulates a custom authentication backend.
|
|
31
|
+
// In a real application you would NOT do this; the section
|
|
32
|
+
// would simply point at your real API endpoint.
|
|
33
|
+
// =============================================================
|
|
34
|
+
(function ()
|
|
35
|
+
{
|
|
36
|
+
var _realFetch = window.fetch;
|
|
37
|
+
var _mockSession = null;
|
|
38
|
+
|
|
39
|
+
window.fetch = function (pURL, pOptions)
|
|
40
|
+
{
|
|
41
|
+
var tmpURL = typeof pURL === 'string' ? pURL : pURL.toString();
|
|
42
|
+
|
|
43
|
+
// --- POST /api/custom-auth ---
|
|
44
|
+
if (tmpURL === '/api/custom-auth' && pOptions && pOptions.method === 'POST')
|
|
45
|
+
{
|
|
46
|
+
var tmpBody = JSON.parse(pOptions.body || '{}');
|
|
47
|
+
var tmpUser = tmpBody.UserName || '';
|
|
48
|
+
var tmpPass = tmpBody.Password || '';
|
|
49
|
+
|
|
50
|
+
if (tmpUser === 'demo' && tmpPass === 'demo')
|
|
51
|
+
{
|
|
52
|
+
_mockSession = {
|
|
53
|
+
LoggedIn: true,
|
|
54
|
+
SessionID: 'mock-session-' + Date.now(),
|
|
55
|
+
UserID: 1,
|
|
56
|
+
UserRecord: { LoginID: 'demo', IDUser: 1, FullName: 'Demo User', Email: 'demo@example.com' }
|
|
57
|
+
};
|
|
58
|
+
return Promise.resolve(new Response(JSON.stringify(_mockSession), { status: 200, headers: { 'Content-Type': 'application/json' } }));
|
|
59
|
+
}
|
|
60
|
+
else
|
|
61
|
+
{
|
|
62
|
+
return Promise.resolve(new Response(JSON.stringify({ LoggedIn: false, Error: 'Invalid credentials. Try demo / demo.' }), { status: 200, headers: { 'Content-Type': 'application/json' } }));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- GET /api/custom-logout ---
|
|
67
|
+
if (tmpURL === '/api/custom-logout')
|
|
68
|
+
{
|
|
69
|
+
_mockSession = null;
|
|
70
|
+
return Promise.resolve(new Response(JSON.stringify({ LoggedIn: false }), { status: 200, headers: { 'Content-Type': 'application/json' } }));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// --- GET /api/custom-session ---
|
|
74
|
+
if (tmpURL === '/api/custom-session')
|
|
75
|
+
{
|
|
76
|
+
if (_mockSession)
|
|
77
|
+
{
|
|
78
|
+
return Promise.resolve(new Response(JSON.stringify(_mockSession), { status: 200, headers: { 'Content-Type': 'application/json' } }));
|
|
79
|
+
}
|
|
80
|
+
return Promise.resolve(new Response(JSON.stringify({ LoggedIn: false }), { status: 200, headers: { 'Content-Type': 'application/json' } }));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// All other requests pass through to the real fetch
|
|
84
|
+
return _realFetch.apply(window, arguments);
|
|
85
|
+
};
|
|
86
|
+
})();
|
|
87
|
+
</script>
|
|
88
|
+
<script src="./pict.js" type="text/javascript"></script>
|
|
89
|
+
<script type="text/javascript">Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(CustomLoginExample, 1); });</script>
|
|
90
|
+
</head>
|
|
91
|
+
<body>
|
|
92
|
+
<div class="pict-example-header">
|
|
93
|
+
<div class="pict-example-badge">
|
|
94
|
+
<svg viewBox="0 0 16 16"><polygon points="8,1 10,6 16,6 11,9.5 13,15 8,11.5 3,15 5,9.5 0,6 6,6"/></svg>
|
|
95
|
+
Pict Example
|
|
96
|
+
</div>
|
|
97
|
+
<div class="pict-example-app-name">Custom Login</div>
|
|
98
|
+
<div class="pict-example-module">pict-section-login</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="pict-example-content">
|
|
101
|
+
<div class="pict-example-info">
|
|
102
|
+
This example demonstrates <code>pict-section-login</code> pointed at a <strong>custom endpoint</strong>
|
|
103
|
+
(<code>/api/custom-auth</code>). No server is needed — <code>window.fetch</code> is mocked
|
|
104
|
+
to simulate a backend. Use <code>demo</code> / <code>demo</code> to log in.
|
|
105
|
+
</div>
|
|
106
|
+
<div id="Pict-Login-Container"></div>
|
|
107
|
+
</div>
|
|
108
|
+
<script src="./custom_login_example.js" type="text/javascript"></script>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "custom_login_example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Custom login example — pict-section-login with a mocked custom endpoint",
|
|
5
|
+
"main": "Custom-Login-Application.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node Custom-Login-Application.js",
|
|
8
|
+
"build": "npx quack build && npx quack copy"
|
|
9
|
+
},
|
|
10
|
+
"author": "steven velozo <steven@velozo.com>",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
},
|
|
14
|
+
"copyFilesSettings": {
|
|
15
|
+
"whenFileExists": "overwrite"
|
|
16
|
+
},
|
|
17
|
+
"copyFiles": [
|
|
18
|
+
{
|
|
19
|
+
"from": "./html/*",
|
|
20
|
+
"to": "./dist/"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"from": "../../node_modules/pict/dist/*",
|
|
24
|
+
"to": "./dist/"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const libPictApplication = require('pict-application');
|
|
2
|
+
const libPictRouter = require('pict-router');
|
|
3
|
+
|
|
4
|
+
// Views
|
|
5
|
+
const libViewLogin = require('./views/PictView-HarnessApp-Login.js');
|
|
6
|
+
const libViewLayout = require('./views/PictView-HarnessApp-Layout.js');
|
|
7
|
+
const libViewTopBar = require('./views/PictView-HarnessApp-TopBar.js');
|
|
8
|
+
const libViewDashboard = require('./views/PictView-HarnessApp-Dashboard.js');
|
|
9
|
+
const libViewBooks = require('./views/PictView-HarnessApp-Books.js');
|
|
10
|
+
const libViewUsers = require('./views/PictView-HarnessApp-Users.js');
|
|
11
|
+
|
|
12
|
+
class HarnessAppApplication extends libPictApplication
|
|
13
|
+
{
|
|
14
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
15
|
+
{
|
|
16
|
+
super(pFable, pOptions, pServiceHash);
|
|
17
|
+
|
|
18
|
+
// Router provider — SkipRouteResolveOnAdd is set in the config JSON
|
|
19
|
+
// so routes only resolve after the layout renders (not during construction).
|
|
20
|
+
this.pict.addProvider('PictRouter',
|
|
21
|
+
require('./providers/PictRouter-HarnessApp.json'),
|
|
22
|
+
libPictRouter);
|
|
23
|
+
|
|
24
|
+
// Login view (extends pict-section-login)
|
|
25
|
+
this.pict.addView('HarnessApp-Login', libViewLogin.default_configuration, libViewLogin);
|
|
26
|
+
|
|
27
|
+
// Layout shell (top bar + content area)
|
|
28
|
+
this.pict.addView('HarnessApp-Layout', libViewLayout.default_configuration, libViewLayout);
|
|
29
|
+
|
|
30
|
+
// Top bar navigation
|
|
31
|
+
this.pict.addView('HarnessApp-TopBar', libViewTopBar.default_configuration, libViewTopBar);
|
|
32
|
+
|
|
33
|
+
// Content views (rendered into the content area by the router)
|
|
34
|
+
this.pict.addView('HarnessApp-Dashboard', libViewDashboard.default_configuration, libViewDashboard);
|
|
35
|
+
this.pict.addView('HarnessApp-Books', libViewBooks.default_configuration, libViewBooks);
|
|
36
|
+
this.pict.addView('HarnessApp-Users', libViewUsers.default_configuration, libViewUsers);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onAfterInitializeAsync(fCallback)
|
|
40
|
+
{
|
|
41
|
+
// Render the login form first
|
|
42
|
+
this.pict.views['HarnessApp-Login'].render();
|
|
43
|
+
|
|
44
|
+
// Inject CSS so the login section styling is applied immediately
|
|
45
|
+
this.pict.CSSMap.injectCSS();
|
|
46
|
+
|
|
47
|
+
// Check if a session already exists (e.g. cookie from a previous visit).
|
|
48
|
+
// The login view's onSessionChecked hook will call showProtectedApp()
|
|
49
|
+
// if a valid session is found.
|
|
50
|
+
this.pict.views['HarnessApp-Login'].checkSession();
|
|
51
|
+
|
|
52
|
+
return super.onAfterInitializeAsync(fCallback);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ===== Application-Level Navigation =====
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Switch from the login screen to the protected application.
|
|
59
|
+
* Called by the login view after a successful login or session check.
|
|
60
|
+
*/
|
|
61
|
+
showProtectedApp()
|
|
62
|
+
{
|
|
63
|
+
// Hide the login container
|
|
64
|
+
let tmpLoginElements = this.pict.ContentAssignment.getElement('#HarnessApp-Login-Container');
|
|
65
|
+
if (tmpLoginElements && tmpLoginElements.length > 0)
|
|
66
|
+
{
|
|
67
|
+
tmpLoginElements[0].style.display = 'none';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Show the protected app container
|
|
71
|
+
let tmpAppElements = this.pict.ContentAssignment.getElement('#HarnessApp-Container');
|
|
72
|
+
if (tmpAppElements && tmpAppElements.length > 0)
|
|
73
|
+
{
|
|
74
|
+
tmpAppElements[0].style.display = 'block';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Render the layout shell (triggers TopBar, Dashboard, CSS injection, and router resolve)
|
|
78
|
+
this.pict.views['HarnessApp-Layout'].render();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Switch from the protected application back to the login screen.
|
|
83
|
+
* Called by the doLogout() method after the session is destroyed.
|
|
84
|
+
*/
|
|
85
|
+
showLogin()
|
|
86
|
+
{
|
|
87
|
+
// Clear session data
|
|
88
|
+
this.pict.AppData.Session = null;
|
|
89
|
+
|
|
90
|
+
// Hide the protected app container
|
|
91
|
+
let tmpAppElements = this.pict.ContentAssignment.getElement('#HarnessApp-Container');
|
|
92
|
+
if (tmpAppElements && tmpAppElements.length > 0)
|
|
93
|
+
{
|
|
94
|
+
tmpAppElements[0].style.display = 'none';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Show the login container and re-render
|
|
98
|
+
let tmpLoginElements = this.pict.ContentAssignment.getElement('#HarnessApp-Login-Container');
|
|
99
|
+
if (tmpLoginElements && tmpLoginElements.length > 0)
|
|
100
|
+
{
|
|
101
|
+
tmpLoginElements[0].style.display = 'block';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Reset the login view state and re-render
|
|
105
|
+
let tmpLoginView = this.pict.views['HarnessApp-Login'];
|
|
106
|
+
if (tmpLoginView)
|
|
107
|
+
{
|
|
108
|
+
tmpLoginView.authenticated = false;
|
|
109
|
+
tmpLoginView.sessionData = null;
|
|
110
|
+
tmpLoginView.initialRenderComplete = false;
|
|
111
|
+
tmpLoginView.render();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Render a specific content view into the content container.
|
|
117
|
+
* Called by the router when a route template matches.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} pViewIdentifier - The view identifier to render
|
|
120
|
+
*/
|
|
121
|
+
showView(pViewIdentifier)
|
|
122
|
+
{
|
|
123
|
+
if (pViewIdentifier in this.pict.views)
|
|
124
|
+
{
|
|
125
|
+
this.pict.views[pViewIdentifier].render();
|
|
126
|
+
}
|
|
127
|
+
else
|
|
128
|
+
{
|
|
129
|
+
this.pict.log.warn('View [' + pViewIdentifier + '] not found; falling back to Dashboard.');
|
|
130
|
+
this.pict.views['HarnessApp-Dashboard'].render();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Navigate to a route using pict-router.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} pRoute - The route path (e.g. '/Dashboard', '/Books')
|
|
138
|
+
*/
|
|
139
|
+
navigateTo(pRoute)
|
|
140
|
+
{
|
|
141
|
+
this.pict.providers.PictRouter.navigate(pRoute);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Log out and return to the login screen.
|
|
146
|
+
* Called from the TopBar logout button.
|
|
147
|
+
*/
|
|
148
|
+
doLogout()
|
|
149
|
+
{
|
|
150
|
+
let tmpLoginView = this.pict.views['HarnessApp-Login'];
|
|
151
|
+
if (tmpLoginView)
|
|
152
|
+
{
|
|
153
|
+
tmpLoginView.logout(() =>
|
|
154
|
+
{
|
|
155
|
+
this.showLogin();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else
|
|
159
|
+
{
|
|
160
|
+
this.showLogin();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = HarnessAppApplication;
|
|
166
|
+
|
|
167
|
+
module.exports.default_configuration = require('./Harness-App-Configuration.json');
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Bookstore Harness - Pict Login + Router</title>
|
|
7
|
+
<!-- PICT Dynamic View CSS Container -->
|
|
8
|
+
<style id="PICT-CSS"></style>
|
|
9
|
+
<!-- Red Rock Mesa Base Theme -->
|
|
10
|
+
<style>
|
|
11
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
12
|
+
body
|
|
13
|
+
{
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
17
|
+
background: #FAEDCD;
|
|
18
|
+
color: #264653;
|
|
19
|
+
}
|
|
20
|
+
a { color: inherit; }
|
|
21
|
+
|
|
22
|
+
/* --- Login wrapper (centered on page) --- */
|
|
23
|
+
#HarnessApp-Login-Container
|
|
24
|
+
{
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
min-height: 100vh;
|
|
30
|
+
padding: 2rem;
|
|
31
|
+
}
|
|
32
|
+
.harnessapp-login-header
|
|
33
|
+
{
|
|
34
|
+
text-align: center;
|
|
35
|
+
margin-bottom: 1.5rem;
|
|
36
|
+
}
|
|
37
|
+
.harnessapp-login-header h1
|
|
38
|
+
{
|
|
39
|
+
font-size: 1.6rem;
|
|
40
|
+
color: #264653;
|
|
41
|
+
margin: 0 0 0.25rem 0;
|
|
42
|
+
}
|
|
43
|
+
.harnessapp-login-header p
|
|
44
|
+
{
|
|
45
|
+
color: #888;
|
|
46
|
+
font-size: 0.85rem;
|
|
47
|
+
margin: 0;
|
|
48
|
+
}
|
|
49
|
+
.harnessapp-login-hint
|
|
50
|
+
{
|
|
51
|
+
margin-top: 1rem;
|
|
52
|
+
text-align: center;
|
|
53
|
+
font-size: 0.8rem;
|
|
54
|
+
color: #999;
|
|
55
|
+
}
|
|
56
|
+
.harnessapp-login-hint code
|
|
57
|
+
{
|
|
58
|
+
background: #264653;
|
|
59
|
+
color: #FAEDCD;
|
|
60
|
+
padding: 0.1rem 0.35rem;
|
|
61
|
+
border-radius: 3px;
|
|
62
|
+
font-size: 0.75rem;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
65
|
+
<script src="./pict.js" type="text/javascript"></script>
|
|
66
|
+
<script type="text/javascript">
|
|
67
|
+
Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(HarnessApp, 1); });
|
|
68
|
+
</script>
|
|
69
|
+
</head>
|
|
70
|
+
<body>
|
|
71
|
+
|
|
72
|
+
<!-- ===== Login Screen (visible when unauthenticated) ===== -->
|
|
73
|
+
<div id="HarnessApp-Login-Container">
|
|
74
|
+
<div class="harnessapp-login-header">
|
|
75
|
+
<h1>Bookstore Harness</h1>
|
|
76
|
+
<p>pict-section-login + pict-router</p>
|
|
77
|
+
</div>
|
|
78
|
+
<!-- pict-section-login renders its login card into this container -->
|
|
79
|
+
<div id="Pict-Login-Container"></div>
|
|
80
|
+
<div class="harnessapp-login-hint">
|
|
81
|
+
Use any valid user (e.g. <code>admin</code>, <code>jdoe</code>, <code>bsmith</code>) with any password.
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<!-- ===== Protected App (hidden until authenticated) ===== -->
|
|
86
|
+
<div id="HarnessApp-Container" style="display:none"></div>
|
|
87
|
+
|
|
88
|
+
<script src="./harness_app.js" type="text/javascript"></script>
|
|
89
|
+
</body>
|
|
90
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "harness_app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Full application example — pict-section-login + pict-router against retold-harness bookstore",
|
|
5
|
+
"main": "Harness-App-Application.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node Harness-App-Application.js",
|
|
8
|
+
"build": "npx quack build && npx quack copy"
|
|
9
|
+
},
|
|
10
|
+
"author": "steven velozo <steven@velozo.com>",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"pict-router": "^1.0.8"
|
|
14
|
+
},
|
|
15
|
+
"copyFilesSettings": {
|
|
16
|
+
"whenFileExists": "overwrite"
|
|
17
|
+
},
|
|
18
|
+
"copyFiles": [
|
|
19
|
+
{
|
|
20
|
+
"from": "./html/*",
|
|
21
|
+
"to": "./dist/"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"from": "../../node_modules/pict/dist/*",
|
|
25
|
+
"to": "./dist/"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ProviderIdentifier": "Pict-Router",
|
|
3
|
+
|
|
4
|
+
"AutoInitialize": true,
|
|
5
|
+
"AutoInitializeOrdinal": 0,
|
|
6
|
+
|
|
7
|
+
"SkipRouteResolveOnAdd": true,
|
|
8
|
+
|
|
9
|
+
"Routes":
|
|
10
|
+
[
|
|
11
|
+
{
|
|
12
|
+
"path": "/Dashboard",
|
|
13
|
+
"template": "{~LV:Pict.PictApplication.showView(`HarnessApp-Dashboard`)~}"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "/Books",
|
|
17
|
+
"template": "{~LV:Pict.PictApplication.showView(`HarnessApp-Books`)~}"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "/Users",
|
|
21
|
+
"template": "{~LV:Pict.PictApplication.showView(`HarnessApp-Users`)~}"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|