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
1 change: 1 addition & 0 deletions dashboard/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,6 @@ declare module 'vue' {
UserGroup: typeof import('./src/components/common/UserGroup.vue')['default']
UserMenu: typeof import('./src/components/UserMenu.vue')['default']
UserSettingsDialog: typeof import('./src/components/UserSettingsDialog.vue')['default']
ZoomLogo: typeof import('./src/components/common/ZoomLogo.vue')['default']
}
}
18 changes: 18 additions & 0 deletions dashboard/src/components/common/ZoomLogo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script setup lang="ts">
withDefaults(defineProps<{ color?: string }>(), { color: "#2196F3" })
</script>

<template>
<svg viewBox="4 4 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.9998 28C22.6275 28 28 22.627 28 15.9998C28 9.37249 22.6275 4 15.9998 4C9.37202 4 4 9.37249 4 15.9998C4 22.627 9.37249 28 15.9998 28Z"
:fill="color"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11.0418 19.4297H18.9783V13.6101C18.9783 12.7333 18.2678 12.0228 17.391 12.0228H9.45445V17.8424C9.45445 18.7192 10.165 19.4297 11.0418 19.4297ZM20.036 17.3138L23.2106 19.4297V12.0228L20.0365 14.1392L20.036 17.3138Z"
fill="white"
/>
</svg>
</template>
103 changes: 72 additions & 31 deletions dashboard/src/components/dashboard/events/EventLocation.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { Combobox } from "frappe-ui"
import { Button, Combobox } from "frappe-ui"
import { computed, ref, watch } from "vue"

import ZoomLogo from "@/components/common/ZoomLogo.vue"
import AddVenueDialog from "@/components/dashboard/events/AddVenueDialog.vue"
import { venues } from "@/data/venues"

Expand All @@ -22,6 +23,8 @@ const zoomMeeting = defineModel<boolean>("zoomMeeting", { default: false })

const isAdding = ref(false)
const suggestedName = ref("")
const isOpen = ref(false)
const query = ref("")

watch(
() => props.team,
Expand All @@ -38,41 +41,38 @@ const selected = computed({
},
})

const addVenue = {
type: "custom" as const,
key: "add-venue",
label: "Add venue",
icon: "lucide-plus",
// Only worth offering once they have typed a name nothing answers to.
condition: ({ query }: { query: string }) =>
Boolean(query.trim()) &&
!(venues.data ?? []).some((row) => row.name.toLowerCase() === query.trim().toLowerCase()),
onClick: ({ query }: { query: string }) => {
suggestedName.value = query.trim()
isAdding.value = true
},
// Nothing answers to what they typed, so Add Manually is the only move left: it is
// pre-armed, and Enter takes it.
const unmatched = computed(() => {
const text = query.value.trim().toLowerCase()
return Boolean(text) && !(venues.data ?? []).some((row) => row.name.toLowerCase().includes(text))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Loading Arms Manual Creation

While the team's venues are still loading, venues.data ?? [] treats the missing data as a completed empty list. Any non-empty query is therefore marked as unmatched, and pressing Enter opens the Add Venue dialog even if that venue already exists. This can lead the organizer into a duplicate-name insert that fails instead of selecting the venue after loading completes. Only arm the shortcut after the venue resource has finished loading.

Knowledge Base Used: Dashboard event management

Prompt To Fix With AI
This is a comment left during a code review.
Path: dashboard/src/components/dashboard/events/EventLocation.vue
Line: 48

Comment:
**Loading Arms Manual Creation**

While the team's venues are still loading, `venues.data ?? []` treats the missing data as a completed empty list. Any non-empty query is therefore marked as unmatched, and pressing Enter opens the Add Venue dialog even if that venue already exists. This can lead the organizer into a duplicate-name insert that fails instead of selecting the venue after loading completes. Only arm the shortcut after the venue resource has finished loading.

**Knowledge Base Used:** [Dashboard event management](https://app.greptile.com/bwh-tech/-/custom-context/knowledge-base/bwhtech/buzz/-/docs/dashboard-event-management.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

})

function addManually() {
isOpen.value = false
suggestedName.value = query.value.trim()
isAdding.value = true
}

function createZoomMeeting() {
isOpen.value = false
selected.value = ZOOM
}

// The Zoom row stays out of the list — the footer offers it. It comes back while
// selected, because the trigger reads its label from a matching option.
const zoomOption = { label: "Create Zoom meeting", value: ZOOM, icon: "lucide-video" }

const options = computed(() => [
{
group: "Venues",
options: [
...(venues.data ?? []).map((row) => ({
label: row.name,
description: row.address,
value: row.name,
})),
addVenue,
],
options: (venues.data ?? []).map((row) => ({
label: row.name,
description: row.address,
value: row.name,
})),
},
...(props.showVirtual
? [
{
group: "Virtual",
options: [{ label: "Create Zoom meeting", value: ZOOM, icon: "lucide-video" }],
},
]
: []),
...(zoomMeeting.value ? [{ group: "Virtual", options: [zoomOption] }] : []),
])

async function onVenueCreated(name: string) {
Expand All @@ -82,14 +82,55 @@ async function onVenueCreated(name: string) {
</script>

<template>
<!-- Enter is prevented: its default action clicks whatever the dialog focuses as it
opens, which closes the dialog again. -->
<Combobox
v-model="selected"
v-model:open="isOpen"
v-model:query="query"
:options="options"
placeholder="Search venues, or add one"
empty-text=""
class="w-full"
:disabled="disabled"
:error="error"
/>
@keydown.enter.prevent="unmatched && addManually()"
>
<!-- Rows size the popover, and an address can be a long URL. Cap them near the
trigger width, minus the row's own padding, so the panel stays put. -->
<template #item-label="{ item }">
<div class="min-w-0 max-w-[calc(var(--reka-combobox-trigger-width)-3rem)]">
<div class="truncate">{{ item.label }}</div>
<div v-if="item.description" class="truncate text-p-sm text-ink-gray-5">
{{ item.description }}
</div>
</div>
</template>

<template #footer>
<div class="flex flex-col gap-1 border-t border-outline-gray-1 p-1">
<Button
class="w-full !justify-start"
:class="unmatched && '!bg-surface-gray-3'"
variant="ghost"
icon-left="lucide-map-pin-plus"
label="Add Manually"
@click="addManually()"
/>
<Button
v-if="showVirtual"
class="w-full !justify-start"
variant="ghost"
label="Create Zoom Meeting"
@click="createZoomMeeting()"
>
<template #prefix>
<ZoomLogo class="size-4" color="var(--ink-gray-5)" />
</template>
</Button>
</div>
</template>
</Combobox>

<AddVenueDialog
v-model="isAdding"
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/pages/manage/events/CreateEvent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ async function save() {
v-model:time-zone="timeZone"
/>

<section id="event-location" class="space-y-3">
<section id="event-location" class="space-y-1.5">
<h2 class="text-sm font-medium uppercase tracking-wide text-ink-gray-5">Where</h2>
<EventLocation
v-model:venue="venue"
Expand Down
Loading