@fy-/fws-vue 2.1.6 → 2.1.7

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.
Files changed (40) hide show
  1. package/components/fws/CmsArticleBoxed.vue +23 -20
  2. package/components/fws/CmsArticleSingle.vue +74 -68
  3. package/components/fws/DataTable.vue +132 -125
  4. package/components/fws/FilterData.vue +99 -101
  5. package/components/fws/UserData.vue +33 -32
  6. package/components/fws/UserFlow.vue +163 -155
  7. package/components/fws/UserOAuth2.vue +73 -72
  8. package/components/fws/UserProfile.vue +98 -101
  9. package/components/fws/UserProfileStrict.vue +65 -64
  10. package/components/ssr/ClientOnly.ts +7 -7
  11. package/components/ui/DefaultBreadcrumb.vue +13 -13
  12. package/components/ui/DefaultConfirm.vue +35 -34
  13. package/components/ui/DefaultDateSelection.vue +19 -17
  14. package/components/ui/DefaultDropdown.vue +25 -25
  15. package/components/ui/DefaultDropdownLink.vue +15 -14
  16. package/components/ui/DefaultGallery.vue +179 -168
  17. package/components/ui/DefaultInput.vue +121 -126
  18. package/components/ui/DefaultLoader.vue +17 -17
  19. package/components/ui/DefaultModal.vue +35 -33
  20. package/components/ui/DefaultNotif.vue +50 -52
  21. package/components/ui/DefaultPaging.vue +92 -95
  22. package/components/ui/DefaultSidebar.vue +29 -25
  23. package/components/ui/DefaultTagInput.vue +121 -119
  24. package/components/ui/transitions/CollapseTransition.vue +1 -1
  25. package/components/ui/transitions/ExpandTransition.vue +1 -1
  26. package/components/ui/transitions/FadeTransition.vue +1 -1
  27. package/components/ui/transitions/ScaleTransition.vue +1 -1
  28. package/components/ui/transitions/SlideTransition.vue +3 -3
  29. package/composables/event-bus.ts +10 -8
  30. package/composables/rest.ts +59 -56
  31. package/composables/seo.ts +106 -95
  32. package/composables/ssr.ts +64 -62
  33. package/composables/templating.ts +57 -57
  34. package/composables/translations.ts +13 -13
  35. package/env.d.ts +6 -4
  36. package/index.ts +101 -98
  37. package/package.json +7 -7
  38. package/stores/serverRouter.ts +25 -25
  39. package/stores/user.ts +79 -72
  40. package/types.d.ts +65 -65
@@ -1,81 +1,82 @@
1
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";
2
+ import { computed, onMounted, ref } from 'vue'
3
+ import { useEventBus } from '../../composables/event-bus'
4
+ import { useRest } from '../../composables/rest'
5
+ import { useTranslation } from '../../composables/translations'
6
+ import { useUserStore } from '../../stores/user'
7
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>>({});
8
+ import DefaultModal from '../ui/DefaultModal.vue'
9
+
10
+ const rest = useRest()
11
+ const eventBus = useEventBus()
12
+ const userStore = useUserStore()
13
+ const isAuth = computed(() => userStore.isAuth)
14
+ const data = ref()
15
+ const providersData = ref()
16
+ const usedProviders = ref<Record<string, boolean>>({})
16
17
  const props = defineProps({
17
18
  returnTo: {
18
19
  type: String,
19
20
  required: false,
20
- default: "/user/account?tab=user_settings",
21
+ default: '/user/account?tab=user_settings',
21
22
  },
22
- });
23
- const returnTo = ref(props.returnTo);
24
- if (returnTo.value == "") {
25
- returnTo.value = "/user/account?tab=user_settings";
23
+ })
24
+ const returnTo = ref(props.returnTo)
25
+ if (returnTo.value === '') {
26
+ returnTo.value = '/user/account?tab=user_settings'
26
27
  }
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;
28
+ async function getOAuth2Providers() {
29
+ eventBus.emit('main-loading', true)
30
+ const d = await rest('User/OAuth2/Providers', 'GET')
31
+ if (d && d.result === 'success') {
32
+ providersData.value = d.data
32
33
  }
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", {
34
+ eventBus.emit('main-loading', false)
35
+ }
36
+ async function getOAuth2Redirect(providerUUID: string) {
37
+ eventBus.emit('main-loading', true)
38
+ const d = await rest(`User/OAuth2/Provider/${providerUUID}`, 'POST', {
38
39
  ReturnTo: returnTo.value,
39
- });
40
- if (d && d.result == "success") {
41
- window.location.href = d.data;
40
+ })
41
+ if (d && d.result === 'success') {
42
+ window.location.href = d.data
42
43
  }
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"),
44
+ eventBus.emit('main-loading', false)
45
+ }
46
+ const translate = useTranslation()
47
+ async function deleteOAuth2Connection(providerUUID: string) {
48
+ eventBus.emit('showConfirm', {
49
+ title: translate('remove_provider_confirm_title'),
50
+ desc: translate('remove_provider_confirm_desc_warning'),
50
51
  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();
52
+ eventBus.emit('main-loading', true)
53
+ const d = await rest(`User/OAuth2/Provider/${providerUUID}`, 'DELETE')
54
+ if (d && d.result === 'success') {
55
+ getOAuth2User()
55
56
  }
56
- eventBus.emit("main-loading", false);
57
+ eventBus.emit('main-loading', false)
57
58
  },
58
- });
59
- };
60
- const getOAuth2User = async () => {
61
- eventBus.emit("main-loading", true);
59
+ })
60
+ }
61
+ async function getOAuth2User() {
62
+ eventBus.emit('main-loading', true)
62
63
  if (!isAuth.value) {
63
- return;
64
+ return
64
65
  }
65
- const d = await rest("User/OAuth2", "GET");
66
- usedProviders.value = {};
67
- if (d && d.result == "success") {
68
- data.value = d.data;
66
+ const d = await rest('User/OAuth2', 'GET')
67
+ usedProviders.value = {}
68
+ if (d && d.result === 'success') {
69
+ data.value = d.data
69
70
  data.value.forEach((p: any) => {
70
- usedProviders.value[p.ProviderUUID] = true;
71
- });
71
+ usedProviders.value[p.ProviderUUID] = true
72
+ })
72
73
  }
73
- eventBus.emit("main-loading", false);
74
- };
74
+ eventBus.emit('main-loading', false)
75
+ }
75
76
  onMounted(() => {
76
- getOAuth2User();
77
- getOAuth2Providers();
78
- });
77
+ getOAuth2User()
78
+ getOAuth2Providers()
79
+ })
79
80
  </script>
80
81
 
81
82
  <template>
@@ -83,28 +84,28 @@ onMounted(() => {
83
84
  <DefaultModal id="providers" :title="$t('providers_modal_title')">
84
85
  <template v-for="provider in providersData" :key="provider.UUID">
85
86
  <div
86
- class="flex items-center gap-3"
87
87
  v-if="!usedProviders[provider.UUID]"
88
+ class="flex items-center gap-3"
88
89
  >
89
90
  <button
90
- @click="
91
- () => {
92
- getOAuth2Redirect(provider.UUID);
93
- }
94
- "
95
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"
96
92
  :style="`background: ${
97
93
  provider.Data.Button.button['background-color']
98
94
  }; color: ${$getContrastingTextColor(
99
95
  provider.Data.Button.button['background-color'],
100
96
  )}`"
97
+ @click="
98
+ () => {
99
+ getOAuth2Redirect(provider.UUID);
100
+ }
101
+ "
101
102
  >
102
103
  <img
103
104
  :key="`${provider.Data.Button.label}oauth`"
104
105
  class="h-12 w-12 block p-2 mr-3"
105
106
  :alt="provider.Data.Button.info.Name"
106
107
  :src="provider.Data.Button.button.logo"
107
- />
108
+ >
108
109
  <div>
109
110
  {{
110
111
  $t("user_flow_signin_with", {
@@ -130,27 +131,27 @@ onMounted(() => {
130
131
  </button>
131
132
  </h2>
132
133
  <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
134
  v-if="
135
- $route.query.error &&
136
- $route.query.error === 'user_oauth2_connection_exists'
135
+ $route.query.error
136
+ && $route.query.error === 'user_oauth2_connection_exists'
137
137
  "
138
+ class="text-red-900 dark:text-red-300 text-sm bg-red-200/[.2] dark:bg-red-900/[.2] p-2 rounded shadow"
138
139
  >
139
140
  {{ $t("oauth2_error_user_oauth2_connection_exists") }}
140
141
  </p>
141
- <div v-if="data && data.length == 0">
142
+ <div v-if="data && data.length === 0">
142
143
  <p>{{ $t("providers_empty") }}</p>
143
144
  </div>
144
145
  <div
145
146
  v-for="provider in data"
146
- class="flex items-center gap-3"
147
147
  :key="provider.ProviderUUID"
148
+ class="flex items-center gap-3"
148
149
  >
149
150
  <img
150
151
  :src="provider.Provider.Button.button.logo"
151
152
  class="w-14 h-14 p-1 rounded-full"
152
153
  :style="`background-color: ${provider.Provider.Button.button['background-color']}`"
153
- />
154
+ >
154
155
  <div>
155
156
  <h3 class="text-xl">
156
157
  {{ provider.Provider.Button.name }}
@@ -1,73 +1,73 @@
1
1
  <script setup lang="ts">
2
- import useVuelidate from "@vuelidate/core";
3
- import DefaultInput from "../ui/DefaultInput.vue";
4
- import DefaultModal from "../ui/DefaultModal.vue";
5
- import { useUserStore } from "../../stores/user";
6
- import { useRest } from "../../composables/rest";
7
- import { useEventBus } from "../../composables/event-bus";
8
- import { computed, reactive, watchEffect, ref } from "vue";
9
- import { required, maxLength } from "@vuelidate/validators";
10
- import VuePictureCropper, { cropper } from "vue-picture-cropper";
11
- import { Uploader } from "@fy-/fws-js";
2
+ import { Uploader } from '@fy-/fws-js'
3
+ import useVuelidate from '@vuelidate/core'
4
+ import { maxLength, required } from '@vuelidate/validators'
5
+ import { computed, reactive, ref, watchEffect } from 'vue'
6
+ import VuePictureCropper, { cropper } from 'vue-picture-cropper'
7
+ import { useEventBus } from '../../composables/event-bus'
8
+ import { useRest } from '../../composables/rest'
9
+ import { useUserStore } from '../../stores/user'
10
+ import DefaultInput from '../ui/DefaultInput.vue'
11
+ import DefaultModal from '../ui/DefaultModal.vue'
12
12
 
13
13
  const props = withDefaults(
14
14
  defineProps<{
15
- imageDomain?: string;
16
- onCompleted?: (data: any) => void;
17
- hidePublic?: boolean;
18
- hideBirthdate?: boolean;
19
- hideGender?: boolean;
15
+ imageDomain?: string
16
+ onCompleted?: (data: any) => void
17
+ hidePublic?: boolean
18
+ hideBirthdate?: boolean
19
+ hideGender?: boolean
20
20
  }>(),
21
21
  {
22
- imageDomain: "https://s.nocachenocry.com",
22
+ imageDomain: 'https://s.nocachenocry.com',
23
23
  onCompleted: () => {},
24
24
  hidePublic: false,
25
25
  hideBirthdate: false,
26
26
  hideGender: false,
27
27
  },
28
- );
29
- const rest = useRest();
30
- const userStore = useUserStore();
31
- const userData = computed(() => userStore.user);
32
- const eventBus = useEventBus();
28
+ )
29
+ const rest = useRest()
30
+ const userStore = useUserStore()
31
+ const userData = computed(() => userStore.user)
32
+ const eventBus = useEventBus()
33
33
  // Default date = 18y from now
34
- const currentDate = new Date();
34
+ const currentDate = new Date()
35
35
  const defaultDate = new Date(
36
36
  currentDate.setFullYear(currentDate.getFullYear() - 18),
37
37
  )
38
38
  .toISOString()
39
- .split("T")[0];
39
+ .split('T')[0]
40
40
 
41
41
  const state = reactive({
42
42
  userData: {
43
- Username: userData.value?.UserProfile?.Username || "",
44
- Gender: userData.value?.UserProfile?.Gender || "",
45
- Bio: userData.value?.UserProfile?.Bio || "",
43
+ Username: userData.value?.UserProfile?.Username || '',
44
+ Gender: userData.value?.UserProfile?.Gender || '',
45
+ Bio: userData.value?.UserProfile?.Bio || '',
46
46
  Birthdate: userData.value?.UserProfile?.Birthdate || defaultDate,
47
47
  PublicGender: userData.value?.UserProfile?.PublicGender || false,
48
48
  PublicBio: userData.value?.UserProfile?.PublicBio || false,
49
49
  PublicBirthdate: userData.value?.UserProfile?.PublicBirthdate || false,
50
50
  },
51
- });
51
+ })
52
52
  watchEffect(() => {
53
53
  state.userData = {
54
- Username: userData.value?.UserProfile?.Username || "",
55
- Gender: userData.value?.UserProfile?.Gender || "",
56
- Bio: userData.value?.UserProfile?.Bio || "",
54
+ Username: userData.value?.UserProfile?.Username || '',
55
+ Gender: userData.value?.UserProfile?.Gender || '',
56
+ Bio: userData.value?.UserProfile?.Bio || '',
57
57
  Birthdate: userData.value?.UserProfile?.Birthdate
58
58
  ? new Date(userData.value?.UserProfile?.Birthdate.unixms)
59
- .toISOString()
60
- .split("T")[0]
59
+ .toISOString()
60
+ .split('T')[0]
61
61
  : defaultDate,
62
62
  PublicGender: userData.value?.UserProfile?.PublicGender || false,
63
63
  PublicBio: userData.value?.UserProfile?.PublicBio || false,
64
64
  PublicBirthdate: userData.value?.UserProfile?.PublicBirthdate || false,
65
- };
66
- });
65
+ }
66
+ })
67
67
  const rules = {
68
68
  userData: {
69
69
  Username: {
70
- required: required,
70
+ required,
71
71
  },
72
72
  Gender: {},
73
73
  Bio: {
@@ -78,85 +78,84 @@ const rules = {
78
78
  PublicBio: {},
79
79
  PublicBirthdate: {},
80
80
  },
81
- };
82
- const v$ = useVuelidate(rules, state);
81
+ }
82
+ const v$ = useVuelidate(rules, state)
83
83
 
84
- const patchUser = async () => {
85
- eventBus.emit("main-loading", true);
84
+ async function patchUser() {
85
+ eventBus.emit('main-loading', true)
86
86
  if (await v$.value.userData.$validate()) {
87
- const data = { ...state.userData };
88
- const birtdate = new Date(`${data.Birthdate}T00:00:00Z`);
87
+ const data = { ...state.userData }
88
+ const birtdate = new Date(`${data.Birthdate}T00:00:00Z`)
89
89
  try {
90
- const birtdateAtUnixms = birtdate.getTime();
91
- // @ts-ignore
92
- data.Birthdate = new Date(birtdateAtUnixms).toISOString().split("T")[0];
93
- } catch (e) {
94
- // @ts-ignore
95
- data.Birthdate = data.Birthdate.toISOString().split("T")[0];
90
+ const birtdateAtUnixms = birtdate.getTime()
91
+ data.Birthdate = new Date(birtdateAtUnixms).toISOString().split('T')[0]
96
92
  }
97
- // @ts-ignore
98
- const response = await rest("User/_Profile", "PATCH", data);
99
- if (response && response.result == "success") {
93
+ catch {
94
+ // @ts-expect-error: Birthdate is a string
95
+ data.Birthdate = data.Birthdate.toISOString().split('T')[0]
96
+ }
97
+ const response = await rest('User/_Profile', 'PATCH', data)
98
+ if (response && response.result === 'success') {
100
99
  if (props.onCompleted) {
101
- props.onCompleted(response);
100
+ props.onCompleted(response)
102
101
  }
103
- eventBus.emit("user:refresh", true);
102
+ eventBus.emit('user:refresh', true)
104
103
  }
105
104
  }
106
- eventBus.emit("main-loading", false);
107
- };
108
- const uploadInput = ref<HTMLInputElement | null>(null);
109
- const pic = ref<string>("");
105
+ eventBus.emit('main-loading', false)
106
+ }
107
+ const uploadInput = ref<HTMLInputElement | null>(null)
108
+ const pic = ref<string>('')
110
109
  const cropResult = reactive({
111
- dataURL: "",
112
- blobURL: "",
113
- });
114
- const uploader = ref(new Uploader());
110
+ dataURL: '',
111
+ blobURL: '',
112
+ })
113
+ const uploader = ref(new Uploader())
115
114
 
116
115
  async function getCropResult() {
117
- if (!cropper) return;
118
- const base64 = cropper.getDataURL({});
119
- const blob: Blob | null = await cropper.getBlob();
120
- if (!blob) return;
121
- eventBus.emit("main-loading", true);
116
+ if (!cropper) return
117
+ const base64 = cropper.getDataURL({})
118
+ const blob: Blob | null = await cropper.getBlob()
119
+ if (!blob) return
120
+ eventBus.emit('main-loading', true)
122
121
 
123
122
  const file = await cropper.getFile({
124
- fileName: "avatar-" + userData.value?.UUID,
125
- });
123
+ fileName: `avatar-${userData.value?.UUID}`,
124
+ })
126
125
 
127
- cropResult.dataURL = base64;
128
- cropResult.blobURL = URL.createObjectURL(blob);
126
+ cropResult.dataURL = base64
127
+ cropResult.blobURL = URL.createObjectURL(blob)
129
128
  if (file) {
130
- uploader.value.addFile(file);
129
+ uploader.value.addFile(file)
131
130
  }
132
131
  const fileUploadCallback = (response: any) => {
133
132
  if (props.onCompleted) {
134
- props.onCompleted(response);
133
+ props.onCompleted(response)
135
134
  }
136
- eventBus.emit("avCropModal", false);
137
- eventBus.emit("main-loading", false);
138
- eventBus.emit("user:refresh", true);
139
- };
140
- uploader.value.startUpload(`/_special/rest/User/_Avatar`, fileUploadCallback);
135
+ eventBus.emit('avCropModal', false)
136
+ eventBus.emit('main-loading', false)
137
+ eventBus.emit('user:refresh', true)
138
+ }
139
+ uploader.value.startUpload(`/_special/rest/User/_Avatar`, fileUploadCallback)
141
140
  }
142
141
 
143
142
  function selectFile(e: Event) {
144
- pic.value = "";
145
- cropResult.dataURL = "";
146
- cropResult.blobURL = "";
143
+ pic.value = ''
144
+ cropResult.dataURL = ''
145
+ cropResult.blobURL = ''
147
146
 
148
- const { files } = e.target as HTMLInputElement;
149
- if (!files || !files.length) return;
147
+ const { files } = e.target as HTMLInputElement
148
+ if (!files || !files.length) return
150
149
 
151
- const file = files[0];
152
- const reader = new FileReader();
153
- reader.readAsDataURL(file);
150
+ const file = files[0]
151
+ const reader = new FileReader()
152
+ reader.readAsDataURL(file)
154
153
  reader.onload = () => {
155
- pic.value = String(reader.result);
156
- eventBus.emit("avCropModal", true);
157
- if (!uploadInput.value) return;
158
- uploadInput.value.value = "";
159
- };
154
+ pic.value = String(reader.result)
155
+ eventBus.emit('avCropModal', true)
156
+ if (!uploadInput.value) return
157
+ uploadInput.value.value = ''
158
+ }
160
159
  }
161
160
  </script>
162
161
 
@@ -174,32 +173,31 @@ function selectFile(e: Event) {
174
173
  />
175
174
  <div class="flex gap-2 items-center mb-4">
176
175
  <img
176
+ v-if="userData?.UserProfile?.AvatarUUID"
177
177
  :src="`${imageDomain}/${userData?.UserProfile?.AvatarUUID}?vars=format=png:resize=100x100`"
178
178
  class="w-16 h-16 rounded-full flex-0 shrink-0 grow-0"
179
- v-if="userData?.UserProfile?.AvatarUUID"
180
- />
179
+ >
181
180
  <div class="flex-1">
182
181
  <label
183
182
  class="block text-sm font-medium mb-2 text-neutral-900 dark:text-white"
184
183
  for="file_input"
185
- >{{ $t("fws_upload_av_label") }}</label
186
- >
184
+ >{{ $t("fws_upload_av_label") }}</label>
187
185
  <input
188
- class="block text-sm w-full text-neutral-900 border border-neutral-300 rounded-lg cursor-pointer bg-neutral-50 dark:text-neutral-400 focus:outline-none dark:bg-neutral-700 dark:border-neutral-600 dark:placeholder-neutral-400"
189
186
  ref="uploadInput"
187
+ class="block text-sm w-full text-neutral-900 border border-neutral-300 rounded-lg cursor-pointer bg-neutral-50 dark:text-neutral-400 focus:outline-none dark:bg-neutral-700 dark:border-neutral-600 dark:placeholder-neutral-400"
190
188
  type="file"
191
189
  accept="image/jpg, image/jpeg, image/png, image/gif"
192
190
  @change="selectFile"
193
- />
191
+ >
194
192
  </div>
195
193
  </div>
196
194
  <DefaultModal id="avCrop" :title="$t('fws_crop_av_title')">
197
- <button @click="getCropResult" class="btn defaults primary">
195
+ <button class="btn defaults primary" @click="getCropResult">
198
196
  {{ $t("fws_crop_av_cta") }}
199
197
  </button>
200
198
  <div class="max-h-[80vh]">
201
199
  <VuePictureCropper
202
- :boxStyle="{
200
+ :box-style="{
203
201
  width: 'auto',
204
202
  height: 'auto',
205
203
  backgroundColor: '#f8f8f8',
@@ -228,13 +226,12 @@ function selectFile(e: Event) {
228
226
  :label="$t('fws_gender_label')"
229
227
  :error-vuelidate="v$.userData.Gender.$errors"
230
228
  />
231
- <!-- @vue-skip -->
232
229
  <DefaultInput
233
230
  id="birthdateFWS"
234
231
  v-model="state.userData.Birthdate"
235
232
  class="mb-4"
236
233
  type="datepicker"
237
- :disableDatesUnder18="true"
234
+ :disable-dates-under18="true"
238
235
  :label="$t('fws_birthdate_label')"
239
236
  :error-vuelidate="v$.userData.Birthdate.$errors"
240
237
  />
@@ -248,12 +245,12 @@ function selectFile(e: Event) {
248
245
  />
249
246
  <template v-if="!hidePublic">
250
247
  <DefaultInput
248
+ v-if="!hideGender"
251
249
  id="publicGenderFWS"
252
250
  v-model:checkbox-value="state.userData.PublicGender"
253
251
  type="toggle"
254
252
  :label="$t('fws_public_gender')"
255
253
  :error-vuelidate="v$.userData.PublicGender.$errors"
256
- v-if="!hideGender"
257
254
  />
258
255
  <DefaultInput
259
256
  id="publicBioFWS"
@@ -263,12 +260,12 @@ function selectFile(e: Event) {
263
260
  :error-vuelidate="v$.userData.PublicBio.$errors"
264
261
  />
265
262
  <DefaultInput
263
+ v-if="!hideBirthdate"
266
264
  id="publicBirthdateFWS"
267
265
  v-model:checkbox-value="state.userData.PublicBirthdate"
268
266
  type="toggle"
269
267
  :label="$t('fws_public_birthdate')"
270
268
  :error-vuelidate="v$.userData.PublicBirthdate.$errors"
271
- v-if="!hideBirthdate"
272
269
  />
273
270
  </template>
274
271
  <div class="flex">