1+ <template >
2+ <aside
3+ :class =" [
4+ 'flex flex-col bg-white border-r border-gray-200 shrink-0 transition-all duration-300 ease-in-out',
5+ sidebarOpen ? 'md:w-60 w-full' : 'w-16'
6+ ]"
7+ class =" sticky top-0 h-screen z-40 overflow-hidden"
8+ >
9+ <!-- Logo + toggle -->
10+ <div class =" flex items-center h-16 px-3 border-b border-gray-100 shrink-0"
11+ :class =" sidebarOpen ? 'justify-between' : 'justify-center'" >
12+ <div v-if =" sidebarOpen" class =" flex items-center gap-2 overflow-hidden" >
13+ <div class =" w-8 h-8 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center shrink-0" >
14+ <svg class =" w-4 h-4 text-white" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
15+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2"
16+ d =" M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
17+ </svg >
18+ </div >
19+ <span class =" font-bold text-gray-900 text-sm whitespace-nowrap" >Payment4You</span >
20+ </div >
21+ <button @click =" sidebarOpen = !sidebarOpen"
22+ class =" p-1.5 rounded-lg hover:bg-gray-100 text-gray-500 transition-colors cursor-pointer shrink-0" >
23+ <!-- Icona col·lapsar / expandir -->
24+ <svg v-if =" sidebarOpen" class =" w-5 h-5" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
25+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M11 19l-7-7 7-7M18 19l-7-7 7-7" />
26+ </svg >
27+ <svg v-else class =" w-5 h-5" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
28+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M13 5l7 7-7 7M6 5l7 7-7 7" />
29+ </svg >
30+ </button >
31+ </div >
32+
33+ <!-- Nav items -->
34+ <nav class =" flex-1 py-4 space-y-1 px-2 overflow-hidden" >
35+ <button v-for =" item in navItems" :key =" item.key"
36+ @click =" changePage(item.url)"
37+ :class =" [
38+ 'w-full flex items-center rounded-xl transition-all cursor-pointer group',
39+ sidebarOpen ? 'px-3 py-2.5 gap-3' : 'px-3 py-2.5',
40+ item.active
41+ ? 'bg-orange-50 text-orange-500'
42+ : 'text-gray-500 hover:bg-gray-50 hover:text-gray-900'
43+ ]" >
44+ <span class =" shrink-0" v-html =" item.icon" ></span >
45+ <span v-if =" sidebarOpen" class =" text-sm font-medium whitespace-nowrap" >{{ t(item.labelKey) }}</span >
46+ </button >
47+ </nav >
48+
49+ <!-- Sign out -->
50+ <div class =" px-2 pb-4 shrink-0" >
51+ <button @click =" userSignOut"
52+ :class =" [
53+ 'w-full flex items-center rounded-xl transition-all cursor-pointer text-gray-500 hover:bg-red-50 hover:text-red-500',
54+ sidebarOpen ? 'px-3 py-2.5 gap-3' : 'px-0 py-2.5 justify-center'
55+ ]" >
56+ <svg class =" w-5 h-5 shrink-0" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
57+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 1.5"
58+ d =" M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
59+ </svg >
60+ <span v-if =" sidebarOpen" class =" text-sm font-medium whitespace-nowrap" >{{ t('sidebar.signOut') }}</span >
61+ </button >
62+ </div >
63+ </aside >
64+ </template >
65+ <script setup lang="ts">
66+ import { useIsMobile } from ' @/composables/isMobile.composable' ;
67+ import { useAuthStore } from ' @/stores/auth.store' ;
68+ import { redirectTo } from ' @/utils' ;
69+ import { onMounted , ref } from ' vue' ;
70+ import { useI18n } from ' vue-i18n' ;
71+
72+ const authStore = useAuthStore ()
73+ const { isMobile } = useIsMobile ()
74+ const { t } = useI18n ()
75+ const sidebarOpen = ref (true )
76+
77+ onMounted (() => {
78+ if (isMobile ) sidebarOpen .value = false
79+ })
80+ // TODO: REFACTOR COLORS IN NAVITEMS
81+ const navItems = [
82+ {
83+ key: ' dashboard' ,
84+ labelKey: ' sidebar.dashboard' ,
85+ active: false ,
86+ icon: ` <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/></svg> ` ,
87+ url: ' '
88+ },
89+ {
90+ key: ' menu' ,
91+ labelKey: ' sidebar.menu' ,
92+ active: true ,
93+ icon: ` <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/></svg> ` ,
94+ url: ' '
95+ },
96+ {
97+ key: ' stock' ,
98+ labelKey: ' sidebar.stock' ,
99+ active: false ,
100+ icon: ` <svg class="w-5 h-5" fill="none" stroke="#df0000" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/></svg> ` ,
101+ url: ' tpv/products'
102+ },
103+ {
104+ key: ' table' ,
105+ labelKey: ' sidebar.table' ,
106+ active: false ,
107+ icon: ` <svg class="w-5 h-5" fill="none" stroke="#df0000" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg> ` ,
108+ url: ' tpv'
109+ },
110+ {
111+ key: ' history' ,
112+ labelKey: ' sidebar.history' ,
113+ active: false ,
114+ icon: ` <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg> ` ,
115+ url: ' '
116+ },
117+ {
118+ key: ' settings' ,
119+ labelKey: ' sidebar.settings' ,
120+ active: false ,
121+ icon: ` <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg> ` ,
122+ url: ' '
123+ },
124+ ]
125+
126+ const userSignOut = async (): Promise <void > => {
127+ try {
128+ await authStore .signOut ()
129+ } catch (_ ) { }
130+ }
131+ const changePage = (url : string ) => {
132+ if (url .trim () !== ' ' ) {
133+ sidebarOpen .value = false
134+ redirectTo (` /${url } ` )
135+ }
136+ }
137+ </script >
0 commit comments