@oxygen-cms/ui 1.5.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/.babelrc +1 -0
- package/.eslintrc.js +22 -0
- package/.github/workflows/node.js.yml +29 -0
- package/.idea/modules.xml +8 -0
- package/.idea/ui.iml +10 -0
- package/.jshintrc +3 -0
- package/README.md +7 -0
- package/assets/oxygen-icon.png +0 -0
- package/jest.init.js +1 -0
- package/package.json +72 -0
- package/src/AuthApi.js +116 -0
- package/src/CrudApi.js +112 -0
- package/src/EventsApi.js +16 -0
- package/src/GroupsApi.js +9 -0
- package/src/Internationalize.js +31 -0
- package/src/MediaApi.js +52 -0
- package/src/MediaDirectoryApi.js +62 -0
- package/src/PreferencesApi.js +47 -0
- package/src/UserPermissions.js +66 -0
- package/src/UserPreferences.js +69 -0
- package/src/UserPreferences.test.js +23 -0
- package/src/UsersApi.js +41 -0
- package/src/api.js +209 -0
- package/src/components/App.vue +61 -0
- package/src/components/AuthenticatedLayout.vue +254 -0
- package/src/components/AuthenticationLog.vue +196 -0
- package/src/components/CodeEditor.vue +90 -0
- package/src/components/EditButtonOnRowHover.vue +21 -0
- package/src/components/Error404.vue +25 -0
- package/src/components/EventsChooser.vue +88 -0
- package/src/components/EventsTable.vue +82 -0
- package/src/components/GenericEditableField.vue +74 -0
- package/src/components/GroupsChooser.vue +58 -0
- package/src/components/GroupsList.vue +129 -0
- package/src/components/ImportExport.vue +45 -0
- package/src/components/LegacyPage.vue +256 -0
- package/src/components/UserJoined.vue +35 -0
- package/src/components/UserManagement.vue +168 -0
- package/src/components/UserProfileForm.vue +214 -0
- package/src/components/ViewProfile.vue +32 -0
- package/src/components/auth/Auth404.vue +16 -0
- package/src/components/auth/Login.vue +135 -0
- package/src/components/auth/LoginLogo.vue +30 -0
- package/src/components/auth/Logout.vue +26 -0
- package/src/components/auth/PasswordRemind.vue +71 -0
- package/src/components/auth/PasswordReset.vue +97 -0
- package/src/components/auth/TwoFactorSetup.vue +115 -0
- package/src/components/auth/VerifyEmail.vue +71 -0
- package/src/components/auth/WelcomeFloat.vue +87 -0
- package/src/components/auth/login.scss +17 -0
- package/src/components/media/MediaChooseDirectory.vue +129 -0
- package/src/components/media/MediaDirectory.vue +109 -0
- package/src/components/media/MediaInsertModal.vue +88 -0
- package/src/components/media/MediaItem.vue +282 -0
- package/src/components/media/MediaItemPreview.vue +45 -0
- package/src/components/media/MediaList.vue +305 -0
- package/src/components/media/MediaPage.vue +44 -0
- package/src/components/media/MediaResponsiveImages.vue +51 -0
- package/src/components/media/MediaUpload.vue +133 -0
- package/src/components/media/media.scss +51 -0
- package/src/components/preferences/PreferencesAdminAppearance.vue +22 -0
- package/src/components/preferences/PreferencesAuthentication.vue +27 -0
- package/src/components/preferences/PreferencesEventTemplates.vue +22 -0
- package/src/components/preferences/PreferencesField.vue +215 -0
- package/src/components/preferences/PreferencesList.vue +50 -0
- package/src/components/preferences/PreferencesPageTemplates.vue +23 -0
- package/src/components/preferences/PreferencesSiteAppearance.vue +22 -0
- package/src/components/preferences/PreferencesThemeChooser.vue +73 -0
- package/src/components/preferences/ShowIfPermitted.vue +37 -0
- package/src/components/preferences/UserPreferences.vue +30 -0
- package/src/components/users/CreateUserModal.vue +73 -0
- package/src/components/util.css +47 -0
- package/src/icons.js +90 -0
- package/src/main.js +112 -0
- package/src/modules/LegacyPages.js +18 -0
- package/src/modules/Media.js +45 -0
- package/src/modules/UserManagement.js +24 -0
- package/src/routes/index.js +92 -0
- package/src/store/index.js +70 -0
- package/src/styles/_variables.scss +23 -0
- package/src/styles/app.scss +76 -0
- package/src/unsavedChanges.js +16 -0
- package/src/util.js +65 -0
- package/src/util.test.js +39 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Vuex from 'vuex';
|
|
2
|
+
|
|
3
|
+
import UserPermissions from "../UserPermissions";
|
|
4
|
+
import AuthApi from "../AuthApi";
|
|
5
|
+
import UserPreferences from "../UserPreferences";
|
|
6
|
+
|
|
7
|
+
export default (vue) => {
|
|
8
|
+
vue.use(Vuex);
|
|
9
|
+
|
|
10
|
+
return new Vuex.Store({
|
|
11
|
+
state: {
|
|
12
|
+
user: null,
|
|
13
|
+
loginStatus: null,
|
|
14
|
+
impersonating: false
|
|
15
|
+
},
|
|
16
|
+
mutations: {
|
|
17
|
+
setUser(state, user) {
|
|
18
|
+
state.user = user;
|
|
19
|
+
state.loginStatus = user !== null;
|
|
20
|
+
},
|
|
21
|
+
setImpersonating(state, user) {
|
|
22
|
+
state.user = user;
|
|
23
|
+
state.loginStatus = user !== null;
|
|
24
|
+
state.impersonating = true;
|
|
25
|
+
},
|
|
26
|
+
stopImpersonating(state, user) {
|
|
27
|
+
state.user = user;
|
|
28
|
+
state.loginStatus = user !== null;
|
|
29
|
+
state.impersonating = false;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
getters: {
|
|
33
|
+
userPermissions: state => {
|
|
34
|
+
if(!state.user) { return null; }
|
|
35
|
+
return new UserPermissions(state.user.permissions);
|
|
36
|
+
},
|
|
37
|
+
userPreferences: state => {
|
|
38
|
+
if(!state.user) { return null; }
|
|
39
|
+
return new UserPreferences(state.user.preferences);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
actions: {
|
|
43
|
+
determineLoginStatus({ commit, state }) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
if(state.loginStatus != null) {
|
|
46
|
+
resolve(state.loginStatus);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('Determining login status');
|
|
51
|
+
|
|
52
|
+
let authApi = new AuthApi(null);
|
|
53
|
+
authApi.login(null, null, null).then((response) => {
|
|
54
|
+
console.log(response);
|
|
55
|
+
if(response.impersonating === true) {
|
|
56
|
+
commit('setImpersonating', response.user);
|
|
57
|
+
} else {
|
|
58
|
+
commit('setUser', response.user);
|
|
59
|
+
}
|
|
60
|
+
resolve(true);
|
|
61
|
+
}).catch(() => {
|
|
62
|
+
commit('setUser', null);
|
|
63
|
+
resolve(false);
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Set your colors
|
|
2
|
+
$primary: #0053b1;
|
|
3
|
+
//$twitter: #4099FF;
|
|
4
|
+
|
|
5
|
+
@import "~bulma/sass/utilities/_all";
|
|
6
|
+
|
|
7
|
+
$menu-item-color: darken($grey, 5%);
|
|
8
|
+
$menu-item-active-background-color: transparent;
|
|
9
|
+
$menu-item-active-color: lighten($grey, 4%);
|
|
10
|
+
|
|
11
|
+
// $menu-item-hover-background-color: rgba(255, 255, 255, 0.1);
|
|
12
|
+
// $menu-item-hover-color: $white-bis;
|
|
13
|
+
|
|
14
|
+
//$navbar-item-active-color: #fff;
|
|
15
|
+
//$navbar-item-hover-background-color: #f3f3f9;
|
|
16
|
+
//
|
|
17
|
+
//// Links
|
|
18
|
+
//$link: $primary;
|
|
19
|
+
//$link-focus-border: $primary;
|
|
20
|
+
|
|
21
|
+
// Import Bulma's core
|
|
22
|
+
// TODO: wtf is going on here??
|
|
23
|
+
@import "~bulma/sass/utilities/_all";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
@import "_variables.scss";
|
|
2
|
+
|
|
3
|
+
// Import Bulma and Buefy styles
|
|
4
|
+
@import "~bulma";
|
|
5
|
+
@import "~buefy/src/scss/buefy";
|
|
6
|
+
|
|
7
|
+
.navbar {
|
|
8
|
+
box-shadow: 0px 0px 2px $dark;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.navbar-item {
|
|
12
|
+
transition: background-color 0.5s ease;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
html, body {
|
|
16
|
+
height: 100%;
|
|
17
|
+
overflow-y: hidden;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pre.no-pre {
|
|
21
|
+
background-color: transparent;
|
|
22
|
+
color: inherit;
|
|
23
|
+
padding: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.transition-container {
|
|
27
|
+
position: relative;
|
|
28
|
+
height: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.transition-container > * {
|
|
32
|
+
position: absolute;
|
|
33
|
+
left: 0;
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.fade-enter-active, .fade-leave-active {
|
|
39
|
+
transition: opacity 0.25s;
|
|
40
|
+
}
|
|
41
|
+
.fade-enter, .fade-leave-to {
|
|
42
|
+
opacity: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.quick-fade-enter-active, .quick-fade-leave-active {
|
|
46
|
+
transition: opacity 0.1s;
|
|
47
|
+
}
|
|
48
|
+
.quick-fade-enter, .quick-fade-leave-to {
|
|
49
|
+
opacity: 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.slide-left-enter-active, .slide-left-leave-active {
|
|
53
|
+
transition: transform 0.2s ease, opacity 0.2s ease;
|
|
54
|
+
}
|
|
55
|
+
.slide-left-leave-to {
|
|
56
|
+
transform: translateX(1em);
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.slide-left-enter {
|
|
61
|
+
transform: translateX(-1em);
|
|
62
|
+
opacity: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.slide-up-enter-active, .slide-up-leave-active {
|
|
66
|
+
transition: transform 0.4s ease, opacity 0.4s ease;
|
|
67
|
+
}
|
|
68
|
+
.slide-up-leave-to {
|
|
69
|
+
transform: translateY(2em);
|
|
70
|
+
opacity: 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.slide-up-enter {
|
|
74
|
+
transform: translateY(-2em);
|
|
75
|
+
opacity: 0;
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
export const checkForUnsavedChanges = (model, serverModel, $buefy, next) => {
|
|
3
|
+
if(JSON.stringify(model) === JSON.stringify(serverModel)) {
|
|
4
|
+
next();
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
$buefy.dialog.confirm({
|
|
8
|
+
message: 'If you leave now, you\'ll lose any unsaved changes',
|
|
9
|
+
onConfirm: () => {
|
|
10
|
+
next();
|
|
11
|
+
},
|
|
12
|
+
onCancel: () => {
|
|
13
|
+
next(false);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
};
|
package/src/util.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { titleCase } from "title-case";
|
|
2
|
+
import { parsePhoneNumberFromString } from "libphonenumber-js";
|
|
3
|
+
|
|
4
|
+
const convertStr = (input) => {
|
|
5
|
+
if(!input) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return titleCase(input.trim());
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const strEquals = (s1, s2) => {
|
|
12
|
+
return s1.trim().toLowerCase() === s2.trim().toLowerCase();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const nestedGet = (object, key) => {
|
|
16
|
+
let parts = key.split('.');
|
|
17
|
+
|
|
18
|
+
for(let part of parts) {
|
|
19
|
+
if(object === null || typeof object === 'undefined') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
object = object[part];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if(typeof object === 'undefined') {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return object;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const nestedSet = (object, key, value) => {
|
|
34
|
+
let parts = key.split('.');
|
|
35
|
+
|
|
36
|
+
for(let i = 0; i < parts.length; i++) {
|
|
37
|
+
if(i < parts.length - 1) {
|
|
38
|
+
if(object[parts[i]] === null || typeof object[parts[i]] === 'undefined') {
|
|
39
|
+
object[parts[i]] = {};
|
|
40
|
+
}
|
|
41
|
+
object = object[parts[i]];
|
|
42
|
+
} else {
|
|
43
|
+
object[parts[i]] = value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const tryParseTelephone = (telephone) => {
|
|
49
|
+
if(typeof telephone !== 'string') {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
let result = parsePhoneNumberFromString(telephone, 'AU');
|
|
53
|
+
if(typeof result === 'undefined' || !result.isValid()) {
|
|
54
|
+
// manual fix for telephone numbers like 9123 4567 which are considered valid by most Australians
|
|
55
|
+
let localPhone = telephone.replace(' ', '');
|
|
56
|
+
if(localPhone.length === 8 && localPhone.match(/^\d+$/)) {
|
|
57
|
+
return telephone;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
} else {
|
|
61
|
+
return result.formatNational();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { strEquals, convertStr, nestedGet, nestedSet, tryParseTelephone };
|
package/src/util.test.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { strEquals, convertStr, nestedGet, nestedSet, tryParseTelephone } from './util';
|
|
2
|
+
|
|
3
|
+
test('string equality', () => {
|
|
4
|
+
expect(strEquals('hello ', ' HeLLo '));
|
|
5
|
+
expect(!strEquals('hallo ', ' HeLLo '));
|
|
6
|
+
expect(!strEquals('he llo ', ' HeLLo '));
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('converting strings to title case', () => {
|
|
10
|
+
expect(convertStr('babel street ')).toBe('Babel Street');
|
|
11
|
+
expect(convertStr('18 foobar lane')).toBe('18 Foobar Lane');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('nested get', () => {
|
|
15
|
+
expect(nestedGet({
|
|
16
|
+
foo: { bar: { baz: 'qux' }}
|
|
17
|
+
}, 'foo.bar.baz')).toBe('qux');
|
|
18
|
+
expect(nestedGet({
|
|
19
|
+
foo: { bar: { baz: 'qux' }}
|
|
20
|
+
}, 'foo.bar.bez')).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('nested set', () => {
|
|
24
|
+
let v = {
|
|
25
|
+
foo: { bar: { baz: 'qux' }}
|
|
26
|
+
};
|
|
27
|
+
nestedSet(v, 'foo.bar.baz', 'qux2');
|
|
28
|
+
expect(v.foo.bar.baz).toBe('qux2');
|
|
29
|
+
|
|
30
|
+
nestedSet(v, 'foo.b.c', 'qux3');
|
|
31
|
+
expect(v.foo.b.c).toBe('qux3');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('telephone parsing', () => {
|
|
35
|
+
expect(tryParseTelephone('+61 0423 456 789')).toBe('0423 456 789');
|
|
36
|
+
expect(tryParseTelephone('0412 456 789')).toBe('0412 456 789');
|
|
37
|
+
expect(tryParseTelephone('03 9876 5432')).toBe('(03) 9876 5432');
|
|
38
|
+
expect(tryParseTelephone('03 9876 54322')).toBeNull();
|
|
39
|
+
});
|