Adding new Admin UI. Updating Fredy to V3.0.0 as it has been a large rewrite. Thanks for all contributions and help on the way!
This commit is contained in:
Christian Kellner
2021-01-21 16:09:23 +01:00
committed by GitHub
parent 8185bfe818
commit b2847f6834
124 changed files with 9768 additions and 1495 deletions

View File

@@ -0,0 +1,43 @@
import { xhrGet } from '../../xhr';
export const jobs = {
state: {
jobs: [],
insights: {},
},
reducers: {
setJobs: (state, payload) => {
return {
...state,
jobs: Object.freeze(payload),
};
},
setJobInsights: (state, payload, jobId) => {
return {
...state,
insights: {
...state.insights,
[jobId]: Object.freeze(payload),
},
};
},
},
effects: {
async getJobs() {
try {
const response = await xhrGet('/api/jobs');
this.setJobs(response.json);
} catch (Exception) {
console.error(`Error while trying to get resource for api/jobs. Error:`, Exception);
}
},
async getInsightDataForJob(jobId) {
try {
const response = await xhrGet(`/api/jobs/insights/${jobId}`);
this.setJobInsights(response.json, jobId);
} catch (Exception) {
console.error(`Error while trying to get resource for api/jobs/insights. Error:`, Exception);
}
},
},
};

View File

@@ -0,0 +1,19 @@
import { xhrGet } from '../../xhr';
export const notificationAdapter = {
state: [],
reducers: {
setAdapter: (state, payload) => {
return [...Object.freeze(payload)];
},
},
effects: {
async getAdapter() {
try {
const response = await xhrGet('/api/jobs/notificationAdapter');
this.setAdapter(response.json);
} catch (Exception) {
console.error(`Error while trying to get resource for api/jobs/notificationAdapter. Error:`, Exception);
}
},
},
};

View File

@@ -0,0 +1,19 @@
import { xhrGet } from '../../xhr';
export const provider = {
state: [],
reducers: {
setProvider: (state, payload) => {
return [...Object.freeze(payload)];
},
},
effects: {
async getProvider() {
try {
const response = await xhrGet('/api/jobs/provider');
this.setProvider(response.json);
} catch (Exception) {
console.error(`Error while trying to get resource for api/jobs/provider. Error:`, Exception);
}
},
},
};

View File

@@ -0,0 +1,41 @@
import { xhrGet } from '../../xhr';
export const user = {
state: {
users: [],
currentUser: null,
},
reducers: {
//only admins
setUsers: (state, payload) => {
return {
...state,
users: payload,
};
},
setCurrentUser: (state, payload) => {
return {
...state,
currentUser: Object.freeze(payload),
};
},
},
effects: {
async getUsers() {
try {
const response = await xhrGet('/api/admin/users');
this.setUsers(response.json);
} catch (Exception) {
console.error('Error while trying to get resource for api/admin/users. Error:', Exception);
}
},
async getCurrentUser() {
try {
const response = await xhrGet('/api/login/user');
this.setCurrentUser(response.json);
} catch (Exception) {
console.error('Error while trying to get resource for api/login/user. Error:', Exception);
}
},
},
};

View File

@@ -0,0 +1,30 @@
import { notificationAdapter } from './models/notificationAdapter';
import createLoadingPlugin from '@rematch/loading';
import { provider } from './models/provider';
import { createLogger } from 'redux-logger';
import { jobs } from './models/jobs';
import { user } from './models/user';
import { init } from '@rematch/core';
const middleware = [];
if (process.env.NODE_ENV === 'development') {
// eslint-disable-line no-redeclare
middleware.push(createLogger({ duration: false, collapsed: (getState, action, logEntry) => !logEntry.error }));
}
const store = init({
name: 'fredy',
models: {
notificationAdapter,
provider,
jobs,
user,
},
plugins: [createLoadingPlugin({})],
redux: {
middlewares: middleware,
},
});
export const reduxStore = store;

View File

@@ -0,0 +1,12 @@
export function format(ts) {
return new Intl.DateTimeFormat('default', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
}).format(ts);
}
export const roundToNext5Minute = (ts) => Math.ceil(ts / (1000 * 60 * 5)) * (1000 * 60 * 5);

View File

@@ -0,0 +1,13 @@
export function transform({ id, name, fields }) {
const fieldValues = {};
Object.keys(fields).map((key) => {
fieldValues[key] = fields[key].value;
});
return {
id,
name,
fields: fieldValues,
};
}

View File

@@ -0,0 +1,8 @@
export function transform({ name, id, enabled, url }) {
return {
name,
id,
enabled,
url,
};
}

140
ui/src/services/xhr.js Normal file
View File

@@ -0,0 +1,140 @@
/**
* post something to our backend.
*
* @param url
* @param data based on the content type, you need to make sure to parse in the proper data
* @param contentType default is json
* @returns {Promise}
*/
export function xhrPost(url, data, contentType = 'application/json; charset=utf-8', isJson = true) {
return executePostOrPutCall(url, contentType, data, isJson, true);
}
/**
* put request to backend.
*
* @param url
* @param data based on the content type, you need to make sure to parse in the proper data
* @param contentType default is json
* @returns {Promise}
*/
export function xhrPut(url, data, contentType = 'application/json; charset=utf-8', isJson = true) {
return executePostOrPutCall(url, contentType, data, isJson, false);
}
function executePostOrPutCall(url, contentType, data, isJson, isPost) {
return new Promise((resolve, reject) => {
fetch(url, {
method: isPost ? 'POST' : 'PUT',
cache: 'no-cache',
credentials: 'include',
mode: 'cors',
headers: {
'Content-Type': contentType,
},
body: data == null ? JSON.stringify({}) : JSON.stringify(data),
})
.then((response) => (isJson ? parseJSON(response) : response))
.then((response) => resolve(response))
.catch((error) => {
reject(error);
});
});
}
/**
* get request to backend
* returns a Promise with
* {
* status: statusCode,
* json: values
* }
*
* if an error occurs, the promise rejects with
* {
* json: errors: ['error', 'error']
* }
* @param url
* @param contentType
* @param isJson
* @returns {Promise}
*/
export function xhrGet(url, contentType = 'application/json; charset=utf-8', isJson = true) {
return new Promise((resolve, reject) => {
fetch(url, {
credentials: 'include',
mode: 'cors',
headers: {
'Content-Type': contentType,
},
})
.then((response) => (isJson ? parseJSON(response) : response))
.then((response) => resolve(response))
.catch((error) => {
reject(error);
});
});
}
/**
* delete request to backend
* returns a Promise with
* {
* status: statusCode,
* json: values
* }
*
* if an error occurs, the promise rejects with
* {
* json: errors: ['error', 'error']
* }
* @param url
* @param data
* @returns {Promise}
*/
export function xhrDelete(url, data, contentType = 'application/json; charset=utf-8') {
return new Promise((resolve, reject) => {
fetch(url, {
method: 'DELETE',
credentials: 'include',
mode: 'cors',
body: data == null ? JSON.stringify({}) : JSON.stringify(data),
headers: {
'Content-Type': contentType,
},
})
.then((response) => parseJSON(response))
.then((response) => resolve(response))
.catch((error) => {
if (error.json != null && error.json.message != null) {
reject(error.json.message);
} else {
reject({ errors: [`Unspecified Network error`] });
}
});
});
}
function parseJSON(response) {
return new Promise((resolve, reject) =>
response
.text()
.then((text) => {
//some responses doesn't contain a body. .json() would throw errors here...
const json = text != null && text.length > 0 ? JSON.parse(text) : {};
if (response.ok) {
resolve({
status: response.status,
json,
});
} else {
reject({
status: response.status,
json,
});
}
})
.catch((error) => reject('Error while trying to parse json.', error))
);
}