mirror of
https://github.com/semaphoreui/semaphore.git
synced 2025-01-22 00:00:42 +01:00
feat(web2): add templates page
This commit is contained in:
parent
c855bf1f6a
commit
669a8d0128
@ -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
web2/src/components/TemplateEditDialog.vue
Normal file
0
web2/src/components/TemplateEditDialog.vue
Normal file
@ -1,7 +1,7 @@
|
|||||||
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
|
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
|
||||||
<div v-if="users != null">
|
<div v-if="items != null">
|
||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="deleteUserDialog"
|
v-model="deleteItemDialog"
|
||||||
max-width="290">
|
max-width="290">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title class="headline">Delete template</v-card-title>
|
<v-card-title class="headline">Delete template</v-card-title>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<v-btn
|
<v-btn
|
||||||
color="blue darken-1"
|
color="blue darken-1"
|
||||||
flat="flat"
|
flat="flat"
|
||||||
@click="deleteUserDialog = false"
|
@click="deleteItemDialog = false"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@ -33,84 +33,44 @@
|
|||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="userDialog"
|
v-model="itemDialog"
|
||||||
max-width="290"
|
max-width="290"
|
||||||
persistent
|
persistent
|
||||||
>
|
>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title class="headline">{{ isNewUser ? 'New user' : 'Edit user' }}</v-card-title>
|
<v-card-title class="headline">
|
||||||
|
{{ isNewItem ? 'New Template' : 'Edit Template' }}
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
<v-alert
|
<v-alert
|
||||||
:value="userFormError"
|
:value="itemFormError"
|
||||||
color="error"
|
color="error"
|
||||||
>
|
>
|
||||||
{{ userFormError }}
|
{{ itemFormError }}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form
|
<v-form
|
||||||
ref="userForm"
|
ref="itemForm"
|
||||||
lazy-validation
|
lazy-validation
|
||||||
v-model="userFormValid"
|
v-model="itemFormValid"
|
||||||
>
|
>
|
||||||
<div style="display: none">
|
|
||||||
<input type="password" tabindex="-1"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-text-field
|
|
||||||
v-model="user.username"
|
|
||||||
label="Username"
|
|
||||||
:rules="[v => !!v || 'Username is required']"
|
|
||||||
required
|
|
||||||
:disabled="!isNewUser || userFormSaving"
|
|
||||||
></v-text-field>
|
|
||||||
|
|
||||||
<v-text-field
|
|
||||||
v-model="user.email"
|
|
||||||
label="Email"
|
|
||||||
:rules="[v => !!v || 'Email is required']"
|
|
||||||
required
|
|
||||||
:disabled="userFormSaving"
|
|
||||||
></v-text-field>
|
|
||||||
|
|
||||||
<v-text-field
|
|
||||||
v-model="user.fullName"
|
|
||||||
label="Full Name"
|
|
||||||
:rules="[v => !!v || 'Full Name is required']"
|
|
||||||
required
|
|
||||||
:disabled="userFormSaving"
|
|
||||||
></v-text-field>
|
|
||||||
|
|
||||||
<v-select
|
|
||||||
:items="userTypes"
|
|
||||||
v-model="user.type"
|
|
||||||
label="Type"
|
|
||||||
:disabled="userFormSaving"
|
|
||||||
></v-select>
|
|
||||||
|
|
||||||
<v-text-field
|
|
||||||
v-model="user.password"
|
|
||||||
label="Password"
|
|
||||||
type="password"
|
|
||||||
:rules="[v => !v || v.length === 0 || v.length >= 6 || 'Min 6 characters']"
|
|
||||||
:disabled="userFormSaving"
|
|
||||||
></v-text-field>
|
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn
|
<v-btn
|
||||||
color="blue darken-1"
|
color="blue darken-1"
|
||||||
flat="flat"
|
text
|
||||||
:disabled="userFormSaving"
|
:disabled="itemFormSaving"
|
||||||
@click="userDialog = false"
|
@click="itemDialog = false"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
color="blue darken-1"
|
color="blue darken-1"
|
||||||
flat="flat"
|
text
|
||||||
:disabled="userFormSaving"
|
:disabled="itemFormSaving"
|
||||||
@click="saveUser"
|
@click="saveUser"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
@ -120,9 +80,9 @@
|
|||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
<v-toolbar flat color="white">
|
<v-toolbar flat color="white">
|
||||||
<v-toolbar-title>Users</v-toolbar-title>
|
<v-toolbar-title>Task Templates</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="primary" @click="addUser">New User</v-btn>
|
<v-btn color="primary" @click="addUser">New template</v-btn>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
@ -130,8 +90,8 @@
|
|||||||
<v-container>
|
<v-container>
|
||||||
<v-data-table
|
<v-data-table
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:items="users"
|
:items="items"
|
||||||
hide-actions
|
hide-default-footer
|
||||||
>
|
>
|
||||||
<template v-slot:items="props">
|
<template v-slot:items="props">
|
||||||
<td>{{ props.item.username }}</td>
|
<td>{{ props.item.username }}</td>
|
||||||
@ -167,51 +127,59 @@ import EventBus from '@/event-bus';
|
|||||||
import { getErrorMessage } from '@/lib/error';
|
import { getErrorMessage } from '@/lib/error';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
props: {
|
||||||
|
projectId: Number,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
userTypes: ['admin', 'user'],
|
|
||||||
headers: [
|
headers: [
|
||||||
{
|
{
|
||||||
text: 'Username',
|
text: 'Alias',
|
||||||
value: 'username',
|
value: 'alias',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Full Name',
|
text: 'Playbook',
|
||||||
value: 'fullName',
|
value: 'playbook',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Email',
|
text: 'SSH key',
|
||||||
value: 'email',
|
value: 'email',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Type',
|
text: 'Inventory',
|
||||||
value: 'type',
|
value: 'inventory',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '',
|
text: 'Environment',
|
||||||
|
value: 'environment',
|
||||||
|
sortable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Repository',
|
||||||
|
value: 'repository',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
users: null,
|
items: null,
|
||||||
|
|
||||||
user: {},
|
item: {},
|
||||||
isNewUser: false,
|
isNewItem: false,
|
||||||
|
|
||||||
userDialog: false,
|
itemDialog: false,
|
||||||
userFormValid: false,
|
itemFormValid: false,
|
||||||
userFormError: null,
|
itemFormError: null,
|
||||||
userFormSaving: false,
|
itemFormSaving: false,
|
||||||
username: '',
|
username: '',
|
||||||
fullName: '',
|
fullName: '',
|
||||||
email: '',
|
email: '',
|
||||||
type: '',
|
type: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
|
||||||
deleteUserDialog: false,
|
deleteItemDialog: false,
|
||||||
deleteUsername: null,
|
deleteItemId: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -221,26 +189,26 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
askDeleteUser(username) {
|
askDeleteUser(username) {
|
||||||
this.deleteUsername = username;
|
this.deleteItemId = username;
|
||||||
this.deleteUserDialog = true;
|
this.deleteItemDialog = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteUser() {
|
async deleteUser() {
|
||||||
try {
|
try {
|
||||||
await axios({
|
await axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: `/api/users/${this.deleteUsername}`,
|
url: `/api/project/${this.projectId}/${this.deleteItemId}`,
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
});
|
});
|
||||||
|
|
||||||
const userIndex = this.users.findIndex((user) => user.username === this.deleteUsername);
|
const userIndex = this.items.findIndex((item) => item.username === this.deleteItemId);
|
||||||
if (userIndex !== -1) {
|
if (userIndex !== -1) {
|
||||||
this.users.splice(userIndex, 1);
|
this.items.splice(userIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBus.$emit('i-snackbar', {
|
EventBus.$emit('i-snackbar', {
|
||||||
color: 'success',
|
color: 'success',
|
||||||
text: `User "${this.deleteUsername}" deleted`,
|
text: `User "${this.deleteItemId}" deleted`,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
EventBus.$emit('i-snackbar', {
|
EventBus.$emit('i-snackbar', {
|
||||||
@ -248,7 +216,7 @@ export default {
|
|||||||
text: getErrorMessage(err),
|
text: getErrorMessage(err),
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
this.deleteUserDialog = false;
|
this.deleteItemDialog = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -257,61 +225,61 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async editUser(username) {
|
async editUser(username) {
|
||||||
this.isNewUser = !username;
|
this.isNewItem = !username;
|
||||||
this.userFormError = null;
|
this.itemFormError = null;
|
||||||
|
|
||||||
this.user = username ? (await axios({
|
this.item = username ? (await axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `/api/templates/${username}`,
|
url: `/api/project/${this.projectId}/templates/${username}`,
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
})).data : { type: 'user' };
|
})).data : { type: 'item' };
|
||||||
|
this.itemDialog = true;
|
||||||
this.$refs.userForm.resetValidation();
|
|
||||||
this.userDialog = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveUser() {
|
async saveUser() {
|
||||||
this.userFormError = null;
|
this.itemFormError = null;
|
||||||
|
|
||||||
if (!this.$refs.userForm.validate()) {
|
if (!this.$refs.itemForm.validate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userFormSaving = true;
|
this.itemFormSaving = true;
|
||||||
try {
|
try {
|
||||||
await axios({
|
await axios({
|
||||||
method: this.isNewUser ? 'post' : 'put',
|
method: this.isNewItem ? 'post' : 'put',
|
||||||
url: this.isNewUser ? '/api/templates' : `/api/templates/${this.user.id}`,
|
url: this.isNewItem
|
||||||
|
? `/api/project/${this.projectId}/templates`
|
||||||
|
: `/api/project/${this.projectId}/templates/${this.item.id}`,
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
data: this.user,
|
data: this.item,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.isNewUser) {
|
if (this.isNewItem) {
|
||||||
this.users.push(this.user);
|
this.items.push(this.item);
|
||||||
} else {
|
} else {
|
||||||
const userIndex = this.users.findIndex((user) => this.user.id === user.id);
|
const userIndex = this.items.findIndex((item) => this.item.id === item.id);
|
||||||
if (userIndex !== -1) {
|
if (userIndex !== -1) {
|
||||||
this.users.splice(userIndex, 1, this.user);
|
this.items.splice(userIndex, 1, this.item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userDialog = false;
|
this.itemDialog = false;
|
||||||
|
|
||||||
EventBus.$emit('i-snackbar', {
|
EventBus.$emit('i-snackbar', {
|
||||||
color: 'success',
|
color: 'success',
|
||||||
text: this.isNewUser ? `User "${this.user.username}" created` : `User "${this.user.username}" changed`,
|
text: this.isNewItem ? `User "${this.item.username}" created` : `User "${this.item.username}" changed`,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.userFormError = getErrorMessage(err);
|
this.itemFormError = getErrorMessage(err);
|
||||||
} finally {
|
} finally {
|
||||||
this.userFormSaving = false;
|
this.itemFormSaving = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadUsers() {
|
async loadUsers() {
|
||||||
this.users = (await axios({
|
this.items = (await axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/templates',
|
url: `/api/project/${this.projectId}/templates`,
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
})).data;
|
})).data;
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user