feat(pricing/ai): improve billing metered pricing + add pricing on ai chat (#14092)

## TODO:

- [x] display "yearly" or "monthly" wording everywhere it's needed
- [ ] Add button with "downgrade" or "upgrade" to save the change of
credits price + modal to validate
- [x] Add renewal date 
- [ ] Implement
https://docs.stripe.com/billing/subscriptions/subscription-schedules for
`switchFromYearlyToMonthly` and decrease number of credits
This commit is contained in:
Antoine Moreaux
2025-08-29 18:23:07 +02:00
committed by GitHub
parent 7df9094939
commit 1c4568c8b1
45 changed files with 2048 additions and 327 deletions
@@ -0,0 +1,42 @@
import { findOrThrow } from '~/utils/array/findOrThrow';
describe('findOrThrow', () => {
it('should return the element that matches the predicate', () => {
const array = [1, 2, 3, 4];
const predicate = (num: number) => num === 3;
const result = findOrThrow(array, predicate);
expect(result).toBe(3);
});
it('should throw an error if no element matches the predicate', () => {
const array = [1, 2, 3, 4];
const predicate = (num: number) => num === 5;
expect(() => findOrThrow(array, predicate)).toThrow('Element not found');
});
it('should work with non-numeric data types', () => {
const array = ['apple', 'banana', 'cherry'];
const predicate = (fruit: string) => fruit === 'banana';
const result = findOrThrow(array, predicate);
expect(result).toBe('banana');
});
it('should throw an error if the array is empty', () => {
const array: number[] = [];
const predicate = (num: number) => num === 1;
expect(() => findOrThrow(array, predicate)).toThrow('Element not found');
});
it('should throw an error if predicate is never satisfied', () => {
const array = [1, 2, 3];
const predicate = (num: number) => num > 10;
expect(() => findOrThrow(array, predicate)).toThrow('Element not found');
});
});
@@ -0,0 +1,14 @@
import { isDefined } from 'twenty-shared/utils';
export const findOrThrow = <T>(
array: T[],
predicate: (value: T) => boolean,
): T => {
const result = array.find(predicate);
if (!isDefined(result)) {
throw new Error('Element not found');
}
return result;
};