@fy-/fws-vue 0.3.57 → 0.3.59
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/components/fws/UserFlow.vue +1 -11
- package/components/fws/UserOAuth2.vue +174 -0
- package/composables/templating.ts +16 -1
- package/index.ts +6 -1
- package/package.json +1 -1
|
@@ -176,16 +176,6 @@ const userFlow = async (params: paramsType = { initial: false }) => {
|
|
|
176
176
|
eventBus.emit("login-loading", false);
|
|
177
177
|
};
|
|
178
178
|
|
|
179
|
-
const getContrastingTextColor = (backgroundColor: string) => {
|
|
180
|
-
const r = parseInt(backgroundColor.substring(1, 3), 16);
|
|
181
|
-
const g = parseInt(backgroundColor.substring(3, 5), 16);
|
|
182
|
-
const b = parseInt(backgroundColor.substring(5, 7), 16);
|
|
183
|
-
|
|
184
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
185
|
-
|
|
186
|
-
return luminance > 0.5 ? "#000000" : "#FFFFFF";
|
|
187
|
-
};
|
|
188
|
-
|
|
189
179
|
onMounted(async () => {
|
|
190
180
|
await userFlow({ initial: true });
|
|
191
181
|
});
|
|
@@ -223,7 +213,7 @@ onMounted(async () => {
|
|
|
223
213
|
class="flex border border-fv-neutral-300 dark:border-fv-neutral-700 shadow items-center gap-2 justify-start btn neutral defaults w-full mx-auto !font-semibold"
|
|
224
214
|
:style="`background: ${
|
|
225
215
|
field.button['background-color']
|
|
226
|
-
}; color: ${getContrastingTextColor(
|
|
216
|
+
}; color: ${$getContrastingTextColor(
|
|
227
217
|
field.button['background-color'],
|
|
228
218
|
)}`"
|
|
229
219
|
>
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useTranslation } from "../../composables/translations";
|
|
3
|
+
import { useEventBus } from "../../composables/event-bus";
|
|
4
|
+
import { useUserStore } from "../../stores/user";
|
|
5
|
+
import { useRest, APIResult } from "../../composables/rest";
|
|
6
|
+
import DefaultModal from "../ui/DefaultModal.vue";
|
|
7
|
+
|
|
8
|
+
import { ref, computed, onMounted } from "vue";
|
|
9
|
+
const rest = useRest();
|
|
10
|
+
const eventBus = useEventBus();
|
|
11
|
+
const userStore = useUserStore();
|
|
12
|
+
const isAuth = computed(() => userStore.isAuth);
|
|
13
|
+
const data = ref();
|
|
14
|
+
const providersData = ref();
|
|
15
|
+
const usedProviders = ref<Record<string, boolean>>({});
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
returnTo: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: false,
|
|
20
|
+
default: "/user/account?tab=user_settings",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const returnTo = ref(props.returnTo);
|
|
24
|
+
if (returnTo.value == "") {
|
|
25
|
+
returnTo.value = "/user/account?tab=user_settings";
|
|
26
|
+
}
|
|
27
|
+
const getOAuth2Providers = async () => {
|
|
28
|
+
eventBus.emit("main-loading", true);
|
|
29
|
+
const d = await rest("User/OAuth2/Providers", "GET");
|
|
30
|
+
if (d && d.result == "success") {
|
|
31
|
+
providersData.value = d.data;
|
|
32
|
+
}
|
|
33
|
+
eventBus.emit("main-loading", false);
|
|
34
|
+
};
|
|
35
|
+
const getOAuth2Redirect = async (providerUUID: string) => {
|
|
36
|
+
eventBus.emit("main-loading", true);
|
|
37
|
+
const d = await rest(`User/OAuth2/Provider/${providerUUID}`, "POST", {
|
|
38
|
+
ReturnTo: returnTo.value,
|
|
39
|
+
});
|
|
40
|
+
if (d && d.result == "success") {
|
|
41
|
+
window.location.href = d.data;
|
|
42
|
+
}
|
|
43
|
+
eventBus.emit("main-loading", false);
|
|
44
|
+
};
|
|
45
|
+
const translate = useTranslation();
|
|
46
|
+
const deleteOAuth2Connection = async (providerUUID: string) => {
|
|
47
|
+
eventBus.emit("showConfirm", {
|
|
48
|
+
title: translate("remove_provider_confirm_title"),
|
|
49
|
+
desc: translate("remove_provider_confirm_desc_warning"),
|
|
50
|
+
onConfirm: async () => {
|
|
51
|
+
eventBus.emit("main-loading", true);
|
|
52
|
+
const d = await rest(`User/OAuth2/Provider/${providerUUID}`, "DELETE");
|
|
53
|
+
if (d && d.result == "success") {
|
|
54
|
+
getOAuth2User();
|
|
55
|
+
}
|
|
56
|
+
eventBus.emit("main-loading", false);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
const getOAuth2User = async () => {
|
|
61
|
+
eventBus.emit("main-loading", true);
|
|
62
|
+
if (!isAuth.value) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const d = await rest("User/OAuth2", "GET");
|
|
66
|
+
usedProviders.value = {};
|
|
67
|
+
if (d && d.result == "success") {
|
|
68
|
+
data.value = d.data;
|
|
69
|
+
data.value.forEach((p: any) => {
|
|
70
|
+
usedProviders.value[p.ProviderUUID] = true;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
eventBus.emit("main-loading", false);
|
|
74
|
+
};
|
|
75
|
+
onMounted(() => {
|
|
76
|
+
getOAuth2User();
|
|
77
|
+
getOAuth2Providers();
|
|
78
|
+
});
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template>
|
|
82
|
+
<div class="flex flex-col gap-3">
|
|
83
|
+
<DefaultModal id="providers" :title="$t('providers_modal_title')">
|
|
84
|
+
<template v-for="provider in providersData" :key="provider.UUID">
|
|
85
|
+
<div
|
|
86
|
+
class="flex items-center gap-3"
|
|
87
|
+
v-if="!usedProviders[provider.UUID]"
|
|
88
|
+
>
|
|
89
|
+
<button
|
|
90
|
+
@click="
|
|
91
|
+
() => {
|
|
92
|
+
getOAuth2Redirect(provider.UUID);
|
|
93
|
+
}
|
|
94
|
+
"
|
|
95
|
+
class="flex border border-fv-neutral-300 dark:border-fv-neutral-700 shadow items-center gap-2 justify-start btn neutral defaults w-full mx-auto !font-semibold"
|
|
96
|
+
:style="`background: ${
|
|
97
|
+
provider.Data.Button.button['background-color']
|
|
98
|
+
}; color: ${$getContrastingTextColor(
|
|
99
|
+
provider.Data.Button.button['background-color'],
|
|
100
|
+
)}`"
|
|
101
|
+
>
|
|
102
|
+
<img
|
|
103
|
+
:key="`${provider.Data.Button.label}oauth`"
|
|
104
|
+
class="h-12 w-12 block p-2 mr-3"
|
|
105
|
+
:alt="provider.Data.Button.info.Name"
|
|
106
|
+
:src="provider.Data.Button.button.logo"
|
|
107
|
+
/>
|
|
108
|
+
<div>
|
|
109
|
+
{{
|
|
110
|
+
$t("user_flow_signin_with", {
|
|
111
|
+
provider: provider.Data.Button.name,
|
|
112
|
+
})
|
|
113
|
+
}}
|
|
114
|
+
</div>
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
</DefaultModal>
|
|
119
|
+
<h2 class="h3 flex items-center justify-between">
|
|
120
|
+
<span>{{ $t("oauth2_providers_title") }}</span>
|
|
121
|
+
<button
|
|
122
|
+
class="btn primary medium !py-1 !px-3"
|
|
123
|
+
@click="
|
|
124
|
+
() => {
|
|
125
|
+
$eventBus.emit('providersModal', true);
|
|
126
|
+
}
|
|
127
|
+
"
|
|
128
|
+
>
|
|
129
|
+
{{ $t("add_oauth2_con_cta") }}
|
|
130
|
+
</button>
|
|
131
|
+
</h2>
|
|
132
|
+
<p
|
|
133
|
+
class="text-red-900 dark:text-red-300 text-sm bg-red-200/[.2] dark:bg-red-900/[.2] p-2 rounded shadow"
|
|
134
|
+
v-if="
|
|
135
|
+
$route.query.error &&
|
|
136
|
+
$route.query.error === 'user_oauth2_connection_exists'
|
|
137
|
+
"
|
|
138
|
+
>
|
|
139
|
+
{{ $t("oauth2_error_user_oauth2_connection_exists") }}
|
|
140
|
+
</p>
|
|
141
|
+
<div v-if="data && data.length == 0">
|
|
142
|
+
<p>{{ $t("providers_empty") }}</p>
|
|
143
|
+
</div>
|
|
144
|
+
<div
|
|
145
|
+
v-for="provider in data"
|
|
146
|
+
class="flex items-center gap-3"
|
|
147
|
+
:key="provider.ProviderUUID"
|
|
148
|
+
>
|
|
149
|
+
<img
|
|
150
|
+
:src="provider.Provider.Button.button.logo"
|
|
151
|
+
class="w-14 h-14 p-1 rounded-full"
|
|
152
|
+
:style="`background-color: ${provider.Provider.Button.button['background-color']}`"
|
|
153
|
+
/>
|
|
154
|
+
<div>
|
|
155
|
+
<h3 class="text-xl">
|
|
156
|
+
{{ provider.Provider.Button.name }}
|
|
157
|
+
<small class="text-xs">({{ provider.ServiceID }})</small>
|
|
158
|
+
</h3>
|
|
159
|
+
<div class="flex gap-2 mt-1">
|
|
160
|
+
<button
|
|
161
|
+
class="btn danger small"
|
|
162
|
+
@click="
|
|
163
|
+
() => {
|
|
164
|
+
deleteOAuth2Connection(provider.ProviderUUID);
|
|
165
|
+
}
|
|
166
|
+
"
|
|
167
|
+
>
|
|
168
|
+
{{ $t("remove_oauth2_con_cta") }}
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</template>
|
|
@@ -8,7 +8,15 @@ const cropText = (str: string, ml = 100, end = "...") => {
|
|
|
8
8
|
}
|
|
9
9
|
return str;
|
|
10
10
|
};
|
|
11
|
+
const getContrastingTextColor = (backgroundColor: string) => {
|
|
12
|
+
const r = parseInt(backgroundColor.substring(1, 3), 16);
|
|
13
|
+
const g = parseInt(backgroundColor.substring(3, 5), 16);
|
|
14
|
+
const b = parseInt(backgroundColor.substring(5, 7), 16);
|
|
11
15
|
|
|
16
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
17
|
+
|
|
18
|
+
return luminance > 0.5 ? "#000000" : "#FFFFFF";
|
|
19
|
+
};
|
|
12
20
|
const formatBytes = (bytes: number, decimals = 2) => {
|
|
13
21
|
if (!+bytes) return "0 Bytes";
|
|
14
22
|
|
|
@@ -76,4 +84,11 @@ const formatTimeago = (dt: Date | string | number) => {
|
|
|
76
84
|
return formatDateTimeago(new Date(_dt), getLocale().replace("_", "-"));
|
|
77
85
|
};
|
|
78
86
|
|
|
79
|
-
export {
|
|
87
|
+
export {
|
|
88
|
+
cropText,
|
|
89
|
+
formatBytes,
|
|
90
|
+
formatDate,
|
|
91
|
+
formatDatetime,
|
|
92
|
+
formatTimeago,
|
|
93
|
+
getContrastingTextColor,
|
|
94
|
+
};
|
package/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
formatDate,
|
|
20
20
|
formatDatetime,
|
|
21
21
|
formatTimeago,
|
|
22
|
+
getContrastingTextColor,
|
|
22
23
|
} from "./composables/templating";
|
|
23
24
|
import { useRest } from "./composables/rest";
|
|
24
25
|
export * from "./stores/serverRouter";
|
|
@@ -48,7 +49,7 @@ import DataTable from "./components/fws/DataTable.vue";
|
|
|
48
49
|
import FilterData from "./components/fws/FilterData.vue";
|
|
49
50
|
import CmsArticleBoxed from "./components/fws/CmsArticleBoxed.vue";
|
|
50
51
|
import CmsArticleSingle from "./components/fws/CmsArticleSingle.vue";
|
|
51
|
-
|
|
52
|
+
import UserOAuth2 from "./components/fws/UserOAuth2.vue";
|
|
52
53
|
// Css
|
|
53
54
|
import "./style.css";
|
|
54
55
|
|
|
@@ -71,6 +72,8 @@ function createFWS(): Plugin {
|
|
|
71
72
|
app.config.globalProperties.$formatTimeago = formatTimeago;
|
|
72
73
|
app.config.globalProperties.$formatDatetime = formatDatetime;
|
|
73
74
|
app.config.globalProperties.$formatDate = formatDate;
|
|
75
|
+
app.config.globalProperties.$getContrastingTextColor =
|
|
76
|
+
getContrastingTextColor;
|
|
74
77
|
|
|
75
78
|
app.component("ClientOnly", ClientOnly);
|
|
76
79
|
}
|
|
@@ -87,6 +90,7 @@ declare module "vue" {
|
|
|
87
90
|
$formatTimeago: typeof formatTimeago;
|
|
88
91
|
$formatDatetime: typeof formatDatetime;
|
|
89
92
|
$formatDate: typeof formatDate;
|
|
93
|
+
$getContrastingTextColor: typeof getContrastingTextColor;
|
|
90
94
|
}
|
|
91
95
|
export interface GlobalComponents {
|
|
92
96
|
ClientOnly: typeof ClientOnly;
|
|
@@ -130,6 +134,7 @@ export {
|
|
|
130
134
|
|
|
131
135
|
// FWS
|
|
132
136
|
UserFlow,
|
|
137
|
+
UserOAuth2,
|
|
133
138
|
DataTable,
|
|
134
139
|
FilterData,
|
|
135
140
|
CmsArticleBoxed,
|