Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions admin/frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ declare module 'vue' {
FilePickerField: typeof import('./src/components/FilePickerField.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
StatusBadge: typeof import('./src/components/StatusBadge.vue')['default']
TerminalOutput: typeof import('./src/components/TerminalOutput.vue')['default']
}
}
28 changes: 28 additions & 0 deletions admin/frontend/src/components/StatusBadge.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script setup>
import { computed } from 'vue'
import { Badge } from 'frappe-ui'

const props = defineProps({
variant: { type: String, default: 'badge-neutral' },
label: { type: String, default: '' },
})

// semantic variants → frappe-ui Badge theme
const VARIANT_THEME = {
'badge-success': 'green',
'badge-warning': 'orange',
'badge-error': 'red',
'badge-neutral': 'gray',
'badge-running': 'blue',
}

const theme = computed(() => VARIANT_THEME[props.variant] ?? 'gray')
</script>

<template>
<Badge :theme="theme" variant="subtle" size="sm" :label="label">
<template v-if="variant === 'badge-running'" #prefix>
<span class="pulse-dot"></span>
</template>
</Badge>
</template>
13 changes: 13 additions & 0 deletions admin/frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,16 @@ html {
.dialog-overlay {
z-index: 50;
}

/* Reusable pulsing dot — indicates live / in-progress state */
.pulse-dot {
@apply relative inline-flex h-2 w-2;
}
.pulse-dot::before {
content: '';
@apply absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-75;
}
.pulse-dot::after {
content: '';
@apply relative inline-flex h-2 w-2 rounded-full bg-blue-500;
}
3 changes: 2 additions & 1 deletion admin/frontend/src/pages/Apps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Button, Badge, Dialog, ListView, FormControl,
LoadingText, ErrorMessage, TextInput, Select,
} from 'frappe-ui'
import StatusBadge from '../components/StatusBadge.vue'

const router = useRouter()
const apps = ref([])
Expand Down Expand Up @@ -81,7 +82,7 @@ const columns = computed(() => [
},
{
label: 'Status', key: '_status', width: '90px',
prefix: ({ row }) => h(Badge, { label: row._status, theme: row._status === 'dirty' ? 'orange' : 'gray' }),
prefix: ({ row }) => h(StatusBadge, { label: row._status, variant: row._status === 'dirty' ? 'badge-warning' : 'badge-neutral' }),
getLabel: () => '',
},
{ label: 'Version', key: 'installed_version', width: '90px' },
Expand Down
14 changes: 10 additions & 4 deletions admin/frontend/src/pages/Processes.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { Badge, ListView, Button, LoadingText, ErrorMessage } from 'frappe-ui'
import { ListView, Button, LoadingText, ErrorMessage } from 'frappe-ui'
import StatusBadge from '../components/StatusBadge.vue'

const processes = ref([])
const loading = ref(true)
Expand All @@ -12,7 +13,12 @@ let countdown = 15
let timer

const router = useRouter()
const STATUS_COLOR = { running: 'green', stopped: 'red', error: 'red', unknown: 'gray' }
const STATUS_COLOR = {
running: 'badge-running',
stopped: 'badge-error',
error: 'badge-error',
unknown: 'badge-neutral',
}

function openLog(filename) {
router.push(`/logs/${filename}`)
Expand Down Expand Up @@ -71,10 +77,10 @@ onUnmounted(() => clearInterval(timer))
:options="{ selectable: false, showTooltip: false }"
>
<template #cell="{ column, item }">
<Badge
<StatusBadge
v-if="column.key === 'status'"
:label="item"
:theme="STATUS_COLOR[item] || 'gray'"
:variant="STATUS_COLOR[item] || 'badge-neutral'"
/>
<button
v-else-if="column.key === 'log_filename' && item"
Expand Down
7 changes: 4 additions & 3 deletions admin/frontend/src/pages/SiteDetail.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Button, Badge, Dialog, FormControl, LoadingText, ErrorMessage, Tabs } from 'frappe-ui'
import { Button, Dialog, FormControl, LoadingText, ErrorMessage, Tabs } from 'frappe-ui'
import LucideDatabase from '~icons/lucide/database'
import LucideServer from '~icons/lucide/server'
import StatusBadge from '../components/StatusBadge.vue'

const route = useRoute()
const router = useRouter()
Expand Down Expand Up @@ -337,9 +338,9 @@ onMounted(() => { load(); loadRegistry() })
<div class="flex flex-col gap-1.5">
<div class="flex items-center gap-2">
<h1 class="text-2xl font-semibold text-ink-gray-9">{{ siteName }}</h1>
<Badge
<StatusBadge
:label="site.exists ? 'Online' : 'Offline'"
:theme="site.exists ? 'green' : 'gray'"
:variant="site.exists ? 'badge-success' : 'badge-neutral'"
/>
<Badge v-if="site.site_config?.ssl" label="SSL" theme="blue" />
</div>
Expand Down
3 changes: 2 additions & 1 deletion admin/frontend/src/pages/Sites.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { h, ref, computed, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { Button, Badge, Dialog, ListView, FormControl, LoadingText, ErrorMessage, Switch, TabButtons } from 'frappe-ui'
import FilePickerField from '../components/FilePickerField.vue'
import StatusBadge from '../components/StatusBadge.vue'

const router = useRouter()
const sites = ref([])
Expand Down Expand Up @@ -49,7 +50,7 @@ const columns = computed(() => [
{ label: 'Name', key: 'name', width: '200px' },
{
label: 'Status', key: '_status', width: '80px',
prefix: ({ row }) => h(Badge, { label: row._status, theme: row._status === 'online' ? 'green' : 'gray' }),
prefix: ({ row }) => h(StatusBadge, { label: row._status, variant: row._status === 'online' ? 'badge-success' : 'badge-neutral' }),
getLabel: () => '',
},
{
Expand Down
15 changes: 11 additions & 4 deletions admin/frontend/src/pages/TaskDetail.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Button, Badge, Dialog, LoadingText, ErrorMessage } from 'frappe-ui'
import { Button, Dialog, LoadingText, ErrorMessage } from 'frappe-ui'
import TerminalOutput from '../components/TerminalOutput.vue'
import { processLine } from '../utils/ansi.js'
import StatusBadge from '../components/StatusBadge.vue'

const route = useRoute()
const router = useRouter()
Expand All @@ -20,7 +21,13 @@ const actionError = ref('')
let es = null
const terminal = ref(null)

const TASK_COLOR = { success: 'green', failed: 'red', running: 'blue', killed: 'gray' }
const TASK_STATUS_BADGE = {
running: 'badge-running',
success: 'badge-success',
failed: 'badge-error',
stopped: 'badge-error',
killed: 'badge-neutral',
}

function fmtDate(iso) {
if (!iso) return '—'
Expand Down Expand Up @@ -114,9 +121,9 @@ onUnmounted(() => { if (es) { es.close(); es = null } })

<!-- Header -->
<div class="flex flex-wrap items-center gap-3">
<Badge
<StatusBadge
:label="streaming ? 'running…' : task.status"
:theme="TASK_COLOR[task.status] || 'gray'"
:variant="streaming ? 'badge-running' : (TASK_STATUS_BADGE[task.status] || 'badge-neutral')"
/>
<span class="font-mono text-sm font-medium">{{ task.command }}</span>
<span
Expand Down
18 changes: 14 additions & 4 deletions admin/frontend/src/pages/TaskList.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
<script setup>
import { h, ref, computed, onMounted } from 'vue'
import { Badge, ListView, TabButtons, LoadingText, ErrorMessage } from 'frappe-ui'
import { ListView, TabButtons, LoadingText, ErrorMessage } from 'frappe-ui'
import StatusBadge from '../components/StatusBadge.vue'

const tasks = ref([])
const loading = ref(true)
const error = ref('')
const statusFilter = ref('all')

const TASK_COLOR = { success: 'green', failed: 'red', running: 'blue', killed: 'gray' }

const filterButtons = ['all', 'running', 'success', 'failed', 'killed'].map(s => ({
label: s.charAt(0).toUpperCase() + s.slice(1),
value: s,
}))

const TASK_STATUS_BADGE = {
running: 'badge-running',
success: 'badge-success',
failed: 'badge-error',
stopped: 'badge-error',
killed: 'badge-neutral',
}

const columns = [
{ label: 'Command', key: 'command', width: '140px' },
{ label: 'Context', key: '_args' },
{
label: 'Status', key: 'status', width: '90px',
prefix: ({ row }) => h(Badge, { label: row.status, theme: TASK_COLOR[row.status] || 'gray' }),
prefix: ({ row }) => h(StatusBadge, {
label: row.status,
variant: TASK_STATUS_BADGE[row.status] || 'badge-neutral',
}),
getLabel: () => '',
},
{ label: 'Started', key: '_started', width: '150px' },
Expand Down
Loading