Client - add/delete an OAuth app (WIP)
This commit is contained in:
170
fittrackee_client/src/components/User/UserApps/AddUserApp.vue
Normal file
170
fittrackee_client/src/components/User/UserApps/AddUserApp.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div id="new-oauth2-app">
|
||||
<p id="new-oauth2-title">{{ $t('oauth2.ADD_A_NEW_APP') }}</p>
|
||||
<div id="apps-form">
|
||||
<form @submit.prevent="createApp">
|
||||
<div class="form-items">
|
||||
<div class="form-item">
|
||||
<label for="app-name">{{ $t('oauth2.APP.NAME') }}*</label>
|
||||
<input
|
||||
id="app-name"
|
||||
type="text"
|
||||
required
|
||||
v-model="appForm.client_name"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="app-description">{{
|
||||
$t('oauth2.APP.DESCRIPTION')
|
||||
}}</label>
|
||||
<CustomTextArea
|
||||
name="app-description"
|
||||
:charLimit="200"
|
||||
:input="appForm.description"
|
||||
@updateValue="updateDescription"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="app-url">{{ $t('oauth2.APP.URL') }}*</label>
|
||||
<input
|
||||
id="app-url"
|
||||
type="text"
|
||||
required
|
||||
v-model="appForm.client_uri"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="app-redirect-uri"
|
||||
>{{ $t('oauth2.APP.REDIRECT_URL') }}*</label
|
||||
>
|
||||
<input
|
||||
id="app-redirect-uri"
|
||||
type="text"
|
||||
required
|
||||
v-model="appForm.redirect_uri"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item-scope">
|
||||
<div class="form-item-scope-label">
|
||||
{{ $t('oauth2.APP.SCOPE.LABEL') }}*
|
||||
</div>
|
||||
<div class="form-item-scope-checkboxes">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="appForm.read"
|
||||
@change="appForm.read = !appForm.read"
|
||||
/>
|
||||
{{ $t('oauth2.APP.SCOPE.READ') }}
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="appForm.write"
|
||||
@change="appForm.write = !appForm.write"
|
||||
/>
|
||||
{{ $t('oauth2.APP.SCOPE.WRITE') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-buttons">
|
||||
<button
|
||||
class="confirm"
|
||||
type="submit"
|
||||
:disabled="!appForm.read && !appForm.write"
|
||||
>
|
||||
{{ $t('buttons.SUBMIT') }}
|
||||
</button>
|
||||
<button
|
||||
class="cancel"
|
||||
@click.prevent="() => $router.push('/profile/apps')"
|
||||
>
|
||||
{{ $t('buttons.CANCEL') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import { OAUTH2_STORE } from '@/store/constants'
|
||||
import { IOAuth2ClientPayload } from '@/types/oauth'
|
||||
import { useStore } from '@/use/useStore'
|
||||
|
||||
const store = useStore()
|
||||
const appForm = reactive({
|
||||
client_name: '',
|
||||
client_uri: '',
|
||||
client_description: '',
|
||||
redirect_uri: '',
|
||||
read: true,
|
||||
write: false,
|
||||
})
|
||||
|
||||
function createApp() {
|
||||
const payload: IOAuth2ClientPayload = {
|
||||
client_name: appForm.client_name,
|
||||
client_description: appForm.client_description,
|
||||
client_uri: appForm.client_uri,
|
||||
redirect_uris: [appForm.redirect_uri],
|
||||
scope: `${appForm.read ? 'read' : ''} ${appForm.write ? 'write' : ''}`,
|
||||
}
|
||||
store.dispatch(OAUTH2_STORE.ACTIONS.CREATE_CLIENT, payload)
|
||||
}
|
||||
function updateDescription(value: string) {
|
||||
appForm.client_description = value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/scss/vars.scss';
|
||||
|
||||
#new-oauth2-app {
|
||||
#new-oauth2-title {
|
||||
font-size: 1.05em;
|
||||
font-weight: bold;
|
||||
padding: 0 $default-padding;
|
||||
}
|
||||
|
||||
#apps-form {
|
||||
.form-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
input {
|
||||
height: 20px;
|
||||
}
|
||||
.form-item-scope {
|
||||
padding: $default-padding;
|
||||
|
||||
.form-item-scope-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-item-scope-checkboxes {
|
||||
display: flex;
|
||||
gap: $default-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $default-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
margin: $default-padding * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
131
fittrackee_client/src/components/User/UserApps/UserApp.vue
Normal file
131
fittrackee_client/src/components/User/UserApps/UserApp.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div id="oauth2-app" class="description-list">
|
||||
<Modal
|
||||
v-if="displayModal"
|
||||
:title="$t('common.CONFIRMATION')"
|
||||
:message="$t('oauth2.APP_DELETION_CONFIRMATION')"
|
||||
@confirmAction="deleteClient(client.id)"
|
||||
@cancelAction="updateDisplayModal(false)"
|
||||
/>
|
||||
<div v-if="client && client.client_id">
|
||||
<dl>
|
||||
<dt>{{ $t('oauth2.APP.CLIENT_ID') }}:</dt>
|
||||
<dd>{{ client.client_id }}</dd>
|
||||
<dt>{{ capitalize($t('oauth2.APP.ISSUE_AT')) }}:</dt>
|
||||
<dd>
|
||||
{{
|
||||
format(
|
||||
getDateWithTZ(client.issued_at, authUser.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
)
|
||||
}}
|
||||
</dd>
|
||||
<dt>{{ $t('oauth2.APP.NAME') }}:</dt>
|
||||
<dd>{{ client.name }}</dd>
|
||||
<dt>{{ $t('oauth2.APP.DESCRIPTION') }}:</dt>
|
||||
<dd :class="{ 'no-description': !client.client_description }">
|
||||
{{
|
||||
client.client_description
|
||||
? client.client_description
|
||||
: $t('oauth2.NO_DESCRIPTION')
|
||||
}}
|
||||
</dd>
|
||||
<dt>{{ $t('oauth2.APP.URL') }}:</dt>
|
||||
<dd>{{ client.website }}</dd>
|
||||
<dt>{{ $t('oauth2.APP.REDIRECT_URL') }}:</dt>
|
||||
<dd>
|
||||
{{ client.redirect_uris.length > 0 ? client.redirect_uris[0] : '' }}
|
||||
</dd>
|
||||
<dt>{{ $t('oauth2.APP.SCOPE.LABEL') }}:</dt>
|
||||
<dd>{{ client.scope }}</dd>
|
||||
</dl>
|
||||
<div class="app-buttons">
|
||||
<button class="danger" @click="updateDisplayModal(true)">
|
||||
{{ $t('oauth2.DELETE_APP') }}
|
||||
</button>
|
||||
<button @click="$router.push('/profile/apps')">
|
||||
{{ $t('buttons.BACK') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="no-app">{{ $t('oauth2.NO_APP') }}</p>
|
||||
<button @click="$router.push('/profile/apps')">
|
||||
{{ $t('buttons.BACK') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import {
|
||||
ComputedRef,
|
||||
Ref,
|
||||
capitalize,
|
||||
computed,
|
||||
onBeforeMount,
|
||||
toRefs,
|
||||
ref,
|
||||
onUnmounted,
|
||||
} from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import { OAUTH2_STORE, ROOT_STORE } from '@/store/constants'
|
||||
import { IOAuth2Client } from '@/types/oauth'
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { getDateWithTZ } from '@/utils/dates'
|
||||
|
||||
interface Props {
|
||||
authUser: IAuthUserProfile
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
|
||||
const { authUser } = toRefs(props)
|
||||
const client: ComputedRef<IOAuth2Client> = computed(
|
||||
() => store.getters[OAUTH2_STORE.GETTERS.CLIENT]
|
||||
)
|
||||
let displayModal: Ref<boolean> = ref(false)
|
||||
|
||||
onBeforeMount(() => {
|
||||
loadClient()
|
||||
})
|
||||
|
||||
function loadClient() {
|
||||
if (route.params.clientId && typeof route.params.clientId === 'string') {
|
||||
store.dispatch(OAUTH2_STORE.ACTIONS.GET_CLIENT, route.params.clientId)
|
||||
}
|
||||
}
|
||||
function deleteClient(clientId: number) {
|
||||
store.dispatch(OAUTH2_STORE.ACTIONS.DELETE_CLIENT, clientId)
|
||||
}
|
||||
function updateDisplayModal(value: boolean) {
|
||||
displayModal.value = value
|
||||
}
|
||||
onUnmounted(() => {
|
||||
store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||
store.commit(OAUTH2_STORE.MUTATIONS.EMPTY_CLIENT)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/scss/vars.scss';
|
||||
#oauth2-app {
|
||||
.app-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $default-padding;
|
||||
}
|
||||
.no-description {
|
||||
font-style: italic;
|
||||
}
|
||||
.no-app {
|
||||
font-style: italic;
|
||||
padding: $default-padding 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -3,7 +3,9 @@
|
||||
<p class="apps-list">{{ $t('oauth2.APPS_LIST') }}</p>
|
||||
<ul v-if="clients.length > 0">
|
||||
<li v-for="client in clients" :key="client.client_id">
|
||||
{{ client.name }}
|
||||
<router-link :to="{ name: 'UserApp', params: { clientId: client.id } }">
|
||||
{{ client.name }}
|
||||
</router-link>
|
||||
<span class="app-issued-at">
|
||||
{{ $t('oauth2.APP.ISSUE_AT') }}
|
||||
{{
|
||||
@ -17,7 +19,12 @@
|
||||
</ul>
|
||||
<div class="no-apps" v-else>{{ $t('oauth2.NO_APPS') }}</div>
|
||||
<Pagination :pagination="pagination" path="/profile/apps" :query="query" />
|
||||
<button @click="$router.push('/')">{{ $t('common.HOME') }}</button>
|
||||
<div class="app-list-buttons">
|
||||
<button @click="$router.push('/profile/apps/new')">
|
||||
{{ $t('oauth2.NEW_APP') }}
|
||||
</button>
|
||||
<button @click="$router.push('/')">{{ $t('common.HOME') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -92,11 +99,16 @@
|
||||
.app-issued-at {
|
||||
font-size: 0.85em;
|
||||
font-style: italic;
|
||||
padding-left: $default-padding;
|
||||
}
|
||||
.apps-list {
|
||||
font-size: 1.05em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.app-list-buttons {
|
||||
display: flex;
|
||||
gap: $default-padding;
|
||||
}
|
||||
.no-apps {
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -5,14 +5,23 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { toRefs } from 'vue'
|
||||
import { onUnmounted, toRefs } from 'vue'
|
||||
|
||||
import { OAUTH2_STORE, ROOT_STORE } from '@/store/constants'
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
|
||||
interface Props {
|
||||
user: IAuthUserProfile
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const { user } = toRefs(props)
|
||||
|
||||
onUnmounted(() => {
|
||||
store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||
store.commit(OAUTH2_STORE.MUTATIONS.SET_CLIENTS, [])
|
||||
})
|
||||
</script>
|
||||
|
@ -7,7 +7,7 @@
|
||||
type="radio"
|
||||
:id="tab"
|
||||
:name="tab"
|
||||
:checked="selectedTab === tab"
|
||||
:checked="selectedTab.split('/')[0] === tab"
|
||||
:disabled="disabled"
|
||||
@input="$router.push(getPath(tab))"
|
||||
/>
|
||||
|
Reference in New Issue
Block a user