@akinon/next 1.100.0-rc.71 → 1.100.0

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +51 -1320
  2. package/__tests__/next-config.test.ts +10 -1
  3. package/bin/pz-prebuild.js +0 -1
  4. package/components/accordion.tsx +5 -20
  5. package/components/file-input.tsx +3 -65
  6. package/components/input.tsx +0 -2
  7. package/components/link.tsx +12 -16
  8. package/components/modal.tsx +16 -32
  9. package/components/plugin-module.tsx +4 -32
  10. package/data/client/checkout.ts +2 -4
  11. package/data/server/category.ts +24 -44
  12. package/data/server/flatpage.ts +12 -16
  13. package/data/server/landingpage.ts +12 -16
  14. package/data/server/list.ts +13 -23
  15. package/data/server/product.ts +39 -66
  16. package/data/server/special-page.ts +12 -16
  17. package/data/urls.ts +2 -6
  18. package/hocs/server/with-segment-defaults.tsx +2 -5
  19. package/hooks/use-localization.ts +3 -2
  20. package/middlewares/complete-gpay.ts +1 -2
  21. package/middlewares/complete-masterpass.ts +1 -2
  22. package/middlewares/default.ts +13 -50
  23. package/middlewares/locale.ts +1 -9
  24. package/middlewares/redirection-payment.ts +1 -2
  25. package/middlewares/saved-card-redirection.ts +1 -2
  26. package/middlewares/three-d-redirection.ts +2 -2
  27. package/middlewares/url-redirection.ts +14 -8
  28. package/package.json +2 -2
  29. package/plugins.d.ts +6 -8
  30. package/plugins.js +1 -3
  31. package/redux/actions.ts +47 -0
  32. package/redux/middlewares/checkout.ts +1 -5
  33. package/redux/middlewares/pre-order/index.ts +3 -1
  34. package/redux/middlewares/pre-order/payment-option-reset.ts +34 -0
  35. package/types/commerce/order.ts +0 -1
  36. package/types/index.ts +1 -34
  37. package/utils/app-fetch.ts +2 -7
  38. package/utils/override-middleware.ts +1 -0
  39. package/utils/redirect.ts +3 -22
  40. package/with-pz-config.js +5 -1
  41. package/__tests__/redirect.test.ts +0 -319
  42. package/api/form.ts +0 -84
  43. package/api/image-proxy.ts +0 -75
  44. package/api/similar-product-list.ts +0 -84
  45. package/api/similar-products.ts +0 -120
  46. package/data/server/basket.ts +0 -72
  47. package/utils/redirect-ignore.ts +0 -35
@@ -35,84 +35,57 @@ const getProductDataHandler = ({
35
35
  .join('&');
36
36
  }
37
37
 
38
- let data: ProductResult;
39
-
40
- try {
41
- data = await appFetch<ProductResult>({
42
- url,
43
- locale,
44
- currency,
45
- init: {
46
- headers: {
47
- Accept: 'application/json',
48
- 'Content-Type': 'application/json',
49
- ...(headers ?? {})
50
- }
38
+ const data = await appFetch<ProductResult>({
39
+ url,
40
+ locale,
41
+ currency,
42
+ init: {
43
+ headers: {
44
+ Accept: 'application/json',
45
+ 'Content-Type': 'application/json',
46
+ ...(headers ?? {})
51
47
  }
52
- });
53
- } catch (error) {
54
- logger.error('Failed to fetch product data', {
55
- handler: 'getProductDataHandler',
56
- pk,
57
- error: error.message,
58
- url
59
- });
60
- return null;
61
- }
48
+ }
49
+ });
62
50
 
63
51
  const categoryUrl = product.categoryUrl(data.product.pk);
64
52
 
65
- let productCategoryData: ProductCategoryResult;
66
- let breadcrumbData: { menu?: unknown } = {};
67
-
68
- try {
69
- productCategoryData = await appFetch<ProductCategoryResult>({
70
- url: categoryUrl,
71
- locale,
72
- currency,
73
- init: {
74
- headers: {
75
- Accept: 'application/json',
76
- 'Content-Type': 'application/json'
77
- }
53
+ const productCategoryData = await appFetch<ProductCategoryResult>({
54
+ url: categoryUrl,
55
+ locale,
56
+ currency,
57
+ init: {
58
+ headers: {
59
+ Accept: 'application/json',
60
+ 'Content-Type': 'application/json'
78
61
  }
79
- });
80
-
81
- const menuItemModel = productCategoryData?.results[0]?.menuitemmodel;
82
-
83
- if (!menuItemModel) {
84
- logger.warn(
85
- 'menuItemModel is undefined, skipping breadcrumbData fetch',
86
- {
87
- handler: 'getProductDataHandler',
88
- pk
89
- }
90
- );
91
- return { data, breadcrumbData: undefined };
92
62
  }
63
+ });
93
64
 
94
- const breadcrumbUrl = product.breadcrumbUrl(menuItemModel);
65
+ const menuItemModel = productCategoryData?.results[0]?.menuitemmodel;
95
66
 
96
- breadcrumbData = await appFetch<{ menu?: unknown }>({
97
- url: breadcrumbUrl,
98
- locale,
99
- currency,
100
- init: {
101
- headers: {
102
- Accept: 'application/json',
103
- 'Content-Type': 'application/json'
104
- }
105
- }
106
- });
107
- } catch (error) {
108
- logger.warn('Failed to fetch breadcrumb data', {
67
+ if (!menuItemModel) {
68
+ logger.warn('menuItemModel is undefined, skipping breadcrumbData fetch', {
109
69
  handler: 'getProductDataHandler',
110
- pk,
111
- error: error.message
70
+ pk
112
71
  });
113
- // Continue without breadcrumb data
72
+ return { data, breadcrumbData: undefined };
114
73
  }
115
74
 
75
+ const breadcrumbUrl = product.breadcrumbUrl(menuItemModel);
76
+
77
+ const breadcrumbData = await appFetch<any>({
78
+ url: breadcrumbUrl,
79
+ locale,
80
+ currency,
81
+ init: {
82
+ headers: {
83
+ Accept: 'application/json',
84
+ 'Content-Type': 'application/json'
85
+ }
86
+ }
87
+ });
88
+
116
89
  return {
117
90
  data,
118
91
  breadcrumbData: breadcrumbData?.menu
@@ -15,24 +15,20 @@ const getSpecialPageDataHandler = (
15
15
  return async function () {
16
16
  const params = generateCommerceSearchParams(searchParams);
17
17
 
18
- try {
19
- const data: GetCategoryResponse = await appFetch({
20
- url: `${category.getSpecialPageByPk(pk)}${params}`,
21
- locale,
22
- currency,
23
- init: {
24
- headers: {
25
- Accept: 'application/json',
26
- 'Content-Type': 'application/json',
27
- ...(headers ?? {})
28
- }
18
+ const data: GetCategoryResponse = await appFetch({
19
+ url: `${category.getSpecialPageByPk(pk)}${params}`,
20
+ locale,
21
+ currency,
22
+ init: {
23
+ headers: {
24
+ Accept: 'application/json',
25
+ 'Content-Type': 'application/json',
26
+ ...(headers ?? {})
29
27
  }
30
- });
28
+ }
29
+ });
31
30
 
32
- return data;
33
- } catch (error) {
34
- return null;
35
- }
31
+ return data;
36
32
  };
37
33
  };
38
34
 
package/data/urls.ts CHANGED
@@ -183,11 +183,7 @@ export const product = {
183
183
  breadcrumbUrl: (menuitemmodel: string) =>
184
184
  `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
185
185
  bundleProduct: (productPk: string, queryString: string) =>
186
- `/bundle-product/${productPk}/?${queryString}`,
187
- similarProducts: (params?: string) =>
188
- `/similar-products${params ? `?${params}` : ''}`,
189
- similarProductsList: (params?: string) =>
190
- `/similar-product-list${params ? `?${params}` : ''}`
186
+ `/bundle-product/${productPk}/?${queryString}`
191
187
  };
192
188
 
193
189
  export const wishlist = {
@@ -251,7 +247,7 @@ export const widgets = {
251
247
  };
252
248
 
253
249
  export const form = {
254
- getForm: (pk: number) => `/forms/${pk}/generate`
250
+ getForm: (pk: number) => `/forms/${pk}/generate/`
255
251
  };
256
252
 
257
253
  const URLS = {
@@ -72,13 +72,10 @@ const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
72
72
  const checkRedisVariables = () => {
73
73
  const requiredVariableValues = [
74
74
  process.env.CACHE_HOST,
75
- process.env.CACHE_PORT
75
+ process.env.CACHE_PORT,
76
+ process.env.CACHE_SECRET
76
77
  ];
77
78
 
78
- if (!settings.usePrettyUrlRoute) {
79
- requiredVariableValues.push(process.env.CACHE_SECRET);
80
- }
81
-
82
79
  if (
83
80
  !requiredVariableValues.every((v) => v) &&
84
81
  process.env.NODE_ENV === 'production'
@@ -4,6 +4,7 @@ import { LocalizationContext } from '../localization/provider';
4
4
  import { useContext } from 'react';
5
5
  import { setCookie, urlLocaleMatcherRegex } from '../utils';
6
6
  import { LocaleUrlStrategy } from '../localization';
7
+ import { useRouter } from 'next/navigation';
7
8
 
8
9
  export const useLocalization = () => {
9
10
  const {
@@ -17,6 +18,8 @@ export const useLocalization = () => {
17
18
  localeUrlStrategy
18
19
  } = useContext(LocalizationContext);
19
20
 
21
+ const router = useRouter();
22
+
20
23
  /**
21
24
  * Sets the locale in the URL.
22
25
  * @param locale Locale value defined in the settings.
@@ -27,8 +30,6 @@ export const useLocalization = () => {
27
30
 
28
31
  let targetUrl;
29
32
 
30
- setCookie('pz-locale', locale);
31
-
32
33
  if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
33
34
  const hostParts = hostname.split('.');
34
35
  const subDomain = hostParts[0];
@@ -148,8 +148,7 @@ const withCompleteGpay =
148
148
  logger.info('Redirecting to order success page', {
149
149
  middleware: 'complete-gpay',
150
150
  redirectUrlWithLocale,
151
- ip,
152
- setCookie: request.headers.get('set-cookie')
151
+ ip
153
152
  });
154
153
 
155
154
  // Using POST method while redirecting causes an error,
@@ -149,8 +149,7 @@ const withCompleteMasterpass =
149
149
  logger.info('Redirecting to order success page', {
150
150
  middleware: 'complete-masterpass',
151
151
  redirectUrlWithLocale,
152
- ip,
153
- setCookie: request.headers.get('set-cookie')
152
+ ip
154
153
  });
155
154
 
156
155
  // Using POST method while redirecting causes an error,
@@ -302,6 +302,19 @@ const withPzDefault =
302
302
  )}`;
303
303
  }
304
304
 
305
+ if (
306
+ !req.middlewareParams.found &&
307
+ Settings.customNotFoundEnabled
308
+ ) {
309
+ const pathname = url.pathname
310
+ .replace(/\/+$/, '')
311
+ .split('/');
312
+ url.pathname = url.pathname.replace(
313
+ pathname.pop(),
314
+ 'pz-not-found'
315
+ );
316
+ }
317
+
305
318
  Settings.rewrites.forEach((rewrite) => {
306
319
  url.pathname = url.pathname.replace(
307
320
  rewrite.source,
@@ -339,24 +352,6 @@ const withPzDefault =
339
352
  middlewareResult = NextResponse.rewrite(url);
340
353
  }
341
354
 
342
- if (
343
- !req.middlewareParams.found &&
344
- Settings.customNotFoundEnabled
345
- ) {
346
- const pathSegments = url.pathname
347
- .replace(/\/+$/, '')
348
- .split('/');
349
- if (pathSegments.length >= 3) {
350
- url.pathname = `/${pathSegments[1]}/${pathSegments[2]}/pz-not-found`;
351
- } else {
352
- url.pathname = '/pz-not-found';
353
- }
354
-
355
- middlewareResult = NextResponse.rewrite(url, {
356
- status: 404
357
- });
358
- }
359
-
360
355
  const { localeUrlStrategy } =
361
356
  Settings.localization;
362
357
 
@@ -406,38 +401,6 @@ const withPzDefault =
406
401
  }
407
402
  );
408
403
 
409
- if (
410
- !url.pathname.startsWith(
411
- `/${currency}/orders`
412
- )
413
- ) {
414
- const currentCookieLocale =
415
- req.cookies.get('pz-locale')?.value;
416
-
417
- const urlHasExplicitLocale =
418
- url.pathname.match(urlLocaleMatcherRegex);
419
- const shouldUpdateCookie =
420
- !currentCookieLocale ||
421
- urlHasExplicitLocale;
422
-
423
- if (shouldUpdateCookie) {
424
- middlewareResult.cookies.set(
425
- 'pz-locale',
426
- locale?.length > 0
427
- ? locale
428
- : defaultLocaleValue,
429
- {
430
- domain: rootHostname,
431
- sameSite: 'none',
432
- secure: true,
433
- expires: new Date(
434
- Date.now() + 1000 * 60 * 60 * 24 * 7
435
- ) // 7 days
436
- }
437
- );
438
- }
439
- }
440
-
441
404
  if (
442
405
  req.cookies.get('pz-locale') &&
443
406
  req.cookies.get('pz-locale').value !== locale
@@ -23,15 +23,7 @@ const getMatchedLocale = (pathname: string, req: PzNextRequest) => {
23
23
  );
24
24
 
25
25
  if (subDomainLocaleMatched && subDomainLocaleMatched[0]) {
26
- const subdomainLocale = subDomainLocaleMatched[0].slice(1);
27
-
28
- const isValidSubdomainLocale = settings.localization.locales.find(
29
- (l) => l.value === subdomainLocale
30
- );
31
-
32
- if (isValidSubdomainLocale) {
33
- matchedLocale = subdomainLocale;
34
- }
26
+ matchedLocale = subDomainLocaleMatched[0].slice(1);
35
27
  }
36
28
  }
37
29
  }
@@ -149,8 +149,7 @@ const withRedirectionPayment =
149
149
  logger.info('Redirecting to order success page', {
150
150
  middleware: 'redirection-payment',
151
151
  redirectUrlWithLocale,
152
- ip,
153
- setCookie: request.headers.get('set-cookie')
152
+ ip
154
153
  });
155
154
 
156
155
  // Using POST method while redirecting causes an error,
@@ -149,8 +149,7 @@ const withSavedCardRedirection =
149
149
  logger.info('Redirecting to order success page', {
150
150
  middleware: 'saved-card-redirection',
151
151
  redirectUrlWithLocale,
152
- ip,
153
- setCookie: request.headers.get('set-cookie')
152
+ ip
154
153
  });
155
154
 
156
155
  // Using POST method while redirecting causes an error,
@@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
4
4
  import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
+ import { ServerVariables } from '../utils/server-variables';
7
8
 
8
9
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
10
  if (stream) {
@@ -148,8 +149,7 @@ const withThreeDRedirection =
148
149
  logger.info('Redirecting to order success page', {
149
150
  middleware: 'three-d-redirection',
150
151
  redirectUrlWithLocale,
151
- ip,
152
- setCookie: request.headers.get('set-cookie')
152
+ ip
153
153
  });
154
154
 
155
155
  // Using POST method while redirecting causes an error,
@@ -4,7 +4,6 @@ import { PzNextRequest } from '.';
4
4
  import logger from '../utils/log';
5
5
  import { urlLocaleMatcherRegex } from '../utils';
6
6
  import { getUrlPathWithLocale } from '../utils/localization';
7
- import { shouldIgnoreRedirect } from '../utils/redirect-ignore';
8
7
  import { ROUTES } from 'routes';
9
8
 
10
9
  // This middleware is used to handle url redirections set in Omnitron
@@ -61,13 +60,20 @@ const withUrlRedirection =
61
60
 
62
61
  const setCookies = request.headers.getSetCookie();
63
62
 
64
- if (
65
- shouldIgnoreRedirect(
66
- url.pathname,
67
- req.middlewareParams.rewrites.locale
68
- )
69
- ) {
70
- return middleware(req, event);
63
+ if (settings.commerceRedirectionIgnoreList) {
64
+ const shouldIgnoreRedirect =
65
+ settings.commerceRedirectionIgnoreList.some((ignorePath) =>
66
+ redirectUrl.pathname.startsWith(
67
+ getUrlPathWithLocale(
68
+ ignorePath,
69
+ req.middlewareParams.rewrites.locale
70
+ )
71
+ )
72
+ );
73
+
74
+ if (shouldIgnoreRedirect) {
75
+ return middleware(req, event);
76
+ }
71
77
  }
72
78
 
73
79
  const response = NextResponse.redirect(redirectUrl.toString(), {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinon/next",
3
3
  "description": "Core package for Project Zero Next",
4
- "version": "1.100.0-rc.71",
4
+ "version": "1.100.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -35,7 +35,7 @@
35
35
  "set-cookie-parser": "2.6.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@akinon/eslint-plugin-projectzero": "1.100.0-rc.71",
38
+ "@akinon/eslint-plugin-projectzero": "1.100.0",
39
39
  "@babel/core": "7.26.10",
40
40
  "@babel/preset-env": "7.26.9",
41
41
  "@babel/preset-typescript": "7.27.0",
package/plugins.d.ts CHANGED
@@ -13,6 +13,7 @@ declare module '@akinon/pz-masterpass/src/redux/reducer' {
13
13
  export const setError: any;
14
14
  export const setOtpModalVisible: any;
15
15
  export const setCvcRequired: any;
16
+ export const resetMasterpassState: any;
16
17
  }
17
18
 
18
19
  declare module '@akinon/pz-otp' {
@@ -30,14 +31,11 @@ declare module '@akinon/pz-saved-card' {
30
31
  export const SavedCardOption: any;
31
32
  }
32
33
 
34
+ declare module '@akinon/pz-iyzico-saved-card' {
35
+ export const iyzicoSavedCardReducer: any;
36
+ export const iyzicoSavedCardMiddleware: any;
37
+ }
38
+
33
39
  declare module '@akinon/pz-apple-pay' {}
34
40
 
35
41
  declare module '@akinon/pz-flow-payment' {}
36
-
37
- declare module '@akinon/pz-similar-products' {
38
- export const SimilarProductsModal: any;
39
- export const SimilarProductsFilterSidebar: any;
40
- export const SimilarProductsResultsGrid: any;
41
- export const SimilarProductsPlugin: any;
42
- export const SimilarProductsButtonPlugin: any;
43
- }
package/plugins.js CHANGED
@@ -16,7 +16,5 @@ module.exports = [
16
16
  'pz-tabby-extension',
17
17
  'pz-apple-pay',
18
18
  'pz-tamara-extension',
19
- 'pz-hepsipay',
20
- 'pz-flow-payment',
21
- 'pz-similar-products'
19
+ 'pz-flow-payment'
22
20
  ];
@@ -0,0 +1,47 @@
1
+ // Re-export all action creators from slice modules
2
+ export * from './reducers/root';
3
+ export * from './reducers/header';
4
+ export * from './reducers/checkout';
5
+ export * from './reducers/config';
6
+
7
+ // Import RTK Query APIs
8
+ import { basketApi } from '../data/client/basket';
9
+ import { productApi } from '../data/client/product';
10
+ import { miscApi } from '../data/client/misc';
11
+ import { checkoutApi } from '../data/client/checkout';
12
+ import { wishlistApi } from '../data/client/wishlist';
13
+
14
+ // Create RTK Query action creators
15
+ export const rtkApiActionCreators = {
16
+ // RTK Query API endpoints
17
+ ...Object.fromEntries(
18
+ Object.entries(basketApi.endpoints).map(([key, endpoint]) => [
19
+ `api_basket_${key}`,
20
+ endpoint.initiate
21
+ ])
22
+ ),
23
+ ...Object.fromEntries(
24
+ Object.entries(productApi.endpoints).map(([key, endpoint]) => [
25
+ `api_product_${key}`,
26
+ endpoint.initiate
27
+ ])
28
+ ),
29
+ ...Object.fromEntries(
30
+ Object.entries(miscApi.endpoints).map(([key, endpoint]) => [
31
+ `api_misc_${key}`,
32
+ endpoint.initiate
33
+ ])
34
+ ),
35
+ ...Object.fromEntries(
36
+ Object.entries(checkoutApi.endpoints).map(([key, endpoint]) => [
37
+ `api_checkout_${key}`,
38
+ endpoint.initiate
39
+ ])
40
+ ),
41
+ ...Object.fromEntries(
42
+ Object.entries(wishlistApi.endpoints).map(([key, endpoint]) => [
43
+ `api_wishlist_${key}`,
44
+ endpoint.initiate
45
+ ])
46
+ )
47
+ } as any;
@@ -51,11 +51,7 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
51
51
  const result: CheckoutResult = next(action);
52
52
  const errors = result?.payload?.errors;
53
53
 
54
- if (
55
- !!errors &&
56
- ((typeof errors === 'object' && Object.keys(errors).length > 0) ||
57
- (Array.isArray(errors) && errors.length > 0))
58
- ) {
54
+ if (errors) {
59
55
  dispatch(setErrors(errors));
60
56
  }
61
57
 
@@ -8,15 +8,17 @@ import { dataSourceShippingOptionMiddleware } from './data-source-shipping-optio
8
8
  import { attributeBasedShippingOptionMiddleware } from './attribute-based-shipping-option';
9
9
  import { paymentOptionMiddleware } from './payment-option';
10
10
  import { installmentOptionMiddleware } from './installment-option';
11
+ import { paymentOptionResetMiddleware } from './payment-option-reset';
11
12
  import { shippingStepMiddleware } from './shipping-step';
12
13
 
13
14
  // ⚠️ WARNING: Redux Toolkit applies middlewares in reverse order (from last to first).
14
- // This list is written **in reverse execution order** to ensure they run in the correct sequence.
15
+ // This list is written **in reverse execution order** to ensure they run in the correct sequence.
15
16
  // If you add a new middleware, make sure to insert it **in reverse order** based on execution priority.
16
17
 
17
18
  export const preOrderMiddlewares = [
18
19
  shippingStepMiddleware,
19
20
  installmentOptionMiddleware,
21
+ paymentOptionResetMiddleware,
20
22
  paymentOptionMiddleware,
21
23
  attributeBasedShippingOptionMiddleware,
22
24
  dataSourceShippingOptionMiddleware,
@@ -0,0 +1,34 @@
1
+ import { Middleware } from '@reduxjs/toolkit';
2
+ import { CheckoutResult, MiddlewareParams } from '../../../types';
3
+ import {
4
+ setBankAccounts,
5
+ setCardType,
6
+ setInstallmentOptions,
7
+ setSelectedBankAccountPk
8
+ } from '../../reducers/checkout';
9
+ import { resetMasterpassState } from '@akinon/pz-masterpass/src/redux/reducer';
10
+
11
+ export const paymentOptionResetMiddleware: Middleware = ({
12
+ dispatch
13
+ }: MiddlewareParams) => {
14
+ return (next) => (action) => {
15
+ const result: CheckoutResult = next(action);
16
+
17
+ if (
18
+ action.type === 'api/executeMutation/fulfilled' &&
19
+ action.meta?.arg?.endpointName === 'setPaymentOption'
20
+ ) {
21
+ dispatch(setInstallmentOptions([]));
22
+ dispatch(setBankAccounts([]));
23
+ dispatch(setSelectedBankAccountPk(null));
24
+ dispatch(setCardType(null));
25
+ dispatch(resetMasterpassState());
26
+ }
27
+
28
+ return result;
29
+ };
30
+ };
31
+
32
+ Object.defineProperty(paymentOptionResetMiddleware, 'name', {
33
+ value: 'paymentOptionResetMiddleware'
34
+ });
@@ -114,7 +114,6 @@ export interface Order {
114
114
  pk: number;
115
115
  name: string;
116
116
  slug: string;
117
- logo: string;
118
117
  [key: string]: any;
119
118
  };
120
119
  }