Client - init login and getting user profile

This commit is contained in:
Sam 2021-08-11 19:39:09 +02:00
parent 4c56d75752
commit cd418c9be2
14 changed files with 194 additions and 10 deletions

View File

@ -0,0 +1,24 @@
import axios from 'axios'
import store from '@/store'
import { USER_STORE } from '@/store/constants'
import { getApiUrl } from '@/utils'
const authApi = axios.create({
baseURL: getApiUrl(),
})
authApi.interceptors.request.use(
(config) => {
const authToken = store.getters[USER_STORE.GETTERS.AUTH_TOKEN]
if (authToken) {
const auth = `Bearer ${authToken}`
if (config.headers.Authorization !== auth) {
config.headers.Authorization = `Bearer ${authToken}`
}
}
return config
},
(error) => Promise.reject(error)
)
export default authApi

View File

@ -0,0 +1,8 @@
import axios from 'axios'
import { getApiUrl } from '@/utils'
const api = axios.create({
baseURL: getApiUrl(),
})
export default api

View File

@ -7,21 +7,21 @@
v-if="action === 'register'" v-if="action === 'register'"
id="username" id="username"
required required
v-model="user.username" v-model="formData.username"
:placeholder="t('user.REGISTER')" :placeholder="t('user.REGISTER')"
/> />
<input <input
id="email" id="email"
required required
type="email" type="email"
v-model="user.email" v-model="formData.email"
:placeholder="t('user.EMAIL')" :placeholder="t('user.EMAIL')"
/> />
<input <input
id="password" id="password"
required required
type="password" type="password"
v-model="user.password" v-model="formData.password"
:placeholder="t('user.PASSWORD')" :placeholder="t('user.PASSWORD')"
/> />
<input <input
@ -29,7 +29,7 @@
id="confirm-password" id="confirm-password"
type="password" type="password"
required required
v-model="user.confirmPassword" v-model="formData.confirmPassword"
:placeholder="t('user.PASSWORD-CONFIRM')" :placeholder="t('user.PASSWORD-CONFIRM')"
/> />
</div> </div>
@ -45,6 +45,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, ref } from 'vue' import { defineComponent, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
import { IFormData } from '@/interfaces.ts'
import { USER_STORE } from '@/store/constants'
export default defineComponent({ export default defineComponent({
name: 'UserForm', name: 'UserForm',
@ -56,21 +59,25 @@
}, },
}, },
setup() { setup() {
const user = reactive({ const formData: IFormData = reactive({
username: '', username: '',
email: '', email: '',
password: '', password: '',
confirmPassword: '', confirmPassword: '',
}) })
const { t } = useI18n() const { t } = useI18n()
function onSubmit(action: string) { const store = useStore()
console.log(action, user) function onSubmit(actionType: string) {
return store.dispatch(USER_STORE.ACTIONS.LOGIN_OR_REGISTER, {
actionType,
formData,
})
} }
return { return {
errorMessage: ref(null), errorMessage: ref(null),
t, t,
user, formData,
onSubmit, onSubmit,
} }
}, },

View File

@ -2,3 +2,10 @@ export interface IDropdownOption {
value: string | number value: string | number
label: string label: string
} }
export interface IFormData {
username: string
email: string
password: string
confirmPassword: string
}

View File

@ -1,10 +1,16 @@
import { RootMutations } from '@/store/modules/root/enums' import { RootMutations } from '@/store/modules/root/enums'
import { UserGetters } from '@/store/modules/user/enums' import {
UserActions,
UserGetters,
UserMutations,
} from '@/store/modules/user/enums'
export const ROOT_STORE = { export const ROOT_STORE = {
MUTATIONS: RootMutations, MUTATIONS: RootMutations,
} }
export const USER_STORE = { export const USER_STORE = {
ACTIONS: UserActions,
GETTERS: UserGetters, GETTERS: UserGetters,
MUTATIONS: UserMutations,
} }

View File

@ -0,0 +1,45 @@
import { ActionContext, ActionTree } from 'vuex'
import { USER_STORE } from '@/store/constants'
import { IUserActions } from '@/store/modules/user/interfaces'
import { IUserState } from '@/store/modules/user/interfaces'
import { IRootState } from '@/store/modules/root/interfaces'
import { ILoginOrRegisterData } from '@/store/modules/user/interfaces'
import api from '@/api/defaultApi'
import authApi from '@/api/authApi'
import router from '@/router'
export const actions: ActionTree<IUserState, IRootState> & IUserActions = {
[USER_STORE.ACTIONS.GET_USER_PROFILE](
context: ActionContext<IUserState, IRootState>
): void {
authApi
.get('auth/profile')
.then((res) => {
if (res.data.status === 'success') {
context.commit(
USER_STORE.MUTATIONS.UPDATE_AUTH_USER_PROFILE,
res.data.data
)
}
})
.catch((err) => console.log(err))
},
[USER_STORE.ACTIONS.LOGIN_OR_REGISTER](
context: ActionContext<IUserState, IRootState>,
data: ILoginOrRegisterData
): void {
api
.post(`/auth/${data.actionType}`, data.formData)
.then((res) => {
if (res.status == 200 && res.data.status === 'success') {
const token = res.data.auth_token
window.localStorage.setItem('authToken', token)
context.commit(USER_STORE.MUTATIONS.UPDATE_AUTH_TOKEN, token)
context
.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
.then(() => router.push('/'))
}
})
.catch((err) => console.log(err))
},
}

View File

@ -1,3 +1,12 @@
export enum UserActions {
GET_USER_PROFILE = 'GET_USER_PROFILE',
LOGIN_OR_REGISTER = 'LOGIN_OR_REGISTER',
}
export enum UserGetters { export enum UserGetters {
IS_AUTHENTICATED = 'IS_AUTHENTICATED', IS_AUTHENTICATED = 'IS_AUTHENTICATED',
AUTH_TOKEN = 'AUTH_TOKEN',
}
export enum UserMutations {
UPDATE_AUTH_TOKEN = 'UPDATE_AUTH_TOKEN',
UPDATE_AUTH_USER_PROFILE = 'UPDATE_AUTH_USER_PROFILE',
} }

View File

@ -4,6 +4,9 @@ import { IRootState } from '@/store/modules/root/interfaces'
import { IUserGetters, IUserState } from '@/store/modules/user/interfaces' import { IUserGetters, IUserState } from '@/store/modules/user/interfaces'
export const getters: GetterTree<IUserState, IRootState> & IUserGetters = { export const getters: GetterTree<IUserState, IRootState> & IUserGetters = {
[USER_STORE.GETTERS.AUTH_TOKEN]: (state: IUserState) => {
return state.authToken
},
[USER_STORE.GETTERS.IS_AUTHENTICATED]: (state: IUserState) => { [USER_STORE.GETTERS.IS_AUTHENTICATED]: (state: IUserState) => {
return state.authToken !== null return state.authToken !== null
}, },

View File

@ -1,12 +1,16 @@
import { Module } from 'vuex' import { Module } from 'vuex'
import { IRootState } from '@/store/modules/root/interfaces' import { IRootState } from '@/store/modules/root/interfaces'
import { actions } from '@/store/modules/user/actions'
import { getters } from '@/store/modules/user/getters' import { getters } from '@/store/modules/user/getters'
import { IUserState } from '@/store/modules/user/interfaces' import { IUserState } from '@/store/modules/user/interfaces'
import { userState } from '@/store/modules/user/state.ts' import { userState } from '@/store/modules/user/state.ts'
import { mutations } from '@/store/modules/user/mutations'
const user: Module<IUserState, IRootState> = { const user: Module<IUserState, IRootState> = {
state: userState, state: userState,
actions,
getters, getters,
mutations,
} }
export default user export default user

View File

@ -1,9 +1,49 @@
import { ActionContext } from 'vuex'
import { IFormData } from '@/interfaces'
import { USER_STORE } from '@/store/constants' import { USER_STORE } from '@/store/constants'
import { IRootState } from '@/store/modules/root/interfaces'
// DATA
export interface IAuthUserProfile {
admin: boolean
bio: string | null
birth_date: string | null
created_at: string
email: string
first_name: string | null
language: string | null
last_name: string | null
location: string | null
nb_sports: number
nb_workouts: number
picture: string | boolean
sports_list: number[]
timezone: string
total_distance: number
total_duration: string
username: string
weekm: boolean
}
export interface ILoginOrRegisterData {
actionType: string
formData: IFormData
}
// STORE
export interface IUserState { export interface IUserState {
authToken: string | null authToken: string | null
authUserProfile: IAuthUserProfile
} }
export interface IUserGetters { export interface IUserGetters {
[USER_STORE.GETTERS.AUTH_TOKEN](state: IUserState): string
[USER_STORE.GETTERS.IS_AUTHENTICATED](state: IUserState): boolean [USER_STORE.GETTERS.IS_AUTHENTICATED](state: IUserState): boolean
} }
export interface IUserActions {
[USER_STORE.ACTIONS.LOGIN_OR_REGISTER](
context: ActionContext<IUserState, IRootState>,
data: ILoginOrRegisterData
): void
}

View File

@ -0,0 +1,19 @@
import { MutationTree } from 'vuex'
import { TUserMutations } from '@/store/modules/user/types'
import { USER_STORE } from '@/store/constants'
import { IAuthUserProfile, IUserState } from '@/store/modules/user/interfaces'
export const mutations: MutationTree<IUserState> & TUserMutations = {
[USER_STORE.MUTATIONS.UPDATE_AUTH_TOKEN](
state: IUserState,
authToken: string
) {
state.authToken = authToken
},
[USER_STORE.MUTATIONS.UPDATE_AUTH_USER_PROFILE](
state: IUserState,
authUserProfile: IAuthUserProfile
) {
state.authUserProfile = authUserProfile
},
}

View File

@ -1,5 +1,6 @@
import { IUserState } from '@/store/modules/user/interfaces' import { IAuthUserProfile, IUserState } from '@/store/modules/user/interfaces'
export const userState: IUserState = { export const userState: IUserState = {
authToken: null, authToken: null,
authUserProfile: <IAuthUserProfile>{},
} }

View File

@ -0,0 +1,6 @@
import { USER_STORE } from '@/store/constants'
import { IUserState } from '@/store/modules/user/interfaces'
export type TUserMutations<S = IUserState> = {
[USER_STORE.MUTATIONS.UPDATE_AUTH_TOKEN](state: S, authToken: string): void
}

View File

@ -0,0 +1,5 @@
export const getApiUrl = (): string => {
return process.env.NODE_ENV === 'production'
? '/api'
: 'http://localhost:5000/api'
}