From a2c2243b4f3c02513f160ef3bcd936dcbdfde057 Mon Sep 17 00:00:00 2001 From: Peter Hayes Date: Tue, 3 Mar 2026 17:12:29 -0500 Subject: [PATCH 1/2] Feature/event campaign type (#90) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add EVENT campaign type with line items New campaign type supporting multiple purchasable items grouped by category (e.g., dinner plates, gifts). Each item has its own price, capacity limit, and can be marked as required. Data model: - CampaignItem: items with price, category, capacity, required flag - DonationLineItem: snapshot of purchased items per donation Backend: - Campaign CRUD extended for EVENT type with item management - Validation: item capacity, per-order limits, required items, amount matching - Donation creation creates line item records - Stripe checkout supports multi-line-item sessions - Webhook recreates line items from metadata - Auto-complete when all capped items are sold out Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add EVENT campaign UI — form, donor item selector, public page, tests - CampaignForm: EVENT type option with item editor (name, category, price, capacity, required flag) - NewPledgeFormClient: EVENT item selector with quantity controls, category grouping, running total - PublicCampaignPage: EVENT items display with category grouping and pricing - Campaign API: EVENT type in Zod schema, items passthrough to service, items in list response - Donate page: serialize campaign items for public display - Tests: 14 new tests for EVENT validation, capacity, auto-complete, API contract (42 total) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: add @map annotations for DonationLineItem columns unitPrice and subtotal columns were missing @map('unit_price') and @map('subtotal') annotations, causing Prisma to look for camelCase column names instead of the snake_case columns in the database. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: enable campaign type-specific editing for TIERED tiers and EVENT items - Show campaign type as read-only label when editing (type cannot be changed) - Remove isEditing gate from TIERED tiers and EVENT items sections - Include tiers/items in PATCH body when editing campaigns - Add tiers and items to campaign update API schema with upsert logic - TIERED: upsert tiers by id, delete removed tiers (only if no donations reference them) - EVENT: upsert items by id, soft-delete removed items (preserve FK references) - Return tiers and items in PATCH response - Include items in GET single campaign response - Add items to CampaignList Campaign interface Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../migration.sql | 50 + prisma/schema.prisma | 44 +- .../services/campaign-constraints.test.ts | 252 ++- .../[slug]/campaigns/[id]/items/route.ts | 145 ++ .../[slug]/campaigns/[id]/route.ts | 118 +- .../organizations/[slug]/campaigns/route.ts | 26 +- .../[slug]/donations/checkout/route.ts | 77 +- .../organizations/[slug]/donations/route.ts | 5 + src/app/api/webhooks/stripe/route.ts | 27 + .../org/[slug]/donate/[campaignId]/page.tsx | 11 + src/components/campaigns/CampaignForm.tsx | 197 +- src/components/campaigns/CampaignList.tsx | 1 + .../campaigns/PublicCampaignPage.tsx | 57 + src/components/org/NewPledgeFormClient.tsx | 125 +- src/generated/prisma/browser.ts | 10 + src/generated/prisma/client.ts | 10 + src/generated/prisma/enums.ts | 3 +- src/generated/prisma/internal/class.ts | 28 +- .../prisma/internal/prismaNamespace.ts | 184 +- .../prisma/internal/prismaNamespaceBrowser.ts | 32 + src/generated/prisma/models.ts | 2 + src/generated/prisma/models/Campaign.ts | 170 ++ src/generated/prisma/models/CampaignItem.ts | 1818 +++++++++++++++++ src/generated/prisma/models/Donation.ts | 213 ++ .../prisma/models/DonationLineItem.ts | 1557 ++++++++++++++ src/services/campaign.service.ts | 251 ++- src/services/donation.service.ts | 56 +- 27 files changed, 5417 insertions(+), 52 deletions(-) create mode 100644 prisma/migrations/20260303220000_add_event_campaign_type/migration.sql create mode 100644 src/app/api/organizations/[slug]/campaigns/[id]/items/route.ts create mode 100644 src/generated/prisma/models/CampaignItem.ts create mode 100644 src/generated/prisma/models/DonationLineItem.ts diff --git a/prisma/migrations/20260303220000_add_event_campaign_type/migration.sql b/prisma/migrations/20260303220000_add_event_campaign_type/migration.sql new file mode 100644 index 0000000..bca3626 --- /dev/null +++ b/prisma/migrations/20260303220000_add_event_campaign_type/migration.sql @@ -0,0 +1,50 @@ +-- AlterEnum +ALTER TYPE "CampaignType" ADD VALUE 'EVENT'; + +-- CreateTable +CREATE TABLE "campaign_items" ( + "id" TEXT NOT NULL, + "campaign_id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "category" TEXT, + "price" DECIMAL(12,2) NOT NULL, + "max_quantity" INTEGER, + "min_per_order" INTEGER NOT NULL DEFAULT 0, + "max_per_order" INTEGER, + "is_required" BOOLEAN NOT NULL DEFAULT false, + "sort_order" INTEGER NOT NULL DEFAULT 0, + "is_active" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "campaign_items_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "donation_line_items" ( + "id" TEXT NOT NULL, + "donation_id" TEXT NOT NULL, + "campaign_item_id" TEXT NOT NULL, + "quantity" INTEGER NOT NULL, + "unit_price" DECIMAL(12,2) NOT NULL, + "subtotal" DECIMAL(12,2) NOT NULL, + + CONSTRAINT "donation_line_items_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "idx_campaign_item_campaign" ON "campaign_items"("campaign_id"); + +-- CreateIndex +CREATE INDEX "idx_line_item_donation" ON "donation_line_items"("donation_id"); + +-- CreateIndex +CREATE INDEX "idx_line_item_campaign_item" ON "donation_line_items"("campaign_item_id"); + +-- AddForeignKey +ALTER TABLE "campaign_items" ADD CONSTRAINT "campaign_items_campaign_id_fkey" FOREIGN KEY ("campaign_id") REFERENCES "campaigns"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "donation_line_items" ADD CONSTRAINT "donation_line_items_donation_id_fkey" FOREIGN KEY ("donation_id") REFERENCES "donations"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "donation_line_items" ADD CONSTRAINT "donation_line_items_campaign_item_id_fkey" FOREIGN KEY ("campaign_item_id") REFERENCES "campaign_items"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 570db93..2195533 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -755,6 +755,7 @@ model Campaign { // Relations donations Donation[] tiers CampaignTier[] + items CampaignItem[] @@index([organizationId, status], name: "idx_campaign_org_status") @@map("campaigns") @@ -771,6 +772,7 @@ enum CampaignType { OPEN // Default: any amount, no capacity limit FIXED_UNIT // Fixed price per unit, optional max units TIERED // Predefined donation levels with optional per-tier caps + EVENT // Multiple purchasable items with categories, prices, and capacity } model CampaignTier { @@ -788,6 +790,27 @@ model CampaignTier { @@map("campaign_tiers") } +model CampaignItem { + id String @id @default(uuid()) + campaignId String @map("campaign_id") + name String // "Adult Plate", "Child Plate", "Gift" + description String? @db.Text + category String? // Grouping label: "Plates", "Extras" + price Decimal @db.Decimal(12, 2) + maxQuantity Int? @map("max_quantity") // Total capacity (null = unlimited) + minPerOrder Int @default(0) @map("min_per_order") // 0 = optional + maxPerOrder Int? @map("max_per_order") // null = unlimited + isRequired Boolean @default(false) @map("is_required") // Must select ≥1 + sortOrder Int @default(0) @map("sort_order") + isActive Boolean @default(true) @map("is_active") + + campaign Campaign @relation(fields: [campaignId], references: [id]) + donationLineItems DonationLineItem[] + + @@index([campaignId], name: "idx_campaign_item_campaign") + @@map("campaign_items") +} + // ============================================ // DONATIONS // ============================================ @@ -832,8 +855,9 @@ model Donation { updatedAt DateTime @updatedAt @map("updated_at") // Relations - campaign Campaign? @relation(fields: [campaignId], references: [id]) - tier CampaignTier? @relation(fields: [tierId], references: [id]) + campaign Campaign? @relation(fields: [campaignId], references: [id]) + tier CampaignTier? @relation(fields: [tierId], references: [id]) + lineItems DonationLineItem[] @@index([organizationId, status], name: "idx_donation_org_status") @@index([organizationId, campaignId], name: "idx_donation_org_campaign") @@ -842,6 +866,22 @@ model Donation { @@map("donations") } +model DonationLineItem { + id String @id @default(uuid()) + donationId String @map("donation_id") + campaignItemId String @map("campaign_item_id") + quantity Int + unitPrice Decimal @map("unit_price") @db.Decimal(12, 2) // Snapshot of price at donation time + subtotal Decimal @map("subtotal") @db.Decimal(12, 2) // quantity × unitPrice + + donation Donation @relation(fields: [donationId], references: [id]) + campaignItem CampaignItem @relation(fields: [campaignItemId], references: [id]) + + @@index([donationId], name: "idx_line_item_donation") + @@index([campaignItemId], name: "idx_line_item_campaign_item") + @@map("donation_line_items") +} + enum DonationType { ONE_TIME PLEDGE diff --git a/src/__tests__/services/campaign-constraints.test.ts b/src/__tests__/services/campaign-constraints.test.ts index 6a2a9f0..60b7b20 100644 --- a/src/__tests__/services/campaign-constraints.test.ts +++ b/src/__tests__/services/campaign-constraints.test.ts @@ -21,6 +21,12 @@ jest.mock('@/lib/prisma', () => ({ aggregate: jest.fn(), count: jest.fn(), }, + donationLineItem: { + aggregate: jest.fn(), + }, + campaignItem: { + findMany: jest.fn(), + }, }, })); @@ -34,6 +40,7 @@ import { checkAndAutoCompleteCampaign, getUnitsSold, getTierSlotsFilled, + getItemQuantitySold, } from '@/services/campaign.service'; describe('Campaign Constraints', () => { @@ -208,6 +215,117 @@ describe('Campaign Constraints', () => { ).resolves.toBeUndefined(); }); }); + + describe('EVENT campaigns', () => { + const eventCampaign = { + id: 'camp-dinner', + campaignType: 'EVENT', + status: 'ACTIVE', + tiers: [], + items: [ + { id: 'item-adult', name: 'Adult Plate', price: 35, maxQuantity: 100, minPerOrder: 0, maxPerOrder: 10, isRequired: false, isActive: true }, + { id: 'item-child', name: 'Child Plate', price: 20, maxQuantity: 50, minPerOrder: 0, maxPerOrder: 5, isRequired: false, isActive: true }, + { id: 'item-gift', name: 'Gift', price: 30, maxQuantity: null, minPerOrder: 0, maxPerOrder: null, isRequired: true, isActive: true }, + ], + }; + + it('should accept valid EVENT donation with line items', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + (prisma.donationLineItem.aggregate as jest.Mock).mockResolvedValue({ _sum: { quantity: 0 } }); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 100, null, null, [ + { campaignItemId: 'item-adult', quantity: 2 }, // 2×35 = 70 + { campaignItemId: 'item-gift', quantity: 1 }, // 1×30 = 30 + ]) + ).resolves.toBeUndefined(); + }); + + it('should reject EVENT donation without line items', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 100) + ).rejects.toThrow('require selecting at least one item'); + }); + + it('should reject EVENT donation with empty line items', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 100, null, null, []) + ).rejects.toThrow('require selecting at least one item'); + }); + + it('should reject when required item is missing', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + (prisma.donationLineItem.aggregate as jest.Mock).mockResolvedValue({ _sum: { quantity: 0 } }); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 35, null, null, [ + { campaignItemId: 'item-adult', quantity: 1 }, + ]) + ).rejects.toThrow('"Gift" is required'); + }); + + it('should reject when amount does not match item total', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + (prisma.donationLineItem.aggregate as jest.Mock).mockResolvedValue({ _sum: { quantity: 0 } }); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 50, null, null, [ + { campaignItemId: 'item-adult', quantity: 1 }, + { campaignItemId: 'item-gift', quantity: 1 }, + ]) + ).rejects.toThrow('does not match item total ($65.00)'); + }); + + it('should reject when exceeding maxPerOrder', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + (prisma.donationLineItem.aggregate as jest.Mock).mockResolvedValue({ _sum: { quantity: 0 } }); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 385, null, null, [ + { campaignItemId: 'item-adult', quantity: 11 }, // max is 10 + { campaignItemId: 'item-gift', quantity: 1 }, + ]) + ).rejects.toThrow('Maximum 10 of "Adult Plate" per order'); + }); + + it('should reject when capacity exceeded', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + // Adult plate: 98 sold, trying to buy 5 more + (prisma.donationLineItem.aggregate as jest.Mock) + .mockResolvedValueOnce({ _sum: { quantity: 98 } }); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 205, null, null, [ + { campaignItemId: 'item-adult', quantity: 5 }, + { campaignItemId: 'item-gift', quantity: 1 }, + ]) + ).rejects.toThrow('Only 2 of "Adult Plate" remaining (requested 5)'); + }); + + it('should reject invalid campaign item ID', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 30, null, null, [ + { campaignItemId: 'item-nonexistent', quantity: 1 }, + ]) + ).rejects.toThrow('Campaign item not found or inactive'); + }); + + it('should reject quantity less than 1', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue(eventCampaign); + + await expect( + validateDonationAgainstCampaign('camp-dinner', 0, null, null, [ + { campaignItemId: 'item-adult', quantity: 0 }, + ]) + ).rejects.toThrow('Quantity must be at least 1'); + }); + }); }); // ── getUnitsSold ──────────────────────────────────────────────────── @@ -258,6 +376,35 @@ describe('Campaign Constraints', () => { }); }); + // ── getItemQuantitySold ─────────────────────────────────────────────── + + describe('getItemQuantitySold', () => { + it('should sum quantity for non-cancelled donations', async () => { + (prisma.donationLineItem.aggregate as jest.Mock).mockResolvedValue({ + _sum: { quantity: 15 }, + }); + + const result = await getItemQuantitySold('item-adult'); + + expect(result).toBe(15); + expect(prisma.donationLineItem.aggregate).toHaveBeenCalledWith({ + where: { + campaignItemId: 'item-adult', + donation: { status: { notIn: ['CANCELLED'] } }, + }, + _sum: { quantity: true }, + }); + }); + + it('should return 0 when no line items', async () => { + (prisma.donationLineItem.aggregate as jest.Mock).mockResolvedValue({ + _sum: { quantity: null }, + }); + + expect(await getItemQuantitySold('item-empty')).toBe(0); + }); + }); + // ── checkAndAutoCompleteCampaign ──────────────────────────────────── describe('checkAndAutoCompleteCampaign', () => { @@ -351,6 +498,64 @@ describe('Campaign Constraints', () => { expect(prisma.campaign.update).not.toHaveBeenCalled(); }); + + it('should auto-complete EVENT campaign when all capped items are sold out', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue({ + id: 'camp-dinner', + campaignType: 'EVENT', + status: 'ACTIVE', + tiers: [], + }); + (prisma.campaignItem.findMany as jest.Mock).mockResolvedValue([ + { id: 'item-adult', maxQuantity: 100 }, + { id: 'item-child', maxQuantity: 50 }, + ]); + // Both at capacity + (prisma.donationLineItem.aggregate as jest.Mock) + .mockResolvedValueOnce({ _sum: { quantity: 100 } }) + .mockResolvedValueOnce({ _sum: { quantity: 50 } }); + + await checkAndAutoCompleteCampaign('camp-dinner'); + + expect(prisma.campaign.update).toHaveBeenCalledWith({ + where: { id: 'camp-dinner' }, + data: { status: 'COMPLETED' }, + }); + }); + + it('should NOT auto-complete EVENT when some items still have capacity', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue({ + id: 'camp-dinner', + campaignType: 'EVENT', + status: 'ACTIVE', + tiers: [], + }); + (prisma.campaignItem.findMany as jest.Mock).mockResolvedValue([ + { id: 'item-adult', maxQuantity: 100 }, + { id: 'item-child', maxQuantity: 50 }, + ]); + (prisma.donationLineItem.aggregate as jest.Mock) + .mockResolvedValueOnce({ _sum: { quantity: 100 } }) + .mockResolvedValueOnce({ _sum: { quantity: 30 } }); // still 20 remaining + + await checkAndAutoCompleteCampaign('camp-dinner'); + + expect(prisma.campaign.update).not.toHaveBeenCalled(); + }); + + it('should NOT auto-complete EVENT when no items have capacity limits', async () => { + (prisma.campaign.findUnique as jest.Mock).mockResolvedValue({ + id: 'camp-dinner', + campaignType: 'EVENT', + status: 'ACTIVE', + tiers: [], + }); + (prisma.campaignItem.findMany as jest.Mock).mockResolvedValue([]); + + await checkAndAutoCompleteCampaign('camp-dinner'); + + expect(prisma.campaign.update).not.toHaveBeenCalled(); + }); }); }); @@ -379,6 +584,14 @@ describe('Campaign API Contract', () => { amount: z.number(), maxSlots: z.number().nullable(), })).optional(), + items: z.array(z.object({ + id: z.string(), + name: z.string(), + price: z.number(), + category: z.string().nullable().optional(), + maxQuantity: z.number().nullable().optional(), + isRequired: z.boolean().optional(), + })).optional(), }); it('should validate OPEN campaign response', () => { @@ -440,7 +653,29 @@ describe('Campaign API Contract', () => { expect(CampaignResponseSchema.safeParse(response).success).toBe(true); }); - it('should validate donation creation schema accepts unitCount and tierId', () => { + it('should validate EVENT campaign response', () => { + const response = { + id: 'camp-4', + name: 'Dinner Event', + description: 'Annual fundraising dinner', + targetAmount: 5000, + campaignType: 'EVENT', + unitPrice: null, + maxUnits: null, + unitLabel: null, + allowMultiUnit: true, + status: 'ACTIVE', + amountRaised: 1200, + tiers: [], + items: [ + { id: 'item-1', name: 'Adult Plate', price: 35, category: 'Plates', maxQuantity: 100, isRequired: false }, + { id: 'item-2', name: 'Gift', price: 30, category: 'Extras', maxQuantity: null, isRequired: true }, + ], + }; + expect(CampaignResponseSchema.safeParse(response).success).toBe(true); + }); + + it('should validate donation creation schema accepts lineItems', () => { const CreateDonationSchema = z.object({ type: z.enum(['ONE_TIME', 'PLEDGE']), amount: z.number().positive(), @@ -448,6 +683,10 @@ describe('Campaign API Contract', () => { campaignId: z.string().uuid().nullable().optional(), unitCount: z.number().int().positive().nullable().optional(), tierId: z.string().uuid().nullable().optional(), + lineItems: z.array(z.object({ + campaignItemId: z.string(), + quantity: z.number().int().positive(), + })).optional(), }); expect(CreateDonationSchema.safeParse({ @@ -470,5 +709,16 @@ describe('Campaign API Contract', () => { type: 'PLEDGE', amount: 100, }).success).toBe(true); + + // EVENT with lineItems + expect(CreateDonationSchema.safeParse({ + type: 'PLEDGE', + amount: 100, + campaignId: '550e8400-e29b-41d4-a716-446655440000', + lineItems: [ + { campaignItemId: 'item-1', quantity: 2 }, + { campaignItemId: 'item-2', quantity: 1 }, + ], + }).success).toBe(true); }); }); diff --git a/src/app/api/organizations/[slug]/campaigns/[id]/items/route.ts b/src/app/api/organizations/[slug]/campaigns/[id]/items/route.ts new file mode 100644 index 0000000..95349ad --- /dev/null +++ b/src/app/api/organizations/[slug]/campaigns/[id]/items/route.ts @@ -0,0 +1,145 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { prisma } from '@/lib/prisma'; +import { z } from 'zod'; +import { withOrgAuth, AuthError, authErrorResponse } from '@/lib/auth/with-org-auth'; +import { addCampaignItem, updateCampaignItem, deleteCampaignItem } from '@/services/campaign.service'; + +const itemSchema = z.object({ + name: z.string().min(1), + description: z.string().nullable().optional(), + category: z.string().nullable().optional(), + price: z.number().positive(), + maxQuantity: z.number().int().positive().nullable().optional(), + minPerOrder: z.number().int().min(0).optional(), + maxPerOrder: z.number().int().positive().nullable().optional(), + isRequired: z.boolean().optional(), + sortOrder: z.number().int().optional(), +}); + +const updateItemSchema = itemSchema.partial().extend({ + isActive: z.boolean().optional(), +}); + +/** + * GET /api/organizations/[slug]/campaigns/[id]/items + */ +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ slug: string; id: string }> } +) { + try { + const { id } = await params; + const items = await prisma.campaignItem.findMany({ + where: { campaignId: id, isActive: true }, + orderBy: { sortOrder: 'asc' }, + }); + return NextResponse.json(items.map(i => ({ ...i, price: Number(i.price) }))); + } catch (error) { + console.error('Error listing campaign items:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +/** + * POST /api/organizations/[slug]/campaigns/[id]/items + */ +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ slug: string; id: string }> } +) { + try { + const { slug, id } = await params; + const ctx = await withOrgAuth(slug, { requiredRole: 'ORG_ADMIN' }); + + const campaign = await prisma.campaign.findUnique({ where: { id } }); + if (!campaign || campaign.organizationId !== ctx.orgId) { + return NextResponse.json({ error: 'Campaign not found' }, { status: 404 }); + } + if (campaign.campaignType !== 'EVENT') { + return NextResponse.json({ error: 'Items can only be added to EVENT campaigns' }, { status: 400 }); + } + + const body = await request.json(); + const validated = itemSchema.parse(body); + const item = await addCampaignItem(id, validated); + + return NextResponse.json({ ...item, price: Number(item.price) }, { status: 201 }); + } catch (error) { + if (error instanceof AuthError) return authErrorResponse(error); + if (error instanceof z.ZodError) { + return NextResponse.json({ error: 'Invalid input', details: error.errors }, { status: 400 }); + } + console.error('Error creating campaign item:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +/** + * PATCH /api/organizations/[slug]/campaigns/[id]/items + * Body must include `itemId` to identify which item to update. + */ +export async function PATCH( + request: NextRequest, + { params }: { params: Promise<{ slug: string; id: string }> } +) { + try { + const { slug, id } = await params; + await withOrgAuth(slug, { requiredRole: 'ORG_ADMIN' }); + + const body = await request.json(); + const { itemId, ...updates } = body; + if (!itemId) { + return NextResponse.json({ error: 'itemId is required' }, { status: 400 }); + } + + // Verify item belongs to this campaign + const existing = await prisma.campaignItem.findUnique({ where: { id: itemId } }); + if (!existing || existing.campaignId !== id) { + return NextResponse.json({ error: 'Item not found' }, { status: 404 }); + } + + const validated = updateItemSchema.parse(updates); + const item = await updateCampaignItem(itemId, validated); + + return NextResponse.json({ ...item, price: Number(item.price) }); + } catch (error) { + if (error instanceof AuthError) return authErrorResponse(error); + if (error instanceof z.ZodError) { + return NextResponse.json({ error: 'Invalid input', details: error.errors }, { status: 400 }); + } + console.error('Error updating campaign item:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +/** + * DELETE /api/organizations/[slug]/campaigns/[id]/items + * Body must include `itemId`. + */ +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ slug: string; id: string }> } +) { + try { + const { slug, id } = await params; + await withOrgAuth(slug, { requiredRole: 'ORG_ADMIN' }); + + const body = await request.json(); + const { itemId } = body; + if (!itemId) { + return NextResponse.json({ error: 'itemId is required' }, { status: 400 }); + } + + const existing = await prisma.campaignItem.findUnique({ where: { id: itemId } }); + if (!existing || existing.campaignId !== id) { + return NextResponse.json({ error: 'Item not found' }, { status: 404 }); + } + + await deleteCampaignItem(itemId); + return NextResponse.json({ success: true }); + } catch (error) { + if (error instanceof AuthError) return authErrorResponse(error); + console.error('Error deleting campaign item:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/src/app/api/organizations/[slug]/campaigns/[id]/route.ts b/src/app/api/organizations/[slug]/campaigns/[id]/route.ts index 5bd9a1e..fc9cdbf 100644 --- a/src/app/api/organizations/[slug]/campaigns/[id]/route.ts +++ b/src/app/api/organizations/[slug]/campaigns/[id]/route.ts @@ -16,6 +16,25 @@ const updateCampaignSchema = z.object({ maxUnits: z.number().int().positive().nullable().optional(), unitLabel: z.string().nullable().optional(), allowMultiUnit: z.boolean().optional(), + tiers: z.array(z.object({ + id: z.string().optional(), + name: z.string().min(1), + amount: z.number().positive(), + maxSlots: z.number().int().positive().nullable().optional(), + sortOrder: z.number().int().optional(), + })).optional(), + items: z.array(z.object({ + id: z.string().optional(), + name: z.string().min(1), + description: z.string().nullable().optional(), + category: z.string().nullable().optional(), + price: z.number().positive(), + maxQuantity: z.number().int().positive().nullable().optional(), + minPerOrder: z.number().int().min(0).optional(), + maxPerOrder: z.number().int().positive().nullable().optional(), + isRequired: z.boolean().optional(), + sortOrder: z.number().int().optional(), + })).optional(), }); /** @@ -43,6 +62,7 @@ export async function GET( } const campaignTiers = (campaign as any).tiers || []; + const campaignItems = (campaign as any).items || []; const enrichedTiers = await Promise.all( campaignTiers.map(async (t: any) => ({ ...t, @@ -56,6 +76,7 @@ export async function GET( targetAmount: campaign.targetAmount ? Number(campaign.targetAmount) : null, unitPrice: campaign.unitPrice ? Number(campaign.unitPrice) : null, tiers: enrichedTiers, + items: campaignItems.map((i: any) => ({ ...i, price: Number(i.price) })), }); } catch (error) { console.error('Error getting campaign:', error); @@ -112,20 +133,101 @@ export async function PATCH( const body = await request.json(); const validated = updateCampaignSchema.parse(body); + const { tiers, items, ...campaignFields } = validated; + const updated = await updateCampaign(id, { - ...validated, - startDate: validated.startDate !== undefined - ? (validated.startDate ? new Date(validated.startDate) : null) + ...campaignFields, + startDate: campaignFields.startDate !== undefined + ? (campaignFields.startDate ? new Date(campaignFields.startDate) : null) : undefined, - endDate: validated.endDate !== undefined - ? (validated.endDate ? new Date(validated.endDate) : null) + endDate: campaignFields.endDate !== undefined + ? (campaignFields.endDate ? new Date(campaignFields.endDate) : null) : undefined, }); + // Sync TIERED tiers: upsert provided, deactivate removed + if (tiers !== undefined && existing.campaignType === 'TIERED') { + const existingTiers = await prisma.campaignTier.findMany({ where: { campaignId: id } }); + const existingIds = new Set(existingTiers.map(t => t.id)); + const incomingIds = new Set(tiers.filter(t => t.id).map(t => t.id!)); + + for (const [i, tier] of tiers.entries()) { + if (tier.id && existingIds.has(tier.id)) { + await prisma.campaignTier.update({ + where: { id: tier.id }, + data: { name: tier.name, amount: tier.amount, maxSlots: tier.maxSlots ?? null, sortOrder: tier.sortOrder ?? i }, + }); + } else { + await prisma.campaignTier.create({ + data: { campaignId: id, name: tier.name, amount: tier.amount, maxSlots: tier.maxSlots ?? null, sortOrder: tier.sortOrder ?? i }, + }); + } + } + // Delete tiers that were removed (only if no donations reference them) + for (const existing of existingTiers) { + if (!incomingIds.has(existing.id)) { + const donationCount = await prisma.donation.count({ where: { tierId: existing.id } }); + if (donationCount === 0) { + await prisma.campaignTier.delete({ where: { id: existing.id } }); + } + } + } + } + + // Sync EVENT items: upsert provided, soft-delete removed + if (items !== undefined && existing.campaignType === 'EVENT') { + const existingItems = await prisma.campaignItem.findMany({ where: { campaignId: id } }); + const existingIds = new Set(existingItems.map(i => i.id)); + const incomingIds = new Set(items.filter(i => i.id).map(i => i.id!)); + + for (const [i, item] of items.entries()) { + if (item.id && existingIds.has(item.id)) { + await prisma.campaignItem.update({ + where: { id: item.id }, + data: { + name: item.name, description: item.description ?? null, category: item.category ?? null, + price: item.price, maxQuantity: item.maxQuantity ?? null, + minPerOrder: item.minPerOrder ?? 0, maxPerOrder: item.maxPerOrder ?? null, + isRequired: item.isRequired ?? false, sortOrder: item.sortOrder ?? i, + }, + }); + } else { + await prisma.campaignItem.create({ + data: { + campaignId: id, name: item.name, description: item.description ?? null, + category: item.category ?? null, price: item.price, maxQuantity: item.maxQuantity ?? null, + minPerOrder: item.minPerOrder ?? 0, maxPerOrder: item.maxPerOrder ?? null, + isRequired: item.isRequired ?? false, sortOrder: item.sortOrder ?? i, + }, + }); + } + } + // Soft-delete items that were removed (preserve FK references) + for (const existing of existingItems) { + if (!incomingIds.has(existing.id)) { + await prisma.campaignItem.update({ + where: { id: existing.id }, + data: { isActive: false }, + }); + } + } + } + + // Re-fetch with tiers + items for response + const result = await prisma.campaign.findUnique({ + where: { id }, + include: { + tiers: { orderBy: { sortOrder: 'asc' } }, + items: { where: { isActive: true }, orderBy: { sortOrder: 'asc' } }, + }, + }); + return NextResponse.json({ - ...updated, - targetAmount: updated.targetAmount ? Number(updated.targetAmount) : null, - unitPrice: updated.unitPrice ? Number(updated.unitPrice) : null, + ...result, + targetAmount: result?.targetAmount ? Number(result.targetAmount) : null, + unitPrice: result?.unitPrice ? Number(result.unitPrice) : null, + tiers: (result as any)?.tiers?.map((t: any) => ({ ...t, amount: Number(t.amount) })) || [], + items: (result as any)?.items?.map((i: any) => ({ ...i, price: Number(i.price) })) || [], }); } catch (error: any) { if (error instanceof z.ZodError) { diff --git a/src/app/api/organizations/[slug]/campaigns/route.ts b/src/app/api/organizations/[slug]/campaigns/route.ts index a4f6829..4778516 100644 --- a/src/app/api/organizations/[slug]/campaigns/route.ts +++ b/src/app/api/organizations/[slug]/campaigns/route.ts @@ -13,7 +13,7 @@ const createCampaignSchema = z.object({ startDate: z.string().nullable().optional(), endDate: z.string().nullable().optional(), // Campaign constraint fields - campaignType: z.enum(['OPEN', 'FIXED_UNIT', 'TIERED']).optional(), + campaignType: z.enum(['OPEN', 'FIXED_UNIT', 'TIERED', 'EVENT']).optional(), unitPrice: z.number().positive().nullable().optional(), maxUnits: z.number().int().positive().nullable().optional(), unitLabel: z.string().nullable().optional(), @@ -25,6 +25,18 @@ const createCampaignSchema = z.object({ maxSlots: z.number().int().positive().nullable().optional(), sortOrder: z.number().int().optional(), })).optional(), + // Items for EVENT campaigns + items: z.array(z.object({ + name: z.string().min(1), + description: z.string().nullable().optional(), + category: z.string().nullable().optional(), + price: z.number().positive(), + maxQuantity: z.number().int().positive().nullable().optional(), + minPerOrder: z.number().int().min(0).optional(), + maxPerOrder: z.number().int().positive().nullable().optional(), + isRequired: z.boolean().optional(), + sortOrder: z.number().int().optional(), + })).optional(), }); /** @@ -66,9 +78,10 @@ export async function GET( const statusFilter = isAdmin ? undefined : 'ACTIVE'; const campaigns = await listCampaigns(organization.id, { statusFilter }); - // Serialize Decimal fields and enrich tiers + // Serialize Decimal fields and enrich tiers/items const serialized = await Promise.all(campaigns.map(async (c) => { const campaignTiers = (c as any).tiers || []; + const campaignItems = (c as any).items || []; const enrichedTiers = await Promise.all( campaignTiers.map(async (t: any) => ({ ...t, @@ -81,6 +94,7 @@ export async function GET( targetAmount: c.targetAmount ? Number(c.targetAmount) : null, unitPrice: c.unitPrice ? Number(c.unitPrice) : null, tiers: enrichedTiers, + items: campaignItems.map((i: any) => ({ ...i, price: Number(i.price) })), }; })); @@ -151,6 +165,7 @@ export async function POST( maxUnits: validated.maxUnits, unitLabel: validated.unitLabel, allowMultiUnit: validated.allowMultiUnit, + items: validated.items, }); // Create tiers for TIERED campaigns @@ -168,10 +183,13 @@ export async function POST( } } - // Re-fetch with tiers + // Re-fetch with tiers and items const result = await prisma.campaign.findUnique({ where: { id: campaign.id }, - include: { tiers: { orderBy: { sortOrder: 'asc' } } }, + include: { + tiers: { orderBy: { sortOrder: 'asc' } }, + items: { orderBy: { sortOrder: 'asc' } }, + }, }); return NextResponse.json(result, { status: 201 }); diff --git a/src/app/api/organizations/[slug]/donations/checkout/route.ts b/src/app/api/organizations/[slug]/donations/checkout/route.ts index ebb4d2b..ec07028 100644 --- a/src/app/api/organizations/[slug]/donations/checkout/route.ts +++ b/src/app/api/organizations/[slug]/donations/checkout/route.ts @@ -10,6 +10,10 @@ const checkoutSchema = z.object({ campaignId: z.string().nullable().optional(), tierId: z.string().nullable().optional(), unitCount: z.number().int().positive().nullable().optional(), + lineItems: z.array(z.object({ + campaignItemId: z.string().uuid(), + quantity: z.number().int().positive(), + })).optional(), donorName: z.string().min(1).optional(), donorEmail: z.string().email().optional(), donorMessage: z.string().optional(), @@ -90,26 +94,68 @@ export async function POST( stripeMethod.stripeFeeFixed ); + // Build Stripe line items + let stripeLineItems; + if (validated.lineItems && validated.lineItems.length > 0 && validated.campaignId) { + // EVENT campaign: one Stripe line item per campaign item + const campaign = await prisma.campaign.findUnique({ + where: { id: validated.campaignId }, + include: { items: { where: { isActive: true } } }, + }); + const itemMap = new Map( + (campaign?.items || []).map(i => [i.id, i]) + ); + stripeLineItems = validated.lineItems.map(li => { + const item = itemMap.get(li.campaignItemId); + const unitPrice = item ? Number(item.price) : 0; + // Apply fees proportionally to each item + const itemTotal = unitPrice * li.quantity; + const feeShare = itemTotal / validated.amount; + const itemWithFees = calculateTotalWithFees( + itemTotal, + stripeMethod.stripeFeePercent * feeShare, + stripeMethod.stripeFeeFixed * feeShare + ); + return { + price_data: { + currency: 'usd' as const, + product_data: { + name: item?.name || 'Event Item', + ...(item?.category ? { description: item.category } : {}), + }, + unit_amount: Math.round((itemWithFees / li.quantity) * 100), + }, + quantity: li.quantity, + }; + }); + description = campaign?.name + ? `${organization.name} — ${campaign.name}` + : description; + } else { + // Standard single line item + stripeLineItems = [ + { + price_data: { + currency: 'usd' as const, + product_data: { + name: description, + ...(validated.donorMessage + ? { description: validated.donorMessage } + : {}), + }, + unit_amount: Math.round(chargeAmount * 100), + }, + quantity: 1, + }, + ]; + } + // Create Checkout Session on the connected account const session = await getStripe().checkout.sessions.create( { mode: "payment", payment_method_types: ["card"], - line_items: [ - { - price_data: { - currency: "usd", - product_data: { - name: description, - ...(validated.donorMessage - ? { description: validated.donorMessage } - : {}), - }, - unit_amount: Math.round(chargeAmount * 100), // cents - }, - quantity: 1, - }, - ], + line_items: stripeLineItems, ...(validated.donorEmail ? { customer_email: validated.donorEmail } : {}), metadata: { organizationId: organization.id, @@ -122,6 +168,7 @@ export async function POST( isAnonymous: String(validated.isAnonymous || false), donationAmount: String(validated.amount), donationId: validated.donationId || "", + lineItems: validated.lineItems ? JSON.stringify(validated.lineItems) : "", }, success_url: `${baseUrl}/org/${slug}/donate/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: validated.campaignId diff --git a/src/app/api/organizations/[slug]/donations/route.ts b/src/app/api/organizations/[slug]/donations/route.ts index a1b03a1..bf44652 100644 --- a/src/app/api/organizations/[slug]/donations/route.ts +++ b/src/app/api/organizations/[slug]/donations/route.ts @@ -16,6 +16,10 @@ const createDonationSchema = z.object({ campaignId: z.string().uuid().nullable().optional(), unitCount: z.number().int().positive().nullable().optional(), tierId: z.string().uuid().nullable().optional(), + lineItems: z.array(z.object({ + campaignItemId: z.string().uuid(), + quantity: z.number().int().positive(), + })).optional(), }); /** @@ -230,6 +234,7 @@ export async function POST( campaignId, unitCount: validated.unitCount ?? undefined, tierId: validated.tierId ?? undefined, + lineItems: validated.lineItems ?? undefined, arAccountId: arAccount.id, revenueAccountId: revenueAccount.id, cashAccountId, diff --git a/src/app/api/webhooks/stripe/route.ts b/src/app/api/webhooks/stripe/route.ts index 6b94281..e999b98 100644 --- a/src/app/api/webhooks/stripe/route.ts +++ b/src/app/api/webhooks/stripe/route.ts @@ -293,6 +293,33 @@ async function handleNewDonation( }, }); + // Create line items for EVENT campaign donations + if (metadata.lineItems) { + try { + const lineItems = JSON.parse(metadata.lineItems) as Array<{ campaignItemId: string; quantity: number }>; + if (lineItems.length > 0) { + const items = await tx.campaignItem.findMany({ + where: { id: { in: lineItems.map(li => li.campaignItemId) } }, + }); + const itemMap = new Map(items.map(i => [i.id, i])); + for (const li of lineItems) { + const item = itemMap.get(li.campaignItemId); + if (!item) continue; + const unitPrice = Number(item.price); + await tx.donationLineItem.create({ + data: { + donationId: donation.id, + campaignItemId: li.campaignItemId, + quantity: li.quantity, + unitPrice, + subtotal: unitPrice * li.quantity, + }, + }); + } + } + } catch { /* ignore malformed lineItems */ } + } + donationRecord = donation; // Create StripePayment record for idempotency and audit trail diff --git a/src/app/org/[slug]/donate/[campaignId]/page.tsx b/src/app/org/[slug]/donate/[campaignId]/page.tsx index 64b5d54..d52d7d8 100644 --- a/src/app/org/[slug]/donate/[campaignId]/page.tsx +++ b/src/app/org/[slug]/donate/[campaignId]/page.tsx @@ -53,6 +53,17 @@ export default async function DonateCampaignPage({ params }: Props) { maxSlots: t.maxSlots, slotsFilled: t.slotsFilled ?? 0, })), + items: ((campaign as any).items || []).map((i: any) => ({ + id: i.id, + name: i.name, + description: i.description, + category: i.category, + price: Number(i.price), + maxQuantity: i.maxQuantity, + minPerOrder: i.minPerOrder, + maxPerOrder: i.maxPerOrder, + isRequired: i.isRequired, + })), }; return ( diff --git a/src/components/campaigns/CampaignForm.tsx b/src/components/campaigns/CampaignForm.tsx index a168779..0b111bc 100644 --- a/src/components/campaigns/CampaignForm.tsx +++ b/src/components/campaigns/CampaignForm.tsx @@ -22,6 +22,17 @@ interface TierInput { maxSlots: string; } +interface EventItemInput { + id?: string; + name: string; + category: string; + price: string; + maxQuantity: string; + minPerOrder: string; + maxPerOrder: string; + isRequired: boolean; +} + interface CampaignFormProps { organizationSlug: string; donationsAccountId?: string | null; @@ -40,6 +51,7 @@ interface CampaignFormProps { unitLabel?: string | null; allowMultiUnit?: boolean; tiers?: { id: string; name: string; amount: number; maxSlots: number | null; sortOrder?: number }[]; + items?: { id: string; name: string; category: string | null; price: number; maxQuantity: number | null; minPerOrder: number; maxPerOrder: number | null; isRequired: boolean; sortOrder?: number }[]; } | null; onSuccess: () => void; onCancel: () => void; @@ -90,6 +102,18 @@ export function CampaignForm({ maxSlots: t.maxSlots ? String(t.maxSlots) : "", })) || [] ); + const [eventItems, setEventItems] = useState( + campaign?.items?.map(i => ({ + id: i.id, + name: i.name, + category: i.category || "", + price: String(i.price), + maxQuantity: i.maxQuantity ? String(i.maxQuantity) : "", + minPerOrder: String(i.minPerOrder), + maxPerOrder: i.maxPerOrder ? String(i.maxPerOrder) : "", + isRequired: i.isRequired, + })) || [] + ); const isEditing = !!campaign; @@ -194,6 +218,24 @@ export function CampaignForm({ maxUnits: campaignType === 'FIXED_UNIT' && maxUnits ? parseInt(maxUnits) : null, unitLabel: campaignType === 'FIXED_UNIT' ? unitLabel || null : null, allowMultiUnit: campaignType === 'FIXED_UNIT' ? allowMultiUnit : true, + tiers: campaignType === 'TIERED' ? tiers.map((t, i) => ({ + id: t.id || undefined, + name: t.name, + amount: parseFloat(t.amount), + maxSlots: t.maxSlots ? parseInt(t.maxSlots) : null, + sortOrder: i, + })) : undefined, + items: campaignType === 'EVENT' ? eventItems.map((item, i) => ({ + id: item.id || undefined, + name: item.name, + category: item.category || null, + price: parseFloat(item.price), + maxQuantity: item.maxQuantity ? parseInt(item.maxQuantity) : null, + minPerOrder: parseInt(item.minPerOrder) || 0, + maxPerOrder: item.maxPerOrder ? parseInt(item.maxPerOrder) : null, + isRequired: item.isRequired, + sortOrder: i, + })) : undefined, } : { accountId: resolvedAccountId, @@ -213,6 +255,16 @@ export function CampaignForm({ maxSlots: t.maxSlots ? parseInt(t.maxSlots) : null, sortOrder: i, })) : undefined, + items: campaignType === 'EVENT' ? eventItems.map((item, i) => ({ + name: item.name, + category: item.category || null, + price: parseFloat(item.price), + maxQuantity: item.maxQuantity ? parseInt(item.maxQuantity) : null, + minPerOrder: parseInt(item.minPerOrder) || 0, + maxPerOrder: item.maxPerOrder ? parseInt(item.maxPerOrder) : null, + isRequired: item.isRequired, + sortOrder: i, + })) : undefined, }; const res = await fetch(url, { @@ -285,9 +337,21 @@ export function CampaignForm({ + )} + {isEditing && ( +
+ +

+ {campaignType === 'OPEN' && 'Open — Any donation amount'} + {campaignType === 'FIXED_UNIT' && 'Fixed Unit — Fixed price per unit'} + {campaignType === 'TIERED' && 'Tiered — Predefined donation levels'} + {campaignType === 'EVENT' && 'Event — Multiple purchasable items'} +

+
+ )} {/* FIXED_UNIT fields */} {campaignType === "FIXED_UNIT" && ( @@ -350,7 +414,7 @@ export function CampaignForm({ )} {/* TIERED fields */} - {campaignType === "TIERED" && !isEditing && ( + {campaignType === "TIERED" && (

Sponsorship Tiers

@@ -425,6 +489,137 @@ export function CampaignForm({
)} + {/* EVENT fields */} + {campaignType === "EVENT" && ( +
+
+

Event Items

+ +
+ {eventItems.length === 0 && ( +

Add items for donors to select (e.g., Adult Plate, Child Plate, Gift).

+ )} + {eventItems.map((item, i) => ( +
+
+
+ + { + const updated = [...eventItems]; + updated[i] = { ...item, name: e.target.value }; + setEventItems(updated); + }} + placeholder="e.g., Adult Dinner Plate" + required + /> +
+
+ + { + const updated = [...eventItems]; + updated[i] = { ...item, price: e.target.value }; + setEventItems(updated); + }} + placeholder="35.00" + required + /> +
+
+ + { + const updated = [...eventItems]; + updated[i] = { ...item, category: e.target.value }; + setEventItems(updated); + }} + placeholder="e.g., Plates" + /> +
+ +
+
+
+ + { + const updated = [...eventItems]; + updated[i] = { ...item, maxQuantity: e.target.value }; + setEventItems(updated); + }} + placeholder="∞" + /> +
+
+ + { + const updated = [...eventItems]; + updated[i] = { ...item, maxPerOrder: e.target.value }; + setEventItems(updated); + }} + placeholder="∞" + /> +
+
+ { + const updated = [...eventItems]; + updated[i] = { ...item, isRequired: e.target.checked }; + setEventItems(updated); + }} + className="h-4 w-4" + /> + +
+
+
+ ))} + {eventItems.length > 0 && eventItems.some(i => i.price) && ( +

+ {eventItems.filter(i => i.price && i.maxQuantity).length === eventItems.length && eventItems.length > 0 + ? `Max revenue: $${eventItems.reduce((sum, i) => sum + (parseFloat(i.price) || 0) * (parseInt(i.maxQuantity) || 0), 0).toFixed(2)}` + : `${eventItems.length} item(s) configured`} +

+ )} +
+ )} +
diff --git a/src/components/campaigns/CampaignList.tsx b/src/components/campaigns/CampaignList.tsx index dea6a1f..68266fe 100644 --- a/src/components/campaigns/CampaignList.tsx +++ b/src/components/campaigns/CampaignList.tsx @@ -41,6 +41,7 @@ interface Campaign { unitsSold?: number; unitsRemaining?: number; tiers?: { id: string; name: string; amount: number; maxSlots: number | null; sortOrder?: number; slotsFilled?: number }[]; + items?: { id: string; name: string; description?: string | null; category: string | null; price: number; maxQuantity: number | null; minPerOrder: number; maxPerOrder: number | null; isRequired: boolean; sortOrder?: number }[]; } interface CampaignListProps { diff --git a/src/components/campaigns/PublicCampaignPage.tsx b/src/components/campaigns/PublicCampaignPage.tsx index 619fa63..472c387 100644 --- a/src/components/campaigns/PublicCampaignPage.tsx +++ b/src/components/campaigns/PublicCampaignPage.tsx @@ -15,6 +15,18 @@ interface CampaignTier { slotsFilled: number; } +interface CampaignItem { + id: string; + name: string; + description: string | null; + category: string | null; + price: number; + maxQuantity: number | null; + minPerOrder: number; + maxPerOrder: number | null; + isRequired: boolean; +} + interface CampaignData { id: string; name: string; @@ -33,6 +45,7 @@ interface CampaignData { startDate: string | null; endDate: string | null; tiers: CampaignTier[]; + items?: CampaignItem[]; } interface PublicCampaignPageProps { @@ -94,6 +107,9 @@ export function PublicCampaignPage({ {campaign.campaignType === 'OPEN' && ( )} + {campaign.campaignType === 'EVENT' && ( + + )}
{/* Stats */} @@ -164,6 +180,47 @@ export function PublicCampaignPage({
)} + {/* Event Items */} + {campaign.campaignType === 'EVENT' && campaign.items && campaign.items.length > 0 && ( +
+

+ Available Items +

+ {(() => { + const grouped = campaign.items!.reduce((g: Record, item) => { + const cat = item.category || 'Items'; + if (!g[cat]) g[cat] = []; + g[cat].push(item); + return g; + }, {} as Record); + return Object.entries(grouped).map(([category, items]) => ( +
+

{category}

+ {items.map((item) => ( +
+
+

+ {item.name} + {item.isRequired && Required} +

+ {item.description && ( +

{item.description}

+ )} +
+

+ {formatCurrency(item.price)} +

+
+ ))} +
+ )); + })()} +
+ )} + {/* CTA */}
)} + {/* EVENT: Item selector with quantities */} + {isEvent && selectedCampaign?.items?.length > 0 && ( +
+ + {Object.entries(eventItemsByCategory).map(([category, items]) => ( +
+

{category}

+ {(items as any[]).map((item: any) => { + const qty = eventQuantities[item.id] || 0; + const maxQty = item.maxPerOrder || item.maxQuantity || 99; + return ( +
0 + ? 'border-amber-400 bg-amber-50' + : 'border-gray-200' + }`} + > +
+

+ {item.name} + {item.isRequired && ( + * + )} +

+ {item.description && ( +

{item.description}

+ )} +
+
+ + {formatCurrency(Number(item.price))} + +
+ + {qty} + +
+
+
+ ); + })} +
+ ))} + {eventTotal > 0 && ( +
+ Total: + {formatCurrency(eventTotal)} +
+ )} +
+ )} + {/* Amount field — hidden for constrained campaigns */} - {!isFixedUnit && !isTiered && ( + {!isFixedUnit && !isTiered && !isEvent && ( { @@ -378,6 +378,16 @@ export interface PrismaClient< */ get campaignTier(): Prisma.CampaignTierDelegate; + /** + * `prisma.campaignItem`: Exposes CRUD operations for the **CampaignItem** model. + * Example usage: + * ```ts + * // Fetch zero or more CampaignItems + * const campaignItems = await prisma.campaignItem.findMany() + * ``` + */ + get campaignItem(): Prisma.CampaignItemDelegate; + /** * `prisma.donation`: Exposes CRUD operations for the **Donation** model. * Example usage: @@ -388,6 +398,16 @@ export interface PrismaClient< */ get donation(): Prisma.DonationDelegate; + /** + * `prisma.donationLineItem`: Exposes CRUD operations for the **DonationLineItem** model. + * Example usage: + * ```ts + * // Fetch zero or more DonationLineItems + * const donationLineItems = await prisma.donationLineItem.findMany() + * ``` + */ + get donationLineItem(): Prisma.DonationLineItemDelegate; + /** * `prisma.contact`: Exposes CRUD operations for the **Contact** model. * Example usage: diff --git a/src/generated/prisma/internal/prismaNamespace.ts b/src/generated/prisma/internal/prismaNamespace.ts index c2857e6..78e7106 100644 --- a/src/generated/prisma/internal/prismaNamespace.ts +++ b/src/generated/prisma/internal/prismaNamespace.ts @@ -403,7 +403,9 @@ export const ModelName = { AccessRequest: 'AccessRequest', Campaign: 'Campaign', CampaignTier: 'CampaignTier', + CampaignItem: 'CampaignItem', Donation: 'Donation', + DonationLineItem: 'DonationLineItem', Contact: 'Contact', Bill: 'Bill', BillPayment: 'BillPayment', @@ -425,7 +427,7 @@ export type TypeMap + fields: Prisma.CampaignItemFieldRefs + operations: { + findUnique: { + args: Prisma.CampaignItemFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CampaignItemFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CampaignItemFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CampaignItemFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CampaignItemFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CampaignItemCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CampaignItemCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CampaignItemCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CampaignItemDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CampaignItemUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CampaignItemDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CampaignItemUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CampaignItemUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CampaignItemUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CampaignItemAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CampaignItemGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CampaignItemCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } Donation: { payload: Prisma.$DonationPayload fields: Prisma.DonationFieldRefs @@ -1909,6 +1985,80 @@ export type TypeMap + fields: Prisma.DonationLineItemFieldRefs + operations: { + findUnique: { + args: Prisma.DonationLineItemFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.DonationLineItemFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.DonationLineItemFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.DonationLineItemFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.DonationLineItemFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.DonationLineItemCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.DonationLineItemCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.DonationLineItemCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.DonationLineItemDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.DonationLineItemUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.DonationLineItemDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.DonationLineItemUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.DonationLineItemUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.DonationLineItemUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.DonationLineItemAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.DonationLineItemGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.DonationLineItemCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } Contact: { payload: Prisma.$ContactPayload fields: Prisma.ContactFieldRefs @@ -2790,6 +2940,24 @@ export const CampaignTierScalarFieldEnum = { export type CampaignTierScalarFieldEnum = (typeof CampaignTierScalarFieldEnum)[keyof typeof CampaignTierScalarFieldEnum] +export const CampaignItemScalarFieldEnum = { + id: 'id', + campaignId: 'campaignId', + name: 'name', + description: 'description', + category: 'category', + price: 'price', + maxQuantity: 'maxQuantity', + minPerOrder: 'minPerOrder', + maxPerOrder: 'maxPerOrder', + isRequired: 'isRequired', + sortOrder: 'sortOrder', + isActive: 'isActive' +} as const + +export type CampaignItemScalarFieldEnum = (typeof CampaignItemScalarFieldEnum)[keyof typeof CampaignItemScalarFieldEnum] + + export const DonationScalarFieldEnum = { id: 'id', organizationId: 'organizationId', @@ -2819,6 +2987,18 @@ export const DonationScalarFieldEnum = { export type DonationScalarFieldEnum = (typeof DonationScalarFieldEnum)[keyof typeof DonationScalarFieldEnum] +export const DonationLineItemScalarFieldEnum = { + id: 'id', + donationId: 'donationId', + campaignItemId: 'campaignItemId', + quantity: 'quantity', + unitPrice: 'unitPrice', + subtotal: 'subtotal' +} as const + +export type DonationLineItemScalarFieldEnum = (typeof DonationLineItemScalarFieldEnum)[keyof typeof DonationLineItemScalarFieldEnum] + + export const ContactScalarFieldEnum = { versionId: 'versionId', id: 'id', @@ -3544,7 +3724,9 @@ export type GlobalOmitConfig = { accessRequest?: Prisma.AccessRequestOmit campaign?: Prisma.CampaignOmit campaignTier?: Prisma.CampaignTierOmit + campaignItem?: Prisma.CampaignItemOmit donation?: Prisma.DonationOmit + donationLineItem?: Prisma.DonationLineItemOmit contact?: Prisma.ContactOmit bill?: Prisma.BillOmit billPayment?: Prisma.BillPaymentOmit diff --git a/src/generated/prisma/internal/prismaNamespaceBrowser.ts b/src/generated/prisma/internal/prismaNamespaceBrowser.ts index 1c095d4..87637e4 100644 --- a/src/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/src/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -70,7 +70,9 @@ export const ModelName = { AccessRequest: 'AccessRequest', Campaign: 'Campaign', CampaignTier: 'CampaignTier', + CampaignItem: 'CampaignItem', Donation: 'Donation', + DonationLineItem: 'DonationLineItem', Contact: 'Contact', Bill: 'Bill', BillPayment: 'BillPayment', @@ -493,6 +495,24 @@ export const CampaignTierScalarFieldEnum = { export type CampaignTierScalarFieldEnum = (typeof CampaignTierScalarFieldEnum)[keyof typeof CampaignTierScalarFieldEnum] +export const CampaignItemScalarFieldEnum = { + id: 'id', + campaignId: 'campaignId', + name: 'name', + description: 'description', + category: 'category', + price: 'price', + maxQuantity: 'maxQuantity', + minPerOrder: 'minPerOrder', + maxPerOrder: 'maxPerOrder', + isRequired: 'isRequired', + sortOrder: 'sortOrder', + isActive: 'isActive' +} as const + +export type CampaignItemScalarFieldEnum = (typeof CampaignItemScalarFieldEnum)[keyof typeof CampaignItemScalarFieldEnum] + + export const DonationScalarFieldEnum = { id: 'id', organizationId: 'organizationId', @@ -522,6 +542,18 @@ export const DonationScalarFieldEnum = { export type DonationScalarFieldEnum = (typeof DonationScalarFieldEnum)[keyof typeof DonationScalarFieldEnum] +export const DonationLineItemScalarFieldEnum = { + id: 'id', + donationId: 'donationId', + campaignItemId: 'campaignItemId', + quantity: 'quantity', + unitPrice: 'unitPrice', + subtotal: 'subtotal' +} as const + +export type DonationLineItemScalarFieldEnum = (typeof DonationLineItemScalarFieldEnum)[keyof typeof DonationLineItemScalarFieldEnum] + + export const ContactScalarFieldEnum = { versionId: 'versionId', id: 'id', diff --git a/src/generated/prisma/models.ts b/src/generated/prisma/models.ts index a680597..1f9ab54 100644 --- a/src/generated/prisma/models.ts +++ b/src/generated/prisma/models.ts @@ -27,7 +27,9 @@ export type * from './models/Invitation' export type * from './models/AccessRequest' export type * from './models/Campaign' export type * from './models/CampaignTier' +export type * from './models/CampaignItem' export type * from './models/Donation' +export type * from './models/DonationLineItem' export type * from './models/Contact' export type * from './models/Bill' export type * from './models/BillPayment' diff --git a/src/generated/prisma/models/Campaign.ts b/src/generated/prisma/models/Campaign.ts index a3fa045..77cde0c 100644 --- a/src/generated/prisma/models/Campaign.ts +++ b/src/generated/prisma/models/Campaign.ts @@ -322,6 +322,7 @@ export type CampaignWhereInput = { updatedAt?: Prisma.DateTimeFilter<"Campaign"> | Date | string donations?: Prisma.DonationListRelationFilter tiers?: Prisma.CampaignTierListRelationFilter + items?: Prisma.CampaignItemListRelationFilter } export type CampaignOrderByWithRelationInput = { @@ -344,6 +345,7 @@ export type CampaignOrderByWithRelationInput = { updatedAt?: Prisma.SortOrder donations?: Prisma.DonationOrderByRelationAggregateInput tiers?: Prisma.CampaignTierOrderByRelationAggregateInput + items?: Prisma.CampaignItemOrderByRelationAggregateInput } export type CampaignWhereUniqueInput = Prisma.AtLeast<{ @@ -369,6 +371,7 @@ export type CampaignWhereUniqueInput = Prisma.AtLeast<{ updatedAt?: Prisma.DateTimeFilter<"Campaign"> | Date | string donations?: Prisma.DonationListRelationFilter tiers?: Prisma.CampaignTierListRelationFilter + items?: Prisma.CampaignItemListRelationFilter }, "id"> export type CampaignOrderByWithAggregationInput = { @@ -439,6 +442,7 @@ export type CampaignCreateInput = { updatedAt?: Date | string donations?: Prisma.DonationCreateNestedManyWithoutCampaignInput tiers?: Prisma.CampaignTierCreateNestedManyWithoutCampaignInput + items?: Prisma.CampaignItemCreateNestedManyWithoutCampaignInput } export type CampaignUncheckedCreateInput = { @@ -461,6 +465,7 @@ export type CampaignUncheckedCreateInput = { updatedAt?: Date | string donations?: Prisma.DonationUncheckedCreateNestedManyWithoutCampaignInput tiers?: Prisma.CampaignTierUncheckedCreateNestedManyWithoutCampaignInput + items?: Prisma.CampaignItemUncheckedCreateNestedManyWithoutCampaignInput } export type CampaignUpdateInput = { @@ -483,6 +488,7 @@ export type CampaignUpdateInput = { updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string donations?: Prisma.DonationUpdateManyWithoutCampaignNestedInput tiers?: Prisma.CampaignTierUpdateManyWithoutCampaignNestedInput + items?: Prisma.CampaignItemUpdateManyWithoutCampaignNestedInput } export type CampaignUncheckedUpdateInput = { @@ -505,6 +511,7 @@ export type CampaignUncheckedUpdateInput = { updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string donations?: Prisma.DonationUncheckedUpdateManyWithoutCampaignNestedInput tiers?: Prisma.CampaignTierUncheckedUpdateManyWithoutCampaignNestedInput + items?: Prisma.CampaignItemUncheckedUpdateManyWithoutCampaignNestedInput } export type CampaignCreateManyInput = { @@ -679,6 +686,20 @@ export type CampaignUpdateOneRequiredWithoutTiersNestedInput = { update?: Prisma.XOR, Prisma.CampaignUncheckedUpdateWithoutTiersInput> } +export type CampaignCreateNestedOneWithoutItemsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CampaignCreateOrConnectWithoutItemsInput + connect?: Prisma.CampaignWhereUniqueInput +} + +export type CampaignUpdateOneRequiredWithoutItemsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CampaignCreateOrConnectWithoutItemsInput + upsert?: Prisma.CampaignUpsertWithoutItemsInput + connect?: Prisma.CampaignWhereUniqueInput + update?: Prisma.XOR, Prisma.CampaignUncheckedUpdateWithoutItemsInput> +} + export type CampaignCreateNestedOneWithoutDonationsInput = { create?: Prisma.XOR connectOrCreate?: Prisma.CampaignCreateOrConnectWithoutDonationsInput @@ -714,6 +735,7 @@ export type CampaignCreateWithoutTiersInput = { createdAt?: Date | string updatedAt?: Date | string donations?: Prisma.DonationCreateNestedManyWithoutCampaignInput + items?: Prisma.CampaignItemCreateNestedManyWithoutCampaignInput } export type CampaignUncheckedCreateWithoutTiersInput = { @@ -735,6 +757,7 @@ export type CampaignUncheckedCreateWithoutTiersInput = { createdAt?: Date | string updatedAt?: Date | string donations?: Prisma.DonationUncheckedCreateNestedManyWithoutCampaignInput + items?: Prisma.CampaignItemUncheckedCreateNestedManyWithoutCampaignInput } export type CampaignCreateOrConnectWithoutTiersInput = { @@ -772,6 +795,7 @@ export type CampaignUpdateWithoutTiersInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string donations?: Prisma.DonationUpdateManyWithoutCampaignNestedInput + items?: Prisma.CampaignItemUpdateManyWithoutCampaignNestedInput } export type CampaignUncheckedUpdateWithoutTiersInput = { @@ -793,6 +817,111 @@ export type CampaignUncheckedUpdateWithoutTiersInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string donations?: Prisma.DonationUncheckedUpdateManyWithoutCampaignNestedInput + items?: Prisma.CampaignItemUncheckedUpdateManyWithoutCampaignNestedInput +} + +export type CampaignCreateWithoutItemsInput = { + id?: string + organizationId: string + accountId: string + name: string + description?: string | null + targetAmount?: runtime.Decimal | runtime.DecimalJsLike | number | string | null + status?: $Enums.CampaignStatus + campaignType?: $Enums.CampaignType + unitPrice?: runtime.Decimal | runtime.DecimalJsLike | number | string | null + maxUnits?: number | null + unitLabel?: string | null + allowMultiUnit?: boolean + startDate?: Date | string | null + endDate?: Date | string | null + createdBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + donations?: Prisma.DonationCreateNestedManyWithoutCampaignInput + tiers?: Prisma.CampaignTierCreateNestedManyWithoutCampaignInput +} + +export type CampaignUncheckedCreateWithoutItemsInput = { + id?: string + organizationId: string + accountId: string + name: string + description?: string | null + targetAmount?: runtime.Decimal | runtime.DecimalJsLike | number | string | null + status?: $Enums.CampaignStatus + campaignType?: $Enums.CampaignType + unitPrice?: runtime.Decimal | runtime.DecimalJsLike | number | string | null + maxUnits?: number | null + unitLabel?: string | null + allowMultiUnit?: boolean + startDate?: Date | string | null + endDate?: Date | string | null + createdBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + donations?: Prisma.DonationUncheckedCreateNestedManyWithoutCampaignInput + tiers?: Prisma.CampaignTierUncheckedCreateNestedManyWithoutCampaignInput +} + +export type CampaignCreateOrConnectWithoutItemsInput = { + where: Prisma.CampaignWhereUniqueInput + create: Prisma.XOR +} + +export type CampaignUpsertWithoutItemsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CampaignWhereInput +} + +export type CampaignUpdateToOneWithWhereWithoutItemsInput = { + where?: Prisma.CampaignWhereInput + data: Prisma.XOR +} + +export type CampaignUpdateWithoutItemsInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + organizationId?: Prisma.StringFieldUpdateOperationsInput | string + accountId?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + targetAmount?: Prisma.NullableDecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string | null + status?: Prisma.EnumCampaignStatusFieldUpdateOperationsInput | $Enums.CampaignStatus + campaignType?: Prisma.EnumCampaignTypeFieldUpdateOperationsInput | $Enums.CampaignType + unitPrice?: Prisma.NullableDecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string | null + maxUnits?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + unitLabel?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + allowMultiUnit?: Prisma.BoolFieldUpdateOperationsInput | boolean + startDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + endDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + donations?: Prisma.DonationUpdateManyWithoutCampaignNestedInput + tiers?: Prisma.CampaignTierUpdateManyWithoutCampaignNestedInput +} + +export type CampaignUncheckedUpdateWithoutItemsInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + organizationId?: Prisma.StringFieldUpdateOperationsInput | string + accountId?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + targetAmount?: Prisma.NullableDecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string | null + status?: Prisma.EnumCampaignStatusFieldUpdateOperationsInput | $Enums.CampaignStatus + campaignType?: Prisma.EnumCampaignTypeFieldUpdateOperationsInput | $Enums.CampaignType + unitPrice?: Prisma.NullableDecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string | null + maxUnits?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + unitLabel?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + allowMultiUnit?: Prisma.BoolFieldUpdateOperationsInput | boolean + startDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + endDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + donations?: Prisma.DonationUncheckedUpdateManyWithoutCampaignNestedInput + tiers?: Prisma.CampaignTierUncheckedUpdateManyWithoutCampaignNestedInput } export type CampaignCreateWithoutDonationsInput = { @@ -814,6 +943,7 @@ export type CampaignCreateWithoutDonationsInput = { createdAt?: Date | string updatedAt?: Date | string tiers?: Prisma.CampaignTierCreateNestedManyWithoutCampaignInput + items?: Prisma.CampaignItemCreateNestedManyWithoutCampaignInput } export type CampaignUncheckedCreateWithoutDonationsInput = { @@ -835,6 +965,7 @@ export type CampaignUncheckedCreateWithoutDonationsInput = { createdAt?: Date | string updatedAt?: Date | string tiers?: Prisma.CampaignTierUncheckedCreateNestedManyWithoutCampaignInput + items?: Prisma.CampaignItemUncheckedCreateNestedManyWithoutCampaignInput } export type CampaignCreateOrConnectWithoutDonationsInput = { @@ -872,6 +1003,7 @@ export type CampaignUpdateWithoutDonationsInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string tiers?: Prisma.CampaignTierUpdateManyWithoutCampaignNestedInput + items?: Prisma.CampaignItemUpdateManyWithoutCampaignNestedInput } export type CampaignUncheckedUpdateWithoutDonationsInput = { @@ -893,6 +1025,7 @@ export type CampaignUncheckedUpdateWithoutDonationsInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string tiers?: Prisma.CampaignTierUncheckedUpdateManyWithoutCampaignNestedInput + items?: Prisma.CampaignItemUncheckedUpdateManyWithoutCampaignNestedInput } @@ -903,11 +1036,13 @@ export type CampaignUncheckedUpdateWithoutDonationsInput = { export type CampaignCountOutputType = { donations: number tiers: number + items: number } export type CampaignCountOutputTypeSelect = { donations?: boolean | CampaignCountOutputTypeCountDonationsArgs tiers?: boolean | CampaignCountOutputTypeCountTiersArgs + items?: boolean | CampaignCountOutputTypeCountItemsArgs } /** @@ -934,6 +1069,13 @@ export type CampaignCountOutputTypeCountTiersArgs = { + where?: Prisma.CampaignItemWhereInput +} + export type CampaignSelect = runtime.Types.Extensions.GetSelect<{ id?: boolean @@ -955,6 +1097,7 @@ export type CampaignSelect tiers?: boolean | Prisma.Campaign$tiersArgs + items?: boolean | Prisma.Campaign$itemsArgs _count?: boolean | Prisma.CampaignCountOutputTypeDefaultArgs }, ExtArgs["result"]["campaign"]> @@ -1022,6 +1165,7 @@ export type CampaignOmit = { donations?: boolean | Prisma.Campaign$donationsArgs tiers?: boolean | Prisma.Campaign$tiersArgs + items?: boolean | Prisma.Campaign$itemsArgs _count?: boolean | Prisma.CampaignCountOutputTypeDefaultArgs } export type CampaignIncludeCreateManyAndReturn = {} @@ -1032,6 +1176,7 @@ export type $CampaignPayload[] tiers: Prisma.$CampaignTierPayload[] + items: Prisma.$CampaignItemPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: string @@ -1447,6 +1592,7 @@ export interface Prisma__CampaignClient = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> tiers = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + items = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -1928,6 +2074,30 @@ export type Campaign$tiersArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + where?: Prisma.CampaignItemWhereInput + orderBy?: Prisma.CampaignItemOrderByWithRelationInput | Prisma.CampaignItemOrderByWithRelationInput[] + cursor?: Prisma.CampaignItemWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CampaignItemScalarFieldEnum | Prisma.CampaignItemScalarFieldEnum[] +} + /** * Campaign without action */ diff --git a/src/generated/prisma/models/CampaignItem.ts b/src/generated/prisma/models/CampaignItem.ts new file mode 100644 index 0000000..dd1b59b --- /dev/null +++ b/src/generated/prisma/models/CampaignItem.ts @@ -0,0 +1,1818 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `CampaignItem` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums" +import type * as Prisma from "../internal/prismaNamespace" + +/** + * Model CampaignItem + * + */ +export type CampaignItemModel = runtime.Types.Result.DefaultSelection + +export type AggregateCampaignItem = { + _count: CampaignItemCountAggregateOutputType | null + _avg: CampaignItemAvgAggregateOutputType | null + _sum: CampaignItemSumAggregateOutputType | null + _min: CampaignItemMinAggregateOutputType | null + _max: CampaignItemMaxAggregateOutputType | null +} + +export type CampaignItemAvgAggregateOutputType = { + price: runtime.Decimal | null + maxQuantity: number | null + minPerOrder: number | null + maxPerOrder: number | null + sortOrder: number | null +} + +export type CampaignItemSumAggregateOutputType = { + price: runtime.Decimal | null + maxQuantity: number | null + minPerOrder: number | null + maxPerOrder: number | null + sortOrder: number | null +} + +export type CampaignItemMinAggregateOutputType = { + id: string | null + campaignId: string | null + name: string | null + description: string | null + category: string | null + price: runtime.Decimal | null + maxQuantity: number | null + minPerOrder: number | null + maxPerOrder: number | null + isRequired: boolean | null + sortOrder: number | null + isActive: boolean | null +} + +export type CampaignItemMaxAggregateOutputType = { + id: string | null + campaignId: string | null + name: string | null + description: string | null + category: string | null + price: runtime.Decimal | null + maxQuantity: number | null + minPerOrder: number | null + maxPerOrder: number | null + isRequired: boolean | null + sortOrder: number | null + isActive: boolean | null +} + +export type CampaignItemCountAggregateOutputType = { + id: number + campaignId: number + name: number + description: number + category: number + price: number + maxQuantity: number + minPerOrder: number + maxPerOrder: number + isRequired: number + sortOrder: number + isActive: number + _all: number +} + + +export type CampaignItemAvgAggregateInputType = { + price?: true + maxQuantity?: true + minPerOrder?: true + maxPerOrder?: true + sortOrder?: true +} + +export type CampaignItemSumAggregateInputType = { + price?: true + maxQuantity?: true + minPerOrder?: true + maxPerOrder?: true + sortOrder?: true +} + +export type CampaignItemMinAggregateInputType = { + id?: true + campaignId?: true + name?: true + description?: true + category?: true + price?: true + maxQuantity?: true + minPerOrder?: true + maxPerOrder?: true + isRequired?: true + sortOrder?: true + isActive?: true +} + +export type CampaignItemMaxAggregateInputType = { + id?: true + campaignId?: true + name?: true + description?: true + category?: true + price?: true + maxQuantity?: true + minPerOrder?: true + maxPerOrder?: true + isRequired?: true + sortOrder?: true + isActive?: true +} + +export type CampaignItemCountAggregateInputType = { + id?: true + campaignId?: true + name?: true + description?: true + category?: true + price?: true + maxQuantity?: true + minPerOrder?: true + maxPerOrder?: true + isRequired?: true + sortOrder?: true + isActive?: true + _all?: true +} + +export type CampaignItemAggregateArgs = { + /** + * Filter which CampaignItem to aggregate. + */ + where?: Prisma.CampaignItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CampaignItems to fetch. + */ + orderBy?: Prisma.CampaignItemOrderByWithRelationInput | Prisma.CampaignItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.CampaignItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CampaignItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CampaignItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned CampaignItems + **/ + _count?: true | CampaignItemCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: CampaignItemAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: CampaignItemSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: CampaignItemMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: CampaignItemMaxAggregateInputType +} + +export type GetCampaignItemAggregateType = { + [P in keyof T & keyof AggregateCampaignItem]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type CampaignItemGroupByArgs = { + where?: Prisma.CampaignItemWhereInput + orderBy?: Prisma.CampaignItemOrderByWithAggregationInput | Prisma.CampaignItemOrderByWithAggregationInput[] + by: Prisma.CampaignItemScalarFieldEnum[] | Prisma.CampaignItemScalarFieldEnum + having?: Prisma.CampaignItemScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: CampaignItemCountAggregateInputType | true + _avg?: CampaignItemAvgAggregateInputType + _sum?: CampaignItemSumAggregateInputType + _min?: CampaignItemMinAggregateInputType + _max?: CampaignItemMaxAggregateInputType +} + +export type CampaignItemGroupByOutputType = { + id: string + campaignId: string + name: string + description: string | null + category: string | null + price: runtime.Decimal + maxQuantity: number | null + minPerOrder: number + maxPerOrder: number | null + isRequired: boolean + sortOrder: number + isActive: boolean + _count: CampaignItemCountAggregateOutputType | null + _avg: CampaignItemAvgAggregateOutputType | null + _sum: CampaignItemSumAggregateOutputType | null + _min: CampaignItemMinAggregateOutputType | null + _max: CampaignItemMaxAggregateOutputType | null +} + +type GetCampaignItemGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof CampaignItemGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type CampaignItemWhereInput = { + AND?: Prisma.CampaignItemWhereInput | Prisma.CampaignItemWhereInput[] + OR?: Prisma.CampaignItemWhereInput[] + NOT?: Prisma.CampaignItemWhereInput | Prisma.CampaignItemWhereInput[] + id?: Prisma.StringFilter<"CampaignItem"> | string + campaignId?: Prisma.StringFilter<"CampaignItem"> | string + name?: Prisma.StringFilter<"CampaignItem"> | string + description?: Prisma.StringNullableFilter<"CampaignItem"> | string | null + category?: Prisma.StringNullableFilter<"CampaignItem"> | string | null + price?: Prisma.DecimalFilter<"CampaignItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.IntNullableFilter<"CampaignItem"> | number | null + minPerOrder?: Prisma.IntFilter<"CampaignItem"> | number + maxPerOrder?: Prisma.IntNullableFilter<"CampaignItem"> | number | null + isRequired?: Prisma.BoolFilter<"CampaignItem"> | boolean + sortOrder?: Prisma.IntFilter<"CampaignItem"> | number + isActive?: Prisma.BoolFilter<"CampaignItem"> | boolean + campaign?: Prisma.XOR + donationLineItems?: Prisma.DonationLineItemListRelationFilter +} + +export type CampaignItemOrderByWithRelationInput = { + id?: Prisma.SortOrder + campaignId?: Prisma.SortOrder + name?: Prisma.SortOrder + description?: Prisma.SortOrderInput | Prisma.SortOrder + category?: Prisma.SortOrderInput | Prisma.SortOrder + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrderInput | Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrderInput | Prisma.SortOrder + isRequired?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + isActive?: Prisma.SortOrder + campaign?: Prisma.CampaignOrderByWithRelationInput + donationLineItems?: Prisma.DonationLineItemOrderByRelationAggregateInput +} + +export type CampaignItemWhereUniqueInput = Prisma.AtLeast<{ + id?: string + AND?: Prisma.CampaignItemWhereInput | Prisma.CampaignItemWhereInput[] + OR?: Prisma.CampaignItemWhereInput[] + NOT?: Prisma.CampaignItemWhereInput | Prisma.CampaignItemWhereInput[] + campaignId?: Prisma.StringFilter<"CampaignItem"> | string + name?: Prisma.StringFilter<"CampaignItem"> | string + description?: Prisma.StringNullableFilter<"CampaignItem"> | string | null + category?: Prisma.StringNullableFilter<"CampaignItem"> | string | null + price?: Prisma.DecimalFilter<"CampaignItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.IntNullableFilter<"CampaignItem"> | number | null + minPerOrder?: Prisma.IntFilter<"CampaignItem"> | number + maxPerOrder?: Prisma.IntNullableFilter<"CampaignItem"> | number | null + isRequired?: Prisma.BoolFilter<"CampaignItem"> | boolean + sortOrder?: Prisma.IntFilter<"CampaignItem"> | number + isActive?: Prisma.BoolFilter<"CampaignItem"> | boolean + campaign?: Prisma.XOR + donationLineItems?: Prisma.DonationLineItemListRelationFilter +}, "id"> + +export type CampaignItemOrderByWithAggregationInput = { + id?: Prisma.SortOrder + campaignId?: Prisma.SortOrder + name?: Prisma.SortOrder + description?: Prisma.SortOrderInput | Prisma.SortOrder + category?: Prisma.SortOrderInput | Prisma.SortOrder + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrderInput | Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrderInput | Prisma.SortOrder + isRequired?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + isActive?: Prisma.SortOrder + _count?: Prisma.CampaignItemCountOrderByAggregateInput + _avg?: Prisma.CampaignItemAvgOrderByAggregateInput + _max?: Prisma.CampaignItemMaxOrderByAggregateInput + _min?: Prisma.CampaignItemMinOrderByAggregateInput + _sum?: Prisma.CampaignItemSumOrderByAggregateInput +} + +export type CampaignItemScalarWhereWithAggregatesInput = { + AND?: Prisma.CampaignItemScalarWhereWithAggregatesInput | Prisma.CampaignItemScalarWhereWithAggregatesInput[] + OR?: Prisma.CampaignItemScalarWhereWithAggregatesInput[] + NOT?: Prisma.CampaignItemScalarWhereWithAggregatesInput | Prisma.CampaignItemScalarWhereWithAggregatesInput[] + id?: Prisma.StringWithAggregatesFilter<"CampaignItem"> | string + campaignId?: Prisma.StringWithAggregatesFilter<"CampaignItem"> | string + name?: Prisma.StringWithAggregatesFilter<"CampaignItem"> | string + description?: Prisma.StringNullableWithAggregatesFilter<"CampaignItem"> | string | null + category?: Prisma.StringNullableWithAggregatesFilter<"CampaignItem"> | string | null + price?: Prisma.DecimalWithAggregatesFilter<"CampaignItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.IntNullableWithAggregatesFilter<"CampaignItem"> | number | null + minPerOrder?: Prisma.IntWithAggregatesFilter<"CampaignItem"> | number + maxPerOrder?: Prisma.IntNullableWithAggregatesFilter<"CampaignItem"> | number | null + isRequired?: Prisma.BoolWithAggregatesFilter<"CampaignItem"> | boolean + sortOrder?: Prisma.IntWithAggregatesFilter<"CampaignItem"> | number + isActive?: Prisma.BoolWithAggregatesFilter<"CampaignItem"> | boolean +} + +export type CampaignItemCreateInput = { + id?: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean + campaign: Prisma.CampaignCreateNestedOneWithoutItemsInput + donationLineItems?: Prisma.DonationLineItemCreateNestedManyWithoutCampaignItemInput +} + +export type CampaignItemUncheckedCreateInput = { + id?: string + campaignId: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean + donationLineItems?: Prisma.DonationLineItemUncheckedCreateNestedManyWithoutCampaignItemInput +} + +export type CampaignItemUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean + campaign?: Prisma.CampaignUpdateOneRequiredWithoutItemsNestedInput + donationLineItems?: Prisma.DonationLineItemUpdateManyWithoutCampaignItemNestedInput +} + +export type CampaignItemUncheckedUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + campaignId?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean + donationLineItems?: Prisma.DonationLineItemUncheckedUpdateManyWithoutCampaignItemNestedInput +} + +export type CampaignItemCreateManyInput = { + id?: string + campaignId: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean +} + +export type CampaignItemUpdateManyMutationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean +} + +export type CampaignItemUncheckedUpdateManyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + campaignId?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean +} + +export type CampaignItemListRelationFilter = { + every?: Prisma.CampaignItemWhereInput + some?: Prisma.CampaignItemWhereInput + none?: Prisma.CampaignItemWhereInput +} + +export type CampaignItemOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type CampaignItemCountOrderByAggregateInput = { + id?: Prisma.SortOrder + campaignId?: Prisma.SortOrder + name?: Prisma.SortOrder + description?: Prisma.SortOrder + category?: Prisma.SortOrder + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrder + isRequired?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + isActive?: Prisma.SortOrder +} + +export type CampaignItemAvgOrderByAggregateInput = { + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder +} + +export type CampaignItemMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + campaignId?: Prisma.SortOrder + name?: Prisma.SortOrder + description?: Prisma.SortOrder + category?: Prisma.SortOrder + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrder + isRequired?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + isActive?: Prisma.SortOrder +} + +export type CampaignItemMinOrderByAggregateInput = { + id?: Prisma.SortOrder + campaignId?: Prisma.SortOrder + name?: Prisma.SortOrder + description?: Prisma.SortOrder + category?: Prisma.SortOrder + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrder + isRequired?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder + isActive?: Prisma.SortOrder +} + +export type CampaignItemSumOrderByAggregateInput = { + price?: Prisma.SortOrder + maxQuantity?: Prisma.SortOrder + minPerOrder?: Prisma.SortOrder + maxPerOrder?: Prisma.SortOrder + sortOrder?: Prisma.SortOrder +} + +export type CampaignItemScalarRelationFilter = { + is?: Prisma.CampaignItemWhereInput + isNot?: Prisma.CampaignItemWhereInput +} + +export type CampaignItemCreateNestedManyWithoutCampaignInput = { + create?: Prisma.XOR | Prisma.CampaignItemCreateWithoutCampaignInput[] | Prisma.CampaignItemUncheckedCreateWithoutCampaignInput[] + connectOrCreate?: Prisma.CampaignItemCreateOrConnectWithoutCampaignInput | Prisma.CampaignItemCreateOrConnectWithoutCampaignInput[] + createMany?: Prisma.CampaignItemCreateManyCampaignInputEnvelope + connect?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] +} + +export type CampaignItemUncheckedCreateNestedManyWithoutCampaignInput = { + create?: Prisma.XOR | Prisma.CampaignItemCreateWithoutCampaignInput[] | Prisma.CampaignItemUncheckedCreateWithoutCampaignInput[] + connectOrCreate?: Prisma.CampaignItemCreateOrConnectWithoutCampaignInput | Prisma.CampaignItemCreateOrConnectWithoutCampaignInput[] + createMany?: Prisma.CampaignItemCreateManyCampaignInputEnvelope + connect?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] +} + +export type CampaignItemUpdateManyWithoutCampaignNestedInput = { + create?: Prisma.XOR | Prisma.CampaignItemCreateWithoutCampaignInput[] | Prisma.CampaignItemUncheckedCreateWithoutCampaignInput[] + connectOrCreate?: Prisma.CampaignItemCreateOrConnectWithoutCampaignInput | Prisma.CampaignItemCreateOrConnectWithoutCampaignInput[] + upsert?: Prisma.CampaignItemUpsertWithWhereUniqueWithoutCampaignInput | Prisma.CampaignItemUpsertWithWhereUniqueWithoutCampaignInput[] + createMany?: Prisma.CampaignItemCreateManyCampaignInputEnvelope + set?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + disconnect?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + delete?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + connect?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + update?: Prisma.CampaignItemUpdateWithWhereUniqueWithoutCampaignInput | Prisma.CampaignItemUpdateWithWhereUniqueWithoutCampaignInput[] + updateMany?: Prisma.CampaignItemUpdateManyWithWhereWithoutCampaignInput | Prisma.CampaignItemUpdateManyWithWhereWithoutCampaignInput[] + deleteMany?: Prisma.CampaignItemScalarWhereInput | Prisma.CampaignItemScalarWhereInput[] +} + +export type CampaignItemUncheckedUpdateManyWithoutCampaignNestedInput = { + create?: Prisma.XOR | Prisma.CampaignItemCreateWithoutCampaignInput[] | Prisma.CampaignItemUncheckedCreateWithoutCampaignInput[] + connectOrCreate?: Prisma.CampaignItemCreateOrConnectWithoutCampaignInput | Prisma.CampaignItemCreateOrConnectWithoutCampaignInput[] + upsert?: Prisma.CampaignItemUpsertWithWhereUniqueWithoutCampaignInput | Prisma.CampaignItemUpsertWithWhereUniqueWithoutCampaignInput[] + createMany?: Prisma.CampaignItemCreateManyCampaignInputEnvelope + set?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + disconnect?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + delete?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + connect?: Prisma.CampaignItemWhereUniqueInput | Prisma.CampaignItemWhereUniqueInput[] + update?: Prisma.CampaignItemUpdateWithWhereUniqueWithoutCampaignInput | Prisma.CampaignItemUpdateWithWhereUniqueWithoutCampaignInput[] + updateMany?: Prisma.CampaignItemUpdateManyWithWhereWithoutCampaignInput | Prisma.CampaignItemUpdateManyWithWhereWithoutCampaignInput[] + deleteMany?: Prisma.CampaignItemScalarWhereInput | Prisma.CampaignItemScalarWhereInput[] +} + +export type CampaignItemCreateNestedOneWithoutDonationLineItemsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CampaignItemCreateOrConnectWithoutDonationLineItemsInput + connect?: Prisma.CampaignItemWhereUniqueInput +} + +export type CampaignItemUpdateOneRequiredWithoutDonationLineItemsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CampaignItemCreateOrConnectWithoutDonationLineItemsInput + upsert?: Prisma.CampaignItemUpsertWithoutDonationLineItemsInput + connect?: Prisma.CampaignItemWhereUniqueInput + update?: Prisma.XOR, Prisma.CampaignItemUncheckedUpdateWithoutDonationLineItemsInput> +} + +export type CampaignItemCreateWithoutCampaignInput = { + id?: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean + donationLineItems?: Prisma.DonationLineItemCreateNestedManyWithoutCampaignItemInput +} + +export type CampaignItemUncheckedCreateWithoutCampaignInput = { + id?: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean + donationLineItems?: Prisma.DonationLineItemUncheckedCreateNestedManyWithoutCampaignItemInput +} + +export type CampaignItemCreateOrConnectWithoutCampaignInput = { + where: Prisma.CampaignItemWhereUniqueInput + create: Prisma.XOR +} + +export type CampaignItemCreateManyCampaignInputEnvelope = { + data: Prisma.CampaignItemCreateManyCampaignInput | Prisma.CampaignItemCreateManyCampaignInput[] + skipDuplicates?: boolean +} + +export type CampaignItemUpsertWithWhereUniqueWithoutCampaignInput = { + where: Prisma.CampaignItemWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CampaignItemUpdateWithWhereUniqueWithoutCampaignInput = { + where: Prisma.CampaignItemWhereUniqueInput + data: Prisma.XOR +} + +export type CampaignItemUpdateManyWithWhereWithoutCampaignInput = { + where: Prisma.CampaignItemScalarWhereInput + data: Prisma.XOR +} + +export type CampaignItemScalarWhereInput = { + AND?: Prisma.CampaignItemScalarWhereInput | Prisma.CampaignItemScalarWhereInput[] + OR?: Prisma.CampaignItemScalarWhereInput[] + NOT?: Prisma.CampaignItemScalarWhereInput | Prisma.CampaignItemScalarWhereInput[] + id?: Prisma.StringFilter<"CampaignItem"> | string + campaignId?: Prisma.StringFilter<"CampaignItem"> | string + name?: Prisma.StringFilter<"CampaignItem"> | string + description?: Prisma.StringNullableFilter<"CampaignItem"> | string | null + category?: Prisma.StringNullableFilter<"CampaignItem"> | string | null + price?: Prisma.DecimalFilter<"CampaignItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.IntNullableFilter<"CampaignItem"> | number | null + minPerOrder?: Prisma.IntFilter<"CampaignItem"> | number + maxPerOrder?: Prisma.IntNullableFilter<"CampaignItem"> | number | null + isRequired?: Prisma.BoolFilter<"CampaignItem"> | boolean + sortOrder?: Prisma.IntFilter<"CampaignItem"> | number + isActive?: Prisma.BoolFilter<"CampaignItem"> | boolean +} + +export type CampaignItemCreateWithoutDonationLineItemsInput = { + id?: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean + campaign: Prisma.CampaignCreateNestedOneWithoutItemsInput +} + +export type CampaignItemUncheckedCreateWithoutDonationLineItemsInput = { + id?: string + campaignId: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean +} + +export type CampaignItemCreateOrConnectWithoutDonationLineItemsInput = { + where: Prisma.CampaignItemWhereUniqueInput + create: Prisma.XOR +} + +export type CampaignItemUpsertWithoutDonationLineItemsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CampaignItemWhereInput +} + +export type CampaignItemUpdateToOneWithWhereWithoutDonationLineItemsInput = { + where?: Prisma.CampaignItemWhereInput + data: Prisma.XOR +} + +export type CampaignItemUpdateWithoutDonationLineItemsInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean + campaign?: Prisma.CampaignUpdateOneRequiredWithoutItemsNestedInput +} + +export type CampaignItemUncheckedUpdateWithoutDonationLineItemsInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + campaignId?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean +} + +export type CampaignItemCreateManyCampaignInput = { + id?: string + name: string + description?: string | null + category?: string | null + price: runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: number | null + minPerOrder?: number + maxPerOrder?: number | null + isRequired?: boolean + sortOrder?: number + isActive?: boolean +} + +export type CampaignItemUpdateWithoutCampaignInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean + donationLineItems?: Prisma.DonationLineItemUpdateManyWithoutCampaignItemNestedInput +} + +export type CampaignItemUncheckedUpdateWithoutCampaignInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean + donationLineItems?: Prisma.DonationLineItemUncheckedUpdateManyWithoutCampaignItemNestedInput +} + +export type CampaignItemUncheckedUpdateManyWithoutCampaignInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + price?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + maxQuantity?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + minPerOrder?: Prisma.IntFieldUpdateOperationsInput | number + maxPerOrder?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + isRequired?: Prisma.BoolFieldUpdateOperationsInput | boolean + sortOrder?: Prisma.IntFieldUpdateOperationsInput | number + isActive?: Prisma.BoolFieldUpdateOperationsInput | boolean +} + + +/** + * Count Type CampaignItemCountOutputType + */ + +export type CampaignItemCountOutputType = { + donationLineItems: number +} + +export type CampaignItemCountOutputTypeSelect = { + donationLineItems?: boolean | CampaignItemCountOutputTypeCountDonationLineItemsArgs +} + +/** + * CampaignItemCountOutputType without action + */ +export type CampaignItemCountOutputTypeDefaultArgs = { + /** + * Select specific fields to fetch from the CampaignItemCountOutputType + */ + select?: Prisma.CampaignItemCountOutputTypeSelect | null +} + +/** + * CampaignItemCountOutputType without action + */ +export type CampaignItemCountOutputTypeCountDonationLineItemsArgs = { + where?: Prisma.DonationLineItemWhereInput +} + + +export type CampaignItemSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + campaignId?: boolean + name?: boolean + description?: boolean + category?: boolean + price?: boolean + maxQuantity?: boolean + minPerOrder?: boolean + maxPerOrder?: boolean + isRequired?: boolean + sortOrder?: boolean + isActive?: boolean + campaign?: boolean | Prisma.CampaignDefaultArgs + donationLineItems?: boolean | Prisma.CampaignItem$donationLineItemsArgs + _count?: boolean | Prisma.CampaignItemCountOutputTypeDefaultArgs +}, ExtArgs["result"]["campaignItem"]> + +export type CampaignItemSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + campaignId?: boolean + name?: boolean + description?: boolean + category?: boolean + price?: boolean + maxQuantity?: boolean + minPerOrder?: boolean + maxPerOrder?: boolean + isRequired?: boolean + sortOrder?: boolean + isActive?: boolean + campaign?: boolean | Prisma.CampaignDefaultArgs +}, ExtArgs["result"]["campaignItem"]> + +export type CampaignItemSelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + campaignId?: boolean + name?: boolean + description?: boolean + category?: boolean + price?: boolean + maxQuantity?: boolean + minPerOrder?: boolean + maxPerOrder?: boolean + isRequired?: boolean + sortOrder?: boolean + isActive?: boolean + campaign?: boolean | Prisma.CampaignDefaultArgs +}, ExtArgs["result"]["campaignItem"]> + +export type CampaignItemSelectScalar = { + id?: boolean + campaignId?: boolean + name?: boolean + description?: boolean + category?: boolean + price?: boolean + maxQuantity?: boolean + minPerOrder?: boolean + maxPerOrder?: boolean + isRequired?: boolean + sortOrder?: boolean + isActive?: boolean +} + +export type CampaignItemOmit = runtime.Types.Extensions.GetOmit<"id" | "campaignId" | "name" | "description" | "category" | "price" | "maxQuantity" | "minPerOrder" | "maxPerOrder" | "isRequired" | "sortOrder" | "isActive", ExtArgs["result"]["campaignItem"]> +export type CampaignItemInclude = { + campaign?: boolean | Prisma.CampaignDefaultArgs + donationLineItems?: boolean | Prisma.CampaignItem$donationLineItemsArgs + _count?: boolean | Prisma.CampaignItemCountOutputTypeDefaultArgs +} +export type CampaignItemIncludeCreateManyAndReturn = { + campaign?: boolean | Prisma.CampaignDefaultArgs +} +export type CampaignItemIncludeUpdateManyAndReturn = { + campaign?: boolean | Prisma.CampaignDefaultArgs +} + +export type $CampaignItemPayload = { + name: "CampaignItem" + objects: { + campaign: Prisma.$CampaignPayload + donationLineItems: Prisma.$DonationLineItemPayload[] + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: string + campaignId: string + name: string + description: string | null + category: string | null + price: runtime.Decimal + maxQuantity: number | null + minPerOrder: number + maxPerOrder: number | null + isRequired: boolean + sortOrder: number + isActive: boolean + }, ExtArgs["result"]["campaignItem"]> + composites: {} +} + +export type CampaignItemGetPayload = runtime.Types.Result.GetResult + +export type CampaignItemCountArgs = + Omit & { + select?: CampaignItemCountAggregateInputType | true + } + +export interface CampaignItemDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['CampaignItem'], meta: { name: 'CampaignItem' } } + /** + * Find zero or one CampaignItem that matches the filter. + * @param {CampaignItemFindUniqueArgs} args - Arguments to find a CampaignItem + * @example + * // Get one CampaignItem + * const campaignItem = await prisma.campaignItem.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one CampaignItem that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {CampaignItemFindUniqueOrThrowArgs} args - Arguments to find a CampaignItem + * @example + * // Get one CampaignItem + * const campaignItem = await prisma.campaignItem.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CampaignItem that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemFindFirstArgs} args - Arguments to find a CampaignItem + * @example + * // Get one CampaignItem + * const campaignItem = await prisma.campaignItem.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first CampaignItem that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemFindFirstOrThrowArgs} args - Arguments to find a CampaignItem + * @example + * // Get one CampaignItem + * const campaignItem = await prisma.campaignItem.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more CampaignItems that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all CampaignItems + * const campaignItems = await prisma.campaignItem.findMany() + * + * // Get first 10 CampaignItems + * const campaignItems = await prisma.campaignItem.findMany({ take: 10 }) + * + * // Only select the `id` + * const campaignItemWithIdOnly = await prisma.campaignItem.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a CampaignItem. + * @param {CampaignItemCreateArgs} args - Arguments to create a CampaignItem. + * @example + * // Create one CampaignItem + * const CampaignItem = await prisma.campaignItem.create({ + * data: { + * // ... data to create a CampaignItem + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many CampaignItems. + * @param {CampaignItemCreateManyArgs} args - Arguments to create many CampaignItems. + * @example + * // Create many CampaignItems + * const campaignItem = await prisma.campaignItem.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create many CampaignItems and returns the data saved in the database. + * @param {CampaignItemCreateManyAndReturnArgs} args - Arguments to create many CampaignItems. + * @example + * // Create many CampaignItems + * const campaignItem = await prisma.campaignItem.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many CampaignItems and only return the `id` + * const campaignItemWithIdOnly = await prisma.campaignItem.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "createManyAndReturn", GlobalOmitOptions>> + + /** + * Delete a CampaignItem. + * @param {CampaignItemDeleteArgs} args - Arguments to delete one CampaignItem. + * @example + * // Delete one CampaignItem + * const CampaignItem = await prisma.campaignItem.delete({ + * where: { + * // ... filter to delete one CampaignItem + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one CampaignItem. + * @param {CampaignItemUpdateArgs} args - Arguments to update one CampaignItem. + * @example + * // Update one CampaignItem + * const campaignItem = await prisma.campaignItem.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more CampaignItems. + * @param {CampaignItemDeleteManyArgs} args - Arguments to filter CampaignItems to delete. + * @example + * // Delete a few CampaignItems + * const { count } = await prisma.campaignItem.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CampaignItems. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many CampaignItems + * const campaignItem = await prisma.campaignItem.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CampaignItems and returns the data updated in the database. + * @param {CampaignItemUpdateManyAndReturnArgs} args - Arguments to update many CampaignItems. + * @example + * // Update many CampaignItems + * const campaignItem = await prisma.campaignItem.updateManyAndReturn({ + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * + * // Update zero or more CampaignItems and only return the `id` + * const campaignItemWithIdOnly = await prisma.campaignItem.updateManyAndReturn({ + * select: { id: true }, + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + updateManyAndReturn(args: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "updateManyAndReturn", GlobalOmitOptions>> + + /** + * Create or update one CampaignItem. + * @param {CampaignItemUpsertArgs} args - Arguments to update or create a CampaignItem. + * @example + * // Update or create a CampaignItem + * const campaignItem = await prisma.campaignItem.upsert({ + * create: { + * // ... data to create a CampaignItem + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the CampaignItem we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__CampaignItemClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of CampaignItems. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemCountArgs} args - Arguments to filter CampaignItems to count. + * @example + * // Count the number of CampaignItems + * const count = await prisma.campaignItem.count({ + * where: { + * // ... the filter for the CampaignItems we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a CampaignItem. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by CampaignItem. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CampaignItemGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends CampaignItemGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: CampaignItemGroupByArgs['orderBy'] } + : { orderBy?: CampaignItemGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetCampaignItemGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the CampaignItem model + */ +readonly fields: CampaignItemFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for CampaignItem. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__CampaignItemClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + campaign = {}>(args?: Prisma.Subset>): Prisma.Prisma__CampaignClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + donationLineItems = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the CampaignItem model + */ +export interface CampaignItemFieldRefs { + readonly id: Prisma.FieldRef<"CampaignItem", 'String'> + readonly campaignId: Prisma.FieldRef<"CampaignItem", 'String'> + readonly name: Prisma.FieldRef<"CampaignItem", 'String'> + readonly description: Prisma.FieldRef<"CampaignItem", 'String'> + readonly category: Prisma.FieldRef<"CampaignItem", 'String'> + readonly price: Prisma.FieldRef<"CampaignItem", 'Decimal'> + readonly maxQuantity: Prisma.FieldRef<"CampaignItem", 'Int'> + readonly minPerOrder: Prisma.FieldRef<"CampaignItem", 'Int'> + readonly maxPerOrder: Prisma.FieldRef<"CampaignItem", 'Int'> + readonly isRequired: Prisma.FieldRef<"CampaignItem", 'Boolean'> + readonly sortOrder: Prisma.FieldRef<"CampaignItem", 'Int'> + readonly isActive: Prisma.FieldRef<"CampaignItem", 'Boolean'> +} + + +// Custom InputTypes +/** + * CampaignItem findUnique + */ +export type CampaignItemFindUniqueArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * Filter, which CampaignItem to fetch. + */ + where: Prisma.CampaignItemWhereUniqueInput +} + +/** + * CampaignItem findUniqueOrThrow + */ +export type CampaignItemFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * Filter, which CampaignItem to fetch. + */ + where: Prisma.CampaignItemWhereUniqueInput +} + +/** + * CampaignItem findFirst + */ +export type CampaignItemFindFirstArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * Filter, which CampaignItem to fetch. + */ + where?: Prisma.CampaignItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CampaignItems to fetch. + */ + orderBy?: Prisma.CampaignItemOrderByWithRelationInput | Prisma.CampaignItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CampaignItems. + */ + cursor?: Prisma.CampaignItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CampaignItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CampaignItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CampaignItems. + */ + distinct?: Prisma.CampaignItemScalarFieldEnum | Prisma.CampaignItemScalarFieldEnum[] +} + +/** + * CampaignItem findFirstOrThrow + */ +export type CampaignItemFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * Filter, which CampaignItem to fetch. + */ + where?: Prisma.CampaignItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CampaignItems to fetch. + */ + orderBy?: Prisma.CampaignItemOrderByWithRelationInput | Prisma.CampaignItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CampaignItems. + */ + cursor?: Prisma.CampaignItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CampaignItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CampaignItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CampaignItems. + */ + distinct?: Prisma.CampaignItemScalarFieldEnum | Prisma.CampaignItemScalarFieldEnum[] +} + +/** + * CampaignItem findMany + */ +export type CampaignItemFindManyArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * Filter, which CampaignItems to fetch. + */ + where?: Prisma.CampaignItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CampaignItems to fetch. + */ + orderBy?: Prisma.CampaignItemOrderByWithRelationInput | Prisma.CampaignItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing CampaignItems. + */ + cursor?: Prisma.CampaignItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CampaignItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CampaignItems. + */ + skip?: number + distinct?: Prisma.CampaignItemScalarFieldEnum | Prisma.CampaignItemScalarFieldEnum[] +} + +/** + * CampaignItem create + */ +export type CampaignItemCreateArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * The data needed to create a CampaignItem. + */ + data: Prisma.XOR +} + +/** + * CampaignItem createMany + */ +export type CampaignItemCreateManyArgs = { + /** + * The data used to create many CampaignItems. + */ + data: Prisma.CampaignItemCreateManyInput | Prisma.CampaignItemCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * CampaignItem createManyAndReturn + */ +export type CampaignItemCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelectCreateManyAndReturn | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * The data used to create many CampaignItems. + */ + data: Prisma.CampaignItemCreateManyInput | Prisma.CampaignItemCreateManyInput[] + skipDuplicates?: boolean + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemIncludeCreateManyAndReturn | null +} + +/** + * CampaignItem update + */ +export type CampaignItemUpdateArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * The data needed to update a CampaignItem. + */ + data: Prisma.XOR + /** + * Choose, which CampaignItem to update. + */ + where: Prisma.CampaignItemWhereUniqueInput +} + +/** + * CampaignItem updateMany + */ +export type CampaignItemUpdateManyArgs = { + /** + * The data used to update CampaignItems. + */ + data: Prisma.XOR + /** + * Filter which CampaignItems to update + */ + where?: Prisma.CampaignItemWhereInput + /** + * Limit how many CampaignItems to update. + */ + limit?: number +} + +/** + * CampaignItem updateManyAndReturn + */ +export type CampaignItemUpdateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelectUpdateManyAndReturn | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * The data used to update CampaignItems. + */ + data: Prisma.XOR + /** + * Filter which CampaignItems to update + */ + where?: Prisma.CampaignItemWhereInput + /** + * Limit how many CampaignItems to update. + */ + limit?: number + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemIncludeUpdateManyAndReturn | null +} + +/** + * CampaignItem upsert + */ +export type CampaignItemUpsertArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * The filter to search for the CampaignItem to update in case it exists. + */ + where: Prisma.CampaignItemWhereUniqueInput + /** + * In case the CampaignItem found by the `where` argument doesn't exist, create a new CampaignItem with this data. + */ + create: Prisma.XOR + /** + * In case the CampaignItem was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * CampaignItem delete + */ +export type CampaignItemDeleteArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null + /** + * Filter which CampaignItem to delete. + */ + where: Prisma.CampaignItemWhereUniqueInput +} + +/** + * CampaignItem deleteMany + */ +export type CampaignItemDeleteManyArgs = { + /** + * Filter which CampaignItems to delete + */ + where?: Prisma.CampaignItemWhereInput + /** + * Limit how many CampaignItems to delete. + */ + limit?: number +} + +/** + * CampaignItem.donationLineItems + */ +export type CampaignItem$donationLineItemsArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + where?: Prisma.DonationLineItemWhereInput + orderBy?: Prisma.DonationLineItemOrderByWithRelationInput | Prisma.DonationLineItemOrderByWithRelationInput[] + cursor?: Prisma.DonationLineItemWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.DonationLineItemScalarFieldEnum | Prisma.DonationLineItemScalarFieldEnum[] +} + +/** + * CampaignItem without action + */ +export type CampaignItemDefaultArgs = { + /** + * Select specific fields to fetch from the CampaignItem + */ + select?: Prisma.CampaignItemSelect | null + /** + * Omit specific fields from the CampaignItem + */ + omit?: Prisma.CampaignItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CampaignItemInclude | null +} diff --git a/src/generated/prisma/models/Donation.ts b/src/generated/prisma/models/Donation.ts index d4b918d..c9c2090 100644 --- a/src/generated/prisma/models/Donation.ts +++ b/src/generated/prisma/models/Donation.ts @@ -370,6 +370,7 @@ export type DonationWhereInput = { updatedAt?: Prisma.DateTimeFilter<"Donation"> | Date | string campaign?: Prisma.XOR | null tier?: Prisma.XOR | null + lineItems?: Prisma.DonationLineItemListRelationFilter } export type DonationOrderByWithRelationInput = { @@ -398,6 +399,7 @@ export type DonationOrderByWithRelationInput = { updatedAt?: Prisma.SortOrder campaign?: Prisma.CampaignOrderByWithRelationInput tier?: Prisma.CampaignTierOrderByWithRelationInput + lineItems?: Prisma.DonationLineItemOrderByRelationAggregateInput } export type DonationWhereUniqueInput = Prisma.AtLeast<{ @@ -429,6 +431,7 @@ export type DonationWhereUniqueInput = Prisma.AtLeast<{ updatedAt?: Prisma.DateTimeFilter<"Donation"> | Date | string campaign?: Prisma.XOR | null tier?: Prisma.XOR | null + lineItems?: Prisma.DonationLineItemListRelationFilter }, "id" | "transactionId" | "billId"> export type DonationOrderByWithAggregationInput = { @@ -515,6 +518,7 @@ export type DonationCreateInput = { updatedAt?: Date | string campaign?: Prisma.CampaignCreateNestedOneWithoutDonationsInput tier?: Prisma.CampaignTierCreateNestedOneWithoutDonationsInput + lineItems?: Prisma.DonationLineItemCreateNestedManyWithoutDonationInput } export type DonationUncheckedCreateInput = { @@ -541,6 +545,7 @@ export type DonationUncheckedCreateInput = { createdBy?: string | null createdAt?: Date | string updatedAt?: Date | string + lineItems?: Prisma.DonationLineItemUncheckedCreateNestedManyWithoutDonationInput } export type DonationUpdateInput = { @@ -567,6 +572,7 @@ export type DonationUpdateInput = { updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string campaign?: Prisma.CampaignUpdateOneWithoutDonationsNestedInput tier?: Prisma.CampaignTierUpdateOneWithoutDonationsNestedInput + lineItems?: Prisma.DonationLineItemUpdateManyWithoutDonationNestedInput } export type DonationUncheckedUpdateInput = { @@ -593,6 +599,7 @@ export type DonationUncheckedUpdateInput = { createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + lineItems?: Prisma.DonationLineItemUncheckedUpdateManyWithoutDonationNestedInput } export type DonationCreateManyInput = { @@ -771,6 +778,11 @@ export type DonationSumOrderByAggregateInput = { unitCount?: Prisma.SortOrder } +export type DonationScalarRelationFilter = { + is?: Prisma.DonationWhereInput + isNot?: Prisma.DonationWhereInput +} + export type DonationCreateNestedManyWithoutCampaignInput = { create?: Prisma.XOR | Prisma.DonationCreateWithoutCampaignInput[] | Prisma.DonationUncheckedCreateWithoutCampaignInput[] connectOrCreate?: Prisma.DonationCreateOrConnectWithoutCampaignInput | Prisma.DonationCreateOrConnectWithoutCampaignInput[] @@ -863,6 +875,20 @@ export type EnumDonationStatusFieldUpdateOperationsInput = { set?: $Enums.DonationStatus } +export type DonationCreateNestedOneWithoutLineItemsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.DonationCreateOrConnectWithoutLineItemsInput + connect?: Prisma.DonationWhereUniqueInput +} + +export type DonationUpdateOneRequiredWithoutLineItemsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.DonationCreateOrConnectWithoutLineItemsInput + upsert?: Prisma.DonationUpsertWithoutLineItemsInput + connect?: Prisma.DonationWhereUniqueInput + update?: Prisma.XOR, Prisma.DonationUncheckedUpdateWithoutLineItemsInput> +} + export type DonationCreateWithoutCampaignInput = { id?: string organizationId: string @@ -886,6 +912,7 @@ export type DonationCreateWithoutCampaignInput = { createdAt?: Date | string updatedAt?: Date | string tier?: Prisma.CampaignTierCreateNestedOneWithoutDonationsInput + lineItems?: Prisma.DonationLineItemCreateNestedManyWithoutDonationInput } export type DonationUncheckedCreateWithoutCampaignInput = { @@ -911,6 +938,7 @@ export type DonationUncheckedCreateWithoutCampaignInput = { createdBy?: string | null createdAt?: Date | string updatedAt?: Date | string + lineItems?: Prisma.DonationLineItemUncheckedCreateNestedManyWithoutDonationInput } export type DonationCreateOrConnectWithoutCampaignInput = { @@ -991,6 +1019,7 @@ export type DonationCreateWithoutTierInput = { createdAt?: Date | string updatedAt?: Date | string campaign?: Prisma.CampaignCreateNestedOneWithoutDonationsInput + lineItems?: Prisma.DonationLineItemCreateNestedManyWithoutDonationInput } export type DonationUncheckedCreateWithoutTierInput = { @@ -1016,6 +1045,7 @@ export type DonationUncheckedCreateWithoutTierInput = { createdBy?: string | null createdAt?: Date | string updatedAt?: Date | string + lineItems?: Prisma.DonationLineItemUncheckedCreateNestedManyWithoutDonationInput } export type DonationCreateOrConnectWithoutTierInput = { @@ -1044,6 +1074,126 @@ export type DonationUpdateManyWithWhereWithoutTierInput = { data: Prisma.XOR } +export type DonationCreateWithoutLineItemsInput = { + id?: string + organizationId: string + contactId: string + type?: $Enums.DonationType + status?: $Enums.DonationStatus + amount: runtime.Decimal | runtime.DecimalJsLike | number | string + amountReceived?: runtime.Decimal | runtime.DecimalJsLike | number | string + description?: string | null + donorMessage?: string | null + isAnonymous?: boolean + isTaxDeductible?: boolean + donationDate: Date | string + receivedDate?: Date | string | null + dueDate?: Date | string | null + unitCount?: number | null + transactionId?: string | null + billId?: string | null + paymentMethod?: $Enums.PaymentMethod | null + createdBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + campaign?: Prisma.CampaignCreateNestedOneWithoutDonationsInput + tier?: Prisma.CampaignTierCreateNestedOneWithoutDonationsInput +} + +export type DonationUncheckedCreateWithoutLineItemsInput = { + id?: string + organizationId: string + contactId: string + type?: $Enums.DonationType + status?: $Enums.DonationStatus + amount: runtime.Decimal | runtime.DecimalJsLike | number | string + amountReceived?: runtime.Decimal | runtime.DecimalJsLike | number | string + description?: string | null + donorMessage?: string | null + isAnonymous?: boolean + isTaxDeductible?: boolean + donationDate: Date | string + receivedDate?: Date | string | null + dueDate?: Date | string | null + campaignId?: string | null + unitCount?: number | null + tierId?: string | null + transactionId?: string | null + billId?: string | null + paymentMethod?: $Enums.PaymentMethod | null + createdBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type DonationCreateOrConnectWithoutLineItemsInput = { + where: Prisma.DonationWhereUniqueInput + create: Prisma.XOR +} + +export type DonationUpsertWithoutLineItemsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.DonationWhereInput +} + +export type DonationUpdateToOneWithWhereWithoutLineItemsInput = { + where?: Prisma.DonationWhereInput + data: Prisma.XOR +} + +export type DonationUpdateWithoutLineItemsInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + organizationId?: Prisma.StringFieldUpdateOperationsInput | string + contactId?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.EnumDonationTypeFieldUpdateOperationsInput | $Enums.DonationType + status?: Prisma.EnumDonationStatusFieldUpdateOperationsInput | $Enums.DonationStatus + amount?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + amountReceived?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + donorMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + isAnonymous?: Prisma.BoolFieldUpdateOperationsInput | boolean + isTaxDeductible?: Prisma.BoolFieldUpdateOperationsInput | boolean + donationDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + receivedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dueDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + unitCount?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + transactionId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + billId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + paymentMethod?: Prisma.NullableEnumPaymentMethodFieldUpdateOperationsInput | $Enums.PaymentMethod | null + createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + campaign?: Prisma.CampaignUpdateOneWithoutDonationsNestedInput + tier?: Prisma.CampaignTierUpdateOneWithoutDonationsNestedInput +} + +export type DonationUncheckedUpdateWithoutLineItemsInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + organizationId?: Prisma.StringFieldUpdateOperationsInput | string + contactId?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.EnumDonationTypeFieldUpdateOperationsInput | $Enums.DonationType + status?: Prisma.EnumDonationStatusFieldUpdateOperationsInput | $Enums.DonationStatus + amount?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + amountReceived?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + donorMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + isAnonymous?: Prisma.BoolFieldUpdateOperationsInput | boolean + isTaxDeductible?: Prisma.BoolFieldUpdateOperationsInput | boolean + donationDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + receivedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dueDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + campaignId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + unitCount?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + tierId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + transactionId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + billId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + paymentMethod?: Prisma.NullableEnumPaymentMethodFieldUpdateOperationsInput | $Enums.PaymentMethod | null + createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + export type DonationCreateManyCampaignInput = { id?: string organizationId: string @@ -1092,6 +1242,7 @@ export type DonationUpdateWithoutCampaignInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string tier?: Prisma.CampaignTierUpdateOneWithoutDonationsNestedInput + lineItems?: Prisma.DonationLineItemUpdateManyWithoutDonationNestedInput } export type DonationUncheckedUpdateWithoutCampaignInput = { @@ -1117,6 +1268,7 @@ export type DonationUncheckedUpdateWithoutCampaignInput = { createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + lineItems?: Prisma.DonationLineItemUncheckedUpdateManyWithoutDonationNestedInput } export type DonationUncheckedUpdateManyWithoutCampaignInput = { @@ -1192,6 +1344,7 @@ export type DonationUpdateWithoutTierInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string campaign?: Prisma.CampaignUpdateOneWithoutDonationsNestedInput + lineItems?: Prisma.DonationLineItemUpdateManyWithoutDonationNestedInput } export type DonationUncheckedUpdateWithoutTierInput = { @@ -1217,6 +1370,7 @@ export type DonationUncheckedUpdateWithoutTierInput = { createdBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + lineItems?: Prisma.DonationLineItemUncheckedUpdateManyWithoutDonationNestedInput } export type DonationUncheckedUpdateManyWithoutTierInput = { @@ -1245,6 +1399,35 @@ export type DonationUncheckedUpdateManyWithoutTierInput = { } +/** + * Count Type DonationCountOutputType + */ + +export type DonationCountOutputType = { + lineItems: number +} + +export type DonationCountOutputTypeSelect = { + lineItems?: boolean | DonationCountOutputTypeCountLineItemsArgs +} + +/** + * DonationCountOutputType without action + */ +export type DonationCountOutputTypeDefaultArgs = { + /** + * Select specific fields to fetch from the DonationCountOutputType + */ + select?: Prisma.DonationCountOutputTypeSelect | null +} + +/** + * DonationCountOutputType without action + */ +export type DonationCountOutputTypeCountLineItemsArgs = { + where?: Prisma.DonationLineItemWhereInput +} + export type DonationSelect = runtime.Types.Extensions.GetSelect<{ id?: boolean @@ -1272,6 +1455,8 @@ export type DonationSelect tier?: boolean | Prisma.Donation$tierArgs + lineItems?: boolean | Prisma.Donation$lineItemsArgs + _count?: boolean | Prisma.DonationCountOutputTypeDefaultArgs }, ExtArgs["result"]["donation"]> export type DonationSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ @@ -1360,6 +1545,8 @@ export type DonationOmit = { campaign?: boolean | Prisma.Donation$campaignArgs tier?: boolean | Prisma.Donation$tierArgs + lineItems?: boolean | Prisma.Donation$lineItemsArgs + _count?: boolean | Prisma.DonationCountOutputTypeDefaultArgs } export type DonationIncludeCreateManyAndReturn = { campaign?: boolean | Prisma.Donation$campaignArgs @@ -1375,6 +1562,7 @@ export type $DonationPayload | null tier: Prisma.$CampaignTierPayload | null + lineItems: Prisma.$DonationLineItemPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: string @@ -1796,6 +1984,7 @@ export interface Prisma__DonationClient = {}>(args?: Prisma.Subset>): Prisma.Prisma__CampaignClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> tier = {}>(args?: Prisma.Subset>): Prisma.Prisma__CampaignTierClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + lineItems = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -2281,6 +2470,30 @@ export type Donation$tierArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + where?: Prisma.DonationLineItemWhereInput + orderBy?: Prisma.DonationLineItemOrderByWithRelationInput | Prisma.DonationLineItemOrderByWithRelationInput[] + cursor?: Prisma.DonationLineItemWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.DonationLineItemScalarFieldEnum | Prisma.DonationLineItemScalarFieldEnum[] +} + /** * Donation without action */ diff --git a/src/generated/prisma/models/DonationLineItem.ts b/src/generated/prisma/models/DonationLineItem.ts new file mode 100644 index 0000000..9513552 --- /dev/null +++ b/src/generated/prisma/models/DonationLineItem.ts @@ -0,0 +1,1557 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `DonationLineItem` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums" +import type * as Prisma from "../internal/prismaNamespace" + +/** + * Model DonationLineItem + * + */ +export type DonationLineItemModel = runtime.Types.Result.DefaultSelection + +export type AggregateDonationLineItem = { + _count: DonationLineItemCountAggregateOutputType | null + _avg: DonationLineItemAvgAggregateOutputType | null + _sum: DonationLineItemSumAggregateOutputType | null + _min: DonationLineItemMinAggregateOutputType | null + _max: DonationLineItemMaxAggregateOutputType | null +} + +export type DonationLineItemAvgAggregateOutputType = { + quantity: number | null + unitPrice: runtime.Decimal | null + subtotal: runtime.Decimal | null +} + +export type DonationLineItemSumAggregateOutputType = { + quantity: number | null + unitPrice: runtime.Decimal | null + subtotal: runtime.Decimal | null +} + +export type DonationLineItemMinAggregateOutputType = { + id: string | null + donationId: string | null + campaignItemId: string | null + quantity: number | null + unitPrice: runtime.Decimal | null + subtotal: runtime.Decimal | null +} + +export type DonationLineItemMaxAggregateOutputType = { + id: string | null + donationId: string | null + campaignItemId: string | null + quantity: number | null + unitPrice: runtime.Decimal | null + subtotal: runtime.Decimal | null +} + +export type DonationLineItemCountAggregateOutputType = { + id: number + donationId: number + campaignItemId: number + quantity: number + unitPrice: number + subtotal: number + _all: number +} + + +export type DonationLineItemAvgAggregateInputType = { + quantity?: true + unitPrice?: true + subtotal?: true +} + +export type DonationLineItemSumAggregateInputType = { + quantity?: true + unitPrice?: true + subtotal?: true +} + +export type DonationLineItemMinAggregateInputType = { + id?: true + donationId?: true + campaignItemId?: true + quantity?: true + unitPrice?: true + subtotal?: true +} + +export type DonationLineItemMaxAggregateInputType = { + id?: true + donationId?: true + campaignItemId?: true + quantity?: true + unitPrice?: true + subtotal?: true +} + +export type DonationLineItemCountAggregateInputType = { + id?: true + donationId?: true + campaignItemId?: true + quantity?: true + unitPrice?: true + subtotal?: true + _all?: true +} + +export type DonationLineItemAggregateArgs = { + /** + * Filter which DonationLineItem to aggregate. + */ + where?: Prisma.DonationLineItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of DonationLineItems to fetch. + */ + orderBy?: Prisma.DonationLineItemOrderByWithRelationInput | Prisma.DonationLineItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.DonationLineItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` DonationLineItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` DonationLineItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned DonationLineItems + **/ + _count?: true | DonationLineItemCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: DonationLineItemAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: DonationLineItemSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: DonationLineItemMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: DonationLineItemMaxAggregateInputType +} + +export type GetDonationLineItemAggregateType = { + [P in keyof T & keyof AggregateDonationLineItem]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type DonationLineItemGroupByArgs = { + where?: Prisma.DonationLineItemWhereInput + orderBy?: Prisma.DonationLineItemOrderByWithAggregationInput | Prisma.DonationLineItemOrderByWithAggregationInput[] + by: Prisma.DonationLineItemScalarFieldEnum[] | Prisma.DonationLineItemScalarFieldEnum + having?: Prisma.DonationLineItemScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: DonationLineItemCountAggregateInputType | true + _avg?: DonationLineItemAvgAggregateInputType + _sum?: DonationLineItemSumAggregateInputType + _min?: DonationLineItemMinAggregateInputType + _max?: DonationLineItemMaxAggregateInputType +} + +export type DonationLineItemGroupByOutputType = { + id: string + donationId: string + campaignItemId: string + quantity: number + unitPrice: runtime.Decimal + subtotal: runtime.Decimal + _count: DonationLineItemCountAggregateOutputType | null + _avg: DonationLineItemAvgAggregateOutputType | null + _sum: DonationLineItemSumAggregateOutputType | null + _min: DonationLineItemMinAggregateOutputType | null + _max: DonationLineItemMaxAggregateOutputType | null +} + +type GetDonationLineItemGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof DonationLineItemGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type DonationLineItemWhereInput = { + AND?: Prisma.DonationLineItemWhereInput | Prisma.DonationLineItemWhereInput[] + OR?: Prisma.DonationLineItemWhereInput[] + NOT?: Prisma.DonationLineItemWhereInput | Prisma.DonationLineItemWhereInput[] + id?: Prisma.StringFilter<"DonationLineItem"> | string + donationId?: Prisma.StringFilter<"DonationLineItem"> | string + campaignItemId?: Prisma.StringFilter<"DonationLineItem"> | string + quantity?: Prisma.IntFilter<"DonationLineItem"> | number + unitPrice?: Prisma.DecimalFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + donation?: Prisma.XOR + campaignItem?: Prisma.XOR +} + +export type DonationLineItemOrderByWithRelationInput = { + id?: Prisma.SortOrder + donationId?: Prisma.SortOrder + campaignItemId?: Prisma.SortOrder + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder + donation?: Prisma.DonationOrderByWithRelationInput + campaignItem?: Prisma.CampaignItemOrderByWithRelationInput +} + +export type DonationLineItemWhereUniqueInput = Prisma.AtLeast<{ + id?: string + AND?: Prisma.DonationLineItemWhereInput | Prisma.DonationLineItemWhereInput[] + OR?: Prisma.DonationLineItemWhereInput[] + NOT?: Prisma.DonationLineItemWhereInput | Prisma.DonationLineItemWhereInput[] + donationId?: Prisma.StringFilter<"DonationLineItem"> | string + campaignItemId?: Prisma.StringFilter<"DonationLineItem"> | string + quantity?: Prisma.IntFilter<"DonationLineItem"> | number + unitPrice?: Prisma.DecimalFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + donation?: Prisma.XOR + campaignItem?: Prisma.XOR +}, "id"> + +export type DonationLineItemOrderByWithAggregationInput = { + id?: Prisma.SortOrder + donationId?: Prisma.SortOrder + campaignItemId?: Prisma.SortOrder + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder + _count?: Prisma.DonationLineItemCountOrderByAggregateInput + _avg?: Prisma.DonationLineItemAvgOrderByAggregateInput + _max?: Prisma.DonationLineItemMaxOrderByAggregateInput + _min?: Prisma.DonationLineItemMinOrderByAggregateInput + _sum?: Prisma.DonationLineItemSumOrderByAggregateInput +} + +export type DonationLineItemScalarWhereWithAggregatesInput = { + AND?: Prisma.DonationLineItemScalarWhereWithAggregatesInput | Prisma.DonationLineItemScalarWhereWithAggregatesInput[] + OR?: Prisma.DonationLineItemScalarWhereWithAggregatesInput[] + NOT?: Prisma.DonationLineItemScalarWhereWithAggregatesInput | Prisma.DonationLineItemScalarWhereWithAggregatesInput[] + id?: Prisma.StringWithAggregatesFilter<"DonationLineItem"> | string + donationId?: Prisma.StringWithAggregatesFilter<"DonationLineItem"> | string + campaignItemId?: Prisma.StringWithAggregatesFilter<"DonationLineItem"> | string + quantity?: Prisma.IntWithAggregatesFilter<"DonationLineItem"> | number + unitPrice?: Prisma.DecimalWithAggregatesFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalWithAggregatesFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemCreateInput = { + id?: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string + donation: Prisma.DonationCreateNestedOneWithoutLineItemsInput + campaignItem: Prisma.CampaignItemCreateNestedOneWithoutDonationLineItemsInput +} + +export type DonationLineItemUncheckedCreateInput = { + id?: string + donationId: string + campaignItemId: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + donation?: Prisma.DonationUpdateOneRequiredWithoutLineItemsNestedInput + campaignItem?: Prisma.CampaignItemUpdateOneRequiredWithoutDonationLineItemsNestedInput +} + +export type DonationLineItemUncheckedUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + donationId?: Prisma.StringFieldUpdateOperationsInput | string + campaignItemId?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemCreateManyInput = { + id?: string + donationId: string + campaignItemId: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUpdateManyMutationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUncheckedUpdateManyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + donationId?: Prisma.StringFieldUpdateOperationsInput | string + campaignItemId?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemListRelationFilter = { + every?: Prisma.DonationLineItemWhereInput + some?: Prisma.DonationLineItemWhereInput + none?: Prisma.DonationLineItemWhereInput +} + +export type DonationLineItemOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type DonationLineItemCountOrderByAggregateInput = { + id?: Prisma.SortOrder + donationId?: Prisma.SortOrder + campaignItemId?: Prisma.SortOrder + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder +} + +export type DonationLineItemAvgOrderByAggregateInput = { + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder +} + +export type DonationLineItemMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + donationId?: Prisma.SortOrder + campaignItemId?: Prisma.SortOrder + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder +} + +export type DonationLineItemMinOrderByAggregateInput = { + id?: Prisma.SortOrder + donationId?: Prisma.SortOrder + campaignItemId?: Prisma.SortOrder + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder +} + +export type DonationLineItemSumOrderByAggregateInput = { + quantity?: Prisma.SortOrder + unitPrice?: Prisma.SortOrder + subtotal?: Prisma.SortOrder +} + +export type DonationLineItemCreateNestedManyWithoutCampaignItemInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutCampaignItemInput[] | Prisma.DonationLineItemUncheckedCreateWithoutCampaignItemInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput | Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput[] + createMany?: Prisma.DonationLineItemCreateManyCampaignItemInputEnvelope + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] +} + +export type DonationLineItemUncheckedCreateNestedManyWithoutCampaignItemInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutCampaignItemInput[] | Prisma.DonationLineItemUncheckedCreateWithoutCampaignItemInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput | Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput[] + createMany?: Prisma.DonationLineItemCreateManyCampaignItemInputEnvelope + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] +} + +export type DonationLineItemUpdateManyWithoutCampaignItemNestedInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutCampaignItemInput[] | Prisma.DonationLineItemUncheckedCreateWithoutCampaignItemInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput | Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput[] + upsert?: Prisma.DonationLineItemUpsertWithWhereUniqueWithoutCampaignItemInput | Prisma.DonationLineItemUpsertWithWhereUniqueWithoutCampaignItemInput[] + createMany?: Prisma.DonationLineItemCreateManyCampaignItemInputEnvelope + set?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + disconnect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + delete?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + update?: Prisma.DonationLineItemUpdateWithWhereUniqueWithoutCampaignItemInput | Prisma.DonationLineItemUpdateWithWhereUniqueWithoutCampaignItemInput[] + updateMany?: Prisma.DonationLineItemUpdateManyWithWhereWithoutCampaignItemInput | Prisma.DonationLineItemUpdateManyWithWhereWithoutCampaignItemInput[] + deleteMany?: Prisma.DonationLineItemScalarWhereInput | Prisma.DonationLineItemScalarWhereInput[] +} + +export type DonationLineItemUncheckedUpdateManyWithoutCampaignItemNestedInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutCampaignItemInput[] | Prisma.DonationLineItemUncheckedCreateWithoutCampaignItemInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput | Prisma.DonationLineItemCreateOrConnectWithoutCampaignItemInput[] + upsert?: Prisma.DonationLineItemUpsertWithWhereUniqueWithoutCampaignItemInput | Prisma.DonationLineItemUpsertWithWhereUniqueWithoutCampaignItemInput[] + createMany?: Prisma.DonationLineItemCreateManyCampaignItemInputEnvelope + set?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + disconnect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + delete?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + update?: Prisma.DonationLineItemUpdateWithWhereUniqueWithoutCampaignItemInput | Prisma.DonationLineItemUpdateWithWhereUniqueWithoutCampaignItemInput[] + updateMany?: Prisma.DonationLineItemUpdateManyWithWhereWithoutCampaignItemInput | Prisma.DonationLineItemUpdateManyWithWhereWithoutCampaignItemInput[] + deleteMany?: Prisma.DonationLineItemScalarWhereInput | Prisma.DonationLineItemScalarWhereInput[] +} + +export type DonationLineItemCreateNestedManyWithoutDonationInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutDonationInput[] | Prisma.DonationLineItemUncheckedCreateWithoutDonationInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutDonationInput | Prisma.DonationLineItemCreateOrConnectWithoutDonationInput[] + createMany?: Prisma.DonationLineItemCreateManyDonationInputEnvelope + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] +} + +export type DonationLineItemUncheckedCreateNestedManyWithoutDonationInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutDonationInput[] | Prisma.DonationLineItemUncheckedCreateWithoutDonationInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutDonationInput | Prisma.DonationLineItemCreateOrConnectWithoutDonationInput[] + createMany?: Prisma.DonationLineItemCreateManyDonationInputEnvelope + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] +} + +export type DonationLineItemUpdateManyWithoutDonationNestedInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutDonationInput[] | Prisma.DonationLineItemUncheckedCreateWithoutDonationInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutDonationInput | Prisma.DonationLineItemCreateOrConnectWithoutDonationInput[] + upsert?: Prisma.DonationLineItemUpsertWithWhereUniqueWithoutDonationInput | Prisma.DonationLineItemUpsertWithWhereUniqueWithoutDonationInput[] + createMany?: Prisma.DonationLineItemCreateManyDonationInputEnvelope + set?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + disconnect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + delete?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + update?: Prisma.DonationLineItemUpdateWithWhereUniqueWithoutDonationInput | Prisma.DonationLineItemUpdateWithWhereUniqueWithoutDonationInput[] + updateMany?: Prisma.DonationLineItemUpdateManyWithWhereWithoutDonationInput | Prisma.DonationLineItemUpdateManyWithWhereWithoutDonationInput[] + deleteMany?: Prisma.DonationLineItemScalarWhereInput | Prisma.DonationLineItemScalarWhereInput[] +} + +export type DonationLineItemUncheckedUpdateManyWithoutDonationNestedInput = { + create?: Prisma.XOR | Prisma.DonationLineItemCreateWithoutDonationInput[] | Prisma.DonationLineItemUncheckedCreateWithoutDonationInput[] + connectOrCreate?: Prisma.DonationLineItemCreateOrConnectWithoutDonationInput | Prisma.DonationLineItemCreateOrConnectWithoutDonationInput[] + upsert?: Prisma.DonationLineItemUpsertWithWhereUniqueWithoutDonationInput | Prisma.DonationLineItemUpsertWithWhereUniqueWithoutDonationInput[] + createMany?: Prisma.DonationLineItemCreateManyDonationInputEnvelope + set?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + disconnect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + delete?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + connect?: Prisma.DonationLineItemWhereUniqueInput | Prisma.DonationLineItemWhereUniqueInput[] + update?: Prisma.DonationLineItemUpdateWithWhereUniqueWithoutDonationInput | Prisma.DonationLineItemUpdateWithWhereUniqueWithoutDonationInput[] + updateMany?: Prisma.DonationLineItemUpdateManyWithWhereWithoutDonationInput | Prisma.DonationLineItemUpdateManyWithWhereWithoutDonationInput[] + deleteMany?: Prisma.DonationLineItemScalarWhereInput | Prisma.DonationLineItemScalarWhereInput[] +} + +export type DonationLineItemCreateWithoutCampaignItemInput = { + id?: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string + donation: Prisma.DonationCreateNestedOneWithoutLineItemsInput +} + +export type DonationLineItemUncheckedCreateWithoutCampaignItemInput = { + id?: string + donationId: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemCreateOrConnectWithoutCampaignItemInput = { + where: Prisma.DonationLineItemWhereUniqueInput + create: Prisma.XOR +} + +export type DonationLineItemCreateManyCampaignItemInputEnvelope = { + data: Prisma.DonationLineItemCreateManyCampaignItemInput | Prisma.DonationLineItemCreateManyCampaignItemInput[] + skipDuplicates?: boolean +} + +export type DonationLineItemUpsertWithWhereUniqueWithoutCampaignItemInput = { + where: Prisma.DonationLineItemWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type DonationLineItemUpdateWithWhereUniqueWithoutCampaignItemInput = { + where: Prisma.DonationLineItemWhereUniqueInput + data: Prisma.XOR +} + +export type DonationLineItemUpdateManyWithWhereWithoutCampaignItemInput = { + where: Prisma.DonationLineItemScalarWhereInput + data: Prisma.XOR +} + +export type DonationLineItemScalarWhereInput = { + AND?: Prisma.DonationLineItemScalarWhereInput | Prisma.DonationLineItemScalarWhereInput[] + OR?: Prisma.DonationLineItemScalarWhereInput[] + NOT?: Prisma.DonationLineItemScalarWhereInput | Prisma.DonationLineItemScalarWhereInput[] + id?: Prisma.StringFilter<"DonationLineItem"> | string + donationId?: Prisma.StringFilter<"DonationLineItem"> | string + campaignItemId?: Prisma.StringFilter<"DonationLineItem"> | string + quantity?: Prisma.IntFilter<"DonationLineItem"> | number + unitPrice?: Prisma.DecimalFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFilter<"DonationLineItem"> | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemCreateWithoutDonationInput = { + id?: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string + campaignItem: Prisma.CampaignItemCreateNestedOneWithoutDonationLineItemsInput +} + +export type DonationLineItemUncheckedCreateWithoutDonationInput = { + id?: string + campaignItemId: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemCreateOrConnectWithoutDonationInput = { + where: Prisma.DonationLineItemWhereUniqueInput + create: Prisma.XOR +} + +export type DonationLineItemCreateManyDonationInputEnvelope = { + data: Prisma.DonationLineItemCreateManyDonationInput | Prisma.DonationLineItemCreateManyDonationInput[] + skipDuplicates?: boolean +} + +export type DonationLineItemUpsertWithWhereUniqueWithoutDonationInput = { + where: Prisma.DonationLineItemWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type DonationLineItemUpdateWithWhereUniqueWithoutDonationInput = { + where: Prisma.DonationLineItemWhereUniqueInput + data: Prisma.XOR +} + +export type DonationLineItemUpdateManyWithWhereWithoutDonationInput = { + where: Prisma.DonationLineItemScalarWhereInput + data: Prisma.XOR +} + +export type DonationLineItemCreateManyCampaignItemInput = { + id?: string + donationId: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUpdateWithoutCampaignItemInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + donation?: Prisma.DonationUpdateOneRequiredWithoutLineItemsNestedInput +} + +export type DonationLineItemUncheckedUpdateWithoutCampaignItemInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + donationId?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUncheckedUpdateManyWithoutCampaignItemInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + donationId?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemCreateManyDonationInput = { + id?: string + campaignItemId: string + quantity: number + unitPrice: runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal: runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUpdateWithoutDonationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + campaignItem?: Prisma.CampaignItemUpdateOneRequiredWithoutDonationLineItemsNestedInput +} + +export type DonationLineItemUncheckedUpdateWithoutDonationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + campaignItemId?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + +export type DonationLineItemUncheckedUpdateManyWithoutDonationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + campaignItemId?: Prisma.StringFieldUpdateOperationsInput | string + quantity?: Prisma.IntFieldUpdateOperationsInput | number + unitPrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string + subtotal?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string +} + + + +export type DonationLineItemSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + donationId?: boolean + campaignItemId?: boolean + quantity?: boolean + unitPrice?: boolean + subtotal?: boolean + donation?: boolean | Prisma.DonationDefaultArgs + campaignItem?: boolean | Prisma.CampaignItemDefaultArgs +}, ExtArgs["result"]["donationLineItem"]> + +export type DonationLineItemSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + donationId?: boolean + campaignItemId?: boolean + quantity?: boolean + unitPrice?: boolean + subtotal?: boolean + donation?: boolean | Prisma.DonationDefaultArgs + campaignItem?: boolean | Prisma.CampaignItemDefaultArgs +}, ExtArgs["result"]["donationLineItem"]> + +export type DonationLineItemSelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + donationId?: boolean + campaignItemId?: boolean + quantity?: boolean + unitPrice?: boolean + subtotal?: boolean + donation?: boolean | Prisma.DonationDefaultArgs + campaignItem?: boolean | Prisma.CampaignItemDefaultArgs +}, ExtArgs["result"]["donationLineItem"]> + +export type DonationLineItemSelectScalar = { + id?: boolean + donationId?: boolean + campaignItemId?: boolean + quantity?: boolean + unitPrice?: boolean + subtotal?: boolean +} + +export type DonationLineItemOmit = runtime.Types.Extensions.GetOmit<"id" | "donationId" | "campaignItemId" | "quantity" | "unitPrice" | "subtotal", ExtArgs["result"]["donationLineItem"]> +export type DonationLineItemInclude = { + donation?: boolean | Prisma.DonationDefaultArgs + campaignItem?: boolean | Prisma.CampaignItemDefaultArgs +} +export type DonationLineItemIncludeCreateManyAndReturn = { + donation?: boolean | Prisma.DonationDefaultArgs + campaignItem?: boolean | Prisma.CampaignItemDefaultArgs +} +export type DonationLineItemIncludeUpdateManyAndReturn = { + donation?: boolean | Prisma.DonationDefaultArgs + campaignItem?: boolean | Prisma.CampaignItemDefaultArgs +} + +export type $DonationLineItemPayload = { + name: "DonationLineItem" + objects: { + donation: Prisma.$DonationPayload + campaignItem: Prisma.$CampaignItemPayload + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: string + donationId: string + campaignItemId: string + quantity: number + unitPrice: runtime.Decimal + subtotal: runtime.Decimal + }, ExtArgs["result"]["donationLineItem"]> + composites: {} +} + +export type DonationLineItemGetPayload = runtime.Types.Result.GetResult + +export type DonationLineItemCountArgs = + Omit & { + select?: DonationLineItemCountAggregateInputType | true + } + +export interface DonationLineItemDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['DonationLineItem'], meta: { name: 'DonationLineItem' } } + /** + * Find zero or one DonationLineItem that matches the filter. + * @param {DonationLineItemFindUniqueArgs} args - Arguments to find a DonationLineItem + * @example + * // Get one DonationLineItem + * const donationLineItem = await prisma.donationLineItem.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one DonationLineItem that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {DonationLineItemFindUniqueOrThrowArgs} args - Arguments to find a DonationLineItem + * @example + * // Get one DonationLineItem + * const donationLineItem = await prisma.donationLineItem.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first DonationLineItem that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemFindFirstArgs} args - Arguments to find a DonationLineItem + * @example + * // Get one DonationLineItem + * const donationLineItem = await prisma.donationLineItem.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first DonationLineItem that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemFindFirstOrThrowArgs} args - Arguments to find a DonationLineItem + * @example + * // Get one DonationLineItem + * const donationLineItem = await prisma.donationLineItem.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more DonationLineItems that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all DonationLineItems + * const donationLineItems = await prisma.donationLineItem.findMany() + * + * // Get first 10 DonationLineItems + * const donationLineItems = await prisma.donationLineItem.findMany({ take: 10 }) + * + * // Only select the `id` + * const donationLineItemWithIdOnly = await prisma.donationLineItem.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a DonationLineItem. + * @param {DonationLineItemCreateArgs} args - Arguments to create a DonationLineItem. + * @example + * // Create one DonationLineItem + * const DonationLineItem = await prisma.donationLineItem.create({ + * data: { + * // ... data to create a DonationLineItem + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many DonationLineItems. + * @param {DonationLineItemCreateManyArgs} args - Arguments to create many DonationLineItems. + * @example + * // Create many DonationLineItems + * const donationLineItem = await prisma.donationLineItem.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create many DonationLineItems and returns the data saved in the database. + * @param {DonationLineItemCreateManyAndReturnArgs} args - Arguments to create many DonationLineItems. + * @example + * // Create many DonationLineItems + * const donationLineItem = await prisma.donationLineItem.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many DonationLineItems and only return the `id` + * const donationLineItemWithIdOnly = await prisma.donationLineItem.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "createManyAndReturn", GlobalOmitOptions>> + + /** + * Delete a DonationLineItem. + * @param {DonationLineItemDeleteArgs} args - Arguments to delete one DonationLineItem. + * @example + * // Delete one DonationLineItem + * const DonationLineItem = await prisma.donationLineItem.delete({ + * where: { + * // ... filter to delete one DonationLineItem + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one DonationLineItem. + * @param {DonationLineItemUpdateArgs} args - Arguments to update one DonationLineItem. + * @example + * // Update one DonationLineItem + * const donationLineItem = await prisma.donationLineItem.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more DonationLineItems. + * @param {DonationLineItemDeleteManyArgs} args - Arguments to filter DonationLineItems to delete. + * @example + * // Delete a few DonationLineItems + * const { count } = await prisma.donationLineItem.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more DonationLineItems. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many DonationLineItems + * const donationLineItem = await prisma.donationLineItem.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more DonationLineItems and returns the data updated in the database. + * @param {DonationLineItemUpdateManyAndReturnArgs} args - Arguments to update many DonationLineItems. + * @example + * // Update many DonationLineItems + * const donationLineItem = await prisma.donationLineItem.updateManyAndReturn({ + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * + * // Update zero or more DonationLineItems and only return the `id` + * const donationLineItemWithIdOnly = await prisma.donationLineItem.updateManyAndReturn({ + * select: { id: true }, + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + updateManyAndReturn(args: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "updateManyAndReturn", GlobalOmitOptions>> + + /** + * Create or update one DonationLineItem. + * @param {DonationLineItemUpsertArgs} args - Arguments to update or create a DonationLineItem. + * @example + * // Update or create a DonationLineItem + * const donationLineItem = await prisma.donationLineItem.upsert({ + * create: { + * // ... data to create a DonationLineItem + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the DonationLineItem we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__DonationLineItemClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of DonationLineItems. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemCountArgs} args - Arguments to filter DonationLineItems to count. + * @example + * // Count the number of DonationLineItems + * const count = await prisma.donationLineItem.count({ + * where: { + * // ... the filter for the DonationLineItems we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a DonationLineItem. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by DonationLineItem. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {DonationLineItemGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends DonationLineItemGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: DonationLineItemGroupByArgs['orderBy'] } + : { orderBy?: DonationLineItemGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetDonationLineItemGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the DonationLineItem model + */ +readonly fields: DonationLineItemFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for DonationLineItem. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__DonationLineItemClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + donation = {}>(args?: Prisma.Subset>): Prisma.Prisma__DonationClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + campaignItem = {}>(args?: Prisma.Subset>): Prisma.Prisma__CampaignItemClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the DonationLineItem model + */ +export interface DonationLineItemFieldRefs { + readonly id: Prisma.FieldRef<"DonationLineItem", 'String'> + readonly donationId: Prisma.FieldRef<"DonationLineItem", 'String'> + readonly campaignItemId: Prisma.FieldRef<"DonationLineItem", 'String'> + readonly quantity: Prisma.FieldRef<"DonationLineItem", 'Int'> + readonly unitPrice: Prisma.FieldRef<"DonationLineItem", 'Decimal'> + readonly subtotal: Prisma.FieldRef<"DonationLineItem", 'Decimal'> +} + + +// Custom InputTypes +/** + * DonationLineItem findUnique + */ +export type DonationLineItemFindUniqueArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * Filter, which DonationLineItem to fetch. + */ + where: Prisma.DonationLineItemWhereUniqueInput +} + +/** + * DonationLineItem findUniqueOrThrow + */ +export type DonationLineItemFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * Filter, which DonationLineItem to fetch. + */ + where: Prisma.DonationLineItemWhereUniqueInput +} + +/** + * DonationLineItem findFirst + */ +export type DonationLineItemFindFirstArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * Filter, which DonationLineItem to fetch. + */ + where?: Prisma.DonationLineItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of DonationLineItems to fetch. + */ + orderBy?: Prisma.DonationLineItemOrderByWithRelationInput | Prisma.DonationLineItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for DonationLineItems. + */ + cursor?: Prisma.DonationLineItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` DonationLineItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` DonationLineItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of DonationLineItems. + */ + distinct?: Prisma.DonationLineItemScalarFieldEnum | Prisma.DonationLineItemScalarFieldEnum[] +} + +/** + * DonationLineItem findFirstOrThrow + */ +export type DonationLineItemFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * Filter, which DonationLineItem to fetch. + */ + where?: Prisma.DonationLineItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of DonationLineItems to fetch. + */ + orderBy?: Prisma.DonationLineItemOrderByWithRelationInput | Prisma.DonationLineItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for DonationLineItems. + */ + cursor?: Prisma.DonationLineItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` DonationLineItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` DonationLineItems. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of DonationLineItems. + */ + distinct?: Prisma.DonationLineItemScalarFieldEnum | Prisma.DonationLineItemScalarFieldEnum[] +} + +/** + * DonationLineItem findMany + */ +export type DonationLineItemFindManyArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * Filter, which DonationLineItems to fetch. + */ + where?: Prisma.DonationLineItemWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of DonationLineItems to fetch. + */ + orderBy?: Prisma.DonationLineItemOrderByWithRelationInput | Prisma.DonationLineItemOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing DonationLineItems. + */ + cursor?: Prisma.DonationLineItemWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` DonationLineItems from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` DonationLineItems. + */ + skip?: number + distinct?: Prisma.DonationLineItemScalarFieldEnum | Prisma.DonationLineItemScalarFieldEnum[] +} + +/** + * DonationLineItem create + */ +export type DonationLineItemCreateArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * The data needed to create a DonationLineItem. + */ + data: Prisma.XOR +} + +/** + * DonationLineItem createMany + */ +export type DonationLineItemCreateManyArgs = { + /** + * The data used to create many DonationLineItems. + */ + data: Prisma.DonationLineItemCreateManyInput | Prisma.DonationLineItemCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * DonationLineItem createManyAndReturn + */ +export type DonationLineItemCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelectCreateManyAndReturn | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * The data used to create many DonationLineItems. + */ + data: Prisma.DonationLineItemCreateManyInput | Prisma.DonationLineItemCreateManyInput[] + skipDuplicates?: boolean + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemIncludeCreateManyAndReturn | null +} + +/** + * DonationLineItem update + */ +export type DonationLineItemUpdateArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * The data needed to update a DonationLineItem. + */ + data: Prisma.XOR + /** + * Choose, which DonationLineItem to update. + */ + where: Prisma.DonationLineItemWhereUniqueInput +} + +/** + * DonationLineItem updateMany + */ +export type DonationLineItemUpdateManyArgs = { + /** + * The data used to update DonationLineItems. + */ + data: Prisma.XOR + /** + * Filter which DonationLineItems to update + */ + where?: Prisma.DonationLineItemWhereInput + /** + * Limit how many DonationLineItems to update. + */ + limit?: number +} + +/** + * DonationLineItem updateManyAndReturn + */ +export type DonationLineItemUpdateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelectUpdateManyAndReturn | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * The data used to update DonationLineItems. + */ + data: Prisma.XOR + /** + * Filter which DonationLineItems to update + */ + where?: Prisma.DonationLineItemWhereInput + /** + * Limit how many DonationLineItems to update. + */ + limit?: number + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemIncludeUpdateManyAndReturn | null +} + +/** + * DonationLineItem upsert + */ +export type DonationLineItemUpsertArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * The filter to search for the DonationLineItem to update in case it exists. + */ + where: Prisma.DonationLineItemWhereUniqueInput + /** + * In case the DonationLineItem found by the `where` argument doesn't exist, create a new DonationLineItem with this data. + */ + create: Prisma.XOR + /** + * In case the DonationLineItem was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * DonationLineItem delete + */ +export type DonationLineItemDeleteArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null + /** + * Filter which DonationLineItem to delete. + */ + where: Prisma.DonationLineItemWhereUniqueInput +} + +/** + * DonationLineItem deleteMany + */ +export type DonationLineItemDeleteManyArgs = { + /** + * Filter which DonationLineItems to delete + */ + where?: Prisma.DonationLineItemWhereInput + /** + * Limit how many DonationLineItems to delete. + */ + limit?: number +} + +/** + * DonationLineItem without action + */ +export type DonationLineItemDefaultArgs = { + /** + * Select specific fields to fetch from the DonationLineItem + */ + select?: Prisma.DonationLineItemSelect | null + /** + * Omit specific fields from the DonationLineItem + */ + omit?: Prisma.DonationLineItemOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.DonationLineItemInclude | null +} diff --git a/src/services/campaign.service.ts b/src/services/campaign.service.ts index 9c10d24..71afc35 100644 --- a/src/services/campaign.service.ts +++ b/src/services/campaign.service.ts @@ -6,7 +6,7 @@ import { prisma } from '@/lib/prisma'; import { buildCurrentVersionWhere } from '@/lib/temporal/temporal-utils'; import { getCampaignDonationSummary } from '@/services/donation.service'; -import type { Campaign } from '@/generated/prisma/client'; +import type { Campaign, CampaignItem } from '@/generated/prisma/client'; export interface CreateCampaignInput { organizationId: string; @@ -18,11 +18,24 @@ export interface CreateCampaignInput { endDate?: Date | null; createdBy?: string; // Campaign constraint fields - campaignType?: 'OPEN' | 'FIXED_UNIT' | 'TIERED'; + campaignType?: 'OPEN' | 'FIXED_UNIT' | 'TIERED' | 'EVENT'; unitPrice?: number | null; maxUnits?: number | null; unitLabel?: string | null; allowMultiUnit?: boolean; + items?: CreateCampaignItemInput[]; +} + +export interface CreateCampaignItemInput { + name: string; + description?: string | null; + category?: string | null; + price: number; + maxQuantity?: number | null; + minPerOrder?: number; + maxPerOrder?: number | null; + isRequired?: boolean; + sortOrder?: number; } export interface UpdateCampaignInput { @@ -88,11 +101,30 @@ export async function createCampaign(input: CreateCampaignInput): Promise i.maxQuantity != null); + if (allHaveMax) { + targetAmount = input.items.reduce( + (sum, i) => sum + i.price * (i.maxQuantity ?? 0), 0 + ); + } + } return await prisma.campaign.create({ data: { @@ -109,7 +141,23 @@ export async function createCampaign(input: CreateCampaignInput): Promise ({ + name: item.name, + description: item.description ?? null, + category: item.category ?? null, + price: item.price, + maxQuantity: item.maxQuantity ?? null, + minPerOrder: item.minPerOrder ?? 0, + maxPerOrder: item.maxPerOrder ?? null, + isRequired: item.isRequired ?? false, + sortOrder: item.sortOrder ?? idx, + })), + }, + }), }, + include: campaignType === 'EVENT' ? { items: { orderBy: { sortOrder: 'asc' } } } : undefined, }); } @@ -159,7 +207,10 @@ export async function listCampaigns( const campaigns = await prisma.campaign.findMany({ where, - include: { tiers: { orderBy: { sortOrder: 'asc' } } }, + include: { + tiers: { orderBy: { sortOrder: 'asc' } }, + items: { orderBy: { sortOrder: 'asc' } }, + }, orderBy: { createdAt: 'desc' }, }); @@ -180,7 +231,10 @@ export async function getCampaignById( ): Promise { const campaign = await prisma.campaign.findUnique({ where: { id: campaignId }, - include: { tiers: { orderBy: { sortOrder: 'asc' } } }, + include: { + tiers: { orderBy: { sortOrder: 'asc' } }, + items: { where: { isActive: true }, orderBy: { sortOrder: 'asc' } }, + }, }); if (!campaign) return null; @@ -271,6 +325,11 @@ export async function getTierSlotsFilled(tierId: string): Promise { }); } +export interface EventLineItemInput { + campaignItemId: string; + quantity: number; +} + /** * Validate a donation against campaign constraints. * Throws if the donation violates any campaign rules. @@ -279,11 +338,12 @@ export async function validateDonationAgainstCampaign( campaignId: string, amount: number, unitCount?: number | null, - tierId?: string | null + tierId?: string | null, + lineItems?: EventLineItemInput[] ): Promise { const campaign = await prisma.campaign.findUnique({ where: { id: campaignId }, - include: { tiers: true }, + include: { tiers: true, items: { where: { isActive: true } } }, }); if (!campaign) throw new Error('Campaign not found'); @@ -335,6 +395,13 @@ export async function validateDonationAgainstCampaign( } break; } + case 'EVENT': { + if (!lineItems || lineItems.length === 0) { + throw new Error('EVENT campaigns require selecting at least one item'); + } + await validateEventLineItems(campaign, lineItems, amount); + break; + } // OPEN: no constraints } } @@ -357,7 +424,6 @@ export async function checkAndAutoCompleteCampaign(campaignId: string): Promise< } else if (campaign.campaignType === 'TIERED') { const tiersWithCaps = campaign.tiers.filter(t => t.maxSlots != null); if (tiersWithCaps.length > 0 && tiersWithCaps.length === campaign.tiers.length) { - // All tiers have caps — check if all are full const checks = await Promise.all( tiersWithCaps.map(async (t) => { const filled = await getTierSlotsFilled(t.id); @@ -366,6 +432,19 @@ export async function checkAndAutoCompleteCampaign(campaignId: string): Promise< ); shouldComplete = checks.every(Boolean); } + } else if (campaign.campaignType === 'EVENT') { + const items = await prisma.campaignItem.findMany({ + where: { campaignId, isActive: true, maxQuantity: { not: null } }, + }); + if (items.length > 0) { + const checks = await Promise.all( + items.map(async (item) => { + const sold = await getItemQuantitySold(item.id); + return sold >= item.maxQuantity!; + }) + ); + shouldComplete = checks.every(Boolean); + } } if (shouldComplete) { @@ -375,3 +454,155 @@ export async function checkAndAutoCompleteCampaign(campaignId: string): Promise< }); } } + +/** + * Get total quantity sold for a specific campaign item. + */ +export async function getItemQuantitySold(campaignItemId: string): Promise { + const result = await prisma.donationLineItem.aggregate({ + where: { + campaignItemId, + donation: { status: { notIn: ['CANCELLED'] } }, + }, + _sum: { quantity: true }, + }); + return result._sum.quantity ?? 0; +} + +/** + * Validate EVENT line items against campaign item constraints. + */ +async function validateEventLineItems( + campaign: Campaign & { items: CampaignItem[] }, + lineItems: EventLineItemInput[], + amount: number +): Promise { + const itemMap = new Map(campaign.items.map(i => [i.id, i])); + + // Check all line items reference valid active items in this campaign + let expectedTotal = 0; + for (const li of lineItems) { + const item = itemMap.get(li.campaignItemId); + if (!item) { + throw new Error(`Campaign item not found or inactive: ${li.campaignItemId}`); + } + if (li.quantity < 1) { + throw new Error(`Quantity must be at least 1 for "${item.name}"`); + } + if (item.maxPerOrder != null && li.quantity > item.maxPerOrder) { + throw new Error(`Maximum ${item.maxPerOrder} of "${item.name}" per order`); + } + if (item.minPerOrder > 0 && li.quantity < item.minPerOrder) { + throw new Error(`Minimum ${item.minPerOrder} of "${item.name}" per order`); + } + // Check total capacity + if (item.maxQuantity != null) { + const sold = await getItemQuantitySold(item.id); + if (sold + li.quantity > item.maxQuantity) { + const remaining = item.maxQuantity - sold; + throw new Error(`Only ${remaining} of "${item.name}" remaining (requested ${li.quantity})`); + } + } + expectedTotal += Number(item.price) * li.quantity; + } + + // Check required items are present + for (const item of campaign.items) { + if (item.isRequired) { + const selected = lineItems.find(li => li.campaignItemId === item.id); + if (!selected || selected.quantity < 1) { + throw new Error(`"${item.name}" is required`); + } + } + } + + // Verify total amount matches + if (Math.abs(amount - expectedTotal) > 0.01) { + throw new Error( + `Donation amount ($${amount.toFixed(2)}) does not match item total ($${expectedTotal.toFixed(2)})` + ); + } +} + +// ============================================ +// CAMPAIGN ITEM CRUD +// ============================================ + +/** + * Add an item to an EVENT campaign. + */ +export async function addCampaignItem( + campaignId: string, + input: CreateCampaignItemInput +): Promise { + const campaign = await prisma.campaign.findUnique({ where: { id: campaignId } }); + if (!campaign) throw new Error('Campaign not found'); + if (campaign.campaignType !== 'EVENT') { + throw new Error('Items can only be added to EVENT campaigns'); + } + + return await prisma.campaignItem.create({ + data: { + campaignId, + name: input.name, + description: input.description ?? null, + category: input.category ?? null, + price: input.price, + maxQuantity: input.maxQuantity ?? null, + minPerOrder: input.minPerOrder ?? 0, + maxPerOrder: input.maxPerOrder ?? null, + isRequired: input.isRequired ?? false, + sortOrder: input.sortOrder ?? 0, + }, + }); +} + +/** + * Update a campaign item. + */ +export async function updateCampaignItem( + itemId: string, + updates: Partial & { isActive?: boolean } +): Promise { + return await prisma.campaignItem.update({ + where: { id: itemId }, + data: { + ...(updates.name !== undefined && { name: updates.name }), + ...(updates.description !== undefined && { description: updates.description }), + ...(updates.category !== undefined && { category: updates.category }), + ...(updates.price !== undefined && { price: updates.price }), + ...(updates.maxQuantity !== undefined && { maxQuantity: updates.maxQuantity }), + ...(updates.minPerOrder !== undefined && { minPerOrder: updates.minPerOrder }), + ...(updates.maxPerOrder !== undefined && { maxPerOrder: updates.maxPerOrder }), + ...(updates.isRequired !== undefined && { isRequired: updates.isRequired }), + ...(updates.sortOrder !== undefined && { sortOrder: updates.sortOrder }), + ...(updates.isActive !== undefined && { isActive: updates.isActive }), + }, + }); +} + +/** + * Delete (deactivate) a campaign item. Soft-delete to preserve line item references. + */ +export async function deleteCampaignItem(itemId: string): Promise { + return await prisma.campaignItem.update({ + where: { id: itemId }, + data: { isActive: false }, + }); +} + +/** + * List items for a campaign. + */ +export async function listCampaignItems( + campaignId: string, + includeInactive = false +): Promise { + return await prisma.campaignItem.findMany({ + where: { + campaignId, + ...(includeInactive ? {} : { isActive: true }), + }, + orderBy: { sortOrder: 'asc' }, + }); +} diff --git a/src/services/donation.service.ts b/src/services/donation.service.ts index 8d216c6..8e9ab51 100644 --- a/src/services/donation.service.ts +++ b/src/services/donation.service.ts @@ -27,6 +27,7 @@ export interface CreateDonationInput { campaignId?: string | null; unitCount?: number | null; // Number of units (FIXED_UNIT campaigns) tierId?: string | null; // Tier selection (TIERED campaigns) + lineItems?: Array<{ campaignItemId: string; quantity: number }>; // EVENT campaigns // Account IDs for the accounting entry arAccountId: string; // Accounts Receivable (for pledges) revenueAccountId: string; // Revenue / campaign account @@ -67,7 +68,7 @@ export async function createPledgeDonation(input: CreateDonationInput): Promise< // Validate against campaign constraints before starting transaction if (input.campaignId) { const { validateDonationAgainstCampaign } = await import('@/services/campaign.service'); - await validateDonationAgainstCampaign(input.campaignId, input.amount, input.unitCount, input.tierId); + await validateDonationAgainstCampaign(input.campaignId, input.amount, input.unitCount, input.tierId, input.lineItems); } const donation = await prisma.$transaction(async (tx) => { @@ -104,7 +105,7 @@ export async function createPledgeDonation(input: CreateDonationInput): Promise< }); // Create the donation record - return await tx.donation.create({ + const donation = await tx.donation.create({ data: { organizationId: input.organizationId, contactId: input.contactId, @@ -126,6 +127,13 @@ export async function createPledgeDonation(input: CreateDonationInput): Promise< createdBy: input.createdBy ?? null, }, }); + + // Create line items for EVENT campaigns + if (input.lineItems && input.lineItems.length > 0) { + await createDonationLineItems(tx, donation.id, input.lineItems); + } + + return donation; }); // After successful creation, check if campaign should auto-complete @@ -149,7 +157,7 @@ export async function createOneTimeDonation(input: CreateDonationInput): Promise // Validate against campaign constraints if (input.campaignId) { const { validateDonationAgainstCampaign } = await import('@/services/campaign.service'); - await validateDonationAgainstCampaign(input.campaignId, input.amount, input.unitCount, input.tierId); + await validateDonationAgainstCampaign(input.campaignId, input.amount, input.unitCount, input.tierId, input.lineItems); } const donation = await prisma.$transaction(async (tx) => { @@ -169,7 +177,7 @@ export async function createOneTimeDonation(input: CreateDonationInput): Promise }); // Create the donation record (immediately received) - return await tx.donation.create({ + const donation = await tx.donation.create({ data: { organizationId: input.organizationId, contactId: input.contactId, @@ -192,6 +200,13 @@ export async function createOneTimeDonation(input: CreateDonationInput): Promise createdBy: input.createdBy ?? null, }, }); + + // Create line items for EVENT campaigns + if (input.lineItems && input.lineItems.length > 0) { + await createDonationLineItems(tx, donation.id, input.lineItems); + } + + return donation; }); // After successful creation, check if campaign should auto-complete @@ -562,6 +577,39 @@ export async function syncDonationFromBill(billId: string, txClient?: any): Prom } } +// ── Line Items (EVENT campaigns) ──────────────────────────────────────── + +/** + * Create DonationLineItem records for an EVENT campaign donation. + * Looks up item prices to snapshot unitPrice and calculate subtotal. + */ +async function createDonationLineItems( + tx: Prisma.TransactionClient, + donationId: string, + lineItems: Array<{ campaignItemId: string; quantity: number }> +): Promise { + const itemIds = lineItems.map(li => li.campaignItemId); + const items = await tx.campaignItem.findMany({ + where: { id: { in: itemIds } }, + }); + const itemMap = new Map(items.map(i => [i.id, i])); + + for (const li of lineItems) { + const item = itemMap.get(li.campaignItemId); + if (!item) continue; + const unitPrice = Number(item.price); + await tx.donationLineItem.create({ + data: { + donationId, + campaignItemId: li.campaignItemId, + quantity: li.quantity, + unitPrice, + subtotal: unitPrice * li.quantity, + }, + }); + } +} + // ── Queries ───────────────────────────────────────────────────────────── /** From 438c93f7998fba83bfab10968ae802ac51fbc2fc Mon Sep 17 00:00:00 2001 From: Peter Hayes Date: Tue, 3 Mar 2026 19:05:28 -0500 Subject: [PATCH 2/2] Fix/public donation flow (#91) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add EVENT campaign type with line items New campaign type supporting multiple purchasable items grouped by category (e.g., dinner plates, gifts). Each item has its own price, capacity limit, and can be marked as required. Data model: - CampaignItem: items with price, category, capacity, required flag - DonationLineItem: snapshot of purchased items per donation Backend: - Campaign CRUD extended for EVENT type with item management - Validation: item capacity, per-order limits, required items, amount matching - Donation creation creates line item records - Stripe checkout supports multi-line-item sessions - Webhook recreates line items from metadata - Auto-complete when all capped items are sold out Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add EVENT campaign UI — form, donor item selector, public page, tests - CampaignForm: EVENT type option with item editor (name, category, price, capacity, required flag) - NewPledgeFormClient: EVENT item selector with quantity controls, category grouping, running total - PublicCampaignPage: EVENT items display with category grouping and pricing - Campaign API: EVENT type in Zod schema, items passthrough to service, items in list response - Donate page: serialize campaign items for public display - Tests: 14 new tests for EVENT validation, capacity, auto-complete, API contract (42 total) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: add @map annotations for DonationLineItem columns unitPrice and subtotal columns were missing @map('unit_price') and @map('subtotal') annotations, causing Prisma to look for camelCase column names instead of the snake_case columns in the database. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: enable campaign type-specific editing for TIERED tiers and EVENT items - Show campaign type as read-only label when editing (type cannot be changed) - Remove isEditing gate from TIERED tiers and EVENT items sections - Include tiers/items in PATCH body when editing campaigns - Add tiers and items to campaign update API schema with upsert logic - TIERED: upsert tiers by id, delete removed tiers (only if no donations reference them) - EVENT: upsert items by id, soft-delete removed items (preserve FK references) - Return tiers and items in PATCH response - Include items in GET single campaign response - Add items to CampaignList Campaign interface Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: public donation flow - support campaign types and unauthenticated donors Problem 1: Non-logged-in users get 'no payment methods' error because Clerk middleware blocks /api/organizations/[slug]/payment-methods for unauthenticated requests. Fix: Add payment-methods, donations/checkout, campaigns, and campaigns/[id] API routes to the public routes list in middleware.ts. Problem 2: Clicking 'Donate' from a campaign page shows generic donation form instead of campaign-type-specific UI (FIXED_UNIT, TIERED, EVENT). Fix: Enhance DonationFlow to accept full campaign data and render type-specific amount selection: - FIXED_UNIT: quantity selector with unit price - TIERED: tier picker with named levels and prices - EVENT: item selector with categories, quantities, required items - OPEN: original free-form amount input (unchanged) Pass full campaign data (type, tiers, items) from the donate page server component to DonationFlow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/app/org/[slug]/donate/page.tsx | 40 ++- src/components/donations/DonationFlow.tsx | 332 ++++++++++++++++++---- src/middleware.ts | 4 + 3 files changed, 312 insertions(+), 64 deletions(-) diff --git a/src/app/org/[slug]/donate/page.tsx b/src/app/org/[slug]/donate/page.tsx index 9247292..3319747 100644 --- a/src/app/org/[slug]/donate/page.tsx +++ b/src/app/org/[slug]/donate/page.tsx @@ -23,15 +23,44 @@ export default async function DonatePage({ params, searchParams }: Props) { notFound(); } - // If a campaign is specified, look it up for context - let campaignName: string | null = null; + // If a campaign is specified, fetch full campaign data for type-specific UI + let campaignData: any = null; if (campaignId) { const campaign = await prisma.campaign.findUnique({ where: { id: campaignId }, - select: { name: true, organizationId: true, status: true }, + include: { + tiers: { orderBy: { sortOrder: 'asc' } }, + items: { where: { isActive: true }, orderBy: { sortOrder: 'asc' } }, + }, }); if (campaign && campaign.organizationId === organization.id && campaign.status === 'ACTIVE') { - campaignName = campaign.name; + campaignData = { + id: campaign.id, + name: campaign.name, + description: campaign.description, + campaignType: campaign.campaignType, + unitPrice: campaign.unitPrice ? Number(campaign.unitPrice) : null, + unitLabel: campaign.unitLabel, + maxUnits: campaign.maxUnits, + allowMultiUnit: campaign.allowMultiUnit, + tiers: campaign.tiers.map((t) => ({ + id: t.id, + name: t.name, + amount: Number(t.amount), + maxSlots: t.maxSlots, + })), + items: campaign.items.map((i) => ({ + id: i.id, + name: i.name, + description: i.description, + category: i.category, + price: Number(i.price), + maxQuantity: i.maxQuantity, + minPerOrder: i.minPerOrder, + maxPerOrder: i.maxPerOrder, + isRequired: i.isRequired, + })), + }; } } @@ -42,7 +71,8 @@ export default async function DonatePage({ params, searchParams }: Props) { organizationName={organization.name} primaryColor={organization.primaryColor} campaignId={campaignId || null} - campaignName={campaignName} + campaignName={campaignData?.name || null} + campaign={campaignData} /> ); diff --git a/src/components/donations/DonationFlow.tsx b/src/components/donations/DonationFlow.tsx index 8c558f6..72dbcc2 100644 --- a/src/components/donations/DonationFlow.tsx +++ b/src/components/donations/DonationFlow.tsx @@ -53,6 +53,38 @@ const METHOD_ICONS: Record = { OTHER: Plus, }; +interface CampaignTier { + id: string; + name: string; + amount: number; + maxSlots: number | null; +} + +interface CampaignItem { + id: string; + name: string; + description?: string | null; + category: string | null; + price: number; + maxQuantity: number | null; + minPerOrder: number; + maxPerOrder: number | null; + isRequired: boolean; +} + +interface CampaignData { + id: string; + name: string; + description?: string | null; + campaignType: string; + unitPrice?: number | null; + unitLabel?: string | null; + maxUnits?: number | null; + allowMultiUnit?: boolean; + tiers: CampaignTier[]; + items: CampaignItem[]; +} + interface DonationFlowProps { organizationSlug: string; organizationName: string; @@ -60,6 +92,7 @@ interface DonationFlowProps { campaignName?: string | null; suggestedAmount?: number | null; primaryColor?: string | null; + campaign?: CampaignData | null; } type FlowStep = 'amount' | 'method' | 'details' | 'manual-confirm'; @@ -71,6 +104,7 @@ export function DonationFlow({ campaignName, suggestedAmount, primaryColor, + campaign, }: DonationFlowProps) { const [step, setStep] = useState('amount'); const [methods, setMethods] = useState([]); @@ -85,6 +119,11 @@ export function DonationFlow({ const [donorMessage, setDonorMessage] = useState(''); const [isAnonymous, setIsAnonymous] = useState(false); + // Campaign-type-specific state + const [unitQuantity, setUnitQuantity] = useState(1); + const [selectedTierId, setSelectedTierId] = useState(null); + const [eventQuantities, setEventQuantities] = useState>({}); + useEffect(() => { fetch(`/api/organizations/${organizationSlug}/payment-methods`) .then((res) => res.json()) @@ -99,32 +138,88 @@ export function DonationFlow({ const isValidAmount = !isNaN(parsedAmount) && parsedAmount >= 1; const isValidStripeAmount = !isNaN(parsedAmount) && parsedAmount >= 5; + // Campaign-type computed values + const campaignType = campaign?.campaignType || 'OPEN'; + const isFixedUnit = campaignType === 'FIXED_UNIT' && campaign?.unitPrice; + const isTiered = campaignType === 'TIERED' && campaign?.tiers && campaign.tiers.length > 0; + const isEvent = campaignType === 'EVENT' && campaign?.items && campaign.items.length > 0; + + const selectedTier = isTiered ? campaign!.tiers.find(t => t.id === selectedTierId) : null; + + const eventTotal = isEvent + ? campaign!.items.reduce((sum, item) => sum + (eventQuantities[item.id] || 0) * item.price, 0) + : 0; + const eventItemsByCategory = isEvent + ? campaign!.items.reduce((groups, item) => { + const cat = item.category || 'Items'; + if (!groups[cat]) groups[cat] = []; + groups[cat].push(item); + return groups; + }, {} as Record) + : {}; + const hasRequiredItems = isEvent && campaign!.items.some(i => i.isRequired); + const allRequiredSelected = !hasRequiredItems || campaign!.items + .filter(i => i.isRequired) + .every(i => (eventQuantities[i.id] || 0) >= Math.max(i.minPerOrder, 1)); + + // Compute effective amount based on campaign type + const effectiveAmount = isFixedUnit + ? campaign!.unitPrice! * unitQuantity + : isTiered && selectedTier + ? selectedTier.amount + : isEvent + ? eventTotal + : parsedAmount; + const isAmountReady = isFixedUnit + ? unitQuantity >= 1 + : isTiered + ? !!selectedTier + : isEvent + ? eventTotal > 0 && allRequiredSelected + : isValidAmount; + // Fee calculation using the selected Stripe method's configured rates const stripeMethod = methods.find((m) => m.type === 'STRIPE'); const feePercent = stripeMethod?.stripeFeePercent ?? 2.9; const feeFixed = stripeMethod?.stripeFeeFixed ?? 0.30; const feeRate = feePercent / 100; - const totalWithFees = isValidAmount - ? Math.ceil(((parsedAmount + feeFixed) / (1 - feeRate)) * 100) / 100 + const donationAmount = effectiveAmount || 0; + const totalWithFees = donationAmount >= 1 + ? Math.ceil(((donationAmount + feeFixed) / (1 - feeRate)) * 100) / 100 : 0; - const feeAmount = isValidAmount ? totalWithFees - parsedAmount : 0; + const feeAmount = donationAmount >= 1 ? totalWithFees - donationAmount : 0; const handleStripeCheckout = async () => { setSubmitting(true); try { + const checkoutBody: any = { + amount: donationAmount, + campaignId: campaignId || null, + donorName, + donorEmail, + donorMessage: donorMessage || undefined, + isAnonymous, + }; + if (isTiered && selectedTierId) { + checkoutBody.tierId = selectedTierId; + } + if (isEvent) { + checkoutBody.lineItems = Object.entries(eventQuantities) + .filter(([, qty]) => qty > 0) + .map(([itemId, quantity]) => { + const item = campaign!.items.find(i => i.id === itemId)!; + return { itemId, quantity, unitPrice: item.price }; + }); + } + if (isFixedUnit) { + checkoutBody.units = unitQuantity; + } const res = await fetch( `/api/organizations/${organizationSlug}/donations/checkout`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - amount: parsedAmount, - campaignId: campaignId || null, - donorName, - donorEmail, - donorMessage: donorMessage || undefined, - isAnonymous, - }), + body: JSON.stringify(checkoutBody), } ); const data = await res.json(); @@ -146,22 +241,37 @@ export function DonationFlow({ const handleManualDonation = async () => { setSubmitting(true); try { + const donationBody: any = { + type: 'ONE_TIME', + amount: donationAmount, + donorName, + donorEmail, + donorMessage: donorMessage || undefined, + isAnonymous, + paymentMethod: selectedMethod?.type, + campaignId: campaignId || null, + selfReported: true, + }; + if (isTiered && selectedTierId) { + donationBody.tierId = selectedTierId; + } + if (isEvent) { + donationBody.lineItems = Object.entries(eventQuantities) + .filter(([, qty]) => qty > 0) + .map(([itemId, quantity]) => { + const item = campaign!.items.find(i => i.id === itemId)!; + return { itemId, quantity, unitPrice: item.price }; + }); + } + if (isFixedUnit) { + donationBody.units = unitQuantity; + } const res = await fetch( `/api/organizations/${organizationSlug}/donations`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - type: 'ONE_TIME', - amount: parsedAmount, - donorName, - donorEmail, - donorMessage: donorMessage || undefined, - isAnonymous, - paymentMethod: selectedMethod?.type, - campaignId: campaignId || null, - selfReported: true, - }), + body: JSON.stringify(donationBody), } ); if (res.ok) { @@ -212,45 +322,149 @@ export function DonationFlow({ )} - {/* Step 1: Amount */} + {/* Step 1: Amount / Campaign-type selection */} {step === 'amount' && (
-
- -
- - $ - - setAmount(e.target.value)} - className="pl-7 text-lg" - placeholder="0.00" - autoFocus - /> + {/* FIXED_UNIT: Quantity selector */} + {isFixedUnit && ( +
+ + {campaign!.allowMultiUnit !== false ? ( +
+ + {unitQuantity} + +
+ ) : ( +

1 {campaign!.unitLabel || 'unit'}

+ )} +

Total: ${(campaign!.unitPrice! * unitQuantity).toFixed(2)}

- {/* Quick amounts */} -
- {[25, 50, 100, 250].map((val) => ( - + )} + + {/* TIERED: Tier picker */} + {isTiered && ( +
+ +
+ {campaign!.tiers.map(tier => ( + + ))} +
+
+ )} + + {/* EVENT: Item selector */} + {isEvent && ( +
+ + {Object.entries(eventItemsByCategory).map(([category, items]) => ( +
+

{category}

+
+ {items.map(item => { + const qty = eventQuantities[item.id] || 0; + const maxQty = item.maxPerOrder || item.maxQuantity || 99; + return ( +
+
+ {item.name} + {item.isRequired && *required} + ${item.price.toFixed(2)} + {item.description &&

{item.description}

} +
+
+ + {qty} + +
+
+ ); + })} +
+
))} + {hasRequiredItems && !allRequiredSelected && ( +

Please select all required items.

+ )} +

Total: ${eventTotal.toFixed(2)}

-
+ )} + + {/* OPEN / default: free-form amount */} + {!isFixedUnit && !isTiered && !isEvent && ( +
+ +
+ + $ + + setAmount(e.target.value)} + className="pl-7 text-lg" + placeholder="0.00" + autoFocus + /> +
+ {/* Quick amounts */} +
+ {[25, 50, 100, 250].map((val) => ( + + ))} +
+
+ )} +

- Donating ${parsedAmount.toFixed(2)} — choose a payment method: + Donating ${donationAmount.toFixed(2)} — choose a payment method:

{methods.map((method) => { @@ -309,7 +523,7 @@ export function DonationFlow({ Back

- ${parsedAmount.toFixed(2)} via{' '} + ${donationAmount.toFixed(2)} via{' '} {selectedMethod.label || selectedMethod.type} @@ -362,18 +576,18 @@ export function DonationFlow({ {/* Stripe: show fee info + direct checkout */} {selectedMethod.type === 'STRIPE' && (

- {!isValidStripeAmount && ( + {!isValidStripeAmount && donationAmount < 5 && (

Minimum donation amount for card payments is $5.

)}

- A processing fee of ${feeAmount.toFixed(2)} will be added — you'll be charged ${totalWithFees.toFixed(2)} so {organizationName} receives the full ${parsedAmount.toFixed(2)}. + A processing fee of ${feeAmount.toFixed(2)} will be added — you'll be charged ${totalWithFees.toFixed(2)} so {organizationName} receives the full ${donationAmount.toFixed(2)}.