1+ <template >
2+ <el-dialog
3+ v-model =" visible"
4+ width =" 400px"
5+ :close-on-click-modal =" !isLoading"
6+ :close-on-press-escape =" !isLoading"
7+ :show-close =" false"
8+ @update:model-value =" handleDialogClose"
9+ class =" leave-org-dialog"
10+ >
11+ <div class =" p-[30px]" >
12+ <!-- Header with icon and close button -->
13+ <div class =" flex items-start justify-between mb-6" >
14+ <div class =" flex-shrink-0 w-12 h-12 bg-red-100 rounded-full flex items-center justify-center" >
15+ <svg width =" 24" height =" 24" viewBox =" 0 0 24 24" fill =" none" xmlns =" http://www.w3.org/2000/svg" >
16+ <path d =" M16 18H22M12 15.5H7.5C6.10444 15.5 5.40665 15.5 4.83886 15.6722C3.56045 16.06 2.56004 17.0605 2.17224 18.3389C2 18.9067 2 19.6044 2 21M14.5 7.5C14.5 9.98528 12.4853 12 10 12C7.51472 12 5.5 9.98528 5.5 7.5C5.5 5.01472 7.51472 3 10 3C12.4853 3 14.5 5.01472 14.5 7.5Z" stroke =" #D92D20" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" />
17+ </svg >
18+ </div >
19+ <button
20+ @click =" handleCancel"
21+ :disabled =" isLoading"
22+ class =" text-gray-400 hover:text-gray-600 transition-colors"
23+ >
24+ <svg width =" 24" height =" 24" viewBox =" 0 0 24 24" fill =" none" xmlns =" http://www.w3.org/2000/svg" >
25+ <path d =" M18 6L6 18M6 6L18 18" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" />
26+ </svg >
27+ </button >
28+ </div >
29+
30+ <!-- Content -->
31+ <div class =" mb-8" >
32+ <h3 class =" text-lg font-semibold text-gray-900 mb-3" >{{ $t('organization.leaveOrgTitle') }}</h3 >
33+ <p class =" text-gray-600 leading-relaxed" >{{ $t('organization.leaveOrgContent') }}</p >
34+ </div >
35+
36+ <!-- Footer buttons -->
37+ <div class =" flex gap-3" >
38+ <button
39+ @click =" handleCancel"
40+ :disabled =" isLoading"
41+ class =" flex-1 h-[44px] px-[10px] bg-white border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 transition-colors font-medium"
42+ >
43+ {{ $t('organization.cancel') }}
44+ </button >
45+ <button
46+ @click =" handleLeave"
47+ :disabled =" isLoading"
48+ class =" flex-1 h-[44px] px-[10px] text-white rounded-md font-medium transition-colors flex items-center justify-center"
49+ :class =" isLoading ? 'bg-gray-400 cursor-not-allowed' : 'bg-[#D92D20] hover:bg-[#B91C1C]'"
50+ >
51+ <svg v-if =" isLoading" class =" animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns =" http://www.w3.org/2000/svg" fill =" none" viewBox =" 0 0 24 24" >
52+ <circle class =" opacity-25" cx =" 12" cy =" 12" r =" 10" stroke =" currentColor" stroke-width =" 4" ></circle >
53+ <path class =" opacity-75" fill =" currentColor" d =" M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" ></path >
54+ </svg >
55+ {{ $t('organization.leaveOrgButton') }}
56+ </button >
57+ </div >
58+ </div >
59+ </el-dialog >
60+ </template >
61+
62+ <script setup>
63+ import { ref , computed } from ' vue'
64+ import { useI18n } from ' vue-i18n'
65+ import { ElMessage } from ' element-plus'
66+ import useFetchApi from ' ../../packs/useFetchApi'
67+ import useUserStore from ' ../../stores/UserStore'
68+
69+ const props = defineProps ({
70+ modelValue: {
71+ type: Boolean ,
72+ default: false
73+ },
74+ organizationName: {
75+ type: String ,
76+ required: true
77+ },
78+ userRole: {
79+ type: String ,
80+ default: ' '
81+ }
82+ })
83+
84+ const emit = defineEmits ([' update:modelValue' , ' success' ])
85+
86+ const { t } = useI18n ()
87+ const userStore = useUserStore ()
88+ const isLoading = ref (false )
89+
90+ const visible = computed ({
91+ get : () => props .modelValue ,
92+ set : (value ) => emit (' update:modelValue' , value)
93+ })
94+
95+ const handleDialogClose = (value ) => {
96+ if (! isLoading .value ) {
97+ emit (' update:modelValue' , value)
98+ }
99+ }
100+
101+ const handleCancel = () => {
102+ if (! isLoading .value ) {
103+ emit (' update:modelValue' , false )
104+ }
105+ }
106+
107+ const handleLeave = async () => {
108+ if (isLoading .value ) return
109+
110+ isLoading .value = true
111+
112+ try {
113+ const { response , error } = await useFetchApi (` /organization/${ props .organizationName } /members/${ userStore .username } ` , { body: JSON .stringify ({role: props .userRole }) })
114+ .delete ()
115+ .json ()
116+
117+ if (error .value ) {
118+ ElMessage .error (t (' organization.leaveOrgFailure' ))
119+ } else if (response .value .ok ) {
120+ // Update organizations in store before redirect
121+ await userStore .fetchOrgs ()
122+ ElMessage .success (t (' organization.leaveOrgSuccess' ))
123+
124+ // Redirect to profile page
125+ window .location .href = ` /profile/${ userStore .username } `
126+ } else {
127+ ElMessage .error (t (' organization.leaveOrgFailure' ))
128+ }
129+ } catch (error) {
130+ console .error (' Leave organization error:' , error)
131+ ElMessage .error (t (' organization.leaveOrgNetworkError' ))
132+ } finally {
133+ isLoading .value = false
134+ }
135+ }
136+ </script >
137+
138+ <style >
139+ .el-dialog.leave-org-dialog {
140+ padding : 0 !important ;
141+ }
142+
143+ .el-dialog.leave-org-dialog .el-dialog__header {
144+ padding : 0 !important ;
145+ }
146+
147+ .el-dialog.leave-org-dialog .el-dialog__body {
148+ padding : 0 !important ;
149+ }
150+ </style >
0 commit comments