From 362233de56480cf441952d4271884827e929fb68 Mon Sep 17 00:00:00 2001 From: Efren Lim Date: Thu, 14 May 2026 12:56:24 +0800 Subject: [PATCH 1/4] feat: added first step in the donate modal Signed-off-by: Efren Lim --- .claude/skills/uikit/references/components.md | 24 ++- .../components/donate-drawer-global.vue | 26 +++ .../donate/components/donate-drawer.vue | 174 +++++++++++++++ .../donate/components/donate-step-amount.vue | 203 ++++++++++++++++++ .../donate/store/donate-drawer.store.ts | 28 +++ .../components/initiative-detail-header.vue | 4 + .../app/components/uikit/drawer/drawer.scss | 10 +- .../app/components/uikit/drawer/drawer.vue | 11 +- frontend/app/layouts/default.vue | 2 + frontend/server/api/donate/index.post.ts | 31 +++ frontend/shared/types/donate.types.ts | 28 +++ 11 files changed, 535 insertions(+), 6 deletions(-) create mode 100644 frontend/app/components/modules/donate/components/donate-drawer-global.vue create mode 100644 frontend/app/components/modules/donate/components/donate-drawer.vue create mode 100644 frontend/app/components/modules/donate/components/donate-step-amount.vue create mode 100644 frontend/app/components/modules/donate/store/donate-drawer.store.ts create mode 100644 frontend/server/api/donate/index.post.ts create mode 100644 frontend/shared/types/donate.types.ts diff --git a/.claude/skills/uikit/references/components.md b/.claude/skills/uikit/references/components.md index 56a7d1b5..69c1de74 100644 --- a/.claude/skills/uikit/references/components.md +++ b/.claude/skills/uikit/references/components.md @@ -236,11 +236,31 @@ Wraps form inputs with label and validation messages. ## LfxDrawer ```vue - - + + + ``` +| Prop | Type | Default | Notes | +| ------------------ | --------------------------- | ------------ | -------------------------------------------------- | +| `modelValue` | `boolean` | — | v-model open/closed state | +| `position` | `'left' \| 'right' \| 'bottom'` | `'right'` | Slide direction | +| `width` | `string` | `'37.5rem'` | max-width for left/right positions | +| `height` | `string` | `'85vh'` | max-height for bottom position | +| `closeFunction` | `() => boolean` | `() => true` | Return false to veto close (e.g. unsaved changes) | +| `hideCloseButton` | `boolean` | `false` | Hide the built-in × button to add your own | + +The `bottom` position adds `rounded-t-2xl` to the panel and fills the full viewport width. + --- ## LfxTooltip diff --git a/frontend/app/components/modules/donate/components/donate-drawer-global.vue b/frontend/app/components/modules/donate/components/donate-drawer-global.vue new file mode 100644 index 00000000..4cdc6d0d --- /dev/null +++ b/frontend/app/components/modules/donate/components/donate-drawer-global.vue @@ -0,0 +1,26 @@ + + + + + + diff --git a/frontend/app/components/modules/donate/components/donate-drawer.vue b/frontend/app/components/modules/donate/components/donate-drawer.vue new file mode 100644 index 00000000..f8f81958 --- /dev/null +++ b/frontend/app/components/modules/donate/components/donate-drawer.vue @@ -0,0 +1,174 @@ + + + + + + diff --git a/frontend/app/components/modules/donate/components/donate-step-amount.vue b/frontend/app/components/modules/donate/components/donate-step-amount.vue new file mode 100644 index 00000000..2f61d524 --- /dev/null +++ b/frontend/app/components/modules/donate/components/donate-step-amount.vue @@ -0,0 +1,203 @@ + + + + + + diff --git a/frontend/app/components/modules/donate/store/donate-drawer.store.ts b/frontend/app/components/modules/donate/store/donate-drawer.store.ts new file mode 100644 index 00000000..9c7cad81 --- /dev/null +++ b/frontend/app/components/modules/donate/store/donate-drawer.store.ts @@ -0,0 +1,28 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { defineStore } from 'pinia'; +import { ref } from 'vue'; + +export interface DonateDrawerInitiative { + id: string; + name: string; + logoUrl?: string; +} + +export const useDonateDrawerStore = defineStore('donateDrawer', () => { + const isOpen = ref(false); + const initiative = ref(null); + + const openDonateDrawer = (data: DonateDrawerInitiative) => { + initiative.value = data; + isOpen.value = true; + }; + + const closeDonateDrawer = () => { + isOpen.value = false; + initiative.value = null; + }; + + return { isOpen, initiative, openDonateDrawer, closeDonateDrawer }; +}); diff --git a/frontend/app/components/modules/initiatives/components/initiative-detail-header.vue b/frontend/app/components/modules/initiatives/components/initiative-detail-header.vue index e54a11ea..b8486ee4 100644 --- a/frontend/app/components/modules/initiatives/components/initiative-detail-header.vue +++ b/frontend/app/components/modules/initiatives/components/initiative-detail-header.vue @@ -81,6 +81,7 @@ SPDX-License-Identifier: MIT icon="hand-heart" icon-position="left" class="!text-accent-500" + @click="openDonateDrawer({ id: initiative.id, name: initiative.name, logoUrl: initiative.logoUrl })" /> @@ -122,12 +123,15 @@ import LfxAvatar from '~/components/uikit/avatar/avatar.vue'; import LfxIcon from '~/components/uikit/icon/icon.vue'; import LfxChip from '~/components/uikit/chip/chip.vue'; import LfxButton from '~/components/uikit/button/button.vue'; +import { useDonateDrawerStore } from '~/components/modules/donate/store/donate-drawer.store'; const props = defineProps<{ initiative: InitiativeDetail; activeTab?: string; }>(); +const { openDonateDrawer } = useDonateDrawerStore(); + defineEmits<{ (e: 'update:activeTab', value: string): void }>(); const tabs = [ diff --git a/frontend/app/components/uikit/drawer/drawer.scss b/frontend/app/components/uikit/drawer/drawer.scss index b8754822..1566f02c 100644 --- a/frontend/app/components/uikit/drawer/drawer.scss +++ b/frontend/app/components/uikit/drawer/drawer.scss @@ -16,8 +16,16 @@ @apply justify-end; } + &--bottom { + @apply items-end; + + .c-drawer__content { + @apply h-auto rounded-t-2xl #{!important}; + } + } + &__content { - @apply w-full bg-white shadow-xl h-full; + @apply relative w-full bg-white shadow-xl h-full; .c-card { @apply shadow-none border-0; diff --git a/frontend/app/components/uikit/drawer/drawer.vue b/frontend/app/components/uikit/drawer/drawer.vue index a752f5ea..b96a72d3 100644 --- a/frontend/app/components/uikit/drawer/drawer.vue +++ b/frontend/app/components/uikit/drawer/drawer.vue @@ -7,16 +7,17 @@ SPDX-License-Identifier: MIT
boolean; - position?: 'left' | 'right'; + position?: 'left' | 'right' | 'bottom'; + hideCloseButton?: boolean; }>(), { width: '37.5rem', + height: '85vh', closeFunction: () => true, position: 'right', + hideCloseButton: false, }, ); diff --git a/frontend/app/layouts/default.vue b/frontend/app/layouts/default.vue index 6bb31fa6..a8cf460e 100644 --- a/frontend/app/layouts/default.vue +++ b/frontend/app/layouts/default.vue @@ -9,10 +9,12 @@ SPDX-License-Identifier: MIT +
diff --git a/frontend/server/api/donate/index.post.ts b/frontend/server/api/donate/index.post.ts new file mode 100644 index 00000000..36dfc2d8 --- /dev/null +++ b/frontend/server/api/donate/index.post.ts @@ -0,0 +1,31 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +import { defineEventHandler, readBody, createError } from 'h3'; +import type { DonateSubmission, DonationRecord } from '#shared/types/donate.types'; + +const donations: DonationRecord[] = []; + +export default defineEventHandler(async (event) => { + const body = await readBody(event); + + if (!body.initiativeId || !body.amountCents || body.amountCents <= 0) { + throw createError({ + statusCode: 400, + statusMessage: 'initiativeId and a positive amountCents are required', + }); + } + + const record: DonationRecord = { + id: crypto.randomUUID(), + initiativeId: body.initiativeId, + tierId: body.tierId ?? null, + tierName: body.tierName ?? null, + amountCents: body.amountCents, + createdAt: new Date().toISOString(), + }; + + donations.push(record); + + return { success: true, donation: record }; +}); diff --git a/frontend/shared/types/donate.types.ts b/frontend/shared/types/donate.types.ts new file mode 100644 index 00000000..5842ddfe --- /dev/null +++ b/frontend/shared/types/donate.types.ts @@ -0,0 +1,28 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +export interface SponsorshipTier { + id: string; + name: string; + amountCents: number; + benefits: string[]; +} + +export interface DonateAmountForm { + tierId: string | null; + tierName: string | null; + customAmountCents: number | null; + amountCents: number; +} + +export interface DonateSubmission { + initiativeId: string; + tierId: string | null; + tierName: string | null; + amountCents: number; +} + +export interface DonationRecord extends DonateSubmission { + id: string; + createdAt: string; +} From b97c14fac2e3303e6508869b602391e5d3def91c Mon Sep 17 00:00:00 2001 From: Efren Lim Date: Thu, 14 May 2026 13:12:05 +0800 Subject: [PATCH 2/4] feat: step 2 of the donate modal Signed-off-by: Efren Lim --- .../donate/components/donate-drawer.vue | 85 +++++++-- .../donate/components/donate-step-contact.vue | 169 ++++++++++++++++++ .../components/uikit/checkbox/checkbox.scss | 15 +- .../components/uikit/checkbox/checkbox.vue | 30 ++-- .../components/uikit/field/field-messages.vue | 4 +- frontend/server/api/donate/index.post.ts | 1 + frontend/shared/types/donate.types.ts | 13 ++ 7 files changed, 282 insertions(+), 35 deletions(-) create mode 100644 frontend/app/components/modules/donate/components/donate-step-contact.vue diff --git a/frontend/app/components/modules/donate/components/donate-drawer.vue b/frontend/app/components/modules/donate/components/donate-drawer.vue index f8f81958..c8474b2e 100644 --- a/frontend/app/components/modules/donate/components/donate-drawer.vue +++ b/frontend/app/components/modules/donate/components/donate-drawer.vue @@ -58,7 +58,12 @@ SPDX-License-Identifier: MIT
+
@@ -70,7 +75,13 @@ SPDX-License-Identifier: MIT @click="close()" /> -
+
+ @@ -95,12 +106,15 @@ SPDX-License-Identifier: MIT + + diff --git a/frontend/app/components/uikit/checkbox/checkbox.scss b/frontend/app/components/uikit/checkbox/checkbox.scss index f9bc6a70..a80f1e53 100644 --- a/frontend/app/components/uikit/checkbox/checkbox.scss +++ b/frontend/app/components/uikit/checkbox/checkbox.scss @@ -1,11 +1,11 @@ // Copyright The Linux Foundation and each contributor to LFX. // SPDX-License-Identifier: MIT .c-checkbox { - @apply inline-flex items-center flex-wrap relative; + @apply inline-flex items-center flex-wrap relative gap-2; input { - @apply h-3.5 w-3.5 border border-solid border-neutral-300 rounded-sm bg-white; - @apply transition-all appearance-none p-0 my-0 ml-0 inline-block cursor-pointer; + @apply absolute inset-0 h-full w-full border border-solid border-neutral-300 rounded-sm bg-white; + @apply transition-all appearance-none p-0 m-0 cursor-pointer; // Checked &:checked { @@ -42,8 +42,11 @@ } } - // Icon - i.c-icon { - @apply absolute top-0.5 left-0.5; + &__box { + @apply relative flex items-center justify-center shrink-0 h-3.5 w-3.5; + } + + &__check { + @apply absolute pointer-events-none; } } diff --git a/frontend/app/components/uikit/checkbox/checkbox.vue b/frontend/app/components/uikit/checkbox/checkbox.vue index 76cad42a..fc0ce6cd 100644 --- a/frontend/app/components/uikit/checkbox/checkbox.vue +++ b/frontend/app/components/uikit/checkbox/checkbox.vue @@ -4,20 +4,22 @@ SPDX-License-Identifier: MIT -->