itube-specs 0.0.766 → 0.0.768

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { useFormatTimeAgo } from '../use-format-time-ago';
3
+
4
+ describe('useFormatTimeAgo', () => {
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ vi.setSystemTime(new Date('2025-06-15T12:00:00Z'));
8
+ });
9
+
10
+ afterEach(() => {
11
+ vi.useRealTimers();
12
+ });
13
+
14
+ const now = () => Math.floor(Date.now() / 1000);
15
+
16
+ it('сегодня', () => {
17
+ expect(useFormatTimeAgo(now(), 'en')).toBe('today');
18
+ });
19
+
20
+ it('вчера', () => {
21
+ expect(useFormatTimeAgo(now() - 86400, 'en')).toBe('yesterday');
22
+ });
23
+
24
+ it('5 дней назад', () => {
25
+ expect(useFormatTimeAgo(now() - 5 * 86400, 'en')).toBe('5 days ago');
26
+ });
27
+
28
+ it('3 месяца назад', () => {
29
+ expect(useFormatTimeAgo(now() - 90 * 86400, 'en')).toBe('3 months ago');
30
+ });
31
+
32
+ it('2 года назад', () => {
33
+ expect(useFormatTimeAgo(now() - 730 * 86400, 'en')).toBe('2 years ago');
34
+ });
35
+
36
+ it('локализация (ru)', () => {
37
+ expect(useFormatTimeAgo(now() - 5 * 86400, 'ru')).toBe('5 дней назад');
38
+ });
39
+ });
@@ -8,14 +8,14 @@ describe('useFilterChipsItems', () => {
8
8
  it('пустой массив без filter_ в query', () => {
9
9
  const filters = ref([]);
10
10
  const route = { query: {} };
11
- const { chipsItems } = useFilterChipsItems(filters, route, t);
11
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
12
12
  expect(chipsItems.value).toEqual([]);
13
13
  });
14
14
 
15
15
  it('игнорирует не-filter параметры', () => {
16
16
  const filters = ref([]);
17
17
  const route = { query: { page: '1', sort: 'popular' } };
18
- const { chipsItems } = useFilterChipsItems(filters, route, t);
18
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
19
19
  expect(chipsItems.value).toEqual([]);
20
20
  });
21
21
 
@@ -29,7 +29,7 @@ describe('useFilterChipsItems', () => {
29
29
  ],
30
30
  }]);
31
31
  const route = { query: { filter_hair_color: 'blonde' } };
32
- const { chipsItems } = useFilterChipsItems(filters, route, t);
32
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
33
33
  expect(chipsItems.value).toHaveLength(1);
34
34
  expect(chipsItems.value[0].title).toBe('Hair color: Blonde');
35
35
  expect(chipsItems.value[0].value).toEqual(['filter_hair_color']);
@@ -45,7 +45,7 @@ describe('useFilterChipsItems', () => {
45
45
  ],
46
46
  }]);
47
47
  const route = { query: { filter_age_from: '18', filter_age_to: '30' } };
48
- const { chipsItems } = useFilterChipsItems(filters, route, t);
48
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
49
49
  expect(chipsItems.value).toHaveLength(1);
50
50
  expect(chipsItems.value[0].title).toBe('Age: 18 - 30');
51
51
  expect(chipsItems.value[0].value).toEqual(['filter_age_from', 'filter_age_to']);
@@ -61,7 +61,21 @@ describe('useFilterChipsItems', () => {
61
61
  ],
62
62
  }]);
63
63
  const route = { query: { filter_weight: '65' } };
64
- const { chipsItems } = useFilterChipsItems(filters, route, t);
64
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
65
65
  expect(chipsItems.value[0].title).toBe('Weight: 65');
66
66
  });
67
+
68
+ it('месяц → локализованное название', () => {
69
+ const filters = ref([{
70
+ name: 'birth_month',
71
+ title: 'Birth month',
72
+ options: [
73
+ { name: '1', title: '1' },
74
+ { name: '12', title: '12' },
75
+ ],
76
+ }]);
77
+ const route = { query: { filter_birth_month: '6' } };
78
+ const { chipsItems } = useFilterChipsItems(filters, route, t, ref('en'));
79
+ expect(chipsItems.value[0].title).toBe('Birth month: June');
80
+ });
67
81
  });
@@ -1,26 +1,18 @@
1
1
  /**
2
- * Форматирует Unix timestamp (в секундах) в относительную строку: «сегодня», «вчера», «3 дня назад» и т.д.
3
- * @param t - функция перевода (i18n)
2
+ * Форматирует Unix timestamp (в секундах) в относительную строку в локали пользователя
3
+ * («сегодня», «вчера», «5 дней назад») через Intl.RelativeTimeFormat.
4
4
  * @param time - Unix timestamp в секундах
5
+ * @param locale - активная локаль (код или BCP-47)
5
6
  */
6
- export const useFormatTimeAgo = (t, time: number) => {
7
- const pastDate = new Date(time * 1000);
8
- const now = new Date();
7
+ export const useFormatTimeAgo = (time: number, locale: string): string => {
8
+ const diffInDays = Math.floor((Date.now() - time * 1000) / 86_400_000);
9
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
9
10
 
10
- const diffInMs = Number(now) - Number(pastDate);
11
- const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
12
-
13
- if (diffInDays === 0) {
14
- return t('date.today');
15
- } else if (diffInDays === 1) {
16
- return t('date.day_ago');
17
- } else if (diffInDays < 30) {
18
- return `${diffInDays} ${t('date.days_ago')}`;
19
- } else if (diffInDays < 365) {
20
- const diffInMonths = Math.floor(diffInDays / 30);
21
- return `${diffInMonths} ${t('date.month')}${diffInMonths > 1 ? 's' : ''}`;
22
- } else {
23
- const diffInYears = Math.floor(diffInDays / 365);
24
- return `${diffInYears} ${t('date.year')}${diffInYears > 1 ? 's' : ''}`;
11
+ if (diffInDays < 30) {
12
+ return rtf.format(-diffInDays, 'day');
13
+ }
14
+ if (diffInDays < 365) {
15
+ return rtf.format(-Math.floor(diffInDays / 30), 'month');
25
16
  }
26
- }
17
+ return rtf.format(-Math.floor(diffInDays / 365), 'year');
18
+ };
@@ -6,7 +6,8 @@ import type { Ref } from 'vue';
6
6
  export function useFilterChipsItems(
7
7
  filters: Ref<IModelFilter[]>,
8
8
  route: { query: LocationQuery },
9
- t: (key: string) => string
9
+ t: (key: string) => string,
10
+ locale: Ref<string>
10
11
  ) {
11
12
  const chipsItems = computed<IChipsItem[]>(() => {
12
13
  const queryItems = Object.keys(route.query)
@@ -60,7 +61,7 @@ export function useFilterChipsItems(
60
61
 
61
62
  if (item.includes('month')) {
62
63
  return getMonth(
63
- t,
64
+ locale.value,
64
65
  Number(value ?? defaultValue?.name) - 1
65
66
  );
66
67
  }
@@ -64,7 +64,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
64
64
  marginClass: '_mb-12'
65
65
  },
66
66
  {
67
- type: 'text',
67
+ type: 'email',
68
68
  value: 'from',
69
69
  label: 'email',
70
70
  hideLabel: true,
@@ -113,7 +113,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
113
113
  marginClass: '_mb-12 _mt-4'
114
114
  },
115
115
  {
116
- type: 'text',
116
+ type: 'email',
117
117
  value: 'from',
118
118
  label: 'email',
119
119
  hideLabel: true,
@@ -153,7 +153,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
153
153
  marginClass: '_mb-12 _mt-4'
154
154
  },
155
155
  {
156
- type: 'text',
156
+ type: 'email',
157
157
  value: 'from',
158
158
  label: 'email',
159
159
  hideLabel: true,
@@ -185,7 +185,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
185
185
  marginClass: '_mb-12'
186
186
  })),
187
187
  {
188
- type: 'text',
188
+ type: 'email',
189
189
  value: 'from',
190
190
  label: 'email',
191
191
  hideLabel: true,
@@ -247,7 +247,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
247
247
  marginClass: '_mt-4 _mb-12'
248
248
  },
249
249
  {
250
- type: 'text',
250
+ type: 'email',
251
251
  value: 'from',
252
252
  label: 'email',
253
253
  hideLabel: true,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "itube-specs",
3
3
  "type": "module",
4
- "version": "0.0.766",
4
+ "version": "0.0.768",
5
5
  "main": "./nuxt.config.ts",
6
6
  "types": "./types/index.d.ts",
7
7
  "scripts": {
package/runtime/index.ts CHANGED
@@ -8,7 +8,6 @@ export * from './constants/report-forms-subjects';
8
8
  export * from './constants/playlist-step';
9
9
  export * from './constants/thumb-size';
10
10
  export * from './utils/format-date';
11
- export * from './utils/format-number';
12
11
  export * from './utils/format-time';
13
12
  export * from './utils/get-multiple-query';
14
13
  export * from './utils/get-duration';
@@ -35,7 +34,6 @@ export * from './utils/cleaners/clean-video-data';
35
34
  export * from './utils/cleaners/clean-video-card';
36
35
  export * from './utils/cleaners/clean-model-info';
37
36
  export * from './utils/video-data-add-model-icon';
38
- export * from './utils/format-time-ago';
39
37
  export * from './utils/converters/convert-categories-to-footer';
40
38
  export * from './utils/converters/convert-categories-to-chips';
41
39
  export * from './utils/converters/convert-model-card-to-chips';
@@ -1,30 +1,32 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { getMonth } from '../get-month';
3
3
 
4
- const t = (key: string) => key;
5
-
6
4
  describe('getMonth', () => {
7
5
  it('январь = 0', () => {
8
- expect(getMonth(t, 0)).toBe('january');
6
+ expect(getMonth('en', 0)).toBe('January');
9
7
  });
10
8
 
11
9
  it('декабрь = 11', () => {
12
- expect(getMonth(t, 11)).toBe('december');
10
+ expect(getMonth('en', 11)).toBe('December');
13
11
  });
14
12
 
15
13
  it('июнь = 5', () => {
16
- expect(getMonth(t, 5)).toBe('june');
14
+ expect(getMonth('en', 5)).toBe('June');
17
15
  });
18
16
 
19
17
  it('undefined → декабрь (дефолт)', () => {
20
- expect(getMonth(t)).toBe('december');
18
+ expect(getMonth('en')).toBe('December');
21
19
  });
22
20
 
23
21
  it('отрицательное число → декабрь', () => {
24
- expect(getMonth(t, -1)).toBe('december');
22
+ expect(getMonth('en', -1)).toBe('December');
25
23
  });
26
24
 
27
25
  it('число >= 12 → декабрь', () => {
28
- expect(getMonth(t, 12)).toBe('december');
26
+ expect(getMonth('en', 12)).toBe('December');
27
+ });
28
+
29
+ it('локализация (ru)', () => {
30
+ expect(getMonth('ru', 5)).toBe('июнь');
29
31
  });
30
32
  });
@@ -1,24 +1,9 @@
1
1
  /**
2
- * Возвращает локализованное название месяца по его номеру (0-based).
3
- * @param t - функция перевода (i18n)
4
- * @param number - номер месяца от 0 (январь) до 11 (декабрь)
2
+ * Возвращает локализованное название месяца по его номеру (0-based) через Intl.DateTimeFormat.
3
+ * @param locale - локаль (код или BCP-47)
4
+ * @param number - номер месяца от 0 (январь) до 11 (декабрь); вне диапазона → декабрь
5
5
  */
6
- export function getMonth(t: (key: string) => string, number?: number) {
7
- const months = [
8
- 'january',
9
- 'february',
10
- 'march',
11
- 'april',
12
- 'may',
13
- 'june',
14
- 'july',
15
- 'august',
16
- 'september',
17
- 'october',
18
- 'november',
19
- 'december'
20
- ];
6
+ export function getMonth(locale: string, number?: number): string {
21
7
  const index = typeof number === 'number' && number >= 0 && number < 12 ? number : 11;
22
- const key = months[index];
23
- return t(key) || key;
8
+ return new Intl.DateTimeFormat(locale, { month: 'long' }).format(new Date(2000, index, 1));
24
9
  }
@@ -1,24 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { formatNumber } from '../format-number';
3
-
4
- describe('formatNumber', () => {
5
- it('тысячи', () => {
6
- expect(formatNumber(10400)).toBe('10.4K');
7
- });
8
-
9
- it('миллионы', () => {
10
- expect(formatNumber(1500000)).toBe('1.5M');
11
- });
12
-
13
- it('маленькое число без сокращения', () => {
14
- expect(formatNumber(999)).toBe('999');
15
- });
16
-
17
- it('ноль', () => {
18
- expect(formatNumber(0)).toBe('0');
19
- });
20
-
21
- it('ровно тысяча', () => {
22
- expect(formatNumber(1000)).toBe('1K');
23
- });
24
- });
@@ -1,49 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { formatTimeAgo } from '../format-time-ago';
3
-
4
- const t = (key: string) => key;
5
-
6
- describe('formatTimeAgo', () => {
7
- beforeEach(() => {
8
- vi.useFakeTimers();
9
- vi.setSystemTime(new Date('2025-06-15T12:00:00Z'));
10
- });
11
-
12
- afterEach(() => {
13
- vi.useRealTimers();
14
- });
15
-
16
- const now = () => Math.floor(Date.now() / 1000);
17
-
18
- it('сегодня', () => {
19
- expect(formatTimeAgo(now(), t)).toBe('today');
20
- });
21
-
22
- it('1 день назад', () => {
23
- expect(formatTimeAgo(now() - 86400, t)).toBe('day_ago');
24
- });
25
-
26
- it('5 дней назад', () => {
27
- expect(formatTimeAgo(now() - 5 * 86400, t)).toBe('5 days_ago');
28
- });
29
-
30
- it('29 дней назад', () => {
31
- expect(formatTimeAgo(now() - 29 * 86400, t)).toBe('29 days_ago');
32
- });
33
-
34
- it('1 месяц', () => {
35
- expect(formatTimeAgo(now() - 31 * 86400, t)).toBe('1 month ago');
36
- });
37
-
38
- it('3 месяца — plural', () => {
39
- expect(formatTimeAgo(now() - 90 * 86400, t)).toBe('3 months ago');
40
- });
41
-
42
- it('1 год', () => {
43
- expect(formatTimeAgo(now() - 366 * 86400, t)).toBe('1 year ago');
44
- });
45
-
46
- it('2 года — plural', () => {
47
- expect(formatTimeAgo(now() - 730 * 86400, t)).toBe('2 years ago');
48
- });
49
- });
@@ -1,12 +0,0 @@
1
- /**
2
- * Форматирует число в короткий формат: 10400 → 10.4K.
3
- * @param number - исходное число
4
- */
5
-
6
- export function formatNumber (number: number) {
7
- return number.toLocaleString('en-US', {
8
- maximumFractionDigits: 2,
9
- notation: 'compact',
10
- compactDisplay: 'short'
11
- });
12
- };
@@ -1,26 +0,0 @@
1
- /**
2
- * Форматирует Unix timestamp (в секундах) в относительную строку: «сегодня», «вчера», «3 дня назад» и т.д.
3
- * @param date - Unix timestamp в секундах
4
- * @param t - функция перевода (i18n)
5
- */
6
- export function formatTimeAgo(date: number, t) {
7
- const pastDate = new Date(date * 1000);
8
- const now = new Date();
9
-
10
- const diffInMs = Number(now) - Number(pastDate);
11
- const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
12
-
13
- if (diffInDays === 0) {
14
- return t('today');
15
- } else if (diffInDays === 1) {
16
- return t('day_ago');
17
- } else if (diffInDays < 30) {
18
- return `${diffInDays} ${t('days_ago')}`;
19
- } else if (diffInDays < 365) {
20
- const diffInMonths = Math.floor(diffInDays / 30);
21
- return `${diffInMonths} ${t('month')}${diffInMonths > 1 ? 's' : ''} ${t('ago')}`;
22
- } else {
23
- const diffInYears = Math.floor(diffInDays / 365);
24
- return `${diffInYears} ${t('year')}${diffInYears > 1 ? 's' : ''} ${t('ago')}`;
25
- }
26
- }