Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export function WorkspaceSelector({ onSelect }: WorkspaceSelectorProps) {
Create New Organization
</CardTitle>
<CardDescription className="text-sm">
Share Kilo for Slack with your team. Starts with a 30-day free trial
Share Kilo for Slack with your team. Starts with a 14-day free trial
</CardDescription>
</div>
</CardContent>
Expand Down
2 changes: 1 addition & 1 deletion src/app/get-started/teams/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default async function TeamsGetStartedPage({ searchParams }: GetStartedPa
callbackPath="/organizations/new"
searchParams={params}
error={error}
signUpText="Try out Kilo Teams with a 30-day free trial, no credit card required. After you sign up, you can directly onboard all your team members."
signUpText="Try out Kilo Teams with a 14-day free trial, no credit card required. After you sign up, you can directly onboard all your team members."
/>
</>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/organizations/NoOrganizationsState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function NoOrganizationsState() {
{canMakeOrgs ? (
<div>
<p className="mb-4 text-center">
Take Kilo on a free 30-day test drive for your team
Take Kilo on a free 14-day test drive for your team
</p>
<div className="flex justify-center">
<NewOrganizationButton />
Expand Down
4 changes: 2 additions & 2 deletions src/components/organizations/new/CreateOrganizationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function CreateOrganizationPage({ mockSelectedOrgName }: CreateOrganizati
<h1 className="mb-2 text-3xl font-bold lg:text-4xl">
Create an organization and start your
<br />
30-day free trial for Kilo Enterprise
14-day free trial for Kilo Enterprise
</h1>
<Link
href="/get-started/personal"
Expand Down Expand Up @@ -249,7 +249,7 @@ export function CreateOrganizationPage({ mockSelectedOrgName }: CreateOrganizati
'Processing...'
) : (
<span className="flex items-center gap-2">
Start 30-day free trial for Kilo Enterprise
Start 14-day free trial for Kilo Enterprise
<ArrowRight className="h-5 w-5" />
</span>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const APP_URL =
? 'https://app.kilo.ai'
: (process.env.APP_URL_OVERRIDE ?? 'http://localhost:3000');

export const TRIAL_DURATION_DAYS = 30;
export const TRIAL_DURATION_DAYS = 14;

export const AUTOCOMPLETE_MODEL = 'codestral-2508';

Expand Down
2 changes: 1 addition & 1 deletion src/lib/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ async function generateTeamsTrialNotification(user: User): Promise<KiloNotificat
return [
{
id: 'teams-free-trial-oct-17',
title: 'Try Kilo with Your Team — Free for 30 Days',
title: 'Try Kilo with Your Team — Free for 14 Days',
message:
'Get usage analytics, centralized billing, shared context, and other features you need to scale AI coding across your org.',
action: {
Expand Down
26 changes: 13 additions & 13 deletions src/lib/organizations/trial-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { getOrgTrialStatusFromDays, getDaysRemainingInTrial } from './trial-util

describe('getOrgTrialStatusFromDays', () => {
it('returns trial_active for 8+ days remaining', () => {
expect(getOrgTrialStatusFromDays(30)).toBe('trial_active');
expect(getOrgTrialStatusFromDays(14)).toBe('trial_active');
expect(getOrgTrialStatusFromDays(8)).toBe('trial_active');
});

Expand Down Expand Up @@ -54,29 +54,29 @@ describe('getDaysRemainingInTrial', () => {
// Organization created 5 days before fixed now
const createdAt = new Date(FIXED_NOW_MS - 5 * 24 * 60 * 60 * 1000).toISOString();

// Organization with trial ending in 30 days
const freeTrialEndAt30 = new Date(FIXED_NOW_MS + 30 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(freeTrialEndAt30, createdAt)).toBe(30);
// Organization with trial ending in 14 days
const freeTrialEndAt14 = new Date(FIXED_NOW_MS + 14 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(freeTrialEndAt14, createdAt)).toBe(14);

// Organization with trial expired 5 days ago
const freeTrialEndAtExpired = new Date(FIXED_NOW_MS - 5 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(freeTrialEndAtExpired, createdAt)).toBe(-5);
});

it('falls back to created_at + 30 days when free_trial_end_at is null', () => {
it('falls back to created_at + 14 days when free_trial_end_at is null', () => {
// Organization created today (no free_trial_end_at set)
expect(getDaysRemainingInTrial(null, FIXED_NOW)).toBe(30);
expect(getDaysRemainingInTrial(null, FIXED_NOW)).toBe(14);

// Organization created 10 days ago (no free_trial_end_at set)
const tenDaysAgo = new Date(FIXED_NOW_MS - 10 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(null, tenDaysAgo)).toBe(20);
expect(getDaysRemainingInTrial(null, tenDaysAgo)).toBe(4);

// Organization created 30 days ago (expires today, no free_trial_end_at set)
const thirtyDaysAgo = new Date(FIXED_NOW_MS - 30 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(null, thirtyDaysAgo)).toBe(0);
// Organization created 14 days ago (expires today, no free_trial_end_at set)
const fourteenDaysAgo = new Date(FIXED_NOW_MS - 14 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(null, fourteenDaysAgo)).toBe(0);

// Organization created 35 days ago (expired 5 days ago, no free_trial_end_at set)
const thirtyFiveDaysAgo = new Date(FIXED_NOW_MS - 35 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(null, thirtyFiveDaysAgo)).toBe(-5);
// Organization created 19 days ago (expired 5 days ago, no free_trial_end_at set)
const nineteenDaysAgo = new Date(FIXED_NOW_MS - 19 * 24 * 60 * 60 * 1000).toISOString();
expect(getDaysRemainingInTrial(null, nineteenDaysAgo)).toBe(-5);
});
});
2 changes: 1 addition & 1 deletion src/lib/organizations/trial-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function getDaysRemainingInTrial(freeTrialEndAt: string | null, createdAt
if (freeTrialEndAt) {
endDate = new Date(freeTrialEndAt);
} else {
// Fallback to created_at + 30 days for backward compatibility
// Fallback to created_at + TRIAL_DURATION_DAYS for backward compatibility
const created = new Date(createdAt);
endDate = new Date(created.getTime() + TRIAL_DURATION_DAYS * 24 * 60 * 60 * 1000);
}
Expand Down
5 changes: 3 additions & 2 deletions src/scripts/db/backfill-free-trial-end-at.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Backfill script for free_trial_end_at column
*
* This script populates free_trial_end_at for existing organizations that have null values.
* It sets free_trial_end_at to created_at + 30 days for all organizations where it's currently null.
* It sets free_trial_end_at to created_at + 14 days for all organizations where it's currently null.
*
* Usage:
* pnpm script src/scripts/db/backfill-free-trial-end-at.ts
Expand Down Expand Up @@ -31,7 +31,8 @@ export async function run() {
const updateResult = await db
.update(organizations)
.set({
free_trial_end_at: sql`${organizations.created_at} + INTERVAL '30 days'`,
// Changed from 30 days; no orgs on trial have null end at this time
free_trial_end_at: sql`${organizations.created_at} + INTERVAL '14 days'`,
})
.where(isNull(organizations.free_trial_end_at));

Expand Down