diff --git a/.env b/.env deleted file mode 100644 index 48b9d90..0000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -VITE_API_URL=https://h57es3eyha.us-east-1.awsapprunner.com \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a049773 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +VITE_API_URL=http://localhost:9501 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 75410cb..d979b98 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ dist dist-ssr coverage *.local +.env /cypress/videos/ /cypress/screenshots/ diff --git a/src/App.vue b/src/App.vue index 83906be..5c88b51 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,15 +1,17 @@ diff --git a/src/assets/default-avatar.png b/src/assets/default-avatar.png new file mode 100644 index 0000000..d4c720f Binary files /dev/null and b/src/assets/default-avatar.png differ diff --git a/src/components/HeaderMenu.vue b/src/components/HeaderMenu.vue index c9f4f5c..45432e3 100644 --- a/src/components/HeaderMenu.vue +++ b/src/components/HeaderMenu.vue @@ -1,67 +1,74 @@ + + diff --git a/src/components/icons/DiscordIcon.vue b/src/components/icons/DiscordIcon.vue new file mode 100644 index 0000000..bd23da3 --- /dev/null +++ b/src/components/icons/DiscordIcon.vue @@ -0,0 +1,8 @@ + diff --git a/src/components/icons/Rectangle.png b/src/components/icons/Rectangle.png new file mode 100644 index 0000000..1e12fd2 Binary files /dev/null and b/src/components/icons/Rectangle.png differ diff --git a/src/components/icons/SearchIcon.vue b/src/components/icons/SearchIcon.vue new file mode 100644 index 0000000..b5272f9 --- /dev/null +++ b/src/components/icons/SearchIcon.vue @@ -0,0 +1,34 @@ + diff --git a/src/router/index.js b/src/router/index.js index 55a3350..8f2688b 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -17,7 +17,6 @@ import SquadCreateView from '../views/squad/CreateView.vue' import { useAuthStore } from '@/stores/auth'; - const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ @@ -39,89 +38,90 @@ const router = createRouter({ { path: '/profile', name: 'profile', - component: ProfileView, - meta : { - auth: true - } + component: ProfileView, + meta: { + auth: true + } + }, + { + path: '/products', + name: 'products', + component: ProductsView, + meta: { + auth: false + } }, - { - path: '/products', - name: 'products', - component: ProductsView, - meta : { - auth: false - } - }, { path: '/product/:uuid', name: 'product-by-id', component: ProductView, - meta : { - auth: true - } + props: true, + meta: { + auth: true + } }, { - path: '/product/create', - name: 'product-create', - component: ProductRegistryView, - meta : { - auth: true, - update: false - } + path: '/product/create', + name: 'product-create', + component: ProductRegistryView, + meta: { + auth: true, + update: false + } }, { - path: '/product/:uuid/update', - name: 'product-update', - component: ProductRegistryView, - meta : { - auth: true, - update: true - } + path: '/product/:uuid/update', + name: 'product-update', + component: ProductRegistryView, + meta: { + auth: true, + update: true + } }, { path: '/squads/:uuid', name: 'squads', - component: SquadsView, - meta : { - auth: true - } + component: SquadsView, + meta: { + auth: true + } }, { path: '/squad/:uuid', name: 'squad-by-id', component: SquadView, - props: true, - meta : { - auth: true - } + props: true, + meta: { + auth: true + } }, { path: '/squad/create/:productUuid', name: 'squad-create', component: SquadCreateView, - meta : { - auth: true, - type: 'create' - } + meta: { + auth: true, + type: 'create' + } }, { path: '/squad/:uuid/update', name: 'squad-update', component: SquadCreateView, - meta : { - auth: true, - type: 'update' - } + meta: { + auth: true, + type: 'update' + } }, { path: '/onboarding', name: 'onboarding', component: () => import('../views/OnboardingView.vue'), - meta : { - auth: true - } + meta: { + auth: true + } }, { path: '/about', @@ -132,24 +132,30 @@ const router = createRouter({ path: '/404', name: 'not-found', component: () => import('../views/NotFoundView.vue') - } + }, ] }) router.beforeEach((to, from, next) => { - const token = localStorage.getItem('token') + const token = localStorage.getItem('token') - const auth = useAuthStore(); + const auth = useAuthStore(); - if (to.meta.auth === true && (token === '' || token === null)) { - router.push({ name: 'login' }) - } + if (to.meta.auth === true && (!token || token === '' || token === null)) { + router.push({ name: 'login' }) + } - if (auth.auth.name === '') { - auth.loginByToken() + if (!auth.auth.name && token) { + try { + auth.loginByToken() + } + catch (error) { + console.log('Erro ao tentar logar com token', error) + router.push({ name: 'login' }) } + } - return next() + return next() }); export default router diff --git a/src/stores/auth.js b/src/stores/auth.js index 6d6f0d0..b95ca31 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -10,7 +10,7 @@ export const useAuthStore = defineStore('auth', () => { const axiosInstance = instance; - const auth = ref({ name: '', email: '', uuid: '', iat: '' }); + const auth = ref({ name: '', email: '', cidade: '', estado:'', linkedin:'', discord:'', uuid: '', iat: '' }); const products = ref([]); const squads = ref([]); const useSnackbar = useSnackbarStore(); @@ -21,14 +21,22 @@ export const useAuthStore = defineStore('auth', () => { const data = response.data; if (data.error) { - alert(data.error) - return; + useSnackbar.showSnackbar({ + text: data.error, + color: "error", + timeout: 3000, + }); + return; } else { const token = data.token; + const user = data.user; + localStorage.setItem('token', token); - auth.value = parseJwt(token); + localStorage.setItem('user', JSON.stringify(user)); + + auth.value = user; await fetchProducts(auth.value.uuid); @@ -47,17 +55,20 @@ export const useAuthStore = defineStore('auth', () => { } catch (error) { if (error.response?.status === 401) { - alert(error.response.data) + useSnackbar.showSnackbar({ + text: error.response?.data?.message || error.message, + color: "error", + timeout: 3000, + }); } } } async function loginByToken() { - - const token = localStorage.getItem('token'); + const user = localStorage.getItem('user'); try { - auth.value = parseJwt(token); + auth.value = JSON.parse(user); await fetchProducts(auth.value.uuid); @@ -73,8 +84,17 @@ export const useAuthStore = defineStore('auth', () => { } catch (error) { if (error.response?.status === 401) { - alert(error.response.data) + useSnackbar.showSnackbar({ + text: error.response?.data?.message || error.message, + color: "error", + timeout: 3000, + }); } + + localStorage.removeItem('token'); + localStorage.removeItem('user'); + + router.push('/'); } } @@ -103,15 +123,16 @@ export const useAuthStore = defineStore('auth', () => { async function logout() { localStorage.removeItem('token'); + localStorage.removeItem('user'); $reset(); router.push('/'); } function $reset() { - auth.value = { name: '', email: '', token: '' } - products.value = [] - squads.value = [] - } + auth.value = { name: '', email: '', cidade: '', estado: '', linkedin: '', discord: '', uuid: '', iat: '' }; + products.value = []; + squads.value = []; + } function squadReset() { squads.value = [] @@ -124,7 +145,6 @@ export const useAuthStore = defineStore('auth', () => { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); - console.log('jsonPayload :', JSON.parse(jsonPayload)); return JSON.parse(jsonPayload); } @@ -147,13 +167,28 @@ export const useAuthStore = defineStore('auth', () => { async function updateProfile(profile) { try { const response = await axiosInstance.put('/user/' + auth.value.uuid, profile); + const updatedUser = profile; + + auth.value = { ...auth.value, ...updatedUser }; - const data = response.data; - - alert(data.message) + localStorage.setItem('user', JSON.stringify(auth.value)); + + useSnackbar.showSnackbar({ + text: response.data.message, + color: "success", + timeout: 3000, + }); } catch (error) { - alert(error.message) + useSnackbar.showSnackbar({ + text: error.response?.data?.error || error.message, + color: "error", + timeout: 3000, + }); } + } + + function getRole() { + return auth.value.permission.charAt(0).toUpperCase() + auth.value.permission.slice(1) } return { @@ -172,7 +207,8 @@ export const useAuthStore = defineStore('auth', () => { updateProfile, setProducts, squadReset, - loginByToken + loginByToken, + getRole } }, diff --git a/src/stores/member.js b/src/stores/member.js index 4dc00a7..820f577 100644 --- a/src/stores/member.js +++ b/src/stores/member.js @@ -1,9 +1,11 @@ import { defineStore } from 'pinia' import memberService from '@/services/member.js' import { ref } from 'vue' +import { useSnackbarStore } from '@/stores/snackbar.js' export const useMemberStore = defineStore('member', () => { const member = ref([]); + const useSnackbar = useSnackbarStore(); async function fetch(uuidSquad) { try { @@ -11,13 +13,21 @@ export const useMemberStore = defineStore('member', () => { const data = response; if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else { member.value = data } } catch (error) { - alert('Catch: ' + error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } @@ -27,13 +37,21 @@ export const useMemberStore = defineStore('member', () => { const data = response; if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else { fetch(data.member.squad_uuid) } } catch (error) { - alert('Catch: ' + error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } @@ -43,13 +61,21 @@ export const useMemberStore = defineStore('member', () => { const data = response; if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else { fetch(data.member.squad_uuid) } } catch (error) { - alert('Catch: ' + error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } @@ -59,13 +85,21 @@ export const useMemberStore = defineStore('member', () => { const data = response; if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else { fetch(uuidSquad) } } catch (error) { - alert('Catch: ' + error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } diff --git a/src/stores/product.js b/src/stores/product.js index 697c6c6..27ba098 100644 --- a/src/stores/product.js +++ b/src/stores/product.js @@ -27,11 +27,15 @@ export const useProductStore = defineStore('product', () => { } async function getProduct(uuid) { - product.value = await productRequest.show(uuid) - + const p = products.value.find(product => product.uuid === uuid) + if (p) { + product.value = p + } else { + product.value = await productRequest.show(uuid) + } return product.value } - + async function byUser(uuid) { products.value = await productRequest.byUser(uuid) return products.value @@ -42,21 +46,25 @@ export const useProductStore = defineStore('product', () => { await tt.fetchProducts(tt.getUuid()) + useSnackbar.showSnackbar({ + text: 'Produto deletado com sucesso', + color: 'success', + timeout: 3000 + }); + router.push('/onboarding' ); //return products.value } async function create(product) { const p = await productRequest.create(product) - console.log('product created:', p); - if (p.statusCode == 200) { - await tt.fetchProducts(tt.getUuid()) + if (p.statusCode == 201) { useSnackbar.showSnackbar({ - text: 'Product created successfully', + text: 'Produto criado com sucesso', color: 'success', timeout: 3000 - }) + }); router.push('/product/' + p.product.uuid); } // return products.value @@ -76,6 +84,12 @@ export const useProductStore = defineStore('product', () => { }) router.push('/product/' + product.uuid); + } else { + useSnackbar.showSnackbar({ + text: p.error || 'Erro ao atualizar produto', + color: 'error', + timeout: 3000 + }); } // return products.value } diff --git a/src/stores/squad.js b/src/stores/squad.js index f4d596d..3526252 100644 --- a/src/stores/squad.js +++ b/src/stores/squad.js @@ -2,28 +2,44 @@ import { defineStore } from 'pinia' import squadService from '@/services/squad.js' import { ref } from 'vue' import { useAuthStore } from './auth.js' +import { useSnackbarStore } from '@/stores/snackbar.js' +import router from '@/router'; export const useSquadStore = defineStore('squad', () => { const squad = ref([]); const useAuth = useAuthStore() + const useSnackbar = useSnackbarStore(); + async function fetch() { try { const response = await squadService.fechtBy(); const data = response.data; if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else if (data.message) { - alert(data.message) + useSnackbar.showSnackbar({ + text: data.message, + color: 'info', + timeout: 3000 + }); } else { squad.value = data } } catch (error) { - alert(error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } @@ -32,19 +48,32 @@ export const useSquadStore = defineStore('squad', () => { const data = await squadService.post(squad); if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else if (data.message) { // alert(data.message) // await fetch() await useAuth.fetchSquads(squad.product_uuid) + useSnackbar.showSnackbar({ + text: 'Squad criado com sucesso', + color: 'success', + timeout: 3000 + }); } else { squad.value = data } } catch (error) { - alert(error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } @@ -53,12 +82,21 @@ export const useSquadStore = defineStore('squad', () => { const data = await squadService.put(squad); if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else if (data.message) { // alert(data.message) // await fetch() await useAuth.fetchSquads(squad.product_uuid) + useSnackbar.showSnackbar({ + text: data.message, + color: 'success', + timeout: 3000 + }); return data } else { @@ -66,7 +104,11 @@ export const useSquadStore = defineStore('squad', () => { } } catch (error) { - alert(error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } @@ -76,16 +118,30 @@ export const useSquadStore = defineStore('squad', () => { const data = await squadService.del(uuid); if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else if (data.message) { // alert(data.message) // await fetch() + useSnackbar.showSnackbar({ + text: 'Squad deletado com sucesso', + color: 'success', + timeout: 3000 + }); await useAuth.squadReset() + router.push('/onboarding'); } } catch (error) { - alert(error) + useSnackbar.showSnackbar({ + text: 'Erro: ' + error, + color: 'error', + timeout: 3000 + }); } } diff --git a/src/stores/user.js b/src/stores/user.js index 4fb60f7..6bf9732 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -2,6 +2,7 @@ import { ref, computed, reactive } from 'vue' import { defineStore } from 'pinia' import axiosInstance from '@/services/http.js' import router from "@/router"; +import { useSnackbarStore } from '@/stores/snackbar.js' export const useUserStore = defineStore('user', () => { @@ -12,21 +13,36 @@ export const useUserStore = defineStore('user', () => { const registered = ref(false) + const useSnackbar = useSnackbarStore(); + async function register(applicant) { try { const response = await axiosInstance.post('/user', applicant); const data = response.data; if (data.error) { - alert(data.error) + useSnackbar.showSnackbar({ + text: data.error, + color: 'error', + timeout: 3000 + }); return; } else if (data.message) { registered.value = true; localStorage.setItem('user', data); + useSnackbar.showSnackbar({ + text: data.message, + color: 'success', + timeout: 3000 + }); } } catch (error) { if (error.response.status === 401 || error.response.status === 422 || error.response.status === 404 || error.response.status === 500) { - alert(error.response.data) + useSnackbar.showSnackbar({ + text: error.response.data, + color: 'error', + timeout: 3000 + }); } } } diff --git a/src/views/user/LoginView.vue b/src/views/user/LoginView.vue index 99a97de..abc0bda 100644 --- a/src/views/user/LoginView.vue +++ b/src/views/user/LoginView.vue @@ -1,7 +1,13 @@