Working stream search + commit table
This commit is contained in:
+3
-2
@@ -1,2 +1,3 @@
|
||||
VUE_APP_SPECKLE_ID=e5ae7168fd
|
||||
VUE_APP_SPECKLE_SECRET=8438ffe22e
|
||||
VUE_APP_SPECKLE_ID=720cce4c99
|
||||
VUE_APP_SPECKLE_SECRET=d26ea2c5c0
|
||||
VUE_APP_SERVER_URL=https://latest.speckle.dev
|
||||
Generated
+24
@@ -4148,12 +4148,22 @@
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
||||
"integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
|
||||
},
|
||||
"de-indent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||
"integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=",
|
||||
"dev": true
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
@@ -5777,6 +5787,12 @@
|
||||
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
|
||||
"dev": true
|
||||
},
|
||||
"graphql-tag": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.11.0.tgz",
|
||||
"integrity": "sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA==",
|
||||
"dev": true
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
||||
@@ -11523,6 +11539,14 @@
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||
"dev": true
|
||||
},
|
||||
"vue-timeago": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-timeago/-/vue-timeago-5.1.3.tgz",
|
||||
"integrity": "sha512-lHTRuOXhQzQXa6SC52IlO6UyWBZ5eIyD819QGIep++D61HeCV15h/WZ7M1iEsOWttjztMpg+3wYWHO3i2Ijdzw==",
|
||||
"requires": {
|
||||
"date-fns": "^1.29.0"
|
||||
}
|
||||
},
|
||||
"vuetify": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.4.11.tgz",
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
"@speckle/objectloader": "^2.0.0",
|
||||
"@speckle/viewer": "^2.0.1",
|
||||
"core-js": "^3.6.5",
|
||||
"debounce": "^1.2.1",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-timeago": "^5.1.3",
|
||||
"vuetify": "^2.4.11",
|
||||
"vuex": "^3.4.0"
|
||||
},
|
||||
@@ -28,6 +30,7 @@
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"graphql-tag": "^2.11.0",
|
||||
"sass": "^1.32.0",
|
||||
"sass-loader": "^10.0.0",
|
||||
"vue-cli-plugin-vuetify": "~2.3.1",
|
||||
|
||||
+1
-8
@@ -40,21 +40,14 @@
|
||||
</v-app-bar>
|
||||
|
||||
<v-main>
|
||||
<Home/>
|
||||
<router-view/>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Home from "@/views/Home";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
||||
components: {
|
||||
Home,
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
//
|
||||
}),
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>
|
||||
|
Before Width: | Height: | Size: 539 B |
@@ -1,151 +0,0 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row class="text-center">
|
||||
<v-col cols="12">
|
||||
<v-img
|
||||
:src="require('../assets/logo.svg')"
|
||||
class="my-3"
|
||||
contain
|
||||
height="200"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col class="mb-4">
|
||||
<h1 class="display-2 font-weight-bold mb-3">
|
||||
Welcome to Vuetify
|
||||
</h1>
|
||||
|
||||
<p class="subheading font-weight-regular">
|
||||
For help and collaboration with other Vuetify developers,
|
||||
<br>please join our online
|
||||
<a
|
||||
href="https://community.vuetifyjs.com"
|
||||
target="_blank"
|
||||
>Discord Community</a>
|
||||
</p>
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
class="mb-5"
|
||||
cols="12"
|
||||
>
|
||||
<h2 class="headline font-weight-bold mb-3">
|
||||
What's next?
|
||||
</h2>
|
||||
|
||||
<v-row justify="center">
|
||||
<a
|
||||
v-for="(next, i) in whatsNext"
|
||||
:key="i"
|
||||
:href="next.href"
|
||||
class="subheading mx-3"
|
||||
target="_blank"
|
||||
>
|
||||
{{ next.text }}
|
||||
</a>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
class="mb-5"
|
||||
cols="12"
|
||||
>
|
||||
<h2 class="headline font-weight-bold mb-3">
|
||||
Important Links
|
||||
</h2>
|
||||
|
||||
<v-row justify="center">
|
||||
<a
|
||||
v-for="(link, i) in importantLinks"
|
||||
:key="i"
|
||||
:href="link.href"
|
||||
class="subheading mx-3"
|
||||
target="_blank"
|
||||
>
|
||||
{{ link.text }}
|
||||
</a>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
class="mb-5"
|
||||
cols="12"
|
||||
>
|
||||
<h2 class="headline font-weight-bold mb-3">
|
||||
Ecosystem
|
||||
</h2>
|
||||
|
||||
<v-row justify="center">
|
||||
<a
|
||||
v-for="(eco, i) in ecosystem"
|
||||
:key="i"
|
||||
:href="eco.href"
|
||||
class="subheading mx-3"
|
||||
target="_blank"
|
||||
>
|
||||
{{ eco.text }}
|
||||
</a>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
|
||||
data: () => ({
|
||||
ecosystem: [
|
||||
{
|
||||
text: 'vuetify-loader',
|
||||
href: 'https://github.com/vuetifyjs/vuetify-loader',
|
||||
},
|
||||
{
|
||||
text: 'github',
|
||||
href: 'https://github.com/vuetifyjs/vuetify',
|
||||
},
|
||||
{
|
||||
text: 'awesome-vuetify',
|
||||
href: 'https://github.com/vuetifyjs/awesome-vuetify',
|
||||
},
|
||||
],
|
||||
importantLinks: [
|
||||
{
|
||||
text: 'Documentation',
|
||||
href: 'https://vuetifyjs.com',
|
||||
},
|
||||
{
|
||||
text: 'Chat',
|
||||
href: 'https://community.vuetifyjs.com',
|
||||
},
|
||||
{
|
||||
text: 'Made with Vuetify',
|
||||
href: 'https://madewithvuejs.com/vuetify',
|
||||
},
|
||||
{
|
||||
text: 'Twitter',
|
||||
href: 'https://twitter.com/vuetifyjs',
|
||||
},
|
||||
{
|
||||
text: 'Articles',
|
||||
href: 'https://medium.com/vuetify',
|
||||
},
|
||||
],
|
||||
whatsNext: [
|
||||
{
|
||||
text: 'Explore components',
|
||||
href: 'https://vuetifyjs.com/components/api-explorer',
|
||||
},
|
||||
{
|
||||
text: 'Select a layout',
|
||||
href: 'https://vuetifyjs.com/getting-started/pre-made-layouts',
|
||||
},
|
||||
{
|
||||
text: 'Frequently Asked Questions',
|
||||
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions',
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<v-autocomplete
|
||||
v-model="selectedSearchResult"
|
||||
:items="streams.items"
|
||||
:search-input.sync="search"
|
||||
no-filter
|
||||
counter="2"
|
||||
rounded
|
||||
filled
|
||||
dense
|
||||
flat
|
||||
hide-no-data
|
||||
hide-details
|
||||
placeholder="Streams Search"
|
||||
item-text="name"
|
||||
item-value="id"
|
||||
return-object
|
||||
clearable
|
||||
append-icon=""
|
||||
@update:search-input="debounceInput"
|
||||
>
|
||||
<template #item="{ item }" color="background">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
<v-row class="pa-0 ma-0">
|
||||
{{ item.name }}
|
||||
<v-spacer></v-spacer>
|
||||
<span class="streamid">{{ item.id }}</span>
|
||||
</v-row>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle class="caption">
|
||||
Updated
|
||||
<timeago :datetime="item.updatedAt"></timeago>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {debounce} from "debounce"
|
||||
|
||||
const TOKEN = 'SpeckleDemo.AuthToken'
|
||||
const SERVER_URL = process.env.VUE_APP_SERVER_URL
|
||||
|
||||
export default {
|
||||
name: "StreamSearch",
|
||||
data: () => ({
|
||||
search: "",
|
||||
streams: {items: []},
|
||||
selectedSearchResult: null
|
||||
}),
|
||||
watch: {
|
||||
selectedSearchResult(val) {
|
||||
this.search = ""
|
||||
this.streams.items = []
|
||||
if (val)
|
||||
this.$emit("selected", val)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchSearchResults(e) {
|
||||
if( !e || e?.length < 3) return;
|
||||
console.log("execute search", e)
|
||||
let token = localStorage.getItem(TOKEN)
|
||||
if (token) {
|
||||
fetch(`${SERVER_URL}/graphql`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `
|
||||
query {
|
||||
streams(query: "${e}") {
|
||||
totalCount
|
||||
cursor
|
||||
items {
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
}
|
||||
)
|
||||
.then(res => res.json()).then(json => { console.warn(json.data.streams); this.streams = json.data.streams }, this)
|
||||
}
|
||||
},
|
||||
debounceInput: debounce(function (e) {
|
||||
this.fetchSearchResults(e)
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -5,6 +5,9 @@ import router from './router'
|
||||
import store from './store'
|
||||
import vuetify from './plugins/vuetify'
|
||||
|
||||
import VueTimeago from 'vue-timeago'
|
||||
Vue.use(VueTimeago, { locale: 'en' })
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
||||
|
||||
+46
-40
@@ -1,8 +1,9 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const TOKEN='SpeckleDemo.AuthToken'
|
||||
const TOKEN = 'SpeckleDemo.AuthToken'
|
||||
const REFRESH_TOKEN = 'SpeckleDemo.RefreshToken'
|
||||
const CHALLENGE = 'SpeckleDemo.Challenge'
|
||||
const SERVER_URL = process.env.VUE_APP_SERVER_URL
|
||||
@@ -43,10 +44,10 @@ export default new Vuex.Store({
|
||||
isAuthenticated: (state) => state.user != null
|
||||
},
|
||||
mutations: {
|
||||
setUser(state, user){
|
||||
setUser(state, user) {
|
||||
state.user = user
|
||||
},
|
||||
setServerInfo(state, info){
|
||||
setServerInfo(state, info) {
|
||||
state.serverInfo = info
|
||||
},
|
||||
setToken(state, token) {
|
||||
@@ -62,65 +63,70 @@ export default new Vuex.Store({
|
||||
context.commit("setServerInfo", null)
|
||||
context.commit("setToken", null)
|
||||
context.commit("setRefreshToken", null)
|
||||
localStorage.removeItem( TOKEN )
|
||||
localStorage.removeItem( REFRESH_TOKEN )
|
||||
localStorage.removeItem(TOKEN)
|
||||
localStorage.removeItem(REFRESH_TOKEN)
|
||||
},
|
||||
async exchangeAccessCode(context, accessCode) {
|
||||
console.log('VUEX - Access code exchange', accessCode)
|
||||
let response = await fetch( `${SERVER_URL}/auth/token/`, {
|
||||
let response = await fetch(`${SERVER_URL}/auth/token/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify( {
|
||||
body: JSON.stringify({
|
||||
accessCode: accessCode,
|
||||
appId: 'explorer',
|
||||
appSecret: 'explorer',
|
||||
challenge: localStorage.getItem( CHALLENGE )
|
||||
} )
|
||||
} )
|
||||
challenge: localStorage.getItem(CHALLENGE)
|
||||
})
|
||||
})
|
||||
|
||||
let data = await response.json( )
|
||||
let data = await response.json()
|
||||
console.log("data", data.token)
|
||||
if ( data.token ) {
|
||||
localStorage.removeItem( CHALLENGE )
|
||||
if (data.token) {
|
||||
localStorage.removeItem(CHALLENGE)
|
||||
context.commit("setToken", data.token)
|
||||
context.commit("setRefreshToken", data.refreshToken)
|
||||
localStorage.setItem( TOKEN, data.token )
|
||||
localStorage.setItem( REFRESH_TOKEN, data.refreshToken )
|
||||
localStorage.setItem(TOKEN, data.token)
|
||||
localStorage.setItem(REFRESH_TOKEN, data.refreshToken)
|
||||
}
|
||||
|
||||
},
|
||||
async getUser(context) {
|
||||
console.log("Getting user!")
|
||||
getUser(context) {
|
||||
let token = localStorage.getItem(TOKEN)
|
||||
if (token) {
|
||||
let testResponse = await fetch(`${SERVER_URL}/graphql`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({query: userInfoQuery})
|
||||
})
|
||||
|
||||
let data = (await testResponse.json()).data
|
||||
// if res.data.user is non null, means the ping was ok & token is valid
|
||||
if (data.user) {
|
||||
console.log("Got user!", data.user)
|
||||
context.commit("setUser", data.user)
|
||||
}
|
||||
if (data.serverInfo){
|
||||
context.commit("setServerInfo", data.serverInfo)
|
||||
}
|
||||
fetch(`${SERVER_URL}/graphql`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({query: userInfoQuery})
|
||||
}
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
var data = json.data
|
||||
if (data.user) {
|
||||
console.log("Got user!", data.user)
|
||||
context.commit("setUser", data.user)
|
||||
console.log("User logged in as " + data.user.name)
|
||||
}
|
||||
if (data.serverInfo) {
|
||||
context.commit("setServerInfo", data.serverInfo)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log("User is not logged in")
|
||||
}
|
||||
},
|
||||
redirectToAuth( ) {
|
||||
var challenge = Math.random( ).toString( 36 ).substring( 2, 15 ) + Math.random( ).toString( 36 ).substring( 2, 15 )
|
||||
redirectToAuth() {
|
||||
// Generate random challenge
|
||||
var challenge = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
||||
// Save challenge in localStorage
|
||||
localStorage.setItem(CHALLENGE, challenge)
|
||||
// Send user to auth page
|
||||
window.location = `${SERVER_URL}/authn/verify/${process.env.VUE_APP_SPECKLE_ID}/${challenge}`
|
||||
}
|
||||
},
|
||||
modules: {
|
||||
}
|
||||
modules: {}
|
||||
})
|
||||
|
||||
+44
-23
@@ -8,14 +8,7 @@
|
||||
sm="6"
|
||||
offset="3"
|
||||
>
|
||||
<v-select
|
||||
v-model="selectedStreamId"
|
||||
:items="streams.items"
|
||||
item-text="name"
|
||||
item-value="id"
|
||||
@change="handleStreamSelection"
|
||||
label="Select a stream"
|
||||
></v-select>
|
||||
<stream-search @selected="streamSelected"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="lastCommitChildren">
|
||||
@@ -33,8 +26,7 @@
|
||||
<v-col class="d-flex" cols="6" offset="3">
|
||||
<v-data-table
|
||||
:headers="filteredHeaders"
|
||||
:items="filteredCommitChildren"
|
||||
:items-per-page="10"
|
||||
:items="lastCommitChildren"
|
||||
class="elevation-1"
|
||||
></v-data-table>
|
||||
</v-col>
|
||||
@@ -46,18 +38,19 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StreamSearch from "@/components/StreamSearch";
|
||||
const TOKEN = 'SpeckleDemo.AuthToken'
|
||||
const SERVER_URL = process.env.VUE_APP_SERVER_URL
|
||||
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {StreamSearch},
|
||||
data: () => {
|
||||
return {
|
||||
selectedStreamId: null,
|
||||
selectedStream: null,
|
||||
lastCommitChildren: null,
|
||||
selectedKeys: ["speckle_type", "totalChildrenCount"]
|
||||
selectedKeys: ["id", "message"]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -99,6 +92,43 @@ export default {
|
||||
})
|
||||
.catch(err => console.error(err))
|
||||
}
|
||||
},
|
||||
streamSelected(stream){
|
||||
console.log("Stream selected", stream)
|
||||
let token = localStorage.getItem(TOKEN)
|
||||
if (token)
|
||||
fetch(
|
||||
`${SERVER_URL}/graphql`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `query {
|
||||
stream(id: "${stream.id}"){
|
||||
commits(limit: 10, cursor: null) {
|
||||
totalCount
|
||||
cursor
|
||||
items{
|
||||
id
|
||||
message
|
||||
branchName
|
||||
sourceApplication
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(json => json.data.stream.commits)
|
||||
.then(commits => {
|
||||
console.log("commits", commits)
|
||||
this.lastCommitChildren = commits.items
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
@@ -108,8 +138,8 @@ export default {
|
||||
},
|
||||
availableKeys: function(){
|
||||
var keys = {}
|
||||
this.lastCommitChildren.forEach(obj => {
|
||||
Object.keys(obj.data).forEach(key => {
|
||||
this.lastCommitChildren?.forEach(obj => {
|
||||
Object.keys(obj).forEach(key => {
|
||||
if(!keys[key]){
|
||||
keys[key] = true
|
||||
}
|
||||
@@ -119,15 +149,6 @@ export default {
|
||||
},
|
||||
filteredHeaders: function() {
|
||||
return this.selectedKeys.map(key => { return { text: key, value: key}})
|
||||
},
|
||||
filteredCommitChildren: function (){
|
||||
return this.lastCommitChildren.map(obj => {
|
||||
var copy = Object.assign({},obj.data)
|
||||
copy.name = copy.speckle_type
|
||||
delete copy.data
|
||||
return copy
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user