Skip to content

Commit

Permalink
Walking skeleton with About box, language choice, copy to clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
monodot committed Sep 24, 2023
1 parent 15c17df commit 8720005
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 145 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<title>Manikure - K8s Manifest Builder</title>
</head>
<body>
<div id="app" class="flex flex-col"></div>
<div id="app" class="fixed top-0 left-0 right-0 bottom-0 flex flex-col"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
63 changes: 13 additions & 50 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,72 +1,35 @@
<script setup>
import { RouterLink, RouterView } from 'vue-router'
import ResourceList from './components/ResourceList.vue'
// import DeploymentView from './views/DeploymentView.vue'
import CanvasView from './views/CanvasView.vue'
import AppHeader from './components/AppHeader.vue'
import AboutBox from './components/AboutBox.vue'
import { ref } from 'vue'
const aboutVisible = ref(false)
const showAbout = () => {
console.log('about received')
aboutVisible.value = true
}
</script>

<template>
<header>
<AppHeader/>
<AppHeader @show-about="showAbout"/>
</header>

<main class="grid grid-cols-sidebar-with-canvas">
<ResourceList />
<CanvasView />
</main>

<AboutBox v-if="aboutVisible" @close="aboutVisible = false" />
</template>

<style scoped>
main {
/* display: grid; */
/* grid-template-columns: 200px 1fr; */
}
/* header {
line-height: 1.5;
max-height: 100vh;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
} */
@media (min-width: 1024px) {
header {
/* display: flex; */
/* padding: 1rem; */
/* border-right: 2px solid var(--color-border); */
/* place-items: center; */
/* padding-right: calc(var(--section-gap) / 2); */
}
.logo {
margin: 0 2rem 0 0;
}
Expand Down
Binary file added src/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions src/components/AboutBox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script setup>
import imgUrl from '../assets/logo.png'
const close = defineEmits(['close'])
const closeModal = () => {
console.log('close modal')
close('close')
}
</script>

<template>
<div class="fixed top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-sky-500/50">
<div class="modal-content bg-white p-6 rounded shadow-lg w-96">
<img :src="imgUrl" alt="Manikure logo" class="w-2/3 mx-auto" />
<h2 class="text-2xl font-semibold my-2 text-center">Manikure</h2>
<p class="text-center">The manifest builder for Kubernetes resources.</p>
<button id="close-modal" @click="closeModal" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded mt-4">
Close
</button>
</div>
</div>
</template>

<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>
32 changes: 30 additions & 2 deletions src/components/AppHeader.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
<script setup>
import { usePreferencesStore } from '../stores/preferences';
const emit = defineEmits(['showAbout'])
const showAbout = () => {
console.log('about clicked')
emit('showAbout')
}
const prefs = usePreferencesStore()
</script>

<template>
<div class="p-4 border-b-2">
Manikure 💅 &#8212; <strong>Mani</strong>fest Builder for <strong>Ku</strong>bernetes <strong>Re</strong>sources
<div class="border-b-2">
<div class="flex justify-between">
<p class="p-4">Manikure Studio 💅 &#8212; <strong>Mani</strong>fest Builder for <strong>Ku</strong>bernetes <strong>Re</strong>sources</p>
<nav class="flex flex-wrap justify-center">
<div>
<label class="inline-block p-4">
<input type="radio" name="code-view-language" value="json" v-model="prefs.codeViewLanguage" class="peer" />
JSON
</label>
<label class="inline-block p-4">
<input type="radio" name="code-view-language" value="yaml" v-model="prefs.codeViewLanguage" class="peer" />
YAML
</label>
</div>
<a @click="showAbout" class="p-4 cursor-pointer">About</a>
<a href="https://github.com/monodot/k8s-manifest-builder" target="_blank" class="p-4">GitHub</a>
</nav>
</div>
</div>
</template>
23 changes: 17 additions & 6 deletions src/components/CodeView.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { useProjectStore } from '../stores/project'
import { usePreferencesStore } from '../stores/preferences'
import jsyaml from 'js-yaml'
const project = useProjectStore()
const prefs = usePreferencesStore()
const tehCode = ref(project.documents[project.selectedDocument])
project.$subscribe((mutation, state) => {
tehCode.value = state.documents[state.selectedDocument]
})
function renderCode(document) {
if (prefs.codeViewLanguage === 'yaml') {
return jsyaml.dump(document)
} else {
return JSON.stringify(document, null, 2)
}
}
const copyToClipboard = () => {
navigator.clipboard.writeText(JSON.stringify(project.documents[project.selectedDocument], null, 2))
}
project.$subscribe((mutation, state) => {
tehCode.value = state.documents[state.selectedDocument]
})
</script>

<template>
<div>
<div class="flex flex-col flex-1 min-w-0">
<!-- Copy to clipboard -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="copyToClipboard">Copy to clipboard</button>

<pre v-text="tehCode" class="font-mono py-2 px-3"></pre>
<textarea v-text="renderCode(tehCode)" class="flex-auto font-mono p-2" readonly=""></textarea>
</div>
</template>
2 changes: 1 addition & 1 deletion src/components/Deployment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const tehObject = ref(project.documents[project.selectedDocument])
</script>

<template>
<div class="p-4" aria-label="Resource Properties">
<div class="p-4" aria-label="Deployment Properties">
<h2 class="text-4xl font-bold text-gray-800 py-4">Deployment</h2>
<p>A Deployment provides declarative updates for Pods and ReplicaSets.</p>
<div class="">
Expand Down
25 changes: 25 additions & 0 deletions src/components/Ingress.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup>
import { ref } from 'vue'
import { useProjectStore } from '../stores/project'
const project = useProjectStore()
const ingress = ref(project.documents[project.selectedDocument])
</script>

<template>
<div class="p-4" aria-label="Ingress Properties">
<h2 class="text-4xl font-bold text-gray-800 py-4">Ingress</h2>
<p>Ingress is a collection of rules that allow inbound connections to reach the
endpoints defined by a backend.</p>
<div class="">
<div class="">
<div>
<label>Ingress name:</label>
<input v-model="ingress.metadata.name"
class="appearance-none bg-gray-200 w-full py-2 px-4 block rounded text-gray-800 focus:outline-none focus:shadow-outline">
</div>
</div>
</div>
</div>
</template>

36 changes: 23 additions & 13 deletions src/components/ResourceList.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
<script setup>
import { useProjectStore } from '../stores/project'
import jsyaml from 'js-yaml'
const project = useProjectStore()
const copyAllAsJSON = () => {
navigator.clipboard.writeText(JSON.stringify(project.documents, null, 2))
}
const copyAllAsYAML = () => {
navigator.clipboard.writeText(jsyaml.dump(project.documents))
}
</script>


<template>
<div class="p-4 border-r-2">
<div class="border-r-2">
<div>
<h2 class="text-lg font-bold">Project</h2>
<div v-for="(document, index) in project.documents" :key="index">
<label>
<input type="radio" v-model="project.selectedDocument" :value="index" />
{{ document.kind }} - {{ document.metadata.name }} ({{ document.apiVersion }})
<button @click="project.removeDocument(index)">Remove</button>
<h2 class="p-4 text-lg font-bold">Project</h2>

<nav v-for="(document, index) in project.documents" :key="index" aria-label="Resource Navigator">
<input type="radio" :id="'document-' + index" v-model="project.selectedDocument" :value="index" class="hidden peer" />
<label :for="'document-' + index" class="block p-4 cursor-pointer bg-gray-100 border-b-2 peer-checked:border-l-pink-500 peer-checked:border-l-4 peer-checked:font-bold peer-checked:bg-white ">
<span class="text-xs text-gray-500 block">{{ document.kind }}</span>
<span class="font-bold">{{ document.metadata.name }} </span>
</label>
</div>
</nav>

<p class="my-4">
<a href="#">+ Deployment</a>
<p class="p-4">
<a @click="copyAllAsJSON" class="cursor-pointer">Copy all as JSON</a>
</p>

<p class="my-4">
<a href="#">Export</a>
<p class="p-4">
<a @click="copyAllAsYAML" class="cursor-pointer">Copy all as YAML</a>
</p>

</div>
</div>
</template>
Expand Down
45 changes: 13 additions & 32 deletions src/components/Service.vue
Original file line number Diff line number Diff line change
@@ -1,41 +1,22 @@
<script setup>
import { defineProps, defineEmits, reactive, watch } from 'vue'
import { useProjectStore } from '../stores/project';
const emit = defineEmits(['updateCode'])
const service = reactive({
apiVersion: "v1",
kind: "Service",
metadata: {
name: "my-service"
},
spec: {
selector: {
app: "my-app-label"
},
ports: [{
protocol: "TCP",
port: 80,
targetPort: 8080
}]
}
})
// This Watcher triggers a callback when the deployment object changes.
watch(service, (newVal, oldVal) => {
emit('updateCode', JSON.stringify(newVal, null, 2))
})
const project = useProjectStore()
const service = reactive(project.documents[project.selectedDocument])
</script>

<template>
<h2 class="text-4xl font-bold text-gray-800 py-4">Service</h2>
<p>An abstract way to expose an application running on a set of Pods as a network service.</p>
<div class="md:flex md:flex-wrap">
<div class="md:w-1/2 md:pr-4">
<div>
<label>Service name:</label>
<input v-model="service.metadata.name"
class="appearance-none bg-gray-200 w-full py-2 px-4 block rounded text-gray-800 focus:outline-none focus:shadow-outline">
<div class="p-4" aria-label="Service Properties">
<h2 class="text-4xl font-bold text-gray-800 py-4">Service</h2>
<p>An abstract way to expose an application running on a set of Pods as a network service.</p>
<div class="md:flex md:flex-wrap">
<div class="md:w-1/2 md:pr-4">
<div>
<label>Service name:</label>
<input v-model="service.metadata.name"
class="appearance-none bg-gray-200 w-full py-2 px-4 block rounded text-gray-800 focus:outline-none focus:shadow-outline">
</div>
</div>
</div>
</div>
Expand Down
27 changes: 13 additions & 14 deletions src/router/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
}
// {
// path: '/',
// name: 'home',
// component: HomeView
// },
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (About.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import('../views/AboutView.vue')
// }
]
})

Expand Down
10 changes: 10 additions & 0 deletions src/stores/preferences.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineStore } from 'pinia'

export const usePreferencesStore = defineStore({
id: 'preferences',
state: () => ({
theme: 'dark',
showSidebar: true,
codeViewLanguage: 'yaml',
}),
})
Loading

0 comments on commit 8720005

Please sign in to comment.