@fy-/fws-vue 2.3.36 → 2.3.37
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/UserData.vue +91 -65
- package/components/fws/UserOAuth2.vue +136 -81
- package/components/fws/UserProfile.vue +167 -109
- package/components/fws/UserProfileStrict.vue +59 -48
- package/package.json +1 -1
|
@@ -72,72 +72,98 @@ async function patchUser() {
|
|
|
72
72
|
</script>
|
|
73
73
|
|
|
74
74
|
<template>
|
|
75
|
-
<form @submit.prevent="patchUser">
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
type="text"
|
|
81
|
-
:label="$t('fws_firstname_label')"
|
|
82
|
-
:help="$t('fws_firstname_help')"
|
|
83
|
-
:error-vuelidate="v$.userData.Firstname.$errors"
|
|
84
|
-
/>
|
|
85
|
-
<DefaultInput
|
|
86
|
-
id="lastnameFWS"
|
|
87
|
-
v-model="state.userData.Lastname"
|
|
88
|
-
class="mb-4"
|
|
89
|
-
type="text"
|
|
90
|
-
:label="$t('fws_lastname_label')"
|
|
91
|
-
:help="$t('fws_lastname_help')"
|
|
92
|
-
:error-vuelidate="v$.userData.Lastname.$errors"
|
|
93
|
-
/>
|
|
94
|
-
<DefaultInput
|
|
95
|
-
id="phoneFWS"
|
|
96
|
-
v-model="state.userData.Phone"
|
|
97
|
-
class="mb-4"
|
|
98
|
-
type="text"
|
|
99
|
-
:label="$t('fws_phone_label')"
|
|
100
|
-
:help="$t('fws_phone_help')"
|
|
101
|
-
:error-vuelidate="v$.userData.Phone.$errors"
|
|
102
|
-
/>
|
|
103
|
-
<DefaultInput
|
|
104
|
-
v-if="!userData?.AcceptedTerms"
|
|
105
|
-
id="acceptedTermsFWS"
|
|
106
|
-
v-model:checkbox-value="state.userData.AcceptedTerms"
|
|
107
|
-
type="toggle"
|
|
108
|
-
:label="$t('fws_accepted_terms_label')"
|
|
109
|
-
:help="$t('fws_accepted_terms_help')"
|
|
110
|
-
:error-vuelidate="v$.userData.AcceptedTerms.$errors"
|
|
111
|
-
/>
|
|
112
|
-
<DefaultInput
|
|
113
|
-
id="enabledNotificationsFWS"
|
|
114
|
-
v-model:checkbox-value="state.userData.EnabledNotifications"
|
|
115
|
-
type="toggle"
|
|
116
|
-
:label="$t('fws_enabled_notifications_label')"
|
|
117
|
-
:help="$t('fws_enabled_notifications_help')"
|
|
118
|
-
:error-vuelidate="v$.userData.EnabledNotifications.$errors"
|
|
119
|
-
/>
|
|
120
|
-
<DefaultInput
|
|
121
|
-
id="enabledEmailsFWS"
|
|
122
|
-
v-model:checkbox-value="state.userData.EnabledEmails"
|
|
123
|
-
type="toggle"
|
|
124
|
-
:label="$t('fws_enabled_emails_label')"
|
|
125
|
-
:help="$t('fws_enabled_emails_help')"
|
|
126
|
-
:error-vuelidate="v$.userData.EnabledEmails.$errors"
|
|
127
|
-
/>
|
|
128
|
-
<DefaultInput
|
|
129
|
-
id="enabledTrainingFromMyDataFWS"
|
|
130
|
-
v-model:checkbox-value="state.userData.EnabledTrainingFromMyData"
|
|
131
|
-
type="toggle"
|
|
132
|
-
:label="$t('fws_enabled_training_from_my_data_label')"
|
|
133
|
-
:help="$t('fws_enabled_training_from_my_data_help')"
|
|
134
|
-
:error-vuelidate="v$.userData.EnabledTrainingFromMyData.$errors"
|
|
135
|
-
/>
|
|
75
|
+
<form class="space-y-4" @submit.prevent="patchUser">
|
|
76
|
+
<div class="bg-white dark:bg-fv-neutral-800 p-4 sm:p-6 rounded-lg shadow-sm border border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
77
|
+
<h3 class="text-lg font-semibold text-fv-neutral-900 dark:text-white mb-4 sm:mb-5 pb-2 border-b border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
78
|
+
{{ $t('fws_personal_data_title') || $t('fws_personal_information') }}
|
|
79
|
+
</h3>
|
|
136
80
|
|
|
137
|
-
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
81
|
+
<!-- Personal Information Section -->
|
|
82
|
+
<div class="grid gap-4 md:grid-cols-2 mb-4 sm:mb-6">
|
|
83
|
+
<DefaultInput
|
|
84
|
+
id="firstnameFWS"
|
|
85
|
+
v-model="state.userData.Firstname"
|
|
86
|
+
type="text"
|
|
87
|
+
:label="$t('fws_firstname_label')"
|
|
88
|
+
:help="$t('fws_firstname_help')"
|
|
89
|
+
:error-vuelidate="v$.userData.Firstname.$errors"
|
|
90
|
+
/>
|
|
91
|
+
<DefaultInput
|
|
92
|
+
id="lastnameFWS"
|
|
93
|
+
v-model="state.userData.Lastname"
|
|
94
|
+
type="text"
|
|
95
|
+
:label="$t('fws_lastname_label')"
|
|
96
|
+
:help="$t('fws_lastname_help')"
|
|
97
|
+
:error-vuelidate="v$.userData.Lastname.$errors"
|
|
98
|
+
/>
|
|
99
|
+
<DefaultInput
|
|
100
|
+
id="phoneFWS"
|
|
101
|
+
v-model="state.userData.Phone"
|
|
102
|
+
type="text"
|
|
103
|
+
:label="$t('fws_phone_label')"
|
|
104
|
+
:help="$t('fws_phone_help')"
|
|
105
|
+
:error-vuelidate="v$.userData.Phone.$errors"
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<!-- Terms & Preferences -->
|
|
110
|
+
<div class="pt-3 sm:pt-4 border-t border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
111
|
+
<h4 class="font-medium text-fv-neutral-800 dark:text-white mb-2 sm:mb-3">
|
|
112
|
+
{{ $t('fws_preferences_and_settings') }}
|
|
113
|
+
</h4>
|
|
114
|
+
|
|
115
|
+
<div class="grid gap-2 sm:gap-3 p-1 sm:p-2">
|
|
116
|
+
<DefaultInput
|
|
117
|
+
v-if="!userData?.AcceptedTerms"
|
|
118
|
+
id="acceptedTermsFWS"
|
|
119
|
+
v-model:checkbox-value="state.userData.AcceptedTerms"
|
|
120
|
+
type="toggle"
|
|
121
|
+
:label="$t('fws_accepted_terms_label')"
|
|
122
|
+
:help="$t('fws_accepted_terms_help')"
|
|
123
|
+
:error-vuelidate="v$.userData.AcceptedTerms.$errors"
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
<div class="grid gap-3 md:grid-cols-2 my-1">
|
|
127
|
+
<DefaultInput
|
|
128
|
+
id="enabledNotificationsFWS"
|
|
129
|
+
v-model:checkbox-value="state.userData.EnabledNotifications"
|
|
130
|
+
type="toggle"
|
|
131
|
+
:label="$t('fws_enabled_notifications_label')"
|
|
132
|
+
:help="$t('fws_enabled_notifications_help')"
|
|
133
|
+
:error-vuelidate="v$.userData.EnabledNotifications.$errors"
|
|
134
|
+
/>
|
|
135
|
+
<DefaultInput
|
|
136
|
+
id="enabledEmailsFWS"
|
|
137
|
+
v-model:checkbox-value="state.userData.EnabledEmails"
|
|
138
|
+
type="toggle"
|
|
139
|
+
:label="$t('fws_enabled_emails_label')"
|
|
140
|
+
:help="$t('fws_enabled_emails_help')"
|
|
141
|
+
:error-vuelidate="v$.userData.EnabledEmails.$errors"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div class="py-2 px-2 sm:px-3 my-2 bg-fv-neutral-50 dark:bg-fv-neutral-700/30 rounded-lg border border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
146
|
+
<DefaultInput
|
|
147
|
+
id="enabledTrainingFromMyDataFWS"
|
|
148
|
+
v-model:checkbox-value="state.userData.EnabledTrainingFromMyData"
|
|
149
|
+
type="toggle"
|
|
150
|
+
:label="$t('fws_enabled_training_from_my_data_label')"
|
|
151
|
+
:help="$t('fws_enabled_training_from_my_data_help')"
|
|
152
|
+
:error-vuelidate="v$.userData.EnabledTrainingFromMyData.$errors"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- Save Button -->
|
|
159
|
+
<div class="flex justify-end mt-6">
|
|
160
|
+
<button type="submit" class="btn defaults primary flex items-center gap-2">
|
|
161
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
162
|
+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
|
163
|
+
</svg>
|
|
164
|
+
{{ $t("fws_save_user_cta") }}
|
|
165
|
+
</button>
|
|
166
|
+
</div>
|
|
141
167
|
</div>
|
|
142
168
|
</form>
|
|
143
169
|
</template>
|
|
@@ -81,93 +81,148 @@ onMounted(() => {
|
|
|
81
81
|
|
|
82
82
|
<template>
|
|
83
83
|
<div class="flex flex-col gap-3">
|
|
84
|
+
<!-- Provider Selection Modal -->
|
|
84
85
|
<DefaultModal id="providers" :title="$t('providers_modal_title')">
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<button
|
|
91
|
-
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"
|
|
92
|
-
:style="`background: ${
|
|
93
|
-
provider.Data.Button.button['background-color']
|
|
94
|
-
}; color: ${$getContrastingTextColor(
|
|
95
|
-
provider.Data.Button.button['background-color'],
|
|
96
|
-
)}`"
|
|
97
|
-
@click="
|
|
98
|
-
() => {
|
|
99
|
-
getOAuth2Redirect(provider.UUID);
|
|
100
|
-
}
|
|
101
|
-
"
|
|
86
|
+
<div class="grid gap-4 p-1">
|
|
87
|
+
<template v-for="provider in providersData" :key="provider.UUID">
|
|
88
|
+
<div
|
|
89
|
+
v-if="!usedProviders[provider.UUID]"
|
|
90
|
+
class="flex items-center"
|
|
102
91
|
>
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
:
|
|
92
|
+
<button
|
|
93
|
+
class="flex border border-fv-neutral-300 dark:border-fv-neutral-700 rounded-lg shadow-sm hover:shadow items-center gap-2 justify-start w-full mx-auto !font-medium p-3 transition-all duration-200"
|
|
94
|
+
:style="`background: ${
|
|
95
|
+
provider.Data.Button.button['background-color']
|
|
96
|
+
}; color: ${$getContrastingTextColor(
|
|
97
|
+
provider.Data.Button.button['background-color'],
|
|
98
|
+
)}`"
|
|
99
|
+
@click="
|
|
100
|
+
() => {
|
|
101
|
+
getOAuth2Redirect(provider.UUID);
|
|
102
|
+
}
|
|
103
|
+
"
|
|
108
104
|
>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
105
|
+
<img
|
|
106
|
+
:key="`${provider.Data.Button.label}oauth`"
|
|
107
|
+
class="h-10 w-10 block p-1.5 mr-3 rounded"
|
|
108
|
+
:alt="provider.Data.Button.info.Name"
|
|
109
|
+
:src="provider.Data.Button.button.logo"
|
|
110
|
+
>
|
|
111
|
+
<div class="text-base">
|
|
112
|
+
{{
|
|
113
|
+
$t("user_flow_signin_with", {
|
|
114
|
+
provider: provider.Data.Button.name,
|
|
115
|
+
})
|
|
116
|
+
}}
|
|
117
|
+
</div>
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
</template>
|
|
121
|
+
</div>
|
|
119
122
|
</DefaultModal>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
|
|
124
|
+
<!-- Main Container -->
|
|
125
|
+
<div class="bg-white dark:bg-fv-neutral-800 rounded-lg shadow-sm border border-fv-neutral-200 dark:border-fv-neutral-700 p-4 sm:p-6">
|
|
126
|
+
<!-- Header -->
|
|
127
|
+
<div class="flex items-center justify-between mb-4 sm:mb-6 pb-2 sm:pb-3 border-b border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
128
|
+
<h2 class="text-lg font-semibold text-fv-neutral-900 dark:text-white">
|
|
129
|
+
{{ $t("oauth2_providers_title") }}
|
|
130
|
+
</h2>
|
|
131
|
+
<button
|
|
132
|
+
class="btn primary small flex items-center gap-2"
|
|
133
|
+
@click="
|
|
134
|
+
() => {
|
|
135
|
+
$eventBus.emit('providersModal', true);
|
|
136
|
+
}
|
|
137
|
+
"
|
|
138
|
+
>
|
|
139
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
|
140
|
+
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
|
|
141
|
+
</svg>
|
|
142
|
+
{{ $t("add_oauth2_con_cta") }}
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<!-- Error Message -->
|
|
147
|
+
<div
|
|
148
|
+
v-if="$route.query.error && $route.query.error === 'user_oauth2_connection_exists'"
|
|
149
|
+
class="mb-3 sm:mb-4 p-2 sm:p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg"
|
|
129
150
|
>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
</div>
|
|
145
|
-
<div
|
|
146
|
-
v-for="provider in data"
|
|
147
|
-
:key="provider.ProviderUUID"
|
|
148
|
-
class="flex items-center gap-3"
|
|
149
|
-
>
|
|
150
|
-
<img
|
|
151
|
-
:src="provider.Provider.Button.button.logo"
|
|
152
|
-
class="w-14 h-14 p-1 rounded-full"
|
|
153
|
-
:style="`background-color: ${provider.Provider.Button.button['background-color']}`"
|
|
151
|
+
<div class="flex items-start">
|
|
152
|
+
<svg class="w-5 h-5 text-red-600 dark:text-red-400 mt-0.5 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
153
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
|
|
154
|
+
</svg>
|
|
155
|
+
<p class="text-sm text-red-700 dark:text-red-300">
|
|
156
|
+
{{ $t("oauth2_error_user_oauth2_connection_exists") }}
|
|
157
|
+
</p>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<!-- Empty State -->
|
|
162
|
+
<div
|
|
163
|
+
v-if="data && data.length === 0"
|
|
164
|
+
class="py-6 sm:py-8 px-3 sm:px-4 text-center bg-fv-neutral-50 dark:bg-fv-neutral-700/30 rounded-lg border border-fv-neutral-200 dark:border-fv-neutral-700"
|
|
154
165
|
>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto text-fv-neutral-400 dark:text-fv-neutral-500 mb-3" viewBox="0 0 20 20" fill="currentColor">
|
|
167
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
|
|
168
|
+
</svg>
|
|
169
|
+
<p class="text-fv-neutral-700 dark:text-fv-neutral-300">
|
|
170
|
+
{{ $t("providers_empty") }}
|
|
171
|
+
</p>
|
|
172
|
+
<button
|
|
173
|
+
class="btn primary small mt-3"
|
|
174
|
+
@click="
|
|
175
|
+
() => {
|
|
176
|
+
$eventBus.emit('providersModal', true);
|
|
177
|
+
}
|
|
178
|
+
"
|
|
179
|
+
>
|
|
180
|
+
{{ $t("add_oauth2_con_cta") }}
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<!-- Connected Providers List -->
|
|
185
|
+
<div v-else class="grid gap-3 sm:gap-4">
|
|
186
|
+
<div
|
|
187
|
+
v-for="provider in data"
|
|
188
|
+
:key="provider.ProviderUUID"
|
|
189
|
+
class="flex items-center p-3 sm:p-4 bg-fv-neutral-50 dark:bg-fv-neutral-700/30 rounded-lg border border-fv-neutral-200 dark:border-fv-neutral-700 transition-all duration-200 hover:shadow-sm"
|
|
190
|
+
>
|
|
191
|
+
<div class="flex-shrink-0 mr-4">
|
|
192
|
+
<div
|
|
193
|
+
class="w-14 h-14 rounded-full flex items-center justify-center p-0.5"
|
|
194
|
+
:style="`background-color: ${provider.Provider.Button.button['background-color']}`"
|
|
195
|
+
>
|
|
196
|
+
<img
|
|
197
|
+
:src="provider.Provider.Button.button.logo"
|
|
198
|
+
class="w-10 h-10 object-contain"
|
|
199
|
+
:alt="provider.Provider.Button.name"
|
|
200
|
+
>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="flex-1 min-w-0">
|
|
204
|
+
<h3 class="text-lg font-medium text-fv-neutral-900 dark:text-white truncate">
|
|
205
|
+
{{ provider.Provider.Button.name }}
|
|
206
|
+
</h3>
|
|
207
|
+
<p class="text-sm text-fv-neutral-500 dark:text-fv-neutral-400 truncate">
|
|
208
|
+
{{ provider.ServiceID }}
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="ml-4">
|
|
212
|
+
<button
|
|
213
|
+
class="btn danger small flex items-center gap-1.5"
|
|
214
|
+
@click="
|
|
215
|
+
() => {
|
|
216
|
+
deleteOAuth2Connection(provider.ProviderUUID);
|
|
217
|
+
}
|
|
218
|
+
"
|
|
219
|
+
>
|
|
220
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
|
221
|
+
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
|
|
222
|
+
</svg>
|
|
223
|
+
{{ $t("remove_oauth2_con_cta") }}
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
171
226
|
</div>
|
|
172
227
|
</div>
|
|
173
228
|
</div>
|
|
@@ -160,118 +160,176 @@ function selectFile(e: Event) {
|
|
|
160
160
|
</script>
|
|
161
161
|
|
|
162
162
|
<template>
|
|
163
|
-
<form @submit.prevent="patchUser">
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
163
|
+
<form class="space-y-4" @submit.prevent="patchUser">
|
|
164
|
+
<div class="bg-white dark:bg-fv-neutral-800 p-4 sm:p-6 rounded-lg shadow-sm border border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
165
|
+
<h3 class="text-lg font-semibold text-fv-neutral-900 dark:text-white mb-4 pb-2 border-b border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
166
|
+
{{ $t('fws_profile_heading') || $t('fws_your_profile') }}
|
|
167
|
+
</h3>
|
|
168
|
+
|
|
169
|
+
<!-- Username and Avatar Section -->
|
|
170
|
+
<div class="grid md:grid-cols-2 gap-4 sm:gap-6 mb-4 sm:mb-6">
|
|
171
|
+
<!-- Avatar Upload -->
|
|
172
|
+
<div class="flex flex-col">
|
|
173
|
+
<h4 class="font-medium text-fv-neutral-800 dark:text-white text-sm mb-3">
|
|
174
|
+
{{ $t("fws_profile_image") }}
|
|
175
|
+
</h4>
|
|
176
|
+
<div class="flex items-center gap-3 sm:gap-4">
|
|
177
|
+
<div class="relative group">
|
|
178
|
+
<img
|
|
179
|
+
v-if="userData?.UserProfile?.AvatarUUID"
|
|
180
|
+
:src="`${imageDomain}/${userData?.UserProfile?.AvatarUUID}?vars=format=png:resize=100x100`"
|
|
181
|
+
class="w-20 h-20 rounded-full object-cover border-2 border-fv-neutral-200 dark:border-fv-neutral-700 shadow-sm"
|
|
182
|
+
alt="Profile Avatar"
|
|
183
|
+
>
|
|
184
|
+
<div v-else class="w-20 h-20 rounded-full bg-fv-neutral-200 dark:bg-fv-neutral-700 flex items-center justify-center">
|
|
185
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-fv-neutral-400 dark:text-fv-neutral-500" viewBox="0 0 20 20" fill="currentColor">
|
|
186
|
+
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd" />
|
|
187
|
+
</svg>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="flex-1">
|
|
192
|
+
<label
|
|
193
|
+
class="block text-sm font-medium mb-2 text-fv-neutral-800 dark:text-white"
|
|
194
|
+
for="file_input"
|
|
195
|
+
>{{ $t("fws_upload_av_label") }}</label>
|
|
196
|
+
<div class="relative">
|
|
197
|
+
<input
|
|
198
|
+
id="file_input"
|
|
199
|
+
ref="uploadInput"
|
|
200
|
+
class="block text-sm w-full text-fv-neutral-700 border border-fv-neutral-300 rounded-lg cursor-pointer bg-fv-neutral-50 dark:text-fv-neutral-400 focus:outline-none dark:bg-fv-neutral-700 dark:border-fv-neutral-600 file:mr-4 file:py-2 file:px-4 file:border-0 file:text-sm file:font-medium file:bg-fv-primary-50 file:text-fv-primary-600 dark:file:bg-fv-primary-900 dark:file:text-fv-primary-300 hover:file:bg-fv-primary-100 dark:hover:file:bg-fv-primary-800"
|
|
201
|
+
type="file"
|
|
202
|
+
accept="image/jpg, image/jpeg, image/png, image/gif"
|
|
203
|
+
@change="selectFile"
|
|
204
|
+
>
|
|
205
|
+
</div>
|
|
206
|
+
<p class="mt-1 text-xs text-fv-neutral-500 dark:text-fv-neutral-400">
|
|
207
|
+
{{ $t("fws_upload_av_help") || "JPG, JPEG, PNG or GIF (max 2MB)" }}
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<!-- Username -->
|
|
214
|
+
<div class="flex flex-col justify-center">
|
|
215
|
+
<DefaultInput
|
|
216
|
+
id="usernameFWS"
|
|
217
|
+
v-model="state.userData.Username"
|
|
218
|
+
type="text"
|
|
219
|
+
:label="$t('fws_username_label')"
|
|
220
|
+
:help="$t('fws_username_help')"
|
|
221
|
+
:error-vuelidate="v$.userData.Username.$errors"
|
|
222
|
+
:disabled="userData?.UserProfile?.HasUsernameAndSlug ? true : false"
|
|
223
|
+
/>
|
|
224
|
+
</div>
|
|
192
225
|
</div>
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
|
|
227
|
+
<!-- Cropper Modal -->
|
|
228
|
+
<DefaultModal id="avCrop" :title="$t('fws_crop_av_title')">
|
|
229
|
+
<div class="flex flex-col gap-4">
|
|
230
|
+
<div class="max-h-[70vh] overflow-hidden rounded-lg border border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
231
|
+
<VuePictureCropper
|
|
232
|
+
:box-style="{
|
|
233
|
+
width: 'auto',
|
|
234
|
+
height: 'auto',
|
|
235
|
+
backgroundColor: '#f8f8f8',
|
|
236
|
+
margin: 'auto',
|
|
237
|
+
}"
|
|
238
|
+
:img="pic"
|
|
239
|
+
:options="{
|
|
240
|
+
viewMode: 1,
|
|
241
|
+
dragMode: 'crop',
|
|
242
|
+
aspectRatio: 1 / 1,
|
|
243
|
+
}"
|
|
244
|
+
class="max-h-[70vh] w-full"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
<button class="btn defaults primary self-end" @click="getCropResult">
|
|
248
|
+
{{ $t("fws_crop_av_cta") }}
|
|
249
|
+
</button>
|
|
250
|
+
</div>
|
|
251
|
+
</DefaultModal>
|
|
252
|
+
|
|
253
|
+
<!-- Profile Information -->
|
|
254
|
+
<div class="grid md:grid-cols-2 gap-x-4 sm:gap-x-6 gap-y-3 sm:gap-y-4 mb-4 sm:mb-6">
|
|
255
|
+
<DefaultInput
|
|
256
|
+
id="genderFWS"
|
|
257
|
+
v-model="state.userData.Gender"
|
|
258
|
+
type="select"
|
|
259
|
+
:options="[
|
|
260
|
+
['female', $t('fws_persona_phys_appearance_opt_female')],
|
|
261
|
+
['male', $t('fws_persona_phys_appearance_opt_male')],
|
|
262
|
+
['non-binary', $t('fws_persona_phys_appearance_opt_non_binary')],
|
|
263
|
+
]"
|
|
264
|
+
:label="$t('fws_gender_label')"
|
|
265
|
+
:error-vuelidate="v$.userData.Gender.$errors"
|
|
266
|
+
/>
|
|
267
|
+
|
|
268
|
+
<DefaultInput
|
|
269
|
+
id="birthdateFWS"
|
|
270
|
+
v-model="state.userData.Birthdate"
|
|
271
|
+
type="datepicker"
|
|
272
|
+
:disable-dates-under18="true"
|
|
273
|
+
:label="$t('fws_birthdate_label')"
|
|
274
|
+
:error-vuelidate="v$.userData.Birthdate.$errors"
|
|
275
|
+
/>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<!-- Bio -->
|
|
279
|
+
<div class="mb-4 sm:mb-6">
|
|
280
|
+
<DefaultInput
|
|
281
|
+
id="bioFWS"
|
|
282
|
+
v-model="state.userData.Bio"
|
|
283
|
+
type="textarea"
|
|
284
|
+
:label="$t('fws_bio_label')"
|
|
285
|
+
:error-vuelidate="v$.userData.Bio.$errors"
|
|
286
|
+
:dp-options="{ counterMax: 1000 }"
|
|
213
287
|
/>
|
|
214
288
|
</div>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
:label="$t('fws_public_bio')"
|
|
260
|
-
:error-vuelidate="v$.userData.PublicBio.$errors"
|
|
261
|
-
/>
|
|
262
|
-
<DefaultInput
|
|
263
|
-
v-if="!hideBirthdate"
|
|
264
|
-
id="publicBirthdateFWS"
|
|
265
|
-
v-model:checkbox-value="state.userData.PublicBirthdate"
|
|
266
|
-
type="toggle"
|
|
267
|
-
:label="$t('fws_public_birthdate')"
|
|
268
|
-
:error-vuelidate="v$.userData.PublicBirthdate.$errors"
|
|
269
|
-
/>
|
|
270
|
-
</template>
|
|
271
|
-
<div class="flex">
|
|
272
|
-
<button type="submit" class="btn defaults primary">
|
|
273
|
-
{{ $t("fws_save_user_cta") }}
|
|
274
|
-
</button>
|
|
289
|
+
|
|
290
|
+
<!-- Privacy Settings -->
|
|
291
|
+
<template v-if="!hidePublic">
|
|
292
|
+
<div class="pt-3 sm:pt-4 border-t border-fv-neutral-200 dark:border-fv-neutral-700 mb-3 sm:mb-4">
|
|
293
|
+
<h4 class="font-medium text-fv-neutral-800 dark:text-white mb-3">
|
|
294
|
+
{{ $t('fws_privacy_settings') }}
|
|
295
|
+
</h4>
|
|
296
|
+
<div class="grid gap-2">
|
|
297
|
+
<DefaultInput
|
|
298
|
+
v-if="!hideGender"
|
|
299
|
+
id="publicGenderFWS"
|
|
300
|
+
v-model:checkbox-value="state.userData.PublicGender"
|
|
301
|
+
type="toggle"
|
|
302
|
+
:label="$t('fws_public_gender')"
|
|
303
|
+
:error-vuelidate="v$.userData.PublicGender.$errors"
|
|
304
|
+
/>
|
|
305
|
+
<DefaultInput
|
|
306
|
+
id="publicBioFWS"
|
|
307
|
+
v-model:checkbox-value="state.userData.PublicBio"
|
|
308
|
+
type="toggle"
|
|
309
|
+
:label="$t('fws_public_bio')"
|
|
310
|
+
:error-vuelidate="v$.userData.PublicBio.$errors"
|
|
311
|
+
/>
|
|
312
|
+
<DefaultInput
|
|
313
|
+
v-if="!hideBirthdate"
|
|
314
|
+
id="publicBirthdateFWS"
|
|
315
|
+
v-model:checkbox-value="state.userData.PublicBirthdate"
|
|
316
|
+
type="toggle"
|
|
317
|
+
:label="$t('fws_public_birthdate')"
|
|
318
|
+
:error-vuelidate="v$.userData.PublicBirthdate.$errors"
|
|
319
|
+
/>
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
</template>
|
|
323
|
+
|
|
324
|
+
<!-- Save Button -->
|
|
325
|
+
<div class="flex justify-end pt-2">
|
|
326
|
+
<button type="submit" class="btn defaults primary flex items-center gap-2">
|
|
327
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
328
|
+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
|
329
|
+
</svg>
|
|
330
|
+
{{ $t("fws_save_user_cta") }}
|
|
331
|
+
</button>
|
|
332
|
+
</div>
|
|
275
333
|
</div>
|
|
276
334
|
</form>
|
|
277
335
|
</template>
|
|
@@ -110,57 +110,68 @@ async function patchUser() {
|
|
|
110
110
|
</script>
|
|
111
111
|
|
|
112
112
|
<template>
|
|
113
|
-
<form @submit.prevent="patchUser">
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
type="text"
|
|
119
|
-
:label="$t('fws_username_label')"
|
|
120
|
-
:help="$t('fws_username_help')"
|
|
121
|
-
:error-vuelidate="v$.userData.Username.$errors"
|
|
122
|
-
:disabled="userData?.UserProfile?.HasUsernameAndSlug ? true : false"
|
|
123
|
-
/>
|
|
124
|
-
<DefaultInput
|
|
125
|
-
id="birthdateFWS"
|
|
126
|
-
v-model="state.userData.Birthdate"
|
|
127
|
-
class="mb-4"
|
|
128
|
-
:disable-dates-under18="true"
|
|
129
|
-
type="datepicker"
|
|
130
|
-
:label="$t('fws_birthdate_label')"
|
|
131
|
-
:error-vuelidate="v$.userData.Birthdate.$errors"
|
|
132
|
-
/>
|
|
113
|
+
<form class="space-y-4" @submit.prevent="patchUser">
|
|
114
|
+
<div class="bg-white dark:bg-fv-neutral-800 p-4 sm:p-6 rounded-lg shadow-sm border border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
115
|
+
<h3 class="text-lg font-semibold text-fv-neutral-900 dark:text-white mb-4 pb-2 border-b border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
116
|
+
{{ $t('fws_user_profile_title') || $t('fws_profile_settings') }}
|
|
117
|
+
</h3>
|
|
133
118
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
:label="$t('fws_gender_label')"
|
|
145
|
-
:error-vuelidate="v$.userData.Gender.$errors"
|
|
146
|
-
/>
|
|
119
|
+
<div class="grid gap-4">
|
|
120
|
+
<DefaultInput
|
|
121
|
+
id="usernameFWS"
|
|
122
|
+
v-model="state.userData.Username"
|
|
123
|
+
type="text"
|
|
124
|
+
:label="$t('fws_username_label')"
|
|
125
|
+
:help="$t('fws_username_help')"
|
|
126
|
+
:error-vuelidate="v$.userData.Username.$errors"
|
|
127
|
+
:disabled="userData?.UserProfile?.HasUsernameAndSlug ? true : false"
|
|
128
|
+
/>
|
|
147
129
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<p v-if="props.termsText" class="terms-box">
|
|
157
|
-
{{ props.termsText }}
|
|
158
|
-
</p>
|
|
130
|
+
<DefaultInput
|
|
131
|
+
id="birthdateFWS"
|
|
132
|
+
v-model="state.userData.Birthdate"
|
|
133
|
+
:disable-dates-under18="true"
|
|
134
|
+
type="datepicker"
|
|
135
|
+
:label="$t('fws_birthdate_label')"
|
|
136
|
+
:error-vuelidate="v$.userData.Birthdate.$errors"
|
|
137
|
+
/>
|
|
159
138
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
139
|
+
<DefaultInput
|
|
140
|
+
id="genderFWS"
|
|
141
|
+
v-model="state.userData.Gender"
|
|
142
|
+
type="select"
|
|
143
|
+
:options="[
|
|
144
|
+
['female', $t('fws_persona_phys_appearance_opt_female')],
|
|
145
|
+
['male', $t('fws_persona_phys_appearance_opt_male')],
|
|
146
|
+
['non-binary', $t('fws_persona_phys_appearance_opt_non_binary')],
|
|
147
|
+
]"
|
|
148
|
+
:label="$t('fws_gender_label')"
|
|
149
|
+
:error-vuelidate="v$.userData.Gender.$errors"
|
|
150
|
+
/>
|
|
151
|
+
|
|
152
|
+
<div class="mt-2 pt-3 border-t border-fv-neutral-200 dark:border-fv-neutral-700">
|
|
153
|
+
<DefaultInput
|
|
154
|
+
id="acceptedTermsFWS"
|
|
155
|
+
v-model:checkbox-value="state.userData.AcceptedTerms"
|
|
156
|
+
type="toggle"
|
|
157
|
+
:label="$t('fws_accepted_terms_label')"
|
|
158
|
+
:help="$t('fws_accepted_terms_help')"
|
|
159
|
+
:error-vuelidate="v$.userData.AcceptedTerms.$errors"
|
|
160
|
+
/>
|
|
161
|
+
<p v-if="props.termsText" class="mt-2 p-2 sm:p-3 text-sm bg-fv-neutral-50 dark:bg-fv-neutral-700 rounded border border-fv-neutral-200 dark:border-fv-neutral-600 text-fv-neutral-700 dark:text-fv-neutral-300">
|
|
162
|
+
{{ props.termsText }}
|
|
163
|
+
</p>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div class="mt-6 flex justify-end">
|
|
168
|
+
<button type="submit" class="btn defaults primary flex items-center gap-2">
|
|
169
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
170
|
+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
|
171
|
+
</svg>
|
|
172
|
+
{{ $t("fws_save_user_cta") }}
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
164
175
|
</div>
|
|
165
176
|
</form>
|
|
166
177
|
</template>
|