mirror of
https://github.com/semaphoreui/semaphore.git
synced 2024-11-21 17:01:04 +01:00
project runners (#2547)
* feat: add feature flag * feat(runners): pro runners ui * feat(runner): api for project level * fix(ui): key of list * feat(be): add mocks for project runners * feat: pro alert * feat(pro): upgrade button * feat(pro): upgrade button * feat(pro): icon and color * feat(pro): change color
This commit is contained in:
parent
cd0755f5a9
commit
4996728daa
51
api/projects/runners.go
Normal file
51
api/projects/runners.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package projects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/context"
|
||||||
|
"github.com/semaphoreui/semaphore/api/helpers"
|
||||||
|
"github.com/semaphoreui/semaphore/db"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRunners(w http.ResponseWriter, r *http.Request) {
|
||||||
|
project := context.Get(r, "project").(db.Project)
|
||||||
|
runners, err := helpers.Store(r).GetRunners(project.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = make([]db.Runner, 0)
|
||||||
|
|
||||||
|
for _, runner := range runners {
|
||||||
|
result = append(result, runner)
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers.WriteJSON(w, http.StatusOK, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRunner(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunnerMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRunner(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateRunner(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteRunner(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRunnerActive(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
@ -12,13 +12,13 @@ import (
|
|||||||
|
|
||||||
"github.com/semaphoreui/semaphore/api/runners"
|
"github.com/semaphoreui/semaphore/api/runners"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"github.com/semaphoreui/semaphore/api/helpers"
|
"github.com/semaphoreui/semaphore/api/helpers"
|
||||||
"github.com/semaphoreui/semaphore/api/projects"
|
"github.com/semaphoreui/semaphore/api/projects"
|
||||||
"github.com/semaphoreui/semaphore/api/sockets"
|
"github.com/semaphoreui/semaphore/api/sockets"
|
||||||
"github.com/semaphoreui/semaphore/api/tasks"
|
"github.com/semaphoreui/semaphore/api/tasks"
|
||||||
"github.com/semaphoreui/semaphore/db"
|
"github.com/semaphoreui/semaphore/db"
|
||||||
"github.com/semaphoreui/semaphore/util"
|
"github.com/semaphoreui/semaphore/util"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var startTime = time.Now().UTC()
|
var startTime = time.Now().UTC()
|
||||||
@ -222,6 +222,16 @@ func Route() *mux.Router {
|
|||||||
projectUserAPI.Path("/integrations").HandlerFunc(projects.AddIntegration).Methods("POST")
|
projectUserAPI.Path("/integrations").HandlerFunc(projects.AddIntegration).Methods("POST")
|
||||||
projectUserAPI.Path("/backup").HandlerFunc(projects.GetBackup).Methods("GET", "HEAD")
|
projectUserAPI.Path("/backup").HandlerFunc(projects.GetBackup).Methods("GET", "HEAD")
|
||||||
|
|
||||||
|
projectUserAPI.Path("/runners").HandlerFunc(projects.GetRunners).Methods("GET", "HEAD")
|
||||||
|
projectUserAPI.Path("/runners").HandlerFunc(projects.AddRunner).Methods("POST")
|
||||||
|
|
||||||
|
projectRunnersAPI := projectUserAPI.PathPrefix("/runners").Subrouter()
|
||||||
|
projectRunnersAPI.Use(globalRunnerMiddleware)
|
||||||
|
projectRunnersAPI.Path("/{runner_id}").HandlerFunc(projects.GetRunner).Methods("GET", "HEAD")
|
||||||
|
projectRunnersAPI.Path("/{runner_id}").HandlerFunc(projects.UpdateRunner).Methods("PUT", "POST")
|
||||||
|
projectRunnersAPI.Path("/{runner_id}/active").HandlerFunc(projects.SetRunnerActive).Methods("POST")
|
||||||
|
projectRunnersAPI.Path("/{runner_id}").HandlerFunc(projects.DeleteRunner).Methods("DELETE")
|
||||||
|
|
||||||
//
|
//
|
||||||
// Updating and deleting project
|
// Updating and deleting project
|
||||||
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()
|
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()
|
||||||
@ -475,6 +485,10 @@ func getSystemInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
"ansible": util.AnsibleVersion(),
|
"ansible": util.AnsibleVersion(),
|
||||||
"web_host": host,
|
"web_host": host,
|
||||||
"use_remote_runner": util.Config.UseRemoteRunner,
|
"use_remote_runner": util.Config.UseRemoteRunner,
|
||||||
|
|
||||||
|
"premium_features": map[string]bool{
|
||||||
|
"project_runners": false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
helpers.WriteJSON(w, http.StatusOK, body)
|
helpers.WriteJSON(w, http.StatusOK, body)
|
||||||
|
@ -9,14 +9,6 @@ import (
|
|||||||
"github.com/gorilla/context"
|
"github.com/gorilla/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
//type minimalGlobalRunner struct {
|
|
||||||
// ID int `json:"id"`
|
|
||||||
// Name string `json:"name"`
|
|
||||||
// Active bool `json:"active"`
|
|
||||||
// Webhook string `db:"webhook" json:"webhook"`
|
|
||||||
// MaxParallelTasks int `db:"max_parallel_tasks" json:"max_parallel_tasks"`
|
|
||||||
//}
|
|
||||||
|
|
||||||
func getGlobalRunners(w http.ResponseWriter, r *http.Request) {
|
func getGlobalRunners(w http.ResponseWriter, r *http.Request) {
|
||||||
runners, err := helpers.Store(r).GetGlobalRunners(false)
|
runners, err := helpers.Store(r).GetGlobalRunners(false)
|
||||||
|
|
||||||
|
@ -528,6 +528,7 @@
|
|||||||
:isAdmin="(user || {}).admin"
|
:isAdmin="(user || {}).admin"
|
||||||
:webHost="(systemInfo || {}).web_host"
|
:webHost="(systemInfo || {}).web_host"
|
||||||
:version="(systemInfo || {version: ''}).version.split('-')[0]"
|
:version="(systemInfo || {version: ''}).version.split('-')[0]"
|
||||||
|
:premiumFeatures="((systemInfo || {premium_features: {}}).premium_features)"
|
||||||
:user="user"
|
:user="user"
|
||||||
></router-view>
|
></router-view>
|
||||||
</v-main>
|
</v-main>
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
'rgba(200, 200, 200, 0.38)' :
|
'rgba(200, 200, 200, 0.38)' :
|
||||||
'rgba(0, 0, 0, 0.38)'
|
'rgba(0, 0, 0, 0.38)'
|
||||||
}">
|
}">
|
||||||
<legend style="padding: 0 3px;">{{ $t('Args') }}</legend>
|
<legend style="padding: 0 3px;">{{ title || $t('Args') }}</legend>
|
||||||
<v-chip-group column style="margin-top: -4px;">
|
<v-chip-group column style="margin-top: -4px;">
|
||||||
<v-chip
|
<v-chip
|
||||||
v-for="(v, i) in modifiedVars"
|
v-for="(v, i) in modifiedVars"
|
||||||
@ -89,6 +89,7 @@
|
|||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
vars: Array,
|
vars: Array,
|
||||||
|
title: String,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
vars(val) {
|
vars(val) {
|
||||||
|
42
web/src/components/DashboardMenu.vue
Normal file
42
web/src/components/DashboardMenu.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<v-tabs show-arrows class="pl-4">
|
||||||
|
<v-tab
|
||||||
|
v-if="projectType === ''"
|
||||||
|
key="history"
|
||||||
|
:to="`/project/${projectId}/history`"
|
||||||
|
>{{ $t('history') }}
|
||||||
|
</v-tab>
|
||||||
|
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||||
|
<v-tab
|
||||||
|
v-if="canUpdateProject"
|
||||||
|
key="settings"
|
||||||
|
:to="`/project/${projectId}/settings`"
|
||||||
|
>{{ $t('settings') }}
|
||||||
|
</v-tab>
|
||||||
|
<v-tab key="runners" :to="`/project/${projectId}/runners`">
|
||||||
|
{{ $t('runners') }}
|
||||||
|
<!-- <v-chip small class="ml-1" color="purple" style="color: white">Pro</v-chip> -->
|
||||||
|
<v-icon class="ml-1" large color="hsl(348deg, 86%, 61%)">mdi-professional-hexagon</v-icon>
|
||||||
|
</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import PermissionsCheck from '@/components/PermissionsCheck';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
mixins: [PermissionsCheck],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
projectId: Number,
|
||||||
|
projectType: String,
|
||||||
|
canUpdateProject: Boolean,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -59,7 +59,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getItemsUrl() {
|
getItemsUrl() {
|
||||||
return '/api/projects/restore';
|
return '/api/project/restore';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -274,6 +274,7 @@
|
|||||||
<ArgsPicker
|
<ArgsPicker
|
||||||
:vars="args"
|
:vars="args"
|
||||||
@change="setArgs"
|
@change="setArgs"
|
||||||
|
title="CLI args"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-checkbox
|
<v-checkbox
|
||||||
|
@ -301,7 +301,7 @@ export default {
|
|||||||
taskLocation: 'Location',
|
taskLocation: 'Location',
|
||||||
empty: 'Empty',
|
empty: 'Empty',
|
||||||
noValues: 'No values',
|
noValues: 'No values',
|
||||||
addArg: 'Add Arg',
|
addArg: 'Add arg',
|
||||||
|
|
||||||
status_success: 'Success',
|
status_success: 'Success',
|
||||||
status_failed: 'Failed',
|
status_failed: 'Failed',
|
||||||
|
@ -44,6 +44,10 @@ const routes = [
|
|||||||
path: '/project/:projectId/activity',
|
path: '/project/:projectId/activity',
|
||||||
component: Activity,
|
component: Activity,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/project/:projectId/runners',
|
||||||
|
component: Runners,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/project/:projectId/schedule',
|
path: '/project/:projectId/schedule',
|
||||||
component: Schedule,
|
component: Schedule,
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
<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="items != null">
|
<div v-if="items != null">
|
||||||
|
|
||||||
|
<v-toolbar flat >
|
||||||
|
<v-app-bar-nav-icon @click="showDrawer()"></v-app-bar-nav-icon>
|
||||||
|
<v-toolbar-title>
|
||||||
|
{{ $t('dashboard2') }}
|
||||||
|
</v-toolbar-title>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<DashboardMenu
|
||||||
|
:project-id="projectId"
|
||||||
|
project-type=""
|
||||||
|
:can-update-project="can(USER_PERMISSIONS.updateProject)"
|
||||||
|
/>
|
||||||
|
|
||||||
<EditDialog
|
<EditDialog
|
||||||
v-model="editDialog"
|
v-model="editDialog"
|
||||||
:save-button-text="itemId === 'new' ? $t('create') : $t('save')"
|
:save-button-text="itemId === 'new' ? $t('create') : $t('save')"
|
||||||
@ -101,7 +115,7 @@
|
|||||||
@yes="deleteItem(itemId)"
|
@yes="deleteItem(itemId)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-toolbar flat>
|
<v-toolbar flat v-if="!projectId">
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
class="mr-4"
|
class="mr-4"
|
||||||
@ -109,6 +123,7 @@
|
|||||||
>
|
>
|
||||||
<v-icon>mdi-arrow-left</v-icon>
|
<v-icon>mdi-arrow-left</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-toolbar-title>{{ $t('runners') }}</v-toolbar-title>
|
<v-toolbar-title>{{ $t('runners') }}</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -118,6 +133,21 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
|
<v-alert
|
||||||
|
v-if="!premiumFeatures.project_runners"
|
||||||
|
type="info"
|
||||||
|
text
|
||||||
|
color="hsl(348deg, 86%, 61%)"
|
||||||
|
style="border-radius: 0;"
|
||||||
|
>
|
||||||
|
Project Runners available only in <b>PRO</b> version.
|
||||||
|
<v-btn
|
||||||
|
class="ml-2"
|
||||||
|
color="hsl(348deg, 86%, 61%)"
|
||||||
|
href="https://semaphoreui.com/pro"
|
||||||
|
>Upgrade</v-btn>
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
<v-data-table
|
<v-data-table
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:items="items"
|
:items="items"
|
||||||
@ -169,11 +199,13 @@ import ItemListPageBase from '@/components/ItemListPageBase';
|
|||||||
import EditDialog from '@/components/EditDialog.vue';
|
import EditDialog from '@/components/EditDialog.vue';
|
||||||
import RunnerForm from '@/components/RunnerForm.vue';
|
import RunnerForm from '@/components/RunnerForm.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [ItemListPageBase],
|
mixins: [ItemListPageBase],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
DashboardMenu,
|
||||||
RunnerForm,
|
RunnerForm,
|
||||||
YesNoDialog,
|
YesNoDialog,
|
||||||
EditDialog,
|
EditDialog,
|
||||||
@ -182,6 +214,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
webHost: String,
|
webHost: String,
|
||||||
version: String,
|
version: String,
|
||||||
|
projectId: Number,
|
||||||
|
premiumFeatures: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
@ -269,10 +303,18 @@ semaphore runner --no-config`;
|
|||||||
},
|
},
|
||||||
|
|
||||||
getItemsUrl() {
|
getItemsUrl() {
|
||||||
|
if (this.projectId) {
|
||||||
|
return `/api/project/${this.projectId}/runners`;
|
||||||
|
}
|
||||||
|
|
||||||
return '/api/runners';
|
return '/api/runners';
|
||||||
},
|
},
|
||||||
|
|
||||||
getSingleItemUrl() {
|
getSingleItemUrl() {
|
||||||
|
if (this.projectId) {
|
||||||
|
return `/api/project/${this.projectId}/runners/${this.itemId}`;
|
||||||
|
}
|
||||||
|
|
||||||
return `/api/runners/${this.itemId}`;
|
return `/api/runners/${this.itemId}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -5,21 +5,11 @@
|
|||||||
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
<v-tabs show-arrows class="pl-4">
|
<DashboardMenu
|
||||||
<v-tab
|
:project-id="projectId"
|
||||||
v-if="projectType === ''"
|
project-type=""
|
||||||
key="history"
|
:can-update-project="can(USER_PERMISSIONS.updateProject)"
|
||||||
:to="`/project/${projectId}/history`"
|
/>
|
||||||
>{{ $t('history') }}</v-tab>
|
|
||||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
|
||||||
<v-tab
|
|
||||||
v-if="can(USER_PERMISSIONS.updateProject)"
|
|
||||||
key="settings"
|
|
||||||
:to="`/project/${projectId}/settings`"
|
|
||||||
>
|
|
||||||
{{ $t('settings') }}
|
|
||||||
</v-tab>
|
|
||||||
</v-tabs>
|
|
||||||
|
|
||||||
<v-data-table
|
<v-data-table
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
@ -35,8 +25,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ItemListPageBase from '@/components/ItemListPageBase';
|
import ItemListPageBase from '@/components/ItemListPageBase';
|
||||||
|
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { DashboardMenu },
|
||||||
|
|
||||||
mixins: [ItemListPageBase],
|
mixins: [ItemListPageBase],
|
||||||
|
|
||||||
|
@ -1,26 +1,17 @@
|
|||||||
<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="items != null">
|
<div v-if="items != null">
|
||||||
<v-toolbar flat >
|
<v-toolbar flat>
|
||||||
<v-app-bar-nav-icon @click="showDrawer()"></v-app-bar-nav-icon>
|
<v-app-bar-nav-icon @click="showDrawer()"></v-app-bar-nav-icon>
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
{{ $t('dashboard2') }}
|
{{ $t('dashboard2') }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
<v-tabs show-arrows class="pl-4">
|
<DashboardMenu
|
||||||
<v-tab
|
:project-id="projectId"
|
||||||
v-if="projectType === ''"
|
project-type=""
|
||||||
key="history"
|
:can-update-project="can(USER_PERMISSIONS.updateProject)"
|
||||||
:to="`/project/${projectId}/history`"
|
/>
|
||||||
>{{ $t('history') }}</v-tab>
|
|
||||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
|
||||||
<v-tab
|
|
||||||
v-if="can(USER_PERMISSIONS.updateProject)"
|
|
||||||
key="settings"
|
|
||||||
:to="`/project/${projectId}/settings`"
|
|
||||||
>{{ $t('settings') }}
|
|
||||||
</v-tab>
|
|
||||||
</v-tabs>
|
|
||||||
|
|
||||||
<v-data-table
|
<v-data-table
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
@ -102,6 +93,7 @@ import TaskLink from '@/components/TaskLink.vue';
|
|||||||
import socket from '@/socket';
|
import socket from '@/socket';
|
||||||
import { TEMPLATE_TYPE_ICONS } from '@/lib/constants';
|
import { TEMPLATE_TYPE_ICONS } from '@/lib/constants';
|
||||||
import AppsMixin from '@/components/AppsMixin';
|
import AppsMixin from '@/components/AppsMixin';
|
||||||
|
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [ItemListPageBase, AppsMixin],
|
mixins: [ItemListPageBase, AppsMixin],
|
||||||
@ -110,7 +102,7 @@ export default {
|
|||||||
return { TEMPLATE_TYPE_ICONS };
|
return { TEMPLATE_TYPE_ICONS };
|
||||||
},
|
},
|
||||||
|
|
||||||
components: { TaskStatus, TaskLink },
|
components: { DashboardMenu, TaskStatus, TaskLink },
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
async projectId() {
|
async projectId() {
|
||||||
|
@ -12,15 +12,11 @@
|
|||||||
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
<v-tabs show-arrows class="pl-4">
|
<DashboardMenu
|
||||||
<v-tab
|
:project-id="projectId"
|
||||||
v-if="projectType === ''"
|
project-type=""
|
||||||
key="history"
|
:can-update-project="true"
|
||||||
:to="`/project/${projectId}/history`"
|
/>
|
||||||
>{{ $t('history') }}</v-tab>
|
|
||||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
|
||||||
<v-tab key="settings" :to="`/project/${projectId}/settings`">{{ $t('settings') }}</v-tab>
|
|
||||||
</v-tabs>
|
|
||||||
|
|
||||||
<div class="project-settings-form">
|
<div class="project-settings-form">
|
||||||
<div style="height: 300px;">
|
<div style="height: 300px;">
|
||||||
@ -107,9 +103,10 @@ import { getErrorMessage } from '@/lib/error';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import YesNoDialog from '@/components/YesNoDialog.vue';
|
import YesNoDialog from '@/components/YesNoDialog.vue';
|
||||||
import delay from '@/lib/delay';
|
import delay from '@/lib/delay';
|
||||||
|
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { YesNoDialog, ProjectForm },
|
components: { DashboardMenu, YesNoDialog, ProjectForm },
|
||||||
props: {
|
props: {
|
||||||
projectId: Number,
|
projectId: Number,
|
||||||
projectType: String,
|
projectType: String,
|
||||||
|
Loading…
Reference in New Issue
Block a user