diff --git a/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.html b/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.html
index 4bc626ab1..c2bf61414 100644
--- a/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.html
+++ b/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.html
@@ -97,6 +97,181 @@
General Fund
+
+
+
+
+
+
+
+
+ @if (isProjectType()) {
+
+
+
+
+ }
+
+
+ @if (isEventType()) {
+
+
Event Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+ @if (isSecurityAudit()) {
+
+
Security Audit Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Enter the total budget in US dollars.
+
+
+
+
+
+
Contacts
+
+
+ @for (group of contactGroups(); track $index) {
+
+
+
+ @for (ct of CONTACT_TYPES; track ct.value) {
+ @if (ct.value === group.value['contactType']) { {{ ct.label }} }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+ @for (ct of CONTACT_TYPES; track ct.value) {
+ @if (!usedContactTypes().includes(ct.value)) {
+
+ }
+ }
+
+
+
+ }
}
diff --git a/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.ts b/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.ts
index 477eff2ca..679919bd0 100644
--- a/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.ts
+++ b/apps/lfx-one/src/app/modules/crowdfunding/initiative-detail/components/initiative-settings-drawer/initiative-settings-drawer.component.ts
@@ -22,6 +22,7 @@ import {
DEFAULT_FUND_DISTRIBUTION,
MAX_LOGO_SIZE_BYTES,
} from '@lfx-one/shared/constants';
+import { FundType } from '@lfx-one/shared/enums';
import { FundDistributionItem, InitiativeDetail, TabOption, UpdateInitiativeInput } from '@lfx-one/shared/interfaces';
import { CrowdfundingService } from '@services/crowdfunding.service';
@@ -70,7 +71,23 @@ export class InitiativeSettingsDrawerComponent {
description: new FormControl('', [Validators.required, Validators.maxLength(500)]),
topics: new FormControl([], Validators.required),
websiteUrl: new FormControl(''),
+ cocUrl: new FormControl(''),
goal: new FormControl(null, [Validators.min(0)]),
+ // project-only
+ ciiProjectId: new FormControl(''),
+ // event-type only
+ eventStartDate: new FormControl(''),
+ eventEndDate: new FormControl(''),
+ applicationUrl: new FormControl(''),
+ eventbriteUrl: new FormControl(''),
+ country: new FormControl(''),
+ city: new FormControl(''),
+ isOnline: new FormControl(false),
+ // security_audit (ostif) only
+ monetizationStrategy: new FormControl(''),
+ currentSecurityStrategy: new FormControl(''),
+ licenseType: new FormControl(''),
+ totalBudgetCents: new FormControl(null, [Validators.min(0)]),
});
protected readonly saving = signal(false);
@@ -79,6 +96,13 @@ export class InitiativeSettingsDrawerComponent {
protected readonly logoUploadError = signal(null);
protected readonly distributionItems = signal(DEFAULT_FUND_DISTRIBUTION.map((i) => ({ ...i })));
protected readonly beneficiaryGroups = signal([]);
+ protected readonly contactGroups = signal([]);
+
+ protected readonly CONTACT_TYPES = [
+ { value: 'primary', label: 'Primary Contact' },
+ { value: 'secondary', label: 'Secondary Contact' },
+ { value: 'technical_lead', label: 'Technical Lead' },
+ ];
protected readonly hasEnabledCategories = computed(() => this.distributionItems().some((i) => i.enabled));
protected readonly totalAllocated = computed(() =>
@@ -101,6 +125,22 @@ export class InitiativeSettingsDrawerComponent {
protected readonly nameLength = computed(() => this.formValue().name?.length ?? 0);
protected readonly descriptionLength = computed(() => this.formValue().description?.length ?? 0);
protected readonly initiativeInitial = computed(() => this.initiative().name.charAt(0));
+ protected readonly isEventType = computed(() => this.initiative().initiativeType === FundType.EVENT);
+ protected readonly isSecurityAudit = computed(() => this.initiative().initiativeType === FundType.SECURITY_AUDIT);
+ protected readonly isProjectType = computed(() => this.initiative().initiativeType === FundType.GENERAL_FUND);
+
+ protected get eventStartDateControl(): FormControl {
+ return this.form.controls['eventStartDate'] as FormControl;
+ }
+ protected get eventEndDateControl(): FormControl {
+ return this.form.controls['eventEndDate'] as FormControl;
+ }
+ protected get isOnlineControl(): FormControl {
+ return this.form.controls['isOnline'] as FormControl;
+ }
+ protected get totalBudgetCentsControl(): FormControl {
+ return this.form.controls['totalBudgetCents'] as FormControl;
+ }
public constructor() {
toObservable(this.visible)
@@ -118,7 +158,20 @@ export class InitiativeSettingsDrawerComponent {
description: init.description,
topics: existingTopics,
websiteUrl: init.websiteUrl ?? '',
+ cocUrl: init.cocUrl ?? '',
goal: init.fundingStatus?.goalsTotalCents != null ? init.fundingStatus.goalsTotalCents / 100 : null,
+ ciiProjectId: init.ciiProjectId ?? '',
+ eventStartDate: init.eventStartDate ? init.eventStartDate.substring(0, 10) : '',
+ eventEndDate: init.eventEndDate ? init.eventEndDate.substring(0, 10) : '',
+ applicationUrl: init.applicationUrl ?? '',
+ eventbriteUrl: init.eventbriteUrl ?? '',
+ country: init.country ?? '',
+ city: init.city ?? '',
+ isOnline: init.isOnline ?? false,
+ monetizationStrategy: init.ostifDetail?.monetizationStrategy ?? '',
+ currentSecurityStrategy: init.ostifDetail?.currentSecurityStrategy ?? '',
+ licenseType: init.ostifDetail?.licenseType ?? '',
+ totalBudgetCents: init.ostifDetail?.totalBudgetCents != null ? init.ostifDetail.totalBudgetCents / 100 : null,
});
this.logoUrl.set(init.logoUrl ?? '');
this.logoUploadError.set(null);
@@ -134,6 +187,7 @@ export class InitiativeSettingsDrawerComponent {
})
);
this.beneficiaryGroups.set([]);
+ this.contactGroups.set((init.contacts ?? []).map((c) => this.makeContactGroup(c)));
this.activeSettingsTab.set('details');
});
}
@@ -151,12 +205,44 @@ export class InitiativeSettingsDrawerComponent {
this.saving.set(true);
try {
- const { name, description, topics, websiteUrl, goal } = this.form.value as {
+ const {
+ name,
+ description,
+ topics,
+ websiteUrl,
+ cocUrl,
+ goal,
+ ciiProjectId,
+ eventStartDate,
+ eventEndDate,
+ applicationUrl,
+ eventbriteUrl,
+ country,
+ city,
+ isOnline,
+ monetizationStrategy,
+ currentSecurityStrategy,
+ licenseType,
+ totalBudgetCents,
+ } = this.form.value as {
name: string;
description: string;
topics: string[];
websiteUrl: string;
+ cocUrl: string;
goal: number | null;
+ ciiProjectId: string;
+ eventStartDate: string;
+ eventEndDate: string;
+ applicationUrl: string;
+ eventbriteUrl: string;
+ country: string;
+ city: string;
+ isOnline: boolean;
+ monetizationStrategy: string;
+ currentSecurityStrategy: string;
+ licenseType: string;
+ totalBudgetCents: number | null;
};
const input: UpdateInitiativeInput = {
@@ -165,8 +251,40 @@ export class InitiativeSettingsDrawerComponent {
industry: topics.join(','),
logoUrl: this.logoUrl(),
websiteUrl: websiteUrl || undefined,
+ cocUrl: cocUrl || undefined,
};
+ if (this.isProjectType()) {
+ input.ciiProjectId = ciiProjectId || undefined;
+ }
+
+ if (this.isEventType()) {
+ input.eventStartDate = eventStartDate || undefined;
+ input.eventEndDate = eventEndDate || undefined;
+ input.applicationUrl = applicationUrl || undefined;
+ input.eventbriteUrl = eventbriteUrl || undefined;
+ input.country = country || undefined;
+ input.city = city || undefined;
+ input.isOnline = isOnline;
+ }
+
+ if (this.isSecurityAudit()) {
+ input.ostifDetail = {
+ monetizationStrategy: monetizationStrategy || undefined,
+ currentSecurityStrategy: currentSecurityStrategy || undefined,
+ licenseType: licenseType || undefined,
+ totalBudgetCents: totalBudgetCents != null ? Math.round(totalBudgetCents * 100) : undefined,
+ };
+ input.contacts = this.contactGroups().map((g) => ({
+ contactType: g.value.contactType as string,
+ firstName: (g.value.firstName as string) || undefined,
+ lastName: (g.value.lastName as string) || undefined,
+ email: (g.value.email as string) || undefined,
+ phoneNumber: (g.value.phoneNumber as string) || undefined,
+ preferredContactMethod: (g.value.preferredContactMethod as string) || undefined,
+ }));
+ }
+
if (goal != null) {
const goalCents = Math.round(goal * 100);
const enabledItems = this.distributionItems().filter((i) => i.enabled);
@@ -293,4 +411,34 @@ export class InitiativeSettingsDrawerComponent {
protected removeBeneficiary(index: number): void {
this.beneficiaryGroups.update((groups) => groups.filter((_, i) => i !== index));
}
+
+ protected addContact(contactType: string): void {
+ this.contactGroups.update((groups) => [...groups, this.makeContactGroup({ contactType })]);
+ }
+
+ protected removeContact(index: number): void {
+ this.contactGroups.update((groups) => groups.filter((_, i) => i !== index));
+ }
+
+ protected usedContactTypes(): string[] {
+ return this.contactGroups().map((g) => g.value.contactType as string);
+ }
+
+ private makeContactGroup(c: {
+ contactType: string;
+ firstName?: string;
+ lastName?: string;
+ email?: string;
+ phoneNumber?: string;
+ preferredContactMethod?: string;
+ }): FormGroup {
+ return new FormGroup({
+ contactType: new FormControl(c.contactType ?? ''),
+ firstName: new FormControl(c.firstName ?? ''),
+ lastName: new FormControl(c.lastName ?? ''),
+ email: new FormControl(c.email ?? '', Validators.email),
+ phoneNumber: new FormControl(c.phoneNumber ?? ''),
+ preferredContactMethod: new FormControl(c.preferredContactMethod ?? 'email'),
+ });
+ }
}
diff --git a/apps/lfx-one/src/server/controllers/crowdfunding.controller.ts b/apps/lfx-one/src/server/controllers/crowdfunding.controller.ts
index c31d7a3c0..c4f97c218 100644
--- a/apps/lfx-one/src/server/controllers/crowdfunding.controller.ts
+++ b/apps/lfx-one/src/server/controllers/crowdfunding.controller.ts
@@ -281,6 +281,40 @@ export class CrowdfundingController {
input.status = rawStatus as CrowdfundingInitiativeStatus;
}
+ if (typeof body['cocUrl'] === 'string') input.cocUrl = body['cocUrl'].trim() || undefined;
+ if (typeof body['acceptFunding'] === 'boolean') input.acceptFunding = body['acceptFunding'];
+ if (typeof body['ciiProjectId'] === 'string') input.ciiProjectId = body['ciiProjectId'].trim() || undefined;
+ if (typeof body['eventStartDate'] === 'string') input.eventStartDate = body['eventStartDate'].trim() || undefined;
+ if (typeof body['eventEndDate'] === 'string') input.eventEndDate = body['eventEndDate'].trim() || undefined;
+ if (typeof body['applicationUrl'] === 'string') input.applicationUrl = body['applicationUrl'].trim() || undefined;
+ if (typeof body['eventbriteUrl'] === 'string') input.eventbriteUrl = body['eventbriteUrl'].trim() || undefined;
+ if (typeof body['country'] === 'string') input.country = body['country'].trim() || undefined;
+ if (typeof body['city'] === 'string') input.city = body['city'].trim() || undefined;
+ if (typeof body['isOnline'] === 'boolean') input.isOnline = body['isOnline'];
+
+ if (body['ostifDetail'] !== null && typeof body['ostifDetail'] === 'object') {
+ const o = body['ostifDetail'] as Record;
+ input.ostifDetail = {
+ monetizationStrategy: typeof o['monetizationStrategy'] === 'string' ? o['monetizationStrategy'] : undefined,
+ currentSecurityStrategy: typeof o['currentSecurityStrategy'] === 'string' ? o['currentSecurityStrategy'] : undefined,
+ licenseType: typeof o['licenseType'] === 'string' ? o['licenseType'] : undefined,
+ totalBudgetCents: typeof o['totalBudgetCents'] === 'number' ? o['totalBudgetCents'] : undefined,
+ termsConditions: typeof o['termsConditions'] === 'boolean' ? o['termsConditions'] : undefined,
+ };
+ }
+
+ if (Array.isArray(body['contacts'])) {
+ input.contacts = (body['contacts'] as Record[]).map((c) => ({
+ contactType: typeof c['contactType'] === 'string' ? c['contactType'] : '',
+ firstName: typeof c['firstName'] === 'string' ? c['firstName'] : undefined,
+ lastName: typeof c['lastName'] === 'string' ? c['lastName'] : undefined,
+ email: typeof c['email'] === 'string' ? c['email'] : undefined,
+ phoneNumber: typeof c['phoneNumber'] === 'string' ? c['phoneNumber'] : undefined,
+ otherContactOption: typeof c['otherContactOption'] === 'string' ? c['otherContactOption'] : undefined,
+ preferredContactMethod: typeof c['preferredContactMethod'] === 'string' ? c['preferredContactMethod'] : undefined,
+ }));
+ }
+
if (Array.isArray(body['goals'])) {
input.goals = (body['goals'] as Record[]).map((g) => ({
name: typeof g['name'] === 'string' ? g['name'] : 'Annual Funding Goal',
diff --git a/apps/lfx-one/src/server/services/crowdfunding.service.ts b/apps/lfx-one/src/server/services/crowdfunding.service.ts
index 4989d865c..a8671714a 100644
--- a/apps/lfx-one/src/server/services/crowdfunding.service.ts
+++ b/apps/lfx-one/src/server/services/crowdfunding.service.ts
@@ -42,6 +42,11 @@ import { logger } from './logger.service';
const cfBaseUrl = (): string => (process.env['CROWDFUNDING_API_BASE_URL'] || '').replace(/\/+$/, '');
+/** Converts a YYYY-MM-DD date string to RFC3339 (required by the Go backend's time.Time JSON decoder). */
+function toRFC3339Date(date: string): string {
+ return /^\d{4}-\d{2}-\d{2}$/.test(date) ? `${date}T00:00:00Z` : date;
+}
+
const CF_TIMEOUT_MS = 30_000;
function throwCfNetworkError(operation: string, error: unknown): never {
@@ -302,7 +307,37 @@ export class CrowdfundingService {
if (input.industry !== undefined) body.industry = input.industry;
if (input.logoUrl !== undefined) body.logo_url = input.logoUrl;
if (input.websiteUrl !== undefined) body.website_url = input.websiteUrl;
+ if (input.cocUrl !== undefined) body.coc_url = input.cocUrl;
+ if (input.acceptFunding !== undefined) body.accept_funding = input.acceptFunding;
if (input.status !== undefined) body.status = input.status;
+ if (input.ciiProjectId !== undefined) body.cii_project_id = input.ciiProjectId;
+ if (input.eventStartDate !== undefined) body.event_start_date = input.eventStartDate ? toRFC3339Date(input.eventStartDate) : input.eventStartDate;
+ if (input.eventEndDate !== undefined) body.event_end_date = input.eventEndDate ? toRFC3339Date(input.eventEndDate) : input.eventEndDate;
+ if (input.applicationUrl !== undefined) body.application_url = input.applicationUrl;
+ if (input.eventbriteUrl !== undefined) body.eventbrite_url = input.eventbriteUrl;
+ if (input.country !== undefined) body.country = input.country;
+ if (input.city !== undefined) body.city = input.city;
+ if (input.isOnline !== undefined) body.is_online = input.isOnline;
+ if (input.ostifDetail !== undefined) {
+ body.ostif_detail = {
+ monetization_strategy: input.ostifDetail.monetizationStrategy,
+ current_security_strategy: input.ostifDetail.currentSecurityStrategy,
+ license_type: input.ostifDetail.licenseType,
+ total_budget_cents: input.ostifDetail.totalBudgetCents,
+ terms_conditions: input.ostifDetail.termsConditions,
+ };
+ }
+ if (input.contacts !== undefined) {
+ body.contacts = input.contacts.map((c) => ({
+ contact_type: c.contactType,
+ first_name: c.firstName,
+ last_name: c.lastName,
+ email: c.email,
+ phone_number: c.phoneNumber,
+ other_contact_option: c.otherContactOption,
+ preferred_contact_method: c.preferredContactMethod,
+ }));
+ }
if (input.goals !== undefined) {
body.goals = input.goals.map((g): BackendGoalInput => ({ name: g.name, amount_cents: g.amountCents }));
}
diff --git a/apps/lfx-one/src/server/types/crowdfunding.types.ts b/apps/lfx-one/src/server/types/crowdfunding.types.ts
index 1b2fe0dd6..264c7a634 100644
--- a/apps/lfx-one/src/server/types/crowdfunding.types.ts
+++ b/apps/lfx-one/src/server/types/crowdfunding.types.ts
@@ -21,6 +21,35 @@ export interface BackendSponsor {
total_cents: number;
}
+export interface BackendOSTIFDetail {
+ monetization_strategy?: string;
+ current_security_strategy?: string;
+ license_type?: string;
+ total_budget_cents: number;
+ terms_conditions: boolean;
+}
+
+export interface BackendContact {
+ id: string;
+ contact_type: string;
+ first_name?: string;
+ last_name?: string;
+ email?: string;
+ phone_number?: string;
+ other_contact_option?: string;
+ preferred_contact_method?: string;
+}
+
+export interface BackendContactInput {
+ contact_type: string;
+ first_name?: string;
+ last_name?: string;
+ email?: string;
+ phone_number?: string;
+ other_contact_option?: string;
+ preferred_contact_method?: string;
+}
+
export interface BackendInitiative {
id: string;
initiative_type: string;
@@ -33,11 +62,19 @@ export interface BackendInitiative {
color?: string;
logo_url?: string;
website_url?: string;
+ coc_url?: string;
+ accept_funding: boolean;
country?: string;
city?: string;
+ is_online: boolean;
application_url?: string;
+ eventbrite_url?: string;
event_start_date?: string;
event_end_date?: string;
+ cii_project_id?: string;
+ entity_details?: Record;
+ ostif_detail?: BackendOSTIFDetail;
+ contacts?: BackendContact[];
created_on: string;
updated_on: string;
financials?: {
@@ -137,6 +174,14 @@ export interface BackendBeneficiaryInput {
email?: string;
}
+export interface BackendOSTIFDetailInput {
+ monetization_strategy?: string;
+ current_security_strategy?: string;
+ license_type?: string;
+ total_budget_cents?: number;
+ terms_conditions?: boolean;
+}
+
/** Snake_case PATCH body sent to PATCH /v1/me/initiatives/{id} on the upstream crowdfunding service. */
export interface BackendUpdateInitiativeInput {
name?: string;
@@ -144,7 +189,22 @@ export interface BackendUpdateInitiativeInput {
industry?: string;
logo_url?: string;
website_url?: string;
+ coc_url?: string;
+ accept_funding?: boolean;
status?: string;
+ // project-only
+ cii_project_id?: string;
+ // event-only
+ event_start_date?: string;
+ event_end_date?: string;
+ application_url?: string;
+ eventbrite_url?: string;
+ country?: string;
+ city?: string;
+ is_online?: boolean;
+ // security_audit-only
+ ostif_detail?: BackendOSTIFDetailInput;
+ contacts?: BackendContactInput[];
goals?: BackendGoalInput[];
beneficiaries?: BackendBeneficiaryInput[];
}
diff --git a/apps/lfx-one/src/server/utils/crowdfunding-mapper.ts b/apps/lfx-one/src/server/utils/crowdfunding-mapper.ts
index 0c1b8a626..96605bb21 100644
--- a/apps/lfx-one/src/server/utils/crowdfunding-mapper.ts
+++ b/apps/lfx-one/src/server/utils/crowdfunding-mapper.ts
@@ -8,7 +8,9 @@ import {
FinancialSummary,
FundingGoal,
InitiativeBase,
+ InitiativeContact,
InitiativeDetail,
+ OSTIFDetail,
SponsorEntry,
CrowdfundingInitiativeStatus,
MyDonation,
@@ -20,9 +22,11 @@ import {
import { FundType } from '@lfx-one/shared/enums';
import {
+ BackendContact,
BackendDonation,
BackendGoal,
BackendInitiative,
+ BackendOSTIFDetail,
BackendSponsor,
BackendSubscription,
BackendTransaction,
@@ -58,6 +62,7 @@ export function mapToInitiativeBase(b: BackendInitiative): InitiativeBase {
applicationUrl: b.application_url,
eventStartDate: b.event_start_date,
eventEndDate: b.event_end_date,
+ ciiProjectId: b.cii_project_id,
fundingStatus: b.financials
? {
goalsTotalCents: b.financials.goals_total_cents,
@@ -71,6 +76,13 @@ export function mapToInitiativeBase(b: BackendInitiative): InitiativeBase {
export function mapToInitiativeDetail(b: BackendInitiative): InitiativeDetail {
return {
...mapToInitiativeBase(b),
+ cocUrl: b.coc_url,
+ acceptFunding: b.accept_funding,
+ isOnline: b.is_online,
+ eventbriteUrl: b.eventbrite_url,
+ entityDetails: b.entity_details,
+ ostifDetail: b.ostif_detail ? mapOSTIFDetail(b.ostif_detail) : undefined,
+ contacts: (b.contacts ?? []).map(mapContact),
currentBalanceCents: b.financials?.available_balance_cents,
sponsors: (b.sponsors ?? []).map(mapSponsor),
fundingGoals: (b.goals ?? []).map(mapFundingGoal),
@@ -83,6 +95,29 @@ export function mapToInitiativeDetail(b: BackendInitiative): InitiativeDetail {
};
}
+function mapOSTIFDetail(o: BackendOSTIFDetail): OSTIFDetail {
+ return {
+ monetizationStrategy: o.monetization_strategy,
+ currentSecurityStrategy: o.current_security_strategy,
+ licenseType: o.license_type,
+ totalBudgetCents: o.total_budget_cents,
+ termsConditions: o.terms_conditions,
+ };
+}
+
+function mapContact(c: BackendContact): InitiativeContact {
+ return {
+ id: c.id,
+ contactType: c.contact_type,
+ firstName: c.first_name,
+ lastName: c.last_name,
+ email: c.email,
+ phoneNumber: c.phone_number,
+ otherContactOption: c.other_contact_option,
+ preferredContactMethod: c.preferred_contact_method,
+ };
+}
+
function mapSponsor(s: BackendSponsor): SponsorEntry {
return {
id: s.id,
diff --git a/packages/shared/src/interfaces/crowdfunding.interface.ts b/packages/shared/src/interfaces/crowdfunding.interface.ts
index 1d392c29d..9840908d4 100644
--- a/packages/shared/src/interfaces/crowdfunding.interface.ts
+++ b/packages/shared/src/interfaces/crowdfunding.interface.ts
@@ -36,6 +36,7 @@ export interface InitiativeBase {
applicationUrl?: string;
eventStartDate?: string;
eventEndDate?: string;
+ ciiProjectId?: string;
initiativeStats?: InitiativeStats;
fundingStatus?: FundingStatus;
}
@@ -137,8 +138,34 @@ export interface ProjectHealthStat {
value: string;
}
+export interface OSTIFDetail {
+ monetizationStrategy?: string;
+ currentSecurityStrategy?: string;
+ licenseType?: string;
+ totalBudgetCents?: number;
+ termsConditions?: boolean;
+}
+
+export interface InitiativeContact {
+ id: string;
+ contactType: string;
+ firstName?: string;
+ lastName?: string;
+ email?: string;
+ phoneNumber?: string;
+ otherContactOption?: string;
+ preferredContactMethod?: string;
+}
+
/** Full initiative data returned by the GET /initiatives/:slug detail endpoint. */
export interface InitiativeDetail extends InitiativeBase {
+ cocUrl?: string;
+ acceptFunding?: boolean;
+ isOnline?: boolean;
+ eventbriteUrl?: string;
+ entityDetails?: Record;
+ ostifDetail?: OSTIFDetail;
+ contacts?: InitiativeContact[];
githubUrl?: string;
currentBalanceCents?: number;
sponsors?: SponsorEntry[];
@@ -265,13 +292,46 @@ export interface UpdateBeneficiaryInput {
email?: string;
}
+export interface UpdateOSTIFDetailInput {
+ monetizationStrategy?: string;
+ currentSecurityStrategy?: string;
+ licenseType?: string;
+ totalBudgetCents?: number;
+ termsConditions?: boolean;
+}
+
+export interface UpdateContactInput {
+ contactType: string;
+ firstName?: string;
+ lastName?: string;
+ email?: string;
+ phoneNumber?: string;
+ otherContactOption?: string;
+ preferredContactMethod?: string;
+}
+
export interface UpdateInitiativeInput {
name?: string;
description?: string;
industry?: string;
logoUrl?: string;
websiteUrl?: string;
+ cocUrl?: string;
+ acceptFunding?: boolean;
status?: CrowdfundingInitiativeStatus;
+ // project-only
+ ciiProjectId?: string;
+ // event-only
+ eventStartDate?: string;
+ eventEndDate?: string;
+ applicationUrl?: string;
+ eventbriteUrl?: string;
+ country?: string;
+ city?: string;
+ isOnline?: boolean;
+ // security_audit-only
+ ostifDetail?: UpdateOSTIFDetailInput;
+ contacts?: UpdateContactInput[];
goals?: UpdateGoalInput[];
beneficiaries?: UpdateBeneficiaryInput[];
}