feat: add PendingRewardDialog, RewardConfirmDialog, and TaskConfirmDialog components
All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 25s
All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 25s
- Implemented PendingRewardDialog for handling pending reward requests. - Created RewardConfirmDialog for confirming reward redemption. - Developed TaskConfirmDialog for task confirmation with child name display. test: add unit tests for ChildView and ParentView components - Added comprehensive tests for ChildView including task triggering and SSE event handling. - Implemented tests for ParentView focusing on override modal and SSE event management. test: add ScrollingList component tests - Created tests for ScrollingList to verify item fetching, loading states, and custom item classes. - Included tests for two-step click interactions and edit button display logic. - Moved toward hashed passwords.
This commit is contained in:
@@ -11,6 +11,9 @@ const props = defineProps<{
|
||||
isParentAuthenticated?: boolean
|
||||
filterFn?: (item: any) => boolean
|
||||
getItemClass?: (item: any) => string | string[] | Record<string, boolean>
|
||||
enableEdit?: boolean
|
||||
childId?: string
|
||||
readyItemId?: string | null
|
||||
}>()
|
||||
|
||||
// Compute the fetch URL with ids if present
|
||||
@@ -24,6 +27,8 @@ const fetchUrl = computed(() => {
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'trigger-item', item: any): void
|
||||
(e: 'edit-item', item: any): void
|
||||
(e: 'item-ready', itemId: string): void
|
||||
}>()
|
||||
|
||||
const items = ref<any[]>([])
|
||||
@@ -32,7 +37,6 @@ const error = ref<string | null>(null)
|
||||
const scrollWrapper = ref<HTMLDivElement | null>(null)
|
||||
const itemRefs = ref<Record<string, HTMLElement | Element | null>>({})
|
||||
const lastCenteredItemId = ref<string | null>(null)
|
||||
const readyItemId = ref<string | null>(null)
|
||||
|
||||
const fetchItems = async () => {
|
||||
loading.value = true
|
||||
@@ -112,23 +116,41 @@ const handleClicked = async (item: any) => {
|
||||
const card = itemRefs.value[item.id]
|
||||
if (!wrapper || !card) return
|
||||
|
||||
// If this item is already ready (has edit button showing)
|
||||
if (props.readyItemId === item.id) {
|
||||
// Second click - trigger the item and reset
|
||||
emit(
|
||||
'trigger-item',
|
||||
items.value.find((i) => i.id === item.id),
|
||||
)
|
||||
emit('item-ready', '')
|
||||
lastCenteredItemId.value = null
|
||||
return
|
||||
}
|
||||
|
||||
// First click or different item clicked
|
||||
// Check if item needs to be centered
|
||||
const wrapperRect = wrapper.getBoundingClientRect()
|
||||
const cardRect = card.getBoundingClientRect()
|
||||
const cardCenter = cardRect.left + cardRect.width / 2
|
||||
const cardFullyVisible = cardCenter >= wrapperRect.left && cardCenter <= wrapperRect.right
|
||||
|
||||
if (!cardFullyVisible || lastCenteredItemId.value !== item.id) {
|
||||
// Center the item, but don't trigger
|
||||
if (!cardFullyVisible) {
|
||||
// Center the item first
|
||||
await centerItem(item.id)
|
||||
lastCenteredItemId.value = item.id
|
||||
readyItemId.value = item.id
|
||||
return
|
||||
}
|
||||
emit(
|
||||
'trigger-item',
|
||||
items.value.find((i) => i.id === item.id),
|
||||
)
|
||||
readyItemId.value = null
|
||||
|
||||
// Mark this item as ready (show edit button)
|
||||
lastCenteredItemId.value = item.id
|
||||
emit('item-ready', item.id)
|
||||
}
|
||||
|
||||
const handleEditClick = (item: any, event: Event) => {
|
||||
event.stopPropagation()
|
||||
emit('edit-item', item)
|
||||
// Reset the 2-step process after opening edit modal
|
||||
emit('item-ready', '')
|
||||
lastCenteredItemId.value = null
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -160,6 +182,21 @@ onBeforeUnmount(() => {
|
||||
:ref="(el) => (itemRefs[item.id] = el)"
|
||||
@click.stop="handleClicked(item)"
|
||||
>
|
||||
<button
|
||||
v-if="enableEdit && readyItemId === item.id"
|
||||
class="edit-button"
|
||||
@click="handleEditClick(item, $event)"
|
||||
title="Edit custom value"
|
||||
>
|
||||
<img src="/edit.png" alt="Edit" />
|
||||
</button>
|
||||
<span
|
||||
v-if="
|
||||
isParentAuthenticated && item.custom_value !== undefined && item.custom_value !== null
|
||||
"
|
||||
class="override-badge"
|
||||
>⭐</span
|
||||
>
|
||||
<slot name="item" :item="item">
|
||||
<div class="item-name">{{ item.name }}</div>
|
||||
</slot>
|
||||
@@ -249,6 +286,45 @@ onBeforeUnmount(() => {
|
||||
box-shadow: var(--item-card-hover-shadow, 0 8px 20px rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: none;
|
||||
background-color: var(--btn-primary);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
transition:
|
||||
opacity 0.2s,
|
||||
transform 0.1s;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.edit-button:hover {
|
||||
opacity: 0.9;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.edit-button img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.override-badge {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
font-size: 12px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@keyframes ready-glow {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 #667eea00;
|
||||
|
||||
Reference in New Issue
Block a user