Files
schemas-app/app/pages/schemas/[id].vue
T
argoyle 072e1b10f1 feat: initial schemas-app implementation
- Add Nuxt 4 application with Vuetify UI framework
- Implement GraphQL schema registry management interface
- Add Apollo Client integration with Auth0 authentication
- Create organization and API key management
- Add schema and ref browsing capabilities
- Implement organization switcher for multi-org users
- Add delete functionality for organizations and API keys
- Create Kubernetes deployment descriptors
- Add Docker configuration with nginx

Features:
- Dashboard with organization overview
- Schema browsing by ref with supergraph viewing
- Ref management with schema details
- Settings page for organizations and API keys
- User list per organization with provider icons
- Admin-only organization creation
- Delete confirmations with warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 17:10:10 +01:00

220 lines
5.7 KiB
Vue

<template>
<div>
<v-row>
<v-col cols="12">
<v-btn
prepend-icon="mdi-arrow-left"
variant="text"
@click="navigateTo('/schemas')"
>
Back to Schemas
</v-btn>
</v-col>
</v-row>
<v-row v-if="schema">
<v-col cols="12">
<h1 class="text-h3 mb-2">{{ schema.service }}</h1>
<v-chip class="mr-2">{{ schema.ref }}</v-chip>
<v-chip color="success">Active</v-chip>
</v-col>
</v-row>
<v-row v-if="schema">
<v-col cols="12" md="6">
<v-card>
<v-card-title>Details</v-card-title>
<v-card-text>
<v-list>
<v-list-item>
<v-list-item-title>Service</v-list-item-title>
<v-list-item-subtitle>{{ schema.service }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>Ref</v-list-item-title>
<v-list-item-subtitle>{{ schema.ref }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>URL</v-list-item-title>
<v-list-item-subtitle>{{ schema.url }}</v-list-item-subtitle>
</v-list-item>
<v-list-item v-if="schema.wsUrl">
<v-list-item-title>WebSocket URL</v-list-item-title>
<v-list-item-subtitle>{{ schema.wsUrl }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>Last Updated</v-list-item-title>
<v-list-item-subtitle>{{ schema.updatedAt }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>Updated By</v-list-item-title>
<v-list-item-subtitle>{{ schema.updatedBy }}</v-list-item-subtitle>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card>
<v-card-title>Actions</v-card-title>
<v-card-text>
<v-btn
block
prepend-icon="mdi-download"
variant="outlined"
class="mb-2"
@click="downloadSchema"
>
Download SDL
</v-btn>
<v-btn
block
prepend-icon="mdi-content-copy"
variant="outlined"
class="mb-2"
@click="copyToClipboard"
>
Copy SDL
</v-btn>
<v-btn
block
prepend-icon="mdi-graph"
variant="outlined"
color="primary"
>
View Federation Graph
</v-btn>
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-row v-if="schema">
<v-col cols="12">
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<span>Schema Definition (SDL)</span>
<v-btn
icon="mdi-content-copy"
variant="text"
size="small"
@click="copyToClipboard"
/>
</v-card-title>
<v-card-text>
<pre class="sdl-viewer"><code>{{ schema.sdl }}</code></pre>
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-row v-if="!schema && !loading">
<v-col cols="12" class="text-center py-12">
<v-icon icon="mdi-alert-circle" size="64" class="mb-4 text-warning" />
<p class="text-h6">Schema not found</p>
</v-col>
</v-row>
<v-snackbar v-model="snackbar" :timeout="2000">
{{ snackbarText }}
</v-snackbar>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const _config = useRuntimeConfig()
const schema = ref<any>(null)
const loading = ref(true)
const snackbar = ref(false)
const snackbarText = ref('')
const downloadSchema = () => {
if (!schema.value) return
const blob = new Blob([schema.value.sdl], { type: 'text/plain' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `${schema.value.service}-${schema.value.ref}.graphql`
a.click()
URL.revokeObjectURL(url)
snackbarText.value = 'Schema downloaded'
snackbar.value = true
}
const copyToClipboard = async () => {
if (!schema.value) return
try {
await navigator.clipboard.writeText(schema.value.sdl)
snackbarText.value = 'SDL copied to clipboard'
snackbar.value = true
} catch (_err) {
snackbarText.value = 'Failed to copy'
snackbar.value = true
}
}
// TODO: Fetch actual data from the GraphQL API
onMounted(() => {
// Mock data for now
setTimeout(() => {
schema.value = {
id: route.params.id,
service: 'users-service',
ref: 'production',
url: 'http://users.example.com/graphql',
wsUrl: 'ws://users.example.com/graphql',
updatedAt: '2024-11-21 19:30:00',
updatedBy: 'john.doe@example.com',
sdl: `type User @key(fields: "id") {
id: ID!
username: String!
email: String!
createdAt: DateTime!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
}
input CreateUserInput {
username: String!
email: String!
}
input UpdateUserInput {
username: String
email: String
}
scalar DateTime`,
}
loading.value = false
}, 500)
})
</script>
<style scoped>
.sdl-viewer {
background: #f5f5f5;
padding: 16px;
border-radius: 4px;
overflow-x: auto;
font-family: Monaco, Menlo, 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.5;
}
</style>