WIP on login from NAS
This commit is contained in:
parent
3927a4cb22
commit
4fce076ee5
|
@ -30,7 +30,7 @@ export default defineComponent({
|
||||||
AppNav,
|
AppNav,
|
||||||
TitleBar
|
TitleBar
|
||||||
},
|
},
|
||||||
data () {
|
setup () {
|
||||||
return {
|
return {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
25
src/JobsJobsJobs/App/src/api/index.ts
Normal file
25
src/JobsJobsJobs/App/src/api/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { LogOnSuccess } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a URL that will access the API
|
||||||
|
* @param url The partial URL for the API
|
||||||
|
* @returns A full URL for the API
|
||||||
|
*/
|
||||||
|
const apiUrl = (url : string) : string => `/api/${url}`
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a citizen on
|
||||||
|
* @param code The authorization code from No Agenda Social
|
||||||
|
* @returns The user result, or an error
|
||||||
|
*/
|
||||||
|
logOn: async (code : string) : Promise<LogOnSuccess | string> => {
|
||||||
|
const resp = await fetch(apiUrl(`/citizen/log-on/${code}`), { method: 'GET' })
|
||||||
|
if (resp.status === 200) return await resp.json() as LogOnSuccess
|
||||||
|
console.error(await resp.text())
|
||||||
|
return 'Error logging on'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from './types'
|
10
src/JobsJobsJobs/App/src/api/types.ts
Normal file
10
src/JobsJobsJobs/App/src/api/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
/** A successful logon */
|
||||||
|
export interface LogOnSuccess {
|
||||||
|
/** The JSON Web Token (JWT) to use for API access */
|
||||||
|
jwt : string
|
||||||
|
/** The ID of the logged-in citizen (as a string) */
|
||||||
|
citizenId : string
|
||||||
|
/** The name of the logged-in citizen */
|
||||||
|
name : string
|
||||||
|
}
|
|
@ -4,20 +4,6 @@
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'AppFooter',
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
p
|
p
|
||||||
padding-top: 2rem
|
padding-top: 2rem
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<router-link to="/"><v-icon icon="mdi-home" /> Home</router-link>
|
<router-link to="/"><v-icon icon="mdi-home" /> Home</router-link>
|
||||||
<!-- If not logged in -->
|
<!-- If not logged in -->
|
||||||
<router-link to="/profile/seeking"><v-icon icon="mdi-view-list-outline" /> Job Seekers</router-link>
|
<router-link to="/profile/seeking"><v-icon icon="mdi-view-list-outline" /> Job Seekers</router-link>
|
||||||
<router-link to="/log-on"><v-icon icon="mdi-login-variant" /> Log On</router-link>
|
<a :href="authUrl"><v-icon icon="mdi-login-variant" /> Log On</a>
|
||||||
<!-- If logged in -->
|
<!-- If logged in -->
|
||||||
<router-link to="/citizen/profile"><v-icon icon="mdi-pencil" /> Edit Your Profile</router-link>
|
<router-link to="/citizen/profile"><v-icon icon="mdi-pencil" /> Edit Your Profile</router-link>
|
||||||
<router-link to="/profile/search"><v-icon icon="mdi-view-list-outline" /> View Profiles</router-link>
|
<router-link to="/profile/search"><v-icon icon="mdi-view-list-outline" /> View Profiles</router-link>
|
||||||
|
@ -23,10 +23,19 @@ import { defineComponent } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'AppNav',
|
name: 'AppNav',
|
||||||
|
setup () {
|
||||||
data () {
|
|
||||||
return {
|
return {
|
||||||
//
|
/** The authorization URL to which the user should be directed */
|
||||||
|
authUrl: (() => {
|
||||||
|
/** The client ID for Jobs, Jobs, Jobs at No Agenda Social */
|
||||||
|
const id = 'k_06zlMy0N451meL4AqlwMQzs5PYr6g3d2Q_dCT-OjU'
|
||||||
|
const client = `client_id=${id}`
|
||||||
|
const scope = 'scope=read:accounts'
|
||||||
|
const redirect = `redirect_uri=${document.location.origin}/citizen/authorized`
|
||||||
|
const respType = 'response_type=code'
|
||||||
|
// TODO: move NAS base URL to config
|
||||||
|
return `https://noagendasocial.com/oauth/authorize?${client}&${scope}&${redirect}&${respType}`
|
||||||
|
})()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,16 +13,20 @@ export default defineComponent({
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
setup (props) {
|
||||||
clipSource () : string {
|
/** The full relative URL for the audio clip */
|
||||||
return `/audio/${this.clip}.mp3`
|
const clipSource = `/audio/${props.clip}.mp3`
|
||||||
}
|
|
||||||
},
|
/** Play the audio file */
|
||||||
methods: {
|
const playFile = () => {
|
||||||
playFile () {
|
const audio = document.getElementById(props.clip) as HTMLAudioElement
|
||||||
const audio = document.getElementById(this.clip) as HTMLAudioElement
|
|
||||||
audio.play()
|
audio.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
clipSource,
|
||||||
|
playFile
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { createApp } from 'vue'
|
||||||
import vuetify from './plugins/vuetify'
|
import vuetify from './plugins/vuetify'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store, { key } from './store'
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
.use(router)
|
.use(router)
|
||||||
.use(store)
|
.use(store, key)
|
||||||
.use(vuetify)
|
.use(vuetify)
|
||||||
.mount('#app')
|
.mount('#app')
|
||||||
|
|
|
@ -2,30 +2,20 @@ import { createRouter, createWebHistory, RouteLocationNormalized, RouteLocationN
|
||||||
import Home from '../views/Home.vue'
|
import Home from '../views/Home.vue'
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
|
{ path: '/', component: Home },
|
||||||
|
{ path: '/how-it-works', component: () => import(/* webpackChunkName: "help" */ '../views/HowItWorks.vue') },
|
||||||
|
{ path: '/privacy-policy', component: () => import(/* webpackChunkName: "legal" */ '../views/PrivacyPolicy.vue') },
|
||||||
|
{ path: '/terms-of-service', component: () => import(/* webpackChunkName: "legal" */ '../views/TermsOfService.vue') },
|
||||||
|
// Citizen URLs
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/citizen/authorized',
|
||||||
name: 'Home',
|
component: () => import(/* webpackChunkName: "logon" */ '../views/citizen/Authorized.vue')
|
||||||
component: Home
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/how-it-works',
|
|
||||||
name: 'HowItWorks',
|
|
||||||
component: () => import(/* webpackChunkName: "help" */ '../views/HowItWorks.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/privacy-policy',
|
|
||||||
name: 'PrivacyPolicy',
|
|
||||||
component: () => import(/* webpackChunkName: "privacy" */ '../views/PrivacyPolicy.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/terms-of-service',
|
|
||||||
name: 'TermsOfService',
|
|
||||||
component: () => import(/* webpackChunkName: "terms" */ '../views/TermsOfService.vue')
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(process.env.BASE_URL),
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
// eslint-disable-next-line
|
||||||
scrollBehavior (to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, savedPosition: any) {
|
scrollBehavior (to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, savedPosition: any) {
|
||||||
return savedPosition ?? { top: 0, left: 0 }
|
return savedPosition ?? { top: 0, left: 0 }
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,45 @@
|
||||||
import { createStore } from 'vuex'
|
import { InjectionKey } from 'vue'
|
||||||
|
import { createStore, Store, useStore as baseUseStore } from 'vuex'
|
||||||
|
import api, { LogOnSuccess } from '../api'
|
||||||
|
|
||||||
|
/** The state tracked by the application */
|
||||||
|
export interface State {
|
||||||
|
user: LogOnSuccess | undefined
|
||||||
|
logOnState: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An injection key to identify this state with Vue */
|
||||||
|
export const key : InjectionKey<Store<State>> = Symbol('VueX Store')
|
||||||
|
|
||||||
|
/** Use this store in component `setup` functions */
|
||||||
|
export function useStore () : Store<State> {
|
||||||
|
return baseUseStore(key)
|
||||||
|
}
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
state: {
|
state: () : State => {
|
||||||
|
return {
|
||||||
|
user: undefined,
|
||||||
|
logOnState: 'Logging you on with No Agenda Social...'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
setUser (state, user: LogOnSuccess) {
|
||||||
|
state.user = user
|
||||||
|
},
|
||||||
|
setLogOnState (state, message) {
|
||||||
|
state.logOnState = message
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
async logOn ({ commit }, code: string) {
|
||||||
|
const logOnResult = await api.logOn(code)
|
||||||
|
if (typeof logOnResult === 'string') {
|
||||||
|
commit('setLogOnState', logOnResult)
|
||||||
|
} else {
|
||||||
|
commit('setUser', logOnResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modules: {
|
modules: {
|
||||||
}
|
}
|
||||||
|
|
34
src/JobsJobsJobs/App/src/views/citizen/Authorized.vue
Normal file
34
src/JobsJobsJobs/App/src/views/citizen/Authorized.vue
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<p>{{message}}</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useStore } from '../../store'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Authorized',
|
||||||
|
setup () {
|
||||||
|
const router = useRouter()
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
/** Pass the code to the API and exchange it for a user and a JWT */
|
||||||
|
const logOn = async () => {
|
||||||
|
const code = router.currentRoute.value.query.code
|
||||||
|
if (code) {
|
||||||
|
await store.dispatch('logOn', code)
|
||||||
|
if (store.state.user !== undefined) { router.push('/citizen/dashboard') }
|
||||||
|
} else {
|
||||||
|
store.commit('setLogOnState', 'Did not receive a token from No Agenda Social (perhaps you clicked "Cancel"?)')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(logOn)
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: computed(() => store.state.logOnState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user