express-generator-typescript 2.4.7 → 2.4.10
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/lib/express-generator-typescript.js +1 -1
- package/lib/project-files/package.json +2 -1
- package/lib/project-files/spec/support/apiCb.ts +76 -0
- package/lib/project-files/spec/tests/users.spec.ts +98 -135
- package/lib/project-files/spec/types/misc.ts +18 -0
- package/lib/project-files/src/models/User.ts +13 -27
- package/lib/project-files/src/public/scripts/http.js +3 -1
- package/lib/project-files/src/public/scripts/users.js +31 -65
- package/lib/project-files/src/public/stylesheets/users.css +4 -6
- package/lib/project-files/src/repos/UserRepo.ts +6 -1
- package/lib/project-files/src/repos/database.json +22 -1
- package/lib/project-files/src/routes/types/express/misc.ts +1 -5
- package/lib/project-files/src/routes/types/types.ts +0 -8
- package/lib/project-files/src/views/users.html +100 -5
- package/lib/project-files-auth/package.json +2 -1
- package/lib/project-files-auth/spec/support/apiCb.ts +76 -0
- package/lib/project-files-auth/spec/support/login.ts +3 -6
- package/lib/project-files-auth/spec/tests/auth.spec.ts +50 -67
- package/lib/project-files-auth/spec/tests/users.spec.ts +98 -142
- package/lib/project-files-auth/spec/types/misc.ts +18 -0
- package/lib/project-files-auth/src/models/User.ts +17 -12
- package/lib/project-files-auth/src/public/scripts/http.js +3 -1
- package/lib/project-files-auth/src/public/scripts/login.js +1 -3
- package/lib/project-files-auth/src/public/scripts/users.js +31 -57
- package/lib/project-files-auth/src/public/stylesheets/users.css +4 -11
- package/lib/project-files-auth/src/repos/UserRepo.ts +6 -1
- package/lib/project-files-auth/src/repos/database.json +1 -1
- package/lib/project-files-auth/src/services/UserService.ts +2 -8
- package/lib/project-files-auth/src/util/SessionUtil.ts +22 -19
- package/lib/project-files-auth/src/views/login.html +26 -3
- package/lib/project-files-auth/src/views/users.html +106 -8
- package/package.json +1 -1
- package/lib/project-files/spec/support/types.ts +0 -1
- package/lib/project-files-auth/spec/support/types.ts +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import supertest, { Test
|
|
1
|
+
import supertest, { Test } from 'supertest';
|
|
2
2
|
import TestAgent from 'supertest/lib/agent';
|
|
3
3
|
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
4
4
|
import insertUrlParams from 'inserturlparams';
|
|
@@ -10,225 +10,181 @@ import User, { IUser } from '@src/models/User';
|
|
|
10
10
|
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
11
11
|
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
12
12
|
|
|
13
|
-
import login from '../support/login';
|
|
14
13
|
import Paths from 'spec/support/Paths';
|
|
15
|
-
import
|
|
14
|
+
import apiCb from 'spec/support/apiCb';
|
|
15
|
+
import login from 'spec/support/login';
|
|
16
|
+
import { TApiCb } from 'spec/types/misc';
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
// **** Variables **** //
|
|
19
|
-
|
|
20
|
-
// StatusCodes
|
|
21
|
-
const {
|
|
22
|
-
OK,
|
|
23
|
-
CREATED,
|
|
24
|
-
NOT_FOUND,
|
|
25
|
-
BAD_REQUEST,
|
|
26
|
-
} = HttpStatusCodes;
|
|
27
|
-
|
|
28
19
|
// Dummy users for GET req
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// Dummy update user
|
|
36
|
-
const DummyUserData = {
|
|
37
|
-
user: User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
38
|
-
} as const;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// **** Types **** //
|
|
42
|
-
|
|
43
|
-
type TRes = Omit<Response, 'body'> & {
|
|
44
|
-
body: {
|
|
45
|
-
users: IUser[];
|
|
46
|
-
error: string;
|
|
47
|
-
}
|
|
20
|
+
const getDummyUsers = () => {
|
|
21
|
+
return [
|
|
22
|
+
User.new('Sean Maxwell', 'sean.maxwell@gmail.com'),
|
|
23
|
+
User.new('John Smith', 'john.smith@gmail.com'),
|
|
24
|
+
User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
25
|
+
];
|
|
48
26
|
};
|
|
49
27
|
|
|
50
28
|
|
|
51
|
-
//
|
|
52
|
-
|
|
29
|
+
// Tests
|
|
53
30
|
describe('UserRouter', () => {
|
|
54
31
|
|
|
55
|
-
let agent: TestAgent<Test
|
|
56
|
-
jwtCookie: string;
|
|
32
|
+
let agent: TestAgent<Test>;
|
|
57
33
|
|
|
58
34
|
// Run before all tests
|
|
59
35
|
beforeAll((done) => {
|
|
60
36
|
agent = supertest.agent(app);
|
|
61
|
-
login(agent,
|
|
62
|
-
|
|
37
|
+
login(agent, cookie => {
|
|
38
|
+
agent.set('Cookie', cookie);
|
|
63
39
|
done();
|
|
64
40
|
});
|
|
65
41
|
});
|
|
66
42
|
|
|
67
|
-
//
|
|
43
|
+
// Get all users
|
|
68
44
|
describe(`"GET:${Paths.Users.Get}"`, () => {
|
|
69
45
|
|
|
70
|
-
|
|
46
|
+
// Setup API
|
|
47
|
+
const api = (cb: TApiCb) =>
|
|
71
48
|
agent
|
|
72
49
|
.get(Paths.Users.Get)
|
|
73
|
-
.
|
|
50
|
+
.end(apiCb(cb));
|
|
74
51
|
|
|
75
52
|
// Success
|
|
76
53
|
it('should return a JSON object with all the users and a status code ' +
|
|
77
|
-
`of "${OK}" if the request was successful.`, (done) => {
|
|
54
|
+
`of "${HttpStatusCodes.OK}" if the request was successful.`, (done) => {
|
|
78
55
|
// Add spy
|
|
79
|
-
|
|
56
|
+
const data = getDummyUsers();
|
|
57
|
+
spyOn(UserRepo, 'getAll').and.resolveTo(data);
|
|
80
58
|
// Call API
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(user).toEqual(DummyGetAllUsers[i]);
|
|
87
|
-
}
|
|
88
|
-
done();
|
|
89
|
-
});
|
|
59
|
+
api(res => {
|
|
60
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
61
|
+
expect(res.body).toEqual({ users: data });
|
|
62
|
+
done();
|
|
63
|
+
});
|
|
90
64
|
});
|
|
91
65
|
});
|
|
92
66
|
|
|
93
67
|
// Test add user
|
|
94
68
|
describe(`"POST:${Paths.Users.Add}"`, () => {
|
|
95
69
|
|
|
96
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
70
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
71
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
97
72
|
|
|
98
|
-
|
|
73
|
+
// Setup API
|
|
74
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
99
75
|
agent
|
|
100
76
|
.post(Paths.Users.Add)
|
|
101
|
-
.
|
|
102
|
-
.
|
|
77
|
+
.send({ user })
|
|
78
|
+
.end(apiCb(cb));
|
|
103
79
|
|
|
104
80
|
// Test add user success
|
|
105
|
-
it(`should return a status code of "${CREATED}" if the
|
|
106
|
-
'successful.', (done) => {
|
|
81
|
+
it(`should return a status code of "${HttpStatusCodes.CREATED}" if the ` +
|
|
82
|
+
'request was successful.', (done) => {
|
|
107
83
|
// Spy
|
|
108
84
|
spyOn(UserRepo, 'add').and.resolveTo();
|
|
109
85
|
// Call api
|
|
110
|
-
callApi(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
done();
|
|
115
|
-
});
|
|
86
|
+
callApi(DUMMY_USER, res => {
|
|
87
|
+
expect(res.status).toBe(HttpStatusCodes.CREATED);
|
|
88
|
+
done();
|
|
89
|
+
});
|
|
116
90
|
});
|
|
117
91
|
|
|
118
92
|
// Missing param
|
|
119
|
-
it(
|
|
120
|
-
`
|
|
93
|
+
it(`should return a JSON object with an error message of "${ERROR_MSG}" ` +
|
|
94
|
+
`and a status code of "${HttpStatusCodes.BAD_REQUEST}" if the user ` +
|
|
121
95
|
'param was missing.', (done) => {
|
|
122
96
|
// Call api
|
|
123
|
-
callApi({
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
});
|
|
97
|
+
callApi(null, res => {
|
|
98
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
99
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
100
|
+
done();
|
|
101
|
+
});
|
|
129
102
|
});
|
|
130
103
|
});
|
|
131
104
|
|
|
132
|
-
//
|
|
105
|
+
// Update users
|
|
133
106
|
describe(`"PUT:${Paths.Users.Update}"`, () => {
|
|
134
107
|
|
|
135
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
108
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
109
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
136
110
|
|
|
137
|
-
|
|
111
|
+
// Setup API
|
|
112
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
138
113
|
agent
|
|
139
114
|
.put(Paths.Users.Update)
|
|
140
|
-
.
|
|
141
|
-
.
|
|
115
|
+
.send({ user })
|
|
116
|
+
.end(apiCb(cb));
|
|
142
117
|
|
|
143
118
|
// Success
|
|
144
|
-
it(`should return a status code of "${OK}" if the
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
expect(res.body.error).toBeUndefined();
|
|
154
|
-
done();
|
|
155
|
-
});
|
|
119
|
+
it(`should return a status code of "${HttpStatusCodes.OK}" if the ` +
|
|
120
|
+
'request was successful.', (done) => {
|
|
121
|
+
// Setup spies
|
|
122
|
+
spyOn(UserRepo, 'update').and.resolveTo();
|
|
123
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
124
|
+
// Call api
|
|
125
|
+
callApi(DUMMY_USER, res => {
|
|
126
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
127
|
+
done();
|
|
156
128
|
});
|
|
129
|
+
});
|
|
157
130
|
|
|
158
131
|
// Param missing
|
|
159
|
-
it(
|
|
160
|
-
`
|
|
132
|
+
it(`should return a JSON object with an error message of "${ERROR_MSG}" ` +
|
|
133
|
+
`and a status code of "${HttpStatusCodes.BAD_REQUEST}" if the user ` +
|
|
161
134
|
'param was missing.', (done) => {
|
|
162
135
|
// Call api
|
|
163
|
-
callApi({
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
});
|
|
136
|
+
callApi(null, res => {
|
|
137
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
138
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
139
|
+
done();
|
|
140
|
+
});
|
|
169
141
|
});
|
|
170
142
|
|
|
171
143
|
// User not found
|
|
172
144
|
it('should return a JSON object with the error message of ' +
|
|
173
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
174
|
-
|
|
145
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
146
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, (done) => {
|
|
175
147
|
// Call api
|
|
176
|
-
callApi(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
});
|
|
148
|
+
callApi(DUMMY_USER, res => {
|
|
149
|
+
expect(res.status).toBe(HttpStatusCodes.NOT_FOUND);
|
|
150
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
151
|
+
done();
|
|
152
|
+
});
|
|
182
153
|
});
|
|
183
154
|
});
|
|
184
155
|
|
|
185
|
-
//
|
|
156
|
+
// Delete User
|
|
186
157
|
describe(`"DELETE:${Paths.Users.Delete}"`, () => {
|
|
187
158
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const callApi = (id: number) =>
|
|
159
|
+
// Call API
|
|
160
|
+
const callApi = (id: number, cb: TApiCb) =>
|
|
191
161
|
agent
|
|
192
162
|
.delete(insertUrlParams(Paths.Users.Delete, { id }))
|
|
193
|
-
.
|
|
163
|
+
.end(apiCb(cb));
|
|
194
164
|
|
|
195
165
|
// Success
|
|
196
|
-
it(`should return a status code of "${OK}" if the
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
expect(res.body.error).toBeUndefined();
|
|
206
|
-
done();
|
|
207
|
-
});
|
|
166
|
+
it(`should return a status code of "${HttpStatusCodes.OK}" if the ` +
|
|
167
|
+
'request was successful.', (done) => {
|
|
168
|
+
// Setup spies
|
|
169
|
+
spyOn(UserRepo, 'delete').and.resolveTo();
|
|
170
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
171
|
+
// Call api
|
|
172
|
+
callApi(5, res => {
|
|
173
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
174
|
+
done();
|
|
208
175
|
});
|
|
176
|
+
});
|
|
209
177
|
|
|
210
178
|
// User not found
|
|
211
179
|
it('should return a JSON object with the error message of ' +
|
|
212
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// Invalid param
|
|
223
|
-
it(`should return a status code of "${BAD_REQUEST}" and return an error ` +
|
|
224
|
-
`message of "${VALIDATOR_ERR}" if the id was not a valid number`,
|
|
225
|
-
(done) => {
|
|
226
|
-
callApi('horse' as unknown as number)
|
|
227
|
-
.end((_: Error, res: TRes) => {
|
|
228
|
-
expect(res.status).toBe(BAD_REQUEST);
|
|
229
|
-
expect(res.body.error).toBe(VALIDATOR_ERR);
|
|
230
|
-
done();
|
|
231
|
-
});
|
|
180
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
181
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, done => {
|
|
182
|
+
// Setup spies
|
|
183
|
+
callApi(-1, res => {
|
|
184
|
+
expect(res.status).toBe(HttpStatusCodes.NOT_FOUND);
|
|
185
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
186
|
+
done();
|
|
187
|
+
});
|
|
232
188
|
});
|
|
233
189
|
});
|
|
234
190
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Response } from 'supertest';
|
|
2
|
+
|
|
3
|
+
import { IUser } from '@src/models/User';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Misc
|
|
7
|
+
export type TReqBody = Record<string, unknown>;
|
|
8
|
+
export type TRes = Omit<Response, 'body'> & { body: TBody };
|
|
9
|
+
export type TApiCb = (res: TRes) => void;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// Response Body: Add things to the body here over time to prevent
|
|
13
|
+
// typescript errors.
|
|
14
|
+
type TBody = {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
user?: IUser;
|
|
17
|
+
users?: IUser[];
|
|
18
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
// **** Variables **** //
|
|
2
5
|
|
|
3
|
-
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an ' +
|
|
4
|
-
'
|
|
6
|
+
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an object ' +
|
|
7
|
+
'with the appropriate user keys.';
|
|
5
8
|
|
|
6
9
|
export enum UserRoles {
|
|
7
10
|
Standard,
|
|
@@ -15,15 +18,15 @@ export interface IUser {
|
|
|
15
18
|
id: number;
|
|
16
19
|
name: string;
|
|
17
20
|
email: string;
|
|
21
|
+
created: Date;
|
|
22
|
+
role: UserRoles;
|
|
18
23
|
pwdHash?: string;
|
|
19
|
-
role?: UserRoles;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export interface ISessionUser {
|
|
23
27
|
id: number;
|
|
24
28
|
email: string;
|
|
25
29
|
name: string;
|
|
26
|
-
role: IUser['role'];
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
|
|
@@ -35,6 +38,7 @@ export interface ISessionUser {
|
|
|
35
38
|
function new_(
|
|
36
39
|
name?: string,
|
|
37
40
|
email?: string,
|
|
41
|
+
created?: Date,
|
|
38
42
|
role?: UserRoles,
|
|
39
43
|
pwdHash?: string,
|
|
40
44
|
id?: number, // id last cause usually set by db
|
|
@@ -43,8 +47,9 @@ function new_(
|
|
|
43
47
|
id: (id ?? -1),
|
|
44
48
|
name: (name ?? ''),
|
|
45
49
|
email: (email ?? ''),
|
|
50
|
+
created: (created ? new Date(created) : new Date()),
|
|
46
51
|
role: (role ?? UserRoles.Standard),
|
|
47
|
-
pwdHash
|
|
52
|
+
...( pwdHash ? { pwdHash } : {}),
|
|
48
53
|
};
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -52,13 +57,11 @@ function new_(
|
|
|
52
57
|
* Get user instance from object.
|
|
53
58
|
*/
|
|
54
59
|
function from(param: object): IUser {
|
|
55
|
-
// Check is user
|
|
56
60
|
if (!isUser(param)) {
|
|
57
61
|
throw new Error(INVALID_CONSTRUCTOR_PARAM);
|
|
58
62
|
}
|
|
59
|
-
// Get user instance
|
|
60
63
|
const p = param as IUser;
|
|
61
|
-
return new_(p.name, p.email, p.
|
|
64
|
+
return new_(p.name, p.email, p.created, p.id);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
@@ -68,14 +71,16 @@ function isUser(arg: unknown): boolean {
|
|
|
68
71
|
return (
|
|
69
72
|
!!arg &&
|
|
70
73
|
typeof arg === 'object' &&
|
|
71
|
-
'id' in arg &&
|
|
72
|
-
'email' in arg &&
|
|
73
|
-
'name' in arg &&
|
|
74
|
-
'
|
|
74
|
+
'id' in arg && typeof arg.id === 'number' &&
|
|
75
|
+
'email' in arg && typeof arg.email === 'string' &&
|
|
76
|
+
'name' in arg && typeof arg.name === 'string' &&
|
|
77
|
+
'created' in arg && moment(arg.created as string | Date).isValid()
|
|
75
78
|
);
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
|
|
82
|
+
|
|
83
|
+
|
|
79
84
|
// **** Export default **** //
|
|
80
85
|
|
|
81
86
|
export default {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
var Http = (() => {
|
|
2
|
+
|
|
2
3
|
// Setup request for json
|
|
3
4
|
var getOptions = (verb, data) => {
|
|
4
5
|
var options = {
|
|
@@ -9,11 +10,12 @@ var Http = (() => {
|
|
|
9
10
|
'Content-Type': 'application/json',
|
|
10
11
|
},
|
|
11
12
|
};
|
|
12
|
-
if (data) {
|
|
13
|
+
if (!!data) {
|
|
13
14
|
options.body = JSON.stringify(data);
|
|
14
15
|
}
|
|
15
16
|
return options;
|
|
16
17
|
};
|
|
18
|
+
|
|
17
19
|
// Set Http methods
|
|
18
20
|
return {
|
|
19
21
|
get: (path) => fetch(path, getOptions('GET')),
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
//
|
|
1
|
+
// **** Variables **** //
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const DateFormatter = new Intl.DateTimeFormat('en-US', {
|
|
4
|
+
year: 'numeric',
|
|
5
|
+
month: '2-digit',
|
|
6
|
+
day: '2-digit',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const formatDate = (date) => DateFormatter.format(new Date(date));
|
|
4
10
|
|
|
5
11
|
|
|
6
|
-
//
|
|
12
|
+
// **** Run **** //
|
|
13
|
+
|
|
14
|
+
// Start
|
|
15
|
+
displayUsers();
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* Call api
|
|
@@ -12,59 +21,22 @@ function displayUsers() {
|
|
|
12
21
|
Http
|
|
13
22
|
.get('/api/users/all')
|
|
14
23
|
.then(resp => resp.json())
|
|
15
|
-
.then(
|
|
16
|
-
var
|
|
17
|
-
|
|
24
|
+
.then(resp => {
|
|
25
|
+
var allUsersTemplate = document.getElementById('all-users-template'),
|
|
26
|
+
allUsersTemplateHtml = allUsersTemplate.innerHTML,
|
|
27
|
+
template = Handlebars.compile(allUsersTemplateHtml);
|
|
18
28
|
var allUsersAnchor = document.getElementById('all-users-anchor');
|
|
19
|
-
allUsersAnchor.innerHTML =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
allUsersAnchor.innerHTML = template({
|
|
30
|
+
users: resp.users.map(user => ({
|
|
31
|
+
...user,
|
|
32
|
+
createdFormatted: formatDate(user.created),
|
|
33
|
+
})),
|
|
23
34
|
});
|
|
24
35
|
});
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
/**
|
|
28
|
-
* Get user display element
|
|
29
|
-
*/
|
|
30
|
-
function getUserDisplayEle(user) {
|
|
31
|
-
return (
|
|
32
|
-
`<div class="user-display-ele">
|
|
33
|
-
|
|
34
|
-
<div class="normal-view">
|
|
35
|
-
<div>Name: ${user.name}</div>
|
|
36
|
-
<div>Email: ${user.email}</div>
|
|
37
|
-
<button class="edit-user-btn" data-user-id="${user.id}" data-user-role="${user.role}">
|
|
38
|
-
Edit
|
|
39
|
-
</button>
|
|
40
|
-
<button class="delete-user-btn" data-user-id="${user.id}">
|
|
41
|
-
Delete
|
|
42
|
-
</button>
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<div class="edit-view">
|
|
46
|
-
<div>
|
|
47
|
-
Name: <input class="name-edit-input" value="${user.name}">
|
|
48
|
-
</div>
|
|
49
|
-
<div>
|
|
50
|
-
Email: <input class="email-edit-input" value="${user.email}">
|
|
51
|
-
</div>
|
|
52
|
-
<button class="submit-edit-btn" data-user-id="${user.id}">
|
|
53
|
-
Submit
|
|
54
|
-
</button>
|
|
55
|
-
<button class="cancel-edit-btn" data-user-id="${user.id}">
|
|
56
|
-
Cancel
|
|
57
|
-
</button>
|
|
58
|
-
</div>
|
|
59
|
-
</div>`
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// **** Add, Edit, and Delete Users **** //
|
|
65
|
-
|
|
66
38
|
// Setup event listener for button click
|
|
67
|
-
document.addEventListener('click',
|
|
39
|
+
document.addEventListener('click', event => {
|
|
68
40
|
event.preventDefault();
|
|
69
41
|
var ele = event.target;
|
|
70
42
|
if (ele.matches('#add-user-btn')) {
|
|
@@ -93,13 +65,17 @@ function addUser() {
|
|
|
93
65
|
id: -1,
|
|
94
66
|
name: nameInput.value,
|
|
95
67
|
email: emailInput.value,
|
|
96
|
-
|
|
68
|
+
created: new Date(),
|
|
97
69
|
},
|
|
98
70
|
};
|
|
99
71
|
// Call api
|
|
100
72
|
Http
|
|
101
73
|
.post('/api/users/add', data)
|
|
102
|
-
.then(() =>
|
|
74
|
+
.then(() => {
|
|
75
|
+
nameInput.value = '';
|
|
76
|
+
emailInput.value = '';
|
|
77
|
+
displayUsers();
|
|
78
|
+
});
|
|
103
79
|
}
|
|
104
80
|
|
|
105
81
|
/**
|
|
@@ -130,13 +106,14 @@ function submitEdit(ele) {
|
|
|
130
106
|
var nameInput = userEle.getElementsByClassName('name-edit-input')[0];
|
|
131
107
|
var emailInput = userEle.getElementsByClassName('email-edit-input')[0];
|
|
132
108
|
var id = ele.getAttribute('data-user-id');
|
|
133
|
-
var
|
|
109
|
+
var created = ele.getAttribute('data-user-created');
|
|
110
|
+
console.log(ele, created)
|
|
134
111
|
var data = {
|
|
135
112
|
user: {
|
|
136
113
|
id: Number(id),
|
|
137
114
|
name: nameInput.value,
|
|
138
115
|
email: emailInput.value,
|
|
139
|
-
|
|
116
|
+
created: new Date(created),
|
|
140
117
|
},
|
|
141
118
|
};
|
|
142
119
|
Http
|
|
@@ -154,9 +131,6 @@ function deleteUser(ele) {
|
|
|
154
131
|
.then(() => displayUsers());
|
|
155
132
|
}
|
|
156
133
|
|
|
157
|
-
|
|
158
|
-
// **** Logout **** //
|
|
159
|
-
|
|
160
134
|
function logoutUser() {
|
|
161
135
|
Http
|
|
162
136
|
.get('/api/auth/logout')
|
|
@@ -22,20 +22,13 @@ body .add-user-col input {
|
|
|
22
22
|
margin-bottom: 10px;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
body .add-user-col #add-user-btn {
|
|
26
|
-
margin-top: 2px;
|
|
27
|
-
margin-bottom: 10px;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
body .add-user-col #logout-btn {
|
|
31
|
-
margin-top: 25px;
|
|
32
|
-
margin-bottom: 10px;
|
|
33
|
-
font-size: 15px;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
25
|
|
|
37
26
|
/** Users Display Column **/
|
|
38
27
|
|
|
28
|
+
.email-edit {
|
|
29
|
+
padding-bottom: 8px;
|
|
30
|
+
}
|
|
31
|
+
|
|
39
32
|
body .users-column .user-display-ele {
|
|
40
33
|
padding-bottom: 10px;
|
|
41
34
|
}
|
|
@@ -56,7 +56,12 @@ async function update(user: IUser): Promise<void> {
|
|
|
56
56
|
const db = await orm.openDb();
|
|
57
57
|
for (let i = 0; i < db.users.length; i++) {
|
|
58
58
|
if (db.users[i].id === user.id) {
|
|
59
|
-
db.users[i]
|
|
59
|
+
const dbUser = db.users[i];
|
|
60
|
+
db.users[i] = {
|
|
61
|
+
...dbUser,
|
|
62
|
+
name: user.name,
|
|
63
|
+
email: user.email,
|
|
64
|
+
};
|
|
60
65
|
return orm.saveDb(db);
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"users":[{"name":"Sean Maxwell","email":"sean.maxwell@gmail.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":1,"id":159123164363},{"name":"
|
|
1
|
+
{"users":[{"name":"Sean Maxwell","email":"sean.maxwell@gmail.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":1,"id":159123164363,"created":"2024-03-22T05:20:55.079Z"},{"name":"John Smith","email":"john.smith@yahoo.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":357437875835,"created":"2024-03-22T05:20:55.079Z"},{"id":28541714318,"name":"Gordan Freeman","email":"nova@prospect.com","created":"2024-03-23T15:56:42.615Z"}]}
|
|
@@ -31,10 +31,7 @@ function addOne(user: IUser): Promise<void> {
|
|
|
31
31
|
async function updateOne(user: IUser): Promise<void> {
|
|
32
32
|
const persists = await UserRepo.persists(user.id);
|
|
33
33
|
if (!persists) {
|
|
34
|
-
throw new RouteError(
|
|
35
|
-
HttpStatusCodes.NOT_FOUND,
|
|
36
|
-
USER_NOT_FOUND_ERR,
|
|
37
|
-
);
|
|
34
|
+
throw new RouteError(HttpStatusCodes.NOT_FOUND, USER_NOT_FOUND_ERR);
|
|
38
35
|
}
|
|
39
36
|
// Return user
|
|
40
37
|
return UserRepo.update(user);
|
|
@@ -46,10 +43,7 @@ async function updateOne(user: IUser): Promise<void> {
|
|
|
46
43
|
async function _delete(id: number): Promise<void> {
|
|
47
44
|
const persists = await UserRepo.persists(id);
|
|
48
45
|
if (!persists) {
|
|
49
|
-
throw new RouteError(
|
|
50
|
-
HttpStatusCodes.NOT_FOUND,
|
|
51
|
-
USER_NOT_FOUND_ERR,
|
|
52
|
-
);
|
|
46
|
+
throw new RouteError(HttpStatusCodes.NOT_FOUND, USER_NOT_FOUND_ERR);
|
|
53
47
|
}
|
|
54
48
|
// Delete user
|
|
55
49
|
return UserRepo.delete(id);
|