|
1 | 1 | <template> |
| 2 | + <!-- Inline variant: the wizard's Verify-step one-liner (Spec 109-b FR-044). --> |
| 3 | + <p |
| 4 | + v-if="variant === 'inline' && visible" |
| 5 | + class="mt-4 border-t border-base-300 pt-3 text-[11px] opacity-60 flex items-center gap-2" |
| 6 | + data-test="wizard-telemetry-notice" |
| 7 | + :data-mode="mode" |
| 8 | + > |
| 9 | + <span class="flex-1"> |
| 10 | + <template v-if="mode === 'off_env' && state"> |
| 11 | + <span data-test="telemetry-off-env">{{ telemetryOffLine(state) }}</span> |
| 12 | + </template> |
| 13 | + <template v-else> |
| 14 | + MCPProxy sends anonymous usage statistics to help improve the product. No personal data is collected. |
| 15 | + </template> |
| 16 | + <a href="https://mcpproxy.app/telemetry" target="_blank" rel="noopener noreferrer" class="link link-hover underline">Learn more</a> |
| 17 | + </span> |
| 18 | + <button |
| 19 | + class="btn btn-ghost btn-xs" |
| 20 | + data-test="wizard-telemetry-notice-dismiss" |
| 21 | + @click="dismiss" |
| 22 | + >Dismiss</button> |
| 23 | + </p> |
| 24 | + |
2 | 25 | <!-- UX audit F14: a daisyUI alert is a GRID that flows in columns, so at 390px |
3 | 26 | the message shared the row with the action buttons and rendered as a |
4 | 27 | ~10-character column ~440px tall — over half the viewport, before any |
5 | 28 | content. Below `sm` the alert stacks (`alert-vertical`) and the text child |
6 | 29 | gets `min-w-0` so it may shrink inside its track. --> |
7 | | - <div v-if="visible" class="alert alert-vertical sm:alert-horizontal alert-info" data-test="telemetry-banner"> |
| 30 | + <div |
| 31 | + v-else-if="variant === 'banner' && visible" |
| 32 | + class="alert alert-vertical sm:alert-horizontal alert-info" |
| 33 | + data-test="telemetry-banner" |
| 34 | + :data-mode="mode" |
| 35 | + > |
8 | 36 | <svg class="w-6 h-6 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"> |
9 | 37 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |
10 | 38 | </svg> |
11 | | - <div class="flex-1 min-w-0"> |
| 39 | + <!-- Environment opt-out (Spec 109 FR-044a): say it is off and why. The |
| 40 | + setting is locked, so no disclosure line and no Manage link. --> |
| 41 | + <div v-if="mode === 'off_env' && state" class="flex-1 min-w-0"> |
| 42 | + <span data-test="telemetry-off-env">{{ telemetryOffLine(state) }}</span> |
| 43 | + <a |
| 44 | + href="https://mcpproxy.app/telemetry" |
| 45 | + target="_blank" |
| 46 | + rel="noopener noreferrer" |
| 47 | + class="link link-hover underline" |
| 48 | + > Learn more</a> |
| 49 | + </div> |
| 50 | + <div v-else class="flex-1 min-w-0"> |
12 | 51 | <span>MCPProxy sends anonymous usage statistics to help improve the product. No personal data is collected. </span> |
13 | 52 | <a |
14 | 53 | href="https://mcpproxy.app/telemetry" |
|
23 | 62 | </div> |
24 | 63 | <div class="flex items-center gap-2 shrink-0"> |
25 | 64 | <RouterLink |
| 65 | + v-if="mode !== 'off_env'" |
26 | 66 | to="/settings?focus=telemetry.enabled" |
27 | 67 | class="btn btn-sm btn-ghost" |
28 | 68 | data-test="telemetry-banner-settings-link" |
|
40 | 80 | </template> |
41 | 81 |
|
42 | 82 | <script setup lang="ts"> |
43 | | -import { computed } from 'vue' |
| 83 | +import { computed, onMounted } from 'vue' |
44 | 84 | import { RouterLink } from 'vue-router' |
45 | 85 | import { useOnboardingStore } from '@/stores/onboarding' |
| 86 | +import { telemetryNoticeMode, telemetryOffLine } from '@/utils/telemetryState' |
| 87 | +
|
| 88 | +const props = withDefaults(defineProps<{ variant?: 'banner' | 'inline' }>(), { variant: 'banner' }) |
46 | 89 |
|
47 | 90 | const onboarding = useOnboardingStore() |
48 | 91 |
|
49 | | -// Spec 109-b FR-044: the notice must not render while the wizard is open — |
50 | | -// it already offers its own one-line version in the Verify step (see |
51 | | -// OnboardingWizard.vue), and showing both would say the same thing twice |
52 | | -// while the wizard sits on top of everything else. Dismissal is the store's |
53 | | -// shared `telemetryNoticeDismissed` ref (backed by one localStorage key), not |
54 | | -// an independent per-component copy, so acting on either surface hides both |
55 | | -// immediately even though this banner and the wizard stay mounted together |
56 | | -// on Dashboard.vue and neither ever remounts. |
57 | | -const visible = computed(() => !onboarding.telemetryNoticeDismissed && !onboarding.wizardOpen) |
| 92 | +const state = computed(() => onboarding.telemetryState) |
| 93 | +const mode = computed(() => telemetryNoticeMode(state.value)) |
| 94 | +
|
| 95 | +// Spec 109-b FR-044: the page banner must not render while the wizard is open — |
| 96 | +// the wizard offers this same notice inline in its Verify step (variant |
| 97 | +// "inline"), and showing both would say the same thing twice while the wizard |
| 98 | +// sits on top of everything else. Dismissal is the store's shared |
| 99 | +// `telemetryNoticeDismissed` ref (backed by one localStorage key), not an |
| 100 | +// independent per-component copy, so acting on either surface hides both |
| 101 | +// immediately even though the banner and the wizard stay mounted together on |
| 102 | +// Dashboard.vue and neither ever remounts. |
| 103 | +// |
| 104 | +// Spec 109 FR-044a: a user who turned telemetry off in their own config is not |
| 105 | +// nagged (`hidden`); an environment opt-out is stated, not disclosed. |
| 106 | +const visible = computed(() => { |
| 107 | + if (onboarding.telemetryNoticeDismissed) return false |
| 108 | + if (mode.value === 'hidden') return false |
| 109 | + return props.variant === 'inline' ? true : !onboarding.wizardOpen |
| 110 | +}) |
58 | 111 |
|
59 | 112 | function dismiss() { |
60 | 113 | onboarding.dismissTelemetryNotice() |
61 | 114 | } |
| 115 | +
|
| 116 | +onMounted(() => { |
| 117 | + void onboarding.loadTelemetryState() |
| 118 | +}) |
62 | 119 | </script> |
0 commit comments