@@ -3,6 +3,7 @@ import { computedAsync } from '@vueuse/core'
33import { storeToRefs } from ' pinia'
44import { computed , onMounted , ref , watch } from ' vue'
55import { useI18n } from ' vue-i18n'
6+ import { useRouter } from ' vue-router'
67import { toast } from ' vue-sonner'
78import IconCheck from ' ~icons/heroicons/check-circle'
89import IconWarning from ' ~icons/heroicons/exclamation-triangle'
@@ -12,6 +13,7 @@ import IconLock from '~icons/heroicons/lock-closed'
1213import IconShield from ' ~icons/heroicons/shield-check'
1314import IconUser from ' ~icons/heroicons/user'
1415import { checkPermissions } from ' ~/services/permissions'
16+ import { createSignedImageUrl } from ' ~/services/storage'
1517import { useSupabase } from ' ~/services/supabase'
1618import { useDialogV2Store } from ' ~/stores/dialogv2'
1719import { useDisplayStore } from ' ~/stores/display'
@@ -41,6 +43,7 @@ const { t } = useI18n()
4143const displayStore = useDisplayStore ()
4244const organizationStore = useOrganizationStore ()
4345const dialogStore = useDialogV2Store ()
46+ const router = useRouter ()
4447const supabase = useSupabase ()
4548const isLoading = ref (true )
4649const isSaving = ref (false )
@@ -160,6 +163,12 @@ function loadApikeyPolicyFromOrg() {
160163 maxApikeyExpirationDays .value = currentOrganization .value ?.max_apikey_expiration_days ?? null
161164}
162165
166+ async function goToMembersPage(closeDialog = false ) {
167+ if (closeDialog )
168+ await dialogStore .closeDialog ()
169+ await router .push (' /settings/organization/members' )
170+ }
171+
163172async function loadData() {
164173 if (! currentOrganization .value ?.gid )
165174 return
@@ -242,13 +251,16 @@ async function loadMembersWithMfaStatus() {
242251 }
243252
244253 // Merge members with MFA status
245- membersWithMfaStatus .value = (members || []).map (member => ({
246- uid: member .uid ,
247- email: member .email ,
248- image_url: member .image_url || ' ' ,
249- role: member .role ,
250- is_tmp: member .is_tmp ,
251- has_2fa: mfaMap .get (member .uid ) ?? false ,
254+ membersWithMfaStatus .value = await Promise .all ((members || []).map (async (member ) => {
255+ const signedImage = member .image_url ? await createSignedImageUrl (member .image_url ) : ' '
256+ return {
257+ uid: member .uid ,
258+ email: member .email ,
259+ image_url: signedImage || ' ' ,
260+ role: member .role ,
261+ is_tmp: member .is_tmp ,
262+ has_2fa: mfaMap .get (member .uid ) ?? false ,
263+ }
252264 }))
253265
254266 // Calculate impacted members (those without 2FA, excluding pending invites)
@@ -304,19 +316,20 @@ async function loadMembersWithPasswordPolicyStatus() {
304316 }
305317
306318 // Merge members with password policy compliance status
307- membersWithPasswordPolicyStatus .value = ( members || []).map ((member ) => {
319+ membersWithPasswordPolicyStatus .value = await Promise . all (( members || []).map (async (member ) => {
308320 const compliance = complianceMap .get (member .uid )
321+ const signedImage = member .image_url ? await createSignedImageUrl (member .image_url ) : ' '
309322 return {
310323 uid: member .uid ,
311324 email: member .email ,
312325 first_name: compliance ?.first_name || null ,
313326 last_name: compliance ?.last_name || null ,
314- image_url: member . image_url || ' ' ,
327+ image_url: signedImage || ' ' ,
315328 role: member .role ,
316329 is_tmp: member .is_tmp ,
317330 password_policy_compliant: compliance ?.compliant ?? false ,
318331 }
319- })
332+ }))
320333
321334 // Calculate non-compliant members (excluding pending invites)
322335 nonCompliantPasswordMembers .value = membersWithPasswordPolicyStatus .value .filter (m => ! m .password_policy_compliant && ! m .is_tmp )
@@ -951,13 +964,22 @@ onMounted(async () => {
951964 {{ t('2fa-impacted-members-title') }}
952965 </h4 >
953966 </div >
954- <button
955- type =" button"
956- class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
957- @click =" copyEmailList"
958- >
959- {{ t('copy-email-list') }}
960- </button >
967+ <div class =" flex items-center gap-2" >
968+ <button
969+ type =" button"
970+ class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
971+ @click =" goToMembersPage()"
972+ >
973+ {{ t('view') }} {{ t('members') }}
974+ </button >
975+ <button
976+ type =" button"
977+ class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
978+ @click =" copyEmailList"
979+ >
980+ {{ t('copy-email-list') }}
981+ </button >
982+ </div >
961983 </div >
962984 <p class =" mb-4 text-sm text-amber-700 dark:text-amber-300" >
963985 {{ t('2fa-impacted-members-description') }}
@@ -1229,13 +1251,22 @@ onMounted(async () => {
12291251 {{ t('password-policy-impacted-members-title') }}
12301252 </h4 >
12311253 </div >
1232- <button
1233- type =" button"
1234- class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
1235- @click =" copyPasswordPolicyEmailList"
1236- >
1237- {{ t('copy-email-list') }}
1238- </button >
1254+ <div class =" flex items-center gap-2" >
1255+ <button
1256+ type =" button"
1257+ class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
1258+ @click =" goToMembersPage()"
1259+ >
1260+ {{ t('view') }} {{ t('members') }}
1261+ </button >
1262+ <button
1263+ type =" button"
1264+ class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
1265+ @click =" copyPasswordPolicyEmailList"
1266+ >
1267+ {{ t('copy-email-list') }}
1268+ </button >
1269+ </div >
12391270 </div >
12401271 <p class =" mb-4 text-sm text-amber-700 dark:text-amber-300" >
12411272 {{ t('password-policy-impacted-members-description') }}
@@ -1389,13 +1420,22 @@ onMounted(async () => {
13891420 <span class =" text-xs text-amber-600 dark:text-amber-400" >({{ member.role.replace('_', ' ') }})</span >
13901421 </li >
13911422 </ul >
1392- <button
1393- type =" button"
1394- class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
1395- @click =" copyEmailList"
1396- >
1397- {{ t('copy-email-list') }}
1398- </button >
1423+ <div class =" flex items-center gap-2" >
1424+ <button
1425+ type =" button"
1426+ class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
1427+ @click =" goToMembersPage(true)"
1428+ >
1429+ {{ t('view') }} {{ t('members') }}
1430+ </button >
1431+ <button
1432+ type =" button"
1433+ class =" px-3 py-2 text-xs font-medium text-center border rounded-lg cursor-pointer text-amber-700 dark:text-amber-300 hover:bg-amber-100 focus:ring-4 focus:ring-amber-300 border-amber-400 dark:border-amber-600 dark:hover:bg-amber-800/30 dark:focus:ring-amber-800 focus:outline-hidden"
1434+ @click =" copyEmailList"
1435+ >
1436+ {{ t('copy-email-list') }}
1437+ </button >
1438+ </div >
13991439 </div >
14001440 </Teleport >
14011441
@@ -1416,6 +1456,13 @@ onMounted(async () => {
14161456 </div >
14171457 </li >
14181458 </ul >
1459+ <button
1460+ type =" button"
1461+ class =" px-3 py-2 mt-4 text-xs font-medium text-center border rounded-lg cursor-pointer text-red-700 dark:text-red-300 hover:bg-red-100 focus:ring-4 focus:ring-red-200 border-red-300 dark:border-red-600 dark:hover:bg-red-900/30 dark:focus:ring-red-900 focus:outline-hidden"
1462+ @click =" goToMembersPage(true)"
1463+ >
1464+ {{ t('view') }} {{ t('members') }}
1465+ </button >
14191466 <p class =" mt-3 text-sm text-red-600 dark:text-red-400" >
14201467 {{ t('users-must-change-password') }}
14211468 </p >
0 commit comments