Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions messages/en.context.json
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@
"bundle-not-found": "Used in Capgo web console areas: pages/app. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"bundle-not-found-description": "Used in Capgo web console areas: pages/app. Role: helper or description text. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"bundle-number": "Used in Capgo web console areas: components/tables, pages/app. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"bundle-range-placeholder": "Used in Capgo web console areas: components/tables. Role: input placeholder for a numeric bundle version such as 1.2.3. Keep the example version unchanged.",
"bundle-to-production-device": "Used in Capgo web console areas: pages/admin/dashboard. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"bundle-to-store-live": "Used in Capgo web console areas: pages/admin/dashboard. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"bundle-to-subscribed": "Used in Capgo web console areas: pages/admin/dashboard. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
Expand Down Expand Up @@ -1339,6 +1340,7 @@
"downgrade": "Used in Capgo web console areas: pages/settings/organization. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"download": "Used in Capgo web console areas: pages, pages/app. Role: button or action label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"download-csv": "Used in Capgo web console areas: components. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"download-json": "Used in Capgo web console areas: components. Role: button or action label for downloading device rows as JSON. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"draft": "Used in Capgo web console areas: pages/app. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"edit": "Used in Capgo web console areas: pages, pages/settings/organization. Role: button or action label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"edit-api-key": "Used in Capgo web console areas: pages. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
Expand Down Expand Up @@ -1447,8 +1449,11 @@
"expired": "Used in Capgo web console areas: pages, services. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"expires": "Used in Capgo web console areas: pages. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"expires-today": "Used in Capgo web console areas: pages/admin/dashboard. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"export": "Used in Capgo web console areas: components. Role: button or action label to export the current table. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"export-failed": "Used in Capgo web console areas: components/tables. Role: toast or status message. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"export-limit-reached": "Used in Capgo web console areas: components/tables. Role: toast after exporting the first {count} matching devices. Keep the {count} placeholder unchanged.",
"export-ready": "Used in Capgo web console areas: components/tables. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"exporting-devices": "Used in Capgo web console areas: components/tables. Role: loading toast while device export is in progress. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"exporting-logs": "Used in Capgo web console areas: components/tables. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"expose-metadata": "Used in Capgo web console areas: components/dashboard. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"expose-metadata-help": "Used in Capgo web console areas: components/dashboard. Role: helper or description text. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
Expand Down Expand Up @@ -2257,6 +2262,7 @@
"organizations-created": "Used in Capgo web console areas: pages/admin/dashboard. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"organizations-to-be-deleted": "Used in Capgo web console areas: pages/settings/account. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"os-version": "Used in Capgo web console areas: pages/app. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"os-version-placeholder": "Used in Capgo web console areas: components/tables. Role: input placeholder for an OS version such as 14 or 17.4. Keep the example versions unchanged.",
"overriding-default-channel": "Used in Capgo web console areas: pages/app. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"overview": "Used in Capgo web console areas: components/dashboard. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"package-name": "Used in Capgo web console areas: pages/app. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
Expand Down Expand Up @@ -3013,6 +3019,13 @@
"verify-password": "Used in Capgo web console areas: pages/settings/account. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"version": "Used in Capgo web console areas: components/bundle, components/dashboard, components/tables, pages/app, services. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"version-builtin": "Used in Capgo web console areas: pages/app. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"version-compare-eq": "Used in Capgo web console areas: components/tables. Role: operator option meaning the OS or bundle version equals the typed prefix. Translate the comparison meaning, not as a programming token.",
"version-compare-gt": "Used in Capgo web console areas: components/tables. Role: operator option meaning greater than. The visible UI is a > symbol; keep that symbol if it is understood locally.",
"version-compare-gte": "Used in Capgo web console areas: components/tables. Role: operator option meaning greater than or equal. The visible UI is a ≥ symbol; keep that symbol if it is understood locally.",
"version-compare-in": "Used in Capgo web console areas: components/tables. Role: operator option meaning the bundle is any of the selected names. Translate for UI.",
"version-compare-lt": "Used in Capgo web console areas: components/tables. Role: operator option meaning less than. The visible UI is a < symbol; keep that symbol if it is understood locally.",
"version-compare-lte": "Used in Capgo web console areas: components/tables. Role: operator option meaning less than or equal. The visible UI is a ≤ symbol; keep that symbol if it is understood locally.",
"version-compare-operator": "Used in Capgo web console areas: components/tables. Role: accessible name for the version comparison operator dropdown. Translate for UI.",
"version-count": "Used in Capgo web console areas: pages/app. Role: short UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"version-deleted-cannot-rollback": "Used in Capgo web console areas: components/tables. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
"version-name-missing": "Used in Capgo web console areas: components/tables. Role: UI label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.",
Expand Down
15 changes: 14 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@
"builds-period": "Builds in period",
"builds-trend": "Native Builds Trend (Total, iOS, Android)",
"bundle": "Bundle",
"bundle-range-placeholder": "1.2.3",
"bundle-active": "Active",
"bundle-adoption": "Bundle reach",
"bundle-adoption-devices": "{count} / {total} devices",
Expand Down Expand Up @@ -1338,6 +1339,7 @@
"download": "Download",
"draft": "Draft",
"download-csv": "Download CSV",
"download-json": "Download JSON",
"edit-role": "Edit role",
"edit-webhook": "Edit Webhook",
"edit-bundle-metadata": "Edit bundle metadata",
Expand Down Expand Up @@ -1436,8 +1438,11 @@
"expired": "Expired",
"expires": "Expires",
"expires-today": "Expires Today",
"export": "Export",
"export-failed": "Export failed",
"export-limit-reached": "Exported the first {count} matching devices",
"export-ready": "Export ready",
"exporting-devices": "Exporting devices...",
"exporting-logs": "Exporting logs...",
"expose-metadata": "Expose bundle metadata to plugin",
"expose-metadata-help": "When enabled, bundle link and comment fields will be sent to the Capacitor Updater plugin (requires plugin version 7.35.0 or higher)",
Expand All @@ -1460,7 +1465,7 @@
"fetching-release-data": "Fetching release data...",
"filter-actions": "Actions",
"filter-options": "Options",
"filter-modal-subtitle": "Narrow results by platform, bundle, or status.",
"filter-modal-subtitle": "Narrow results by platform, OS version, bundle, or status.",
"filter-logs-modal-subtitle": "Quickly switch between update process logs and observe events, or pick specific actions.",
"filter-shortcut-all-fail": "All failures",
"filter-shortcut-updates": "Update process",
Expand Down Expand Up @@ -2083,6 +2088,7 @@
"organizations": "Organizations",
"organizations-to-be-deleted": "Organizations to be deleted",
"os-version": "OS version",
"os-version-placeholder": "14 or 17.4",
"overriding-default-channel": "Overriding default channel",
"overview": "Overview",
"package-name": "Package Name",
Expand Down Expand Up @@ -2773,6 +2779,13 @@
"version": "Version",
"version-builtin": "Version builtin",
"version-deleted-cannot-rollback": "Bundle deleted, cannot rollback to it",
"version-compare-eq": "is",
"version-compare-gt": ">",
"version-compare-gte": "≥",
"version-compare-in": "is any of",
"version-compare-lt": "<",
"version-compare-lte": "≤",
"version-compare-operator": "Comparison",
"version-name-missing": "Version name is missing",
"version-rules-tester": "Version rules tester",
"version-rules-tester-description": "Learn how versions work and simulate update scenarios",
Expand Down
8 changes: 8 additions & 0 deletions playwright/visual-diff.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export const visualDiffRoutes: VisualDiffRoute[] = [
return
}
await page.locator('[data-test="device-platform-filter"]').waitFor({ state: 'visible' })
// Head has OS range filters; base from #2792 only has platform/bundle.
const osFilter = page.locator('[data-test="device-os-version-filter"]')
try {
await osFilter.waitFor({ state: 'visible', timeout: 3000 })
}
catch {
// Keep the platform/bundle modal screenshot on older bases.
}
},
},
{ slug: 'observe', path: '/app/com.demo.app/observe/updater', auth: true },
Expand Down
1 change: 1 addition & 0 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ declare module 'vue' {
UpdateStatsChart: typeof import('./components/dashboard/UpdateStatsChart.vue')['default']
Usage: typeof import('./components/dashboard/Usage.vue')['default']
UsageCard: typeof import('./components/dashboard/UsageCard.vue')['default']
VersionCompareField: typeof import('./components/tables/VersionCompareField.vue')['default']
WebhookDeliveryLog: typeof import('./components/WebhookDeliveryLog.vue')['default']
WebhookForm: typeof import('./components/WebhookForm.vue')['default']
WelcomeBanner: typeof import('./components/dashboard/WelcomeBanner.vue')['default']
Expand Down
100 changes: 100 additions & 0 deletions src/components/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useDebounceFn } from '@vueuse/core'
import DOMPurify from 'dompurify'
import {
computed,
nextTick,
onMounted,
onUnmounted,
ref,
Expand All @@ -23,6 +24,7 @@ import plusOutline from '~icons/ion/add-outline'
import IconSortDown from '~icons/lucide/chevron-down'
import IconSortUp from '~icons/lucide/chevron-up'
import IconSort from '~icons/lucide/chevrons-up-down'
import IconDownload from '~icons/lucide/download'
import IconFilter from '~icons/system-uicons/filtering'
import IconReload from '~icons/tabler/reload'
import FilterModal from '~/components/FilterModal.vue'
Expand All @@ -41,6 +43,8 @@ interface Props {
addDisabled?: boolean
addTooltip?: string
addButtonTestId?: string
exportable?: boolean
exportLoading?: boolean
search?: string
total: number
/** Fixed page size used for last-page / next calculations. Prefer this over inferring from the current page length. */
Expand All @@ -57,6 +61,8 @@ const props = withDefaults(defineProps<Props>(), {
autoReload: true,
mobileFixedPagination: true,
extraFilterCount: 0,
exportable: false,
exportLoading: false,
})
const emit = defineEmits([
'add',
Expand All @@ -74,11 +80,54 @@ const emit = defineEmits([
'selectRow',
'massDelete',
'clearExtraFilters',
'export',
])
const isFilterModalOpen = ref(false)
const filterOpenButtonRef = ref<HTMLButtonElement | null>(null)
const filterModalTitleId = `${useId()}-filters-title`
const addTooltipId = `${useId()}-add-tooltip`
const exportMenuId = `${useId()}-export-menu`
const exportMenuOpen = ref(false)
const exportTriggerRef = ref<HTMLButtonElement | null>(null)

function focusExportTrigger() {
if (props.exportLoading)
return
exportTriggerRef.value?.focus()
}

function closeExportMenu() {
exportMenuOpen.value = false
nextTick(focusExportTrigger)
}

function toggleExportMenu() {
if (exportMenuOpen.value)
closeExportMenu()
else
exportMenuOpen.value = true
}

function onExportFocusOut(event: FocusEvent) {
const root = event.currentTarget as HTMLElement | null
const next = event.relatedTarget as Node | null
if (!root || (next && root.contains(next)))
return
nextTick(() => {
if (!root.contains(document.activeElement))
exportMenuOpen.value = false
})
}

function exportTable(format: 'csv' | 'json') {
closeExportMenu()
emit('export', format)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

watch(() => props.exportLoading, (loading, wasLoading) => {
if (wasLoading && !loading)
nextTick(focusExportTrigger)
})
const slots = useSlots()
const { t } = useI18n()
const searchVal = ref(props.search ?? '')
Expand Down Expand Up @@ -519,6 +568,57 @@ const paginationClass = computed(() => props.mobileFixedPagination
<Spinner v-else size="w-[16.8px] h-[16.8px] m-1 mr-2" />
<span class="hidden text-sm md:block">{{ t("reload") }}</span>
</button>
<div
v-if="exportable"
class="d-dropdown"
@focusout="onExportFocusOut"
@keydown.escape.prevent="closeExportMenu"
>
<button
ref="exportTriggerRef"
type="button"
class="d-btn d-btn-sm inline-flex h-full items-center rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-500 shadow-none dark:border-gray-600 dark:bg-gray-800 dark:text-white"
:disabled="isLoading || exportLoading"
data-test="data-table-export"
:aria-label="t('export')"
aria-haspopup="true"
:aria-expanded="exportMenuOpen"
:aria-controls="exportMenuId"
@click="toggleExportMenu"
>
<IconDownload v-if="!exportLoading" class="m-1 md:mr-2" />
<Spinner v-else size="w-[16.8px] h-[16.8px] m-1 mr-2" />
<span class="hidden text-sm md:block">{{ t('export') }}</span>
</button>
<ul
v-show="exportMenuOpen"
:id="exportMenuId"
class="d-dropdown-content d-menu z-20 mt-1 mr-2 w-40 rounded-md border border-slate-200 bg-white p-1 shadow-lg dark:border-slate-700 dark:bg-slate-900"
>
<li>
<button
type="button"
class="d-btn d-btn-ghost d-btn-sm w-full justify-start rounded-md px-3 py-2 text-left text-sm font-normal text-slate-700 shadow-none dark:text-slate-200"
data-test="data-table-export-csv"
:disabled="isLoading || exportLoading"
@click="exportTable('csv')"
>
{{ t('download-csv') }}
</button>
</li>
<li>
<button
type="button"
class="d-btn d-btn-ghost d-btn-sm w-full justify-start rounded-md px-3 py-2 text-left text-sm font-normal text-slate-700 shadow-none dark:text-slate-200"
data-test="data-table-export-json"
:disabled="isLoading || exportLoading"
@click="exportTable('json')"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
>
{{ t('download-json') }}
</button>
</li>
</ul>
</div>
<div v-if="showAdd" class="p-px mr-2 rounded-lg from-cyan-500 to-purple-500 bg-linear-to-r">
<button
:data-test="addButtonTestId"
Expand Down
5 changes: 4 additions & 1 deletion src/components/tables/BundleMultiFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'
const props = defineProps<{
options: string[]
modelValue: string[]
hideLabel?: boolean
}>()

const emit = defineEmits<{
Expand Down Expand Up @@ -78,13 +79,15 @@ function clearSelection() {

<template>
<div class="flex w-full flex-col gap-2" data-test="device-bundle-filter">
<div class="flex items-center justify-between gap-2">
<div v-if="!hideLabel || modelValue.length" class="flex items-center justify-between gap-2">
<label
v-if="!hideLabel"
:for="searchId"
class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400"
>
{{ t('bundle') }}
</label>
<span v-else />
<button
v-if="modelValue.length"
type="button"
Expand Down
Loading
Loading