Skip to content

Commit 8c530cf

Browse files
authored
Merge pull request #111 from reqcore-inc/feat/better-demo
feat(demo): add 'Get Started' options for demo mode in AppTopBar and enhance PreviewUpsellModal
2 parents f0ae97f + e607520 commit 8c530cf

2 files changed

Lines changed: 146 additions & 17 deletions

File tree

app/components/AppTopBar.vue

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Sun, Moon, MessageSquarePlus, Settings,
66
ChevronDown, Menu, X, Users, ChevronLeft,
77
LayoutDashboard, Calendar, ArrowUpCircle,
8+
Cloud, Server, Sparkles,
89
} from 'lucide-vue-next'
910
1011
const route = useRoute()
@@ -17,6 +18,22 @@ const { isDark, toggle: toggleColorMode } = useColorMode()
1718
const showFeedbackModal = ref(false)
1819
const showUserMenu = ref(false)
1920
const showMobileMenu = ref(false)
21+
const showGetStartedMenu = ref(false)
22+
23+
const config = useRuntimeConfig()
24+
const { activeOrg } = useCurrentOrg()
25+
26+
const isDemo = computed(() => {
27+
const slug = config.public.demoOrgSlug
28+
return slug && activeOrg.value?.slug === slug
29+
})
30+
31+
const getStartedMenuRef = useTemplateRef<HTMLElement>('getStartedMenuRoot')
32+
function onClickOutsideGetStarted(e: MouseEvent) {
33+
if (getStartedMenuRef.value && !getStartedMenuRef.value.contains(e.target as Node)) {
34+
showGetStartedMenu.value = false
35+
}
36+
}
2037
2138
const userName = computed(() => session.value?.user?.name ?? 'User')
2239
const userEmail = computed(() => session.value?.user?.email ?? '')
@@ -119,6 +136,7 @@ function isActiveRoute(to: string, exact: boolean) {
119136
watch(() => route.path, () => {
120137
showUserMenu.value = false
121138
showMobileMenu.value = false
139+
showGetStartedMenu.value = false
122140
})
123141
124142
// Close user menu on outside click
@@ -128,8 +146,14 @@ function onClickOutsideUser(e: MouseEvent) {
128146
showUserMenu.value = false
129147
}
130148
}
131-
onMounted(() => document.addEventListener('click', onClickOutsideUser))
132-
onUnmounted(() => document.removeEventListener('click', onClickOutsideUser))
149+
onMounted(() => {
150+
document.addEventListener('click', onClickOutsideUser)
151+
document.addEventListener('click', onClickOutsideGetStarted)
152+
})
153+
onUnmounted(() => {
154+
document.removeEventListener('click', onClickOutsideUser)
155+
document.removeEventListener('click', onClickOutsideGetStarted)
156+
})
133157
</script>
134158

135159
<template>
@@ -167,6 +191,67 @@ onUnmounted(() => document.removeEventListener('click', onClickOutsideUser))
167191

168192
<!-- Right: Actions -->
169193
<div class="flex items-center gap-1 lg:gap-1.5">
194+
<!-- Get Started CTA (demo mode only) -->
195+
<div v-if="isDemo" ref="getStartedMenuRoot" class="relative hidden sm:block">
196+
<button
197+
class="group inline-flex items-center gap-2 rounded-lg bg-gradient-to-r from-brand-600 to-violet-600 px-4 py-1.5 text-[13px] font-semibold text-white shadow-md shadow-brand-600/25 hover:shadow-lg hover:shadow-brand-600/30 active:shadow-sm transition-all duration-200 cursor-pointer border-0"
198+
@click="showGetStartedMenu = !showGetStartedMenu"
199+
>
200+
<Sparkles class="size-3.5 transition-transform duration-300 group-hover:rotate-12" />
201+
Get Started
202+
<ChevronDown
203+
class="size-3 opacity-70 transition-transform duration-200"
204+
:class="showGetStartedMenu ? 'rotate-180' : ''"
205+
/>
206+
</button>
207+
208+
<Transition
209+
enter-active-class="transition duration-150 ease-out"
210+
enter-from-class="opacity-0 scale-95 -translate-y-1"
211+
enter-to-class="opacity-100 scale-100 translate-y-0"
212+
leave-active-class="transition duration-100 ease-in"
213+
leave-from-class="opacity-100 scale-100 translate-y-0"
214+
leave-to-class="opacity-0 scale-95 -translate-y-1"
215+
>
216+
<div
217+
v-if="showGetStartedMenu"
218+
class="absolute right-0 top-[calc(100%+6px)] w-72 rounded-xl border border-surface-200 dark:border-surface-700 bg-white dark:bg-surface-900 shadow-xl shadow-surface-900/8 dark:shadow-surface-950/30 overflow-hidden"
219+
>
220+
<div class="px-4 py-3 border-b border-surface-100 dark:border-surface-800">
221+
<p class="text-xs font-medium text-surface-500 dark:text-surface-400 uppercase tracking-wider">Choose your setup</p>
222+
</div>
223+
<div class="p-2 space-y-1">
224+
<NuxtLink
225+
:to="$localePath('/auth/fresh-signup')"
226+
class="flex items-start gap-3 rounded-lg px-3 py-2.5 transition-colors hover:bg-brand-50 dark:hover:bg-brand-950/30 no-underline group/item"
227+
>
228+
<div class="flex items-center justify-center size-8 rounded-lg bg-brand-100 dark:bg-brand-900/40 text-brand-600 dark:text-brand-400 shrink-0 mt-0.5">
229+
<Cloud class="size-4" />
230+
</div>
231+
<div>
232+
<div class="text-sm font-semibold text-surface-900 dark:text-surface-100 group-hover/item:text-brand-700 dark:group-hover/item:text-brand-300 transition-colors">Cloud Hosted</div>
233+
<div class="text-xs text-surface-500 dark:text-surface-400 mt-0.5">Start free in seconds — we handle hosting, updates &amp; backups</div>
234+
</div>
235+
</NuxtLink>
236+
<a
237+
href="https://github.com/reqcore-inc/reqcore#quick-start"
238+
target="_blank"
239+
rel="noopener noreferrer"
240+
class="flex items-start gap-3 rounded-lg px-3 py-2.5 transition-colors hover:bg-surface-50 dark:hover:bg-surface-800/60 no-underline group/item"
241+
>
242+
<div class="flex items-center justify-center size-8 rounded-lg bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 shrink-0 mt-0.5">
243+
<Server class="size-4" />
244+
</div>
245+
<div>
246+
<div class="text-sm font-semibold text-surface-900 dark:text-surface-100 group-hover/item:text-surface-700 dark:group-hover/item:text-surface-200 transition-colors">Self-Host</div>
247+
<div class="text-xs text-surface-500 dark:text-surface-400 mt-0.5">Deploy on your own infrastructure — full control, 100% free</div>
248+
</div>
249+
</a>
250+
</div>
251+
</div>
252+
</Transition>
253+
</div>
254+
170255
<!-- New Job button (desktop) -->
171256
<NuxtLink
172257
:to="$localePath('/dashboard/jobs/new')"
@@ -393,6 +478,29 @@ onUnmounted(() => document.removeEventListener('click', onClickOutsideUser))
393478
<Plus class="size-4" />
394479
New Job
395480
</NuxtLink>
481+
482+
<!-- Get Started CTA (demo mode, mobile) -->
483+
<template v-if="isDemo">
484+
<div class="mt-2 pt-2 border-t border-surface-200 dark:border-surface-700">
485+
<p class="px-3 mb-1.5 text-xs font-medium text-surface-500 dark:text-surface-400 uppercase tracking-wider">Get Started</p>
486+
<NuxtLink
487+
:to="$localePath('/auth/fresh-signup')"
488+
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-brand-700 dark:text-brand-300 bg-brand-50 dark:bg-brand-950/40 hover:bg-brand-100 dark:hover:bg-brand-950/60 transition-colors no-underline"
489+
>
490+
<Cloud class="size-4" />
491+
Cloud Hosted — Start Free
492+
</NuxtLink>
493+
<a
494+
href="https://github.com/reqcore-inc/reqcore#quick-start"
495+
target="_blank"
496+
rel="noopener noreferrer"
497+
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-surface-600 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors no-underline mt-1"
498+
>
499+
<Server class="size-4" />
500+
Self-Host — Deploy Free
501+
</a>
502+
</div>
503+
</template>
396504
</nav>
397505

398506
<div class="px-4 pb-3 flex flex-col gap-2 border-t border-surface-100 dark:border-surface-800 pt-3 lg:hidden">

app/components/PreviewUpsellModal.vue

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { Eye, X, Github, Rocket } from 'lucide-vue-next'
2+
import { Eye, X, Github, Rocket, Cloud } from 'lucide-vue-next'
33
44
const emit = defineEmits<{
55
(e: 'close'): void
@@ -38,30 +38,51 @@ function closeModal() {
3838
</p>
3939

4040
<p class="text-sm text-surface-500 dark:text-surface-400">
41-
Reqcore is 100% free and open-source. Deploy your own instance in minutes to get full read &amp; write access.
41+
Get full read &amp; write access — choose the option that works best for you:
4242
</p>
4343

44-
<div class="flex flex-wrap items-center gap-2">
45-
<a
46-
href="https://github.com/reqcore-inc/reqcore#quick-start"
47-
target="_blank"
48-
rel="noopener noreferrer"
49-
class="inline-flex items-center gap-2 rounded-lg bg-brand-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-brand-700"
44+
<div class="space-y-2">
45+
<!-- Cloud hosted option -->
46+
<NuxtLink
47+
:to="$localePath('/auth/fresh-signup')"
48+
class="flex items-start gap-3 rounded-xl border border-brand-200 dark:border-brand-800 bg-gradient-to-r from-brand-50 to-violet-50 dark:from-brand-950/30 dark:to-violet-950/30 px-4 py-3 transition-all hover:shadow-md hover:border-brand-300 dark:hover:border-brand-700 no-underline group"
49+
@click="closeModal"
5050
>
51-
<Rocket class="size-4" />
52-
Deploy your own instance
53-
</a>
51+
<div class="flex items-center justify-center size-9 rounded-lg bg-gradient-to-br from-brand-500 to-violet-600 text-white shrink-0 shadow-sm mt-0.5">
52+
<Cloud class="size-4" />
53+
</div>
54+
<div>
55+
<div class="text-sm font-semibold text-surface-900 dark:text-surface-100 group-hover:text-brand-700 dark:group-hover:text-brand-300 transition-colors">Use Cloud Hosted</div>
56+
<div class="text-xs text-surface-500 dark:text-surface-400 mt-0.5">Start free in seconds — we handle hosting, updates &amp; backups</div>
57+
</div>
58+
</NuxtLink>
5459

60+
<!-- Self-host option -->
5561
<a
56-
href="https://github.com/reqcore-inc/reqcore"
62+
href="https://github.com/reqcore-inc/reqcore#quick-start"
5763
target="_blank"
5864
rel="noopener noreferrer"
59-
class="inline-flex items-center gap-2 rounded-lg border border-surface-300 px-3 py-2 text-sm font-medium text-surface-700 transition-colors hover:bg-surface-50 dark:border-surface-700 dark:text-surface-200 dark:hover:bg-surface-800"
65+
class="flex items-start gap-3 rounded-xl border border-surface-200 dark:border-surface-700 px-4 py-3 transition-all hover:shadow-md hover:border-surface-300 dark:hover:border-surface-600 hover:bg-surface-50/50 dark:hover:bg-surface-800/40 no-underline group"
6066
>
61-
<Github class="size-4" />
62-
View on GitHub
67+
<div class="flex items-center justify-center size-9 rounded-lg bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 shrink-0 shadow-sm mt-0.5">
68+
<Rocket class="size-4" />
69+
</div>
70+
<div>
71+
<div class="text-sm font-semibold text-surface-900 dark:text-surface-100 group-hover:text-surface-700 dark:group-hover:text-surface-200 transition-colors">Self-Host (Free &amp; Open Source)</div>
72+
<div class="text-xs text-surface-500 dark:text-surface-400 mt-0.5">Deploy on your own infrastructure — full control over your data</div>
73+
</div>
6374
</a>
6475
</div>
76+
77+
<a
78+
href="https://github.com/reqcore-inc/reqcore"
79+
target="_blank"
80+
rel="noopener noreferrer"
81+
class="inline-flex items-center gap-1.5 text-xs text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors no-underline"
82+
>
83+
<Github class="size-3.5" />
84+
View on GitHub
85+
</a>
6586
</div>
6687
</div>
6788
</div>

0 commit comments

Comments
 (0)