feat: add new static pages

This commit is contained in:
Сюткина Дарья Александровна (4047910) 2024-01-24 21:43:16 +04:00
parent fcd103b8c8
commit c94c69202e
92 changed files with 2005 additions and 325 deletions

View File

@ -2,15 +2,31 @@
"Header": { "Header": {
"registration": "Registration", "registration": "Registration",
"enter": "Enter", "enter": "Enter",
"account": "My Account",
"menu": { "menu": {
"faq": "Start grow with BB", "bb-client": "Start grow with BB",
"experts": "Become BB Expert", "bb-expert": "Become BB Expert",
"news": "Blog&News" "blog": "Blog&News"
} }
}, },
"Main": { "Main": {
"title": "Bbuddy - Main", "title": "Bbuddy - Main",
"description": "Bbuddy desc" "description": "Bbuddy desc",
"header": "Mentorship, Career\nDevelopment & Coaching.",
"header-desc": "The ins-and-outs of building a career in tech, gaining experience from a mentor, and getting your feet wet with coaching.",
"news": "Professional Articles & Project News",
"popular": "Popular Topics"
},
"BbClient": {
"title": "Bbuddy - Client",
"description": "Bbuddy desc client",
"header": "Personal, professional, and business\ndevelopment mobile application"
},
"BbExpert": {
"title": "Bbuddy - Expert",
"description": "Bbuddy desc expert",
"header": "Be where your customers are",
"header-desc": "B Buddy is an application for active people interested in growth on all fronts — personal, professional and business. Right now they are looking for you."
}, },
"Account": { "Account": {
"menu": { "menu": {
@ -71,5 +87,22 @@
"faq": "FAQ", "faq": "FAQ",
"privacy-policy": "Privacy Policy" "privacy-policy": "Privacy Policy"
} }
},
"Experts": {
"title": "Find a expert",
"filter": {
"price": "Price from {from}€ to {to}€",
"duration": "Duration from {from}min to {to}min",
"apply": "Apply",
"find": "Find",
"search": "Search for an Expert",
"sort": "Sort",
"language": "Language"
},
"list": {
"price": "0€",
"duration": "0min",
"details": "Details"
}
} }
} }

View File

@ -2,16 +2,31 @@
"Header": { "Header": {
"registration": "Registration", "registration": "Registration",
"enter": "Enter", "enter": "Enter",
"account": "My Account",
"menu": { "menu": {
"faq": "Start grow with BB", "bb-client": "Start grow with BB",
"experts": "Become BB Expert", "bb-expert": "Become BB Expert",
"news": "Blog&News" "blog": "Blog&News"
} }
}, },
"Main": { "Main": {
"title": "Bbuddy - Main", "title": "Bbuddy - Main",
"description": "Bbuddy desc", "description": "Bbuddy desc",
"news": "Professional Articles & Project News" "header": "Mentorship, Career\nDevelopment & Coaching.",
"header-desc": "The ins-and-outs of building a career in tech, gaining experience from a mentor, and getting your feet wet with coaching.",
"news": "Professional Articles & Project News",
"popular": "Popular Topics"
},
"BbClient": {
"title": "Bbuddy - Client",
"description": "Bbuddy desc client",
"header": "Personal, professional, and business\ndevelopment mobile application"
},
"BbExpert": {
"title": "Bbuddy - Expert",
"description": "Bbuddy desc expert",
"header": "Be where your customers are",
"header-desc": "B Buddy is an application for active people interested in growth on all fronts — personal, professional and business. Right now they are looking for you."
}, },
"Account": { "Account": {
"menu": { "menu": {
@ -72,5 +87,22 @@
"faq": "FAQ", "faq": "FAQ",
"privacy-policy": "Privacy Policy" "privacy-policy": "Privacy Policy"
} }
},
"Experts": {
"title": "Find a expert",
"filter": {
"price": "Price from {from}€ to {to}€",
"duration": "Duration from {from}min to {to}min",
"apply": "Apply",
"find": "Find",
"search": "Search for an Expert",
"sort": "Sort",
"language": "Language"
},
"list": {
"price": "0€",
"duration": "0min",
"details": "Details"
}
} }
} }

View File

@ -2,15 +2,31 @@
"Header": { "Header": {
"registration": "Registration", "registration": "Registration",
"enter": "Enter", "enter": "Enter",
"account": "My Account",
"menu": { "menu": {
"faq": "Start grow with BB", "bb-client": "Start grow with BB",
"experts": "Become BB Expert", "bb-expert": "Become BB Expert",
"news": "Blog&News" "blog": "Blog&News"
} }
}, },
"Main": { "Main": {
"title": "Bbuddy - Main", "title": "Bbuddy - Main",
"description": "Bbuddy desc" "description": "Bbuddy desc",
"header": "Mentorship, Career\nDevelopment & Coaching.",
"header-desc": "The ins-and-outs of building a career in tech, gaining experience from a mentor, and getting your feet wet with coaching.",
"news": "Professional Articles & Project News",
"popular": "Popular Topics"
},
"BbClient": {
"title": "Bbuddy - Client",
"description": "Bbuddy desc client",
"header": "Personal, professional, and business\ndevelopment mobile application"
},
"BbExpert": {
"title": "Bbuddy - Expert",
"description": "Bbuddy desc expert",
"header": "Be where your customers are",
"header-desc": "B Buddy is an application for active people interested in growth on all fronts — personal, professional and business. Right now they are looking for you."
}, },
"Account": { "Account": {
"menu": { "menu": {
@ -71,5 +87,22 @@
"faq": "FAQ", "faq": "FAQ",
"privacy-policy": "Privacy Policy" "privacy-policy": "Privacy Policy"
} }
},
"Experts": {
"title": "Find a expert",
"filter": {
"price": "Price from {from}€ to {to}€",
"duration": "Duration from {from}min to {to}min",
"apply": "Apply",
"find": "Find",
"search": "Search for an Expert",
"sort": "Sort",
"language": "Language"
},
"list": {
"price": "0€",
"duration": "0min",
"details": "Details"
}
} }
} }

View File

@ -2,15 +2,31 @@
"Header": { "Header": {
"registration": "Registration", "registration": "Registration",
"enter": "Enter", "enter": "Enter",
"account": "My Account",
"menu": { "menu": {
"faq": "Start grow with BB", "bb-client": "Start grow with BB",
"experts": "Become BB Expert", "bb-expert": "Become BB Expert",
"news": "Blog&News" "blog": "Blog&News"
} }
}, },
"Main": { "Main": {
"title": "Bbuddy - Main", "title": "Bbuddy - Main",
"description": "Bbuddy desc" "description": "Bbuddy desc",
"header": "Mentorship, Career\nDevelopment & Coaching.",
"header-desc": "The ins-and-outs of building a career in tech, gaining experience from a mentor, and getting your feet wet with coaching.",
"news": "Professional Articles & Project News",
"popular": "Popular Topics"
},
"BbClient": {
"title": "Bbuddy - Client",
"description": "Bbuddy desc client",
"header": "Personal, professional, and business\ndevelopment mobile application"
},
"BbExpert": {
"title": "Bbuddy - Expert",
"description": "Bbuddy desc expert",
"header": "Be where your customers are",
"header-desc": "B Buddy is an application for active people interested in growth on all fronts — personal, professional and business. Right now they are looking for you."
}, },
"Account": { "Account": {
"menu": { "menu": {
@ -71,5 +87,22 @@
"faq": "FAQ", "faq": "FAQ",
"privacy-policy": "Privacy Policy" "privacy-policy": "Privacy Policy"
} }
},
"Experts": {
"title": "Find a expert",
"filter": {
"price": "Price from {from}€ to {to}€",
"duration": "Duration from {from}min to {to}min",
"apply": "Apply",
"find": "Find",
"search": "Search for an Expert",
"sort": "Sort",
"language": "Language"
},
"list": {
"price": "0€",
"duration": "0min",
"details": "Details"
}
} }
} }

View File

@ -2,15 +2,31 @@
"Header": { "Header": {
"registration": "Registration", "registration": "Registration",
"enter": "Enter", "enter": "Enter",
"account": "My Account",
"menu": { "menu": {
"faq": "Start grow with BB", "bb-client": "Start grow with BB",
"experts": "Become BB Expert", "bb-expert": "Become BB Expert",
"news": "Blog&News" "blog": "Blog&News"
} }
}, },
"Main": { "Main": {
"title": "Bbuddy - Main", "title": "Bbuddy - Main",
"description": "Bbuddy desc" "description": "Bbuddy desc",
"header": "Mentorship, Career\nDevelopment & Coaching.",
"header-desc": "The ins-and-outs of building a career in tech, gaining experience from a mentor, and getting your feet wet with coaching.",
"news": "Professional Articles & Project News",
"popular": "Popular Topics"
},
"BbClient": {
"title": "Bbuddy - Client",
"description": "Bbuddy desc client",
"header": "Personal, professional, and business\ndevelopment mobile application"
},
"BbExpert": {
"title": "Bbuddy - Expert",
"description": "Bbuddy desc expert",
"header": "Be where your customers are",
"header-desc": "B Buddy is an application for active people interested in growth on all fronts — personal, professional and business. Right now they are looking for you."
}, },
"Account": { "Account": {
"menu": { "menu": {
@ -71,5 +87,22 @@
"faq": "FAQ", "faq": "FAQ",
"privacy-policy": "Privacy Policy" "privacy-policy": "Privacy Policy"
} }
},
"Experts": {
"title": "Find a expert",
"filter": {
"price": "Price from {from}€ to {to}€",
"duration": "Duration from {from}min to {to}min",
"apply": "Apply",
"find": "Find",
"search": "Search for an Expert",
"sort": "Sort",
"language": "Language"
},
"list": {
"price": "0€",
"duration": "0min",
"details": "Details"
}
} }
} }

View File

@ -2,15 +2,31 @@
"Header": { "Header": {
"registration": "Регистрация", "registration": "Регистрация",
"enter": "Вход", "enter": "Вход",
"account": "Мой аккаунт",
"menu": { "menu": {
"faq": "Начни вместе с BB", "bb-client": "Начни вместе с BB",
"experts": "Стань BB экспертом", "bb-expert": "Стань BB экспертом",
"news": "Блог&Новости" "blog": "Блог&Новости"
} }
}, },
"Main": { "Main": {
"title": "Bbuddy - Главная", "title": "Bbuddy - Главная",
"description": "Bbuddy описание" "description": "Bbuddy описание",
"header": "Mentorship, Career\nDevelopment & Coaching.",
"header-desc": "The ins-and-outs of building a career in tech, gaining experience from a mentor, and getting your feet wet with coaching.",
"news": "Professional Articles & Project News",
"popular": "Popular Topics"
},
"BbClient": {
"title": "Bbuddy - Client",
"description": "Bbuddy desc client",
"header": "Personal, professional, and business\ndevelopment mobile application"
},
"BbExpert": {
"title": "Bbuddy - Expert",
"description": "Bbuddy desc expert",
"header": "Be where your customers are",
"header-desc": "B Buddy is an application for active people interested in growth on all fronts — personal, professional and business. Right now they are looking for you."
}, },
"Account": { "Account": {
"menu": { "menu": {
@ -71,5 +87,22 @@
"faq": "Помощь", "faq": "Помощь",
"privacy-policy": "Политика конфиденциальности" "privacy-policy": "Политика конфиденциальности"
} }
},
"Experts": {
"title": "Найти эксперта",
"filter": {
"price": "Цена от {from}₽ до {to}₽",
"duration": "Продолжительность от {from}мин до {to}мин",
"apply": "Применить",
"find": "Найти",
"search": "Поиск",
"sort": "Сортировка",
"language": "Язык"
},
"list": {
"price": "0₽",
"duration": "0мин",
"details": "Детали"
}
} }
} }

View File

@ -19,22 +19,13 @@ const nextConfig = {
}, },
experimental: { experimental: {
taint: true, taint: true,
typedRoutes: true // typedRoutes: true
}, },
output: 'export', output: 'export',
distDir: 'dist', distDir: 'dist',
poweredByHeader: false, poweredByHeader: false,
productionBrowserSourceMaps: true, productionBrowserSourceMaps: true,
trailingSlash: true trailingSlash: true
// redirects: async () => {
// return [
// {
// source: '/en/',
// destination: '/en.html',
// permanent: true
// }
// ]
// }
}; };
module.exports = withNextIntl(nextConfig); module.exports = withNextIntl(nextConfig);

76
package-lock.json generated
View File

@ -16,6 +16,8 @@
"next-intl": "^3.3.1", "next-intl": "^3.3.1",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"react-slick": "^0.29.0",
"slick-carousel": "^1.8.1",
"styled-components": "^6.1.1" "styled-components": "^6.1.1"
}, },
"devDependencies": { "devDependencies": {
@ -1617,6 +1619,11 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
},
"node_modules/es-abstract": { "node_modules/es-abstract": {
"version": "1.22.3", "version": "1.22.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
@ -3071,6 +3078,12 @@
"set-function-name": "^2.0.1" "set-function-name": "^2.0.1"
} }
}, },
"node_modules/jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"peer": true
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -3196,6 +3209,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -4356,6 +4374,22 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true "dev": true
}, },
"node_modules/react-slick": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz",
"integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==",
"dependencies": {
"classnames": "^2.2.5",
"enquire.js": "^2.1.6",
"json2mq": "^0.2.0",
"lodash.debounce": "^4.0.8",
"resize-observer-polyfill": "^1.5.0"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@ -4656,6 +4690,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/slick-carousel": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
"peerDependencies": {
"jquery": ">=1.8.0"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@ -6376,6 +6418,11 @@
"tapable": "^2.2.0" "tapable": "^2.2.0"
} }
}, },
"enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
},
"es-abstract": { "es-abstract": {
"version": "1.22.3", "version": "1.22.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
@ -7449,6 +7496,12 @@
"set-function-name": "^2.0.1" "set-function-name": "^2.0.1"
} }
}, },
"jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"peer": true
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -7553,6 +7606,11 @@
"p-locate": "^5.0.0" "p-locate": "^5.0.0"
} }
}, },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"lodash.merge": { "lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -8337,6 +8395,18 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true "dev": true
}, },
"react-slick": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz",
"integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==",
"requires": {
"classnames": "^2.2.5",
"enquire.js": "^2.1.6",
"json2mq": "^0.2.0",
"lodash.debounce": "^4.0.8",
"resize-observer-polyfill": "^1.5.0"
}
},
"readdirp": { "readdirp": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@ -8547,6 +8617,12 @@
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true "dev": true
}, },
"slick-carousel": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
"requires": {}
},
"source-map-js": { "source-map-js": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",

View File

@ -17,6 +17,8 @@
"next-intl": "^3.3.1", "next-intl": "^3.3.1",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"react-slick": "^0.29.0",
"slick-carousel": "^1.8.1",
"styled-components": "^6.1.1" "styled-components": "^6.1.1"
}, },
"devDependencies": { "devDependencies": {

Binary file not shown.

BIN
public/images/b-2-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

17
public/images/b-3-1.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 86 KiB

17
public/images/b-3-2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 180 KiB

17
public/images/b-3-3.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 312 KiB

17
public/images/b-3-4.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 90 KiB

17
public/images/b-5-1.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 297 KiB

17
public/images/b-5-2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 149 KiB

17
public/images/b-5-3.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 384 KiB

17
public/images/b-5-4.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 112 KiB

BIN
public/images/b-6-img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.1404 17.5312C20.396 16.7812 18.5932 15.6867 17.7185 15.2456C16.5795 14.6718 16.4857 14.6249 15.5904 15.2901C14.9932 15.734 14.5962 16.1306 13.8973 15.9815C13.1984 15.8324 11.6796 14.992 10.3498 13.6663C9.01994 12.3407 8.13072 10.7779 7.98119 10.0813C7.83166 9.38478 8.23478 8.99244 8.67447 8.39385C9.29415 7.5501 9.24728 7.40947 8.71759 6.27041C8.30462 5.38447 7.17822 3.59853 6.42541 2.85791C5.62009 2.06244 5.62009 2.20306 5.10119 2.41869C4.67874 2.59643 4.27345 2.81249 3.89041 3.06416C3.14041 3.56244 2.72416 3.97635 2.43306 4.59838C2.14197 5.22041 2.01119 6.67869 3.51447 9.40963C5.01775 12.1406 6.07244 13.537 8.2554 15.7138C10.4384 17.8907 12.117 19.0612 14.5709 20.4374C17.6065 22.1376 18.7709 21.8062 19.3948 21.5156C20.0187 21.2249 20.4345 20.8124 20.9337 20.0624C21.186 19.68 21.4025 19.2752 21.5806 18.8531C21.7967 18.336 21.9373 18.336 21.1404 17.5312Z" stroke="#fff" stroke-width="2" stroke-miterlimit="10"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

9
public/images/cash.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 137 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 196 KiB

BIN
public/images/faq-img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 113 KiB

BIN
public/images/girl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

21
public/images/guard.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 109 KiB

17
public/images/man.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 137 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 163 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 106 KiB

9
public/images/note.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 95 KiB

9
public/images/search.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 72 KiB

BIN
public/images/slider-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/images/slider-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
public/images/slider-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
public/images/slider-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
public/images/slider-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

17
public/images/target.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 122 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 116 KiB

BIN
public/images/waves-top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,12 @@
<svg width="170" height="170" viewBox="0 0 170 170" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_f_1643_242)">
<path d="M138 85C138 114.271 114.271 138 85 138C55.7289 138 32 114.271 32 85C32 55.7289 55.7289 32 85 32C114.271 32 138 55.7289 138 85Z" fill="white"/>
</g>
<defs>
<filter id="filter0_f_1643_242" x="0" y="0" width="170" height="170" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="16" result="effect1_foregroundBlur_1643_242"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 755 B

17
src/actions/tags.ts Normal file
View File

@ -0,0 +1,17 @@
import { apiClient } from '../lib/apiClient';
import { SearchData } from '../types/tags';
export const getTagList = async (locale: string) => {
const response = await apiClient.post(
'/home/searchdata',
{},
{
headers: {
'X-User-Language': locale,
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJuYW1laWQiOiIxNzIiLCJuYmYiOjE3MDM2ODMyMDgsImV4cCI6MTczNTIxOTIwOCwiaWF0IjoxNzAzNjgzMjA4fQ.KgnYfKO7oVFLlDuKhfyNN6RAaXKdeSzJd7F4r6_15AA'
}
}
);
return response.data as SearchData || null;
};

View File

@ -1,40 +1,30 @@
import React from 'react'; import React from 'react';
import { CustomPagination } from '../../../../components/view'; import { getTranslations } from 'next-intl/server';
import { ExpertsFilter } from '../../../../components/Experts/Filter'; import { getFilter } from '../../../../utils/filter';
import { ExpertsAdditionalFilter } from '../../../../components/Experts/AdditionalFilter';
import { ExpertsList } from '../../../../components/Experts/ExpertsList';
import { getExpertsList } from '../../../../actions/experts'; import { getExpertsList } from '../../../../actions/experts';
import { getTagList } from '../../../../actions/tags';
import { Experts } from '../../../../components/Experts/Experts';
export default async function Experts({ params }: { params: { locale: string } }) { export default async function ExpertsPage({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) {
const data = await getExpertsList({ const searchData = await getTagList(params.locale);
"themesTagIds": [ const filter = getFilter(searchData, searchParams);
1,2,3,4,5,6,7,8 console.log('filter main page', filter);
], const experts = await getExpertsList(filter, params.locale);
"priceFrom": null, const t = await getTranslations('Experts');
"priceTo": null,
"durationFrom": null,
"durationTo": null
}, params.locale);
return ( return (
<div className="main-find"> <div className="main-find">
<div className="b-inner"> <div className="b-inner">
<div className="main-find__top"> <div className="main-find__top">
<h2 className="title-h2">Find an expert</h2> <h2 className="title-h2">{t('title')}</h2>
<div className="open-filter"> <div className="open-filter">
<img src="/images/options-outline.svg" className="" alt=""/> <img src="/images/options-outline.svg" className="" alt=""/>
</div> </div>
</div> </div>
<div className="row"> <Experts
<div className="col-xl-3 col-lg-4 d-none d-lg-block"> experts={experts}
<ExpertsFilter locale={params.locale} /> searchData={searchData}
</div> />
<div className="col-xl-9 col-lg-8 ">
<ExpertsAdditionalFilter />
<ExpertsList data={data} locale={params.locale} />
<CustomPagination total={20} />
</div>
</div>
</div> </div>
</div> </div>
); );

View File

@ -1,5 +1,7 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
import { GeneralTopSection } from '../../../components/Page';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Main', title: 'Bbuddy - Main',
@ -7,41 +9,13 @@ export const metadata: Metadata = {
}; };
export default function Home() { export default function Home() {
const t = useTranslations('Main');
return ( return (
<div className="main-top"> <GeneralTopSection
<div className="b-inner"> title={t('header')}
<h1 className="title-h1"> description={t('header-desc')}
Mentorship, Career mainImage="main-top.png"
Development & Coaching. />
</h1>
<div className="main-top__wrap-text">
<p className="main-top__text">The ins-and-outs of building a career in tech, gaining experience </p>
<p className="main-top__text">from a mentor, and getting your feet wet with coaching.</p>
</div>
<div className="main-top__wrap-btn">
<a
href="https://apps.apple.com/us/app/bbuddy/id6443601377?platform=iphone"
className="main-top__btn main-top__btn--apple"
target="_blank"
>
<img className="" src="/images/logo-apple.svg" alt=""/>
<span className="line" />
AppStore
</a>
<a
href="https://play.google.com/store/apps/details?id=com.bbuddy.whistle"
className="main-top__btn main-top__btn--android"
target="_blank"
>
<img className="" src="/images/logo-android.svg" alt=""/>
<span className="line" />
GooglePlay
</a>
</div>
<div className="main-top__img">
<img className="" src="/images/main-top.png" alt=""/>
</div>
</div>
</div>
); );
}; };

View File

@ -1,5 +0,0 @@
import { redirect } from 'next/navigation';
export default function AccountMainPage({ params }: { params: { userId: string } }) {
redirect(`/${params.userId}/sessions`);
};

View File

@ -0,0 +1,12 @@
import { redirect, notFound } from 'next/navigation';
// import { checkAuthToken } from '../../../utils/storage/auth';
export default function AccountMainPage() {
// if (checkAuthToken()) {
// redirect('/sessions');
// } else {
// notFound();
// }
redirect('/sessions');
};

View File

@ -0,0 +1,197 @@
import React from 'react';
import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
import { GeneralTopSection } from '../../../components/Page';
export const metadata: Metadata = {
title: 'Bbuddy - Take the lead with BB',
description: 'Bbuddy desc Take the lead with BB'
};
export default function BbClientPage() {
const t = useTranslations('BbClient');
return (
<>
<GeneralTopSection
title={t('header')}
mainImage="banner-phone.png"
/>
<div className="main-articles bb-client">
<div className="b-inner">
<h2 className="title-h2">How do you want to advance today?</h2>
<div className="tag-list">
<div className="tag-item">Work - Life Balance</div>
<div className="tag-item">Personal growth</div>
<div className="tag-item">Career planning</div>
<div className="tag-item">Executive coaching</div>
<div className="tag-item">Financial thinking</div>
<div className="tag-item">Business development</div>
<div className="tag-item">Startup</div>
<div className="tag-item">Strategic session</div>
<div className="tag-item">Relationships</div>
<div className="tag-item">Healthy lifestyle</div>
<div className="tag-item">Relocation</div>
</div>
</div>
</div>
<section className="b2">
<div className="b-inner">
<h2 className="title-h2">Start the path to big goals with us</h2>
<div className="row">
<div className="col-12 col-md-5 col-lg-6">
<img src="/images/b-2-bg.png" alt="" className="b2-img" />
</div>
<div className="col-12 col-md-7 col-lg-6">
<div className="advice-list">
<div className="advice-item">Take advantage of coaching sessions to help you grow
confidently by enhancing your skills and personal qualities.
</div>
<div className="advice-item">Allow our professionals to use their expertise to assist
you in making a difference.
</div>
<div className="advice-item">Develop the required skills and steer clear of common
pitfalls by working with a mentor.
</div>
<div className="advice-item">Learn new things to equip yourself with cutting-edge
resources for advancement.
</div>
<div className="advice-item">Communicate with like-minded people to get even more out of
B BUDDY.
</div>
</div>
</div>
</div>
</div>
</section>
<section className="b3">
<div className="b-inner">
<h2 className="title-h2">B Buddy: Reliable and convenient</h2>
<div className="row">
<div className="col-12 col-sm-6 col col-lg-3">
<div className="b3-item">
<img src="/images/b-3-1.svg" className="b3-img"/>
<div className="b3-title">from 300 hours</div>
<div className="b3-text">experience of each coach</div>
</div>
</div>
<div className="col-12 col-sm-6 col col-lg-3">
<div className="b3-item">
<img src="/images/b-3-2.svg" className="b3-img"/>
<div className="b3-title">25 countries
</div>
<div className="b3-text">location of participants
</div>
</div>
</div>
<div className="col-12 col-sm-6 col col-lg-3">
<div className="b3-item">
<img src="/images/b-3-3.svg" className="b3-img"/>
<div className="b3-title">15 criteria</div>
<div className="b3-text">use B BUDDY to find the best mentor
</div>
</div>
</div>
<div className="col-12 col-sm-6 col col-lg-3">
<div className="b3-item">
<img src="/images/b-3-4.svg" className="b3-img"/>
<div className="b3-title">5 minutes</div>
<div className="b3-text">from registering to speaking with an expert
</div>
</div>
</div>
</div>
</div>
</section>
<section className="b4">
<div className="b-inner">
<div className="row">
<div className="col-md-7 col-lg-6">
<div className="b4-list">
<div className="b4-item">
<div className="b4-title">All in one</div>
<div className="b4-descr">
Whether it's self-evaluation, public speaking, or
financial planning, B BUDDY can provide the support you need in one single
application. Now your smartphone has even more room for stunning photos.
</div>
</div>
<div className="b4-item">
<div className="b4-title">The solution is right in your pocket</div>
<div className="b4-descr">
Get expert help as soon as you need it. A smartphone is
always within reach. Your competitive advantage is speed.
</div>
</div>
<div className="b4-item">
<div className="b4-title">Focus on your top priorities</div>
<div className="b4-descr">
Make a call, schedule a meeting, set a reminder, and save
a link to a video conference. This can all be done with a single click using the
B BUDDY app. Let us take care of the rest while you focus on the things that
count.
</div>
</div>
</div>
</div>
<div className="col-md-5 col-lg-6 position-relative">
<img src="/images/girl.png" alt="" className="b4-img" />
</div>
</div>
</div>
</section>
<section className="b5 waves-top">
<div className="b-inner">
<h2 className="title-h2">B BUDDY will</h2>
<div className="row">
<div className="col-12 col-sm-6 col-lg-3">
<div className="b5-item">
<img src="/images/b-5-1.svg" alt="" className="b5-img" />
<div className="b5-text">Choose a professional who is a perfect fit for you</div>
</div>
</div>
<div className="col-12 col-sm-6 col-lg-3">
<div className="b5-item">
<img src="/images/b-5-2.svg" alt="" className="b5-img" />
<div className="b5-text">Set up meetings</div>
</div>
</div>
<div className="col-12 col-sm-6 col-lg-3">
<div className="b5-item">
<img src="/images/b-5-3.svg" alt="" className="b5-img" />
<div className="b5-text">Notify you in advance of the scheduled session</div>
</div>
</div>
<div className="col-12 col-sm-6 col-lg-3">
<div className="b5-item">
<img src="/images/b-5-4.svg" alt="" className="b5-img" />
<div className="b5-text">Invite you to a video call at the appropriate time</div>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<h3>Isnt that amazing?</h3>
</div>
</div>
</div>
</section>
<section className="b6">
<div className="b-inner">
<div className="b6-banner">
<div className="b6-content">
<div className="b6-title">Being formed by business owners and educators, B BUDDY is
completely aware of what is required and how to deliver it to you.
</div>
<div className="b6-descr">We work hard to support people in becoming more successful! We
therefore strive daily to make B BUDDY the top coaching, mentoring, and consulting
application.
</div>
</div>
<img src="/images/b-6-img.png" alt="" className="b6-img" />
</div>
</div>
</section>
</>
);
};

View File

@ -0,0 +1,129 @@
import React from 'react';
import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
import { GeneralTopSection } from '../../../components/Page';
import { ScreenCarousel } from '../../../components/Page/ScreenCarousel/index';
export const metadata: Metadata = {
title: 'Bbuddy - Become a BB expert',
description: 'Bbuddy desc Become a BB expert'
};
export default function BbExpertPage() {
const t = useTranslations('BbExpert');
return (
<>
<GeneralTopSection
title={t('header')}
description={t('header-desc')}
mainImage="banner-people.png"
/>
<section className="b-slider waves-top">
<ScreenCarousel />
</section>
<section className="b2" style={{ padding: '40px 0' }}>
<div className="b-inner">
<h2 className="title-h2">Everything you need in one application</h2>
<div className="row">
<div className="col-12">
<div className="advice-list">
<div className="advice-item advice-item--with-img">
<img src="/images/search.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
Customer search
</div>
<div className="advice-text">
The application algorithm will match your profile data to the customers
request and recommend the best option. BB customers will come directly
to you!
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/note.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
Automation of appointments
</div>
<div className="advice-text">
Create your work schedule only once. Customers will make their own
appointments thereafter selecting a time slot on your calendar.
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/videocall.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
Video calls
</div>
<div className="advice-text">
Call up directly in B Buddy at the touch of a button. No more flicking
through apps in search of a link. A gentle reminder required? We will
send one.
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/guard.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
Legal security
</div>
<div className="advice-text">All required documents, including the contract
and consent to the processing of personal data, have been verified and
are available in the app.
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/feedback.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
Feedback
</div>
<div className="advice-text">Gather feedback from all the customers in one
place. B Buddy will remind the customer to leave feedback after the
meeting is over.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section className="b3">
<div className="b-inner">
<h2 className="title-h2">B Buddy expert has:</h2>
<div className="row">
<div className="col-12 col-md-4">
<div className="b3-item">
<img src="/images/calendar.svg" className="b3-img"/>
<div className="b3-title">From 300 hours</div>
<div className="b3-text">Coaching practice</div>
</div>
</div>
<div className="col-12 col-md-4">
<div className="b3-item">
<img src="/images/man.svg" className="b3-img"/>
<div className="b3-title">From 5 years</div>
<div className="b3-text">Professional experience</div>
</div>
</div>
<div className="col-12 col-md-4">
<div className="b3-item">
<img src="/images/target.svg" className="b3-img"/>
<div className="b3-title">Expertise</div>
<div className="b3-text">Confirmed by successfully implemented projects
</div>
</div>
</div>
</div>
</div>
</section>
</>
);
};

View File

@ -1,48 +1,37 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { getFilter } from '../../../utils/filter';
import { getExpertsList } from '../../../actions/experts'; import { getExpertsList } from '../../../actions/experts';
import { ExpertsFilter } from '../../../components/Experts/Filter'; import { getTagList } from '../../../actions/tags';
import { ExpertsAdditionalFilter } from '../../../components/Experts/AdditionalFilter'; import { Experts } from '../../../components/Experts/Experts';
import { ExpertsList } from '../../../components/Experts/ExpertsList';
import { CustomPagination } from '../../../components/view';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Experts', title: 'Bbuddy - Experts',
description: 'Bbuddy desc experts' description: 'Bbuddy desc experts'
}; };
export default async function Experts({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) { export default async function ExpertsPage({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) {
console.log('search params', searchParams); const searchData = await getTagList(params.locale);
const data = await getExpertsList({ const filter = getFilter(searchData, searchParams);
"themesTagIds": [ console.log('filter experts page', filter);
1,2,3,4,5,6,7,8 const experts = await getExpertsList(filter, params.locale);
], const t = await getTranslations('Experts');
"priceFrom": null,
"priceTo": null,
"durationFrom": null,
"durationTo": null
}, params.locale);
return ( return (
<div className="page-search"> <div className="page-search">
<div className="main-find"> <div className="main-find">
<div className="b-inner"> <div className="b-inner">
<div className="main-find__top"> <div className="main-find__top">
<h2 className="title-h2">Find a expert</h2> <h2 className="title-h2">{t('title')}</h2>
<div className="open-filter"> <div className="open-filter">
<img src="/images/options-outline.svg" alt="" /> <img src="/images/options-outline.svg" alt="" />
</div> </div>
</div> </div>
<div className="row"> <Experts
<div className="col-xl-3 col-lg-4 d-none d-lg-block"> experts={experts}
<ExpertsFilter locale={params.locale} /> searchData={searchData}
</div> />
<div className="col-xl-9 col-lg-8 ">
<ExpertsAdditionalFilter />
<ExpertsList data={data} locale={params.locale} />
<CustomPagination total={20} />
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,8 +8,82 @@ export const metadata: Metadata = {
export default function Faq() { export default function Faq() {
return ( return (
<div> <>
Страница faq <section className="banner banner-faq">
<div className="b-inner">
<div className="row">
<div className="col-12 col-md-7 col-xl-6 text-center">
<h1 className="mb-4">Frequently asked questions</h1>
<div className="banner-note mb-4">
Do you have any questions? Call!
</div> </div>
<div className="store-buttons justify-content-center">
<a href="tel:+35799752176" className="store-button phone">+3 579 97 52 176</a>
</div>
</div>
<div className="col-12 col-md-5 col-xl-6 position-relative">
<img src="/images/faq-img.png" alt="" className="banner-img" />
</div>
</div>
</div>
</section>
<section className="b2 mb-5">
<div className="b-inner">
<div className="row">
<div className="col-12">
<div className="advice-list mb-5">
<div className="advice-item advice-item--with-img">
<img src="/images/cash.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
How much will I earn for the consultation?
</div>
<div className="advice-text">
It is entirely up to you because you set the price for the services.
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/comission.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
What will BBuddys commission be?
</div>
<div className="advice-text">
B Buddy will receive 15% commission of the consultation cost.
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/new-client.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
Can I bring my customer?
</div>
<div className="advice-text">
You will be able to invite existing customers to the application via a
unique link. B Buddy will not charge a fee for transferring your
existing customers.
</div>
</div>
</div>
<div className="advice-item advice-item--with-img">
<img src="/images/new-expert.svg" alt="" className="advice-img" />
<div className="advice-content">
<div className="advice-title">
What about if I introduce a new expert to B Buddy?
</div>
<div className="advice-text">
Within two months of the date of his platform registration, you will
receive 5% of each of his consultations.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</>
); );
} };

View File

@ -1,15 +0,0 @@
import React from 'react';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Bbuddy - Privacy Policy',
description: 'Bbuddy desc search'
};
export default function PrivacyPolicy() {
return (
<div>
Privacy policy
</div>
);
}

View File

@ -1,51 +1,85 @@
'use client'; 'use client';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Select } from 'antd'; import {Button, Select} from 'antd';
import { useSearchParams } from 'next/navigation';
import { useRouter } from '../../navigation';
import { AdditionalFilter } from '../../types/experts'; import { AdditionalFilter } from '../../types/experts';
import { INITIAL_ADD_FILTER } from '../../constants/experts';
import { LOCALES } from '../../constants/locale'; import { LOCALES } from '../../constants/locale';
import { getObjectByFilter, getObjectByAdditionalFilter } from '../../utils/filter';
import { CustomInput } from '../view'; import { CustomInput } from '../view';
export const ExpertsAdditionalFilter = () => { type ExpertAdditionalFilterProps = {
const [filter, setFilter] = useState<AdditionalFilter>(INITIAL_ADD_FILTER); searchPlaceholder: string;
sortLabel: string;
langLabel: string;
buttonFind: string;
basePath?: string;
};
export const ExpertsAdditionalFilter = ({
searchPlaceholder,
sortLabel,
langLabel,
buttonFind,
basePath = '/',
}): ExpertAdditionalFilterProps => {
const searchParams = useSearchParams();
const router = useRouter();
const [filter, setFilter] = useState<AdditionalFilter | undefined>(getObjectByAdditionalFilter(searchParams));
const onChangeFilter = useCallback((key: string, value: any) => { const onChangeFilter = useCallback((key: string, value: any) => {
setFilter({ // setFilter({
...filter, // ...filter,
[key]: value // [key]: value
}) // })
}, [filter]); }, [filter]);
const goToFilterPage = useCallback(() => {
router.push({
pathname: basePath,
query: {
...getObjectByFilter(searchParams),
...filter
}
})
}, [filter, searchParams]);
return ( return (
<div className="main-find__search"> <div className="main-find__search">
<div className="main-find__search__input"> <div className="main-find__search__input">
<CustomInput <CustomInput
placeholder="Search for an Expert" placeholder={searchPlaceholder}
defaultValue={filter?.text}
onChange={(e: any) => onChangeFilter('text', e?.target?.value)} onChange={(e: any) => onChangeFilter('text', e?.target?.value)}
/> />
</div> </div>
<div className="main-find__search__sort"> <div className="main-find__search__sort">
<Select <Select
defaultValue={INITIAL_ADD_FILTER.sort} defaultValue={filter?.sort}
onChange={(val) => onChangeFilter('sort', val)} onChange={(val) => onChangeFilter('sort', val)}
options={[ options={[
{ value: 'By top views', label: 'By top views' }, { value: 'byTop', label: 'By top views' },
{ value: 'By Price Ascending', label: 'By Price Ascending' }, { value: 'byPriceAsc', label: 'By price ascending' },
{ value: 'By Price Descending', label: 'By Price Descending' }, { value: 'byPriceDesc', label: 'By price descending' },
{ value: 'By rating', label: 'By rating' } { value: 'byRating', label: 'By rating' }
]} ]}
/> />
</div> </div>
<div className="main-find__search__language"> <div className="main-find__search__language">
<Select <Select
mode="multiple" mode="multiple"
defaultValue={INITIAL_ADD_FILTER.language} defaultValue={filter?.language}
onChange={(val) => onChangeFilter('language', val)} onChange={(val) => onChangeFilter('language', val)}
options={Object.entries(LOCALES).map(([ value, label ]) => ({ value, label }))} options={Object.entries(LOCALES).map(([ value, label ]) => ({ value, label }))}
/> />
</div> </div>
<button className="btn-apply">Find</button> <Button
className="btn-apply"
onClick={goToFilterPage}
>
{buttonFind}
</Button>
</div> </div>
); );
}; };

View File

@ -0,0 +1,47 @@
import React from 'react';
import { useTranslations } from 'next-intl';
import { SearchData } from '../../types/tags';
import { ExpertsData } from '../../types/experts';
import { ExpertsFilter } from './Filter';
import { ExpertsAdditionalFilter } from './AdditionalFilter';
import { ExpertsList } from './ExpertsList';
import { CustomPagination } from '../view/CustomPagination';
type ExpertsProps = {
searchData?: SearchData;
experts?: ExpertsData;
};
export const Experts = ({ searchData, experts }: ExpertsProps) => {
const t = useTranslations('Experts');
return (
<div className="row">
<div className="col-xl-3 col-lg-4 d-none d-lg-block">
<ExpertsFilter
searchData={searchData}
basePath="/experts"
priceTitle={t('filter.price', { from: searchData.sessionCostMin, to: searchData.sessionCostMax })}
durationTitle={t('filter.duration', { from: searchData.sessionDurationMin, to: searchData.sessionDurationMax })}
buttonApply={t('filter.apply')}
/>
</div>
<div className="col-xl-9 col-lg-8 ">
<ExpertsAdditionalFilter
searchPlaceholder={t('filter.search')}
sortLabel={t('filter.sort')}
langLabel={t('filter.language')}
buttonFind={t('filter.find')}
basePath="/experts"
/>
<ExpertsList
data={experts}
priceTitle={t('list.price')}
durationTitle={t('list.duration')}
detailButton={t('list.details')}
/>
<CustomPagination total={20} />
</div>
</div>
)
};

View File

@ -5,11 +5,22 @@ import { List, Tag } from 'antd';
import { RightOutlined } from '@ant-design/icons'; import { RightOutlined } from '@ant-design/icons';
import Image from 'next/image'; import Image from 'next/image';
import { Link } from '../../navigation'; import { Link } from '../../navigation';
import { Locale } from '../../types/locale';
import { ExpertsData } from '../../types/experts'; import { ExpertsData } from '../../types/experts';
export const ExpertsList = ({ data, locale }: { data: ExpertsData, locale: string }) => { type ExpertListProps = {
const isRus = locale === Locale.ru; data: ExpertsData;
priceTitle: string;
durationTitle: string;
detailButton: string;
};
export const ExpertsList = ({
data,
priceTitle,
durationTitle,
detailButton
}: ExpertListProps) => {
const getTitle = (str: string, value?: any): string => (value ? str.replace('0', value) : str);
return ( return (
<List <List
@ -32,7 +43,7 @@ export const ExpertsList = ({ data, locale }: { data: ExpertsData, locale: strin
<div className="card-profile__header__name">{`${item.name} ${item?.surname || ''}`}</div> <div className="card-profile__header__name">{`${item.name} ${item?.surname || ''}`}</div>
</Link> </Link>
<div className="card-profile__header__price"> <div className="card-profile__header__price">
{`${item?.sessionCost}${isRus ? '₽' : '€'}`} <span>/ {`${item?.sessionDuration}${isRus ? 'мин' : 'min'}`}</span> {getTitle(priceTitle, item?.sessionCost)} <span>/ {getTitle(durationTitle, item?.sessionDuration)}</span>
</div> </div>
</div> </div>
)} )}
@ -55,7 +66,7 @@ export const ExpertsList = ({ data, locale }: { data: ExpertsData, locale: strin
<div className="card-profile__desc">{item?.description}</div> <div className="card-profile__desc">{item?.description}</div>
<div className="card-profile__footer"> <div className="card-profile__footer">
<Link href={`/experts/${item?.id}` as any}> <Link href={`/experts/${item?.id}` as any}>
Details {detailButton}
<RightOutlined style={{ fontSize: '10px', padding: '0 7px' }}/> <RightOutlined style={{ fontSize: '10px', padding: '0 7px' }}/>
</Link> </Link>
</div> </div>

View File

@ -1,65 +1,97 @@
'use client'; 'use client';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { List } from 'antd'; import { Button, List } from 'antd';
import { FilterType, INITIAL_FILTER } from '../../constants/experts'; import { useSearchParams } from 'next/navigation';
import { Filter } from '../../types/experts'; import { Filter } from '../../types/experts';
import { SearchData, Tag } from '../../types/tags';
import { useRouter } from '../../navigation';
import { getObjectByFilter, getObjectByAdditionalFilter } from '../../utils/filter';
import { CustomSwitch, CustomSlider } from '../view'; import { CustomSwitch, CustomSlider } from '../view';
import {Locale} from "../../types/locale";
const dataCoaching = [ type ExpertsFilterProps = {
{ searchData: SearchData;
key: FilterType.WorkLifeBalance, basePath?: string;
title: 'Work-life Balance' priceTitle: string;
}, durationTitle: string;
{ buttonApply: string;
key: FilterType.StrategicSession, };
title: 'Strategic Session'
}, export const ExpertsFilter = ({
{ searchData,
key: FilterType.PersonalGrowth, basePath = '/',
title: 'Personal Growth' priceTitle,
}, durationTitle,
{ buttonApply
key: FilterType.PersonalGrowth, }: ExpertsFilterProps) => {
title: 'Career Planning' const searchParams = useSearchParams();
}, const router = useRouter();
{ const [filter, setFilter] = useState<Filter | undefined>(getObjectByFilter(searchParams));
key: FilterType.ExecutiveCoaching,
title: 'Executive Coaching' const onChangeTags = useCallback((id: number, checked: boolean) => {
let themesTagIds = filter?.themesTagIds || [];
if (checked && !themesTagIds?.includes(id)) {
themesTagIds.push(id);
} }
];
const dataMentoring = [ if (!checked && themesTagIds?.includes(id)) {
{ themesTagIds = themesTagIds.filter((tId) => tId != id);
key: FilterType.CareerDevelopment,
title: 'Career Development'
},
{
key: FilterType.Networking,
title: 'Networking'
} }
];
const dataConsultation = [
{
key: FilterType.BusinessModelReview,
title: 'Business Model Review'
}
];
export const ExpertsFilter = ({ locale }: { locale: string }) => {
const [filter, setFilter] = useState<Filter>(INITIAL_FILTER);
const isRus = locale === Locale.ru;
const onChangeFilter = useCallback((key: string, value: any) => {
setFilter({ setFilter({
...filter, ...filter,
[key]: value themesTagIds
}) });
}, [filter]); }, [filter, searchParams, searchData]);
const getList = useCallback((data: any[]) => ( const onChangePrice = useCallback(([min, max]: number[]) => {
const newFilter: Filter = {
...filter,
priceFrom: min,
priceTo: max
};
if (newFilter.priceFrom === searchData?.sessionCostMin) {
delete newFilter.priceFrom;
}
if (newFilter.priceTo === searchData?.sessionCostMax) {
delete newFilter.priceTo;
}
setFilter(newFilter);
}, [filter, searchParams, searchData]);
const onChangeDuration = useCallback(([min, max]: number[]) => {
const newFilter: Filter = {
...filter,
durationFrom: min,
durationTo: max
};
if (newFilter.durationFrom === searchData?.sessionDurationMin) {
delete newFilter.durationFrom;
}
if (newFilter.durationTo === searchData?.sessionDurationMax) {
delete newFilter.durationTo;
}
setFilter(newFilter);
}, [filter, searchParams, searchData]);
const goToFilterPage = useCallback(() => {
router.push({
pathname: basePath,
query: {
...filter,
...getObjectByAdditionalFilter(searchParams)
}
})
}, [filter, searchParams, searchData]);
const getList = useCallback((data: Tag[]) => (
<div className="b-filter__inner"> <div className="b-filter__inner">
<List <List
itemLayout="vertical" itemLayout="vertical"
@ -67,52 +99,57 @@ export const ExpertsFilter = ({ locale }: { locale: string }) => {
dataSource={data} dataSource={data}
split={false} split={false}
style={{ width: '100%' }} style={{ width: '100%' }}
renderItem={({ key, title }) => ( renderItem={({ id, name }) => (
<List.Item key={key} style={{ padding: 0 }}> <List.Item key={id} style={{ padding: 0 }}>
<div className="b-filter__item"> <div className="b-filter__item">
<div className="b-filter__title">{title}</div> <div className="b-filter__title">{name}</div>
<CustomSwitch <CustomSwitch
defaultChecked={filter[key]} defaultChecked={filter?.themesTagIds?.includes(id) || false}
onChange={(checked: boolean) => onChangeFilter(key, checked)} onChange={(checked: boolean) => onChangeTags(id, checked)}
/> />
</div> </div>
</List.Item> </List.Item>
)} )}
/> />
</div> </div>
), [filter]); ), [filter, searchParams, searchData]);
return ( return (
<div className="b-filter"> <div className="b-filter">
<h3 className="title-h3">Coaching</h3> {searchData.themesGroups?.length && searchData.themesGroups.map(({ id, name, tags }) => (
{getList(dataCoaching)} <div key={id}>
<h3 className="title-h3">Mentoring</h3> <h3 className="title-h3">{name}</h3>
{getList(dataMentoring)} {getList(tags)}
<h3 className="title-h3">Business-consultation</h3> </div>
{getList(dataConsultation)} ))}
<h3 className="title-h3">{`Price from 45${isRus ? '₽' : '€'} to 170${isRus ? '₽' : '€'}`}</h3> <h3 className="title-h3">{priceTitle}</h3>
<div className="b-filter__slider"> <div className="b-filter__slider">
<CustomSlider <CustomSlider
range range
step={1} step={1}
defaultValue={INITIAL_FILTER[FilterType.Price]} defaultValue={[filter?.priceFrom || searchData.sessionCostMin, filter?.priceTo || searchData.sessionCostMax]}
min={45} min={searchData.sessionCostMin}
max={170} max={searchData.sessionCostMax}
onChange={(val: any) => onChangeFilter(FilterType.Price, val)} onChange={onChangePrice}
/> />
</div> </div>
<h3 className="title-h3">{`Duration from 45${isRus ? 'мин' : 'min'} to 120${isRus ? 'мин' : 'min'}`}</h3> <h3 className="title-h3">{durationTitle}</h3>
<div className="b-filter__slider"> <div className="b-filter__slider">
<CustomSlider <CustomSlider
range range
step={1} step={1}
defaultValue={INITIAL_FILTER[FilterType.Duration]} defaultValue={[filter?.durationFrom || searchData.sessionDurationMin, filter?.durationTo || searchData.sessionDurationMax]}
min={45} min={searchData.sessionDurationMin}
max={120} max={searchData.sessionDurationMax}
onChange={(val: any) => onChangeFilter(FilterType.Duration, val)} onChange={onChangeDuration}
/> />
</div> </div>
<button className="btn-apply">Apply</button> <Button
className="btn-apply"
onClick={goToFilterPage}
>
{buttonApply}
</Button>
</div> </div>
); );
}; };

View File

@ -8,6 +8,7 @@ import { CloseOutlined } from '@ant-design/icons';
import { styled } from 'styled-components'; import { styled } from 'styled-components';
import { CustomInput } from '../view'; import { CustomInput } from '../view';
import { getAuth } from '../../actions/auth'; import { getAuth } from '../../actions/auth';
import {setAuthToken} from "../../utils/storage/auth";
type AuthModalProps = { type AuthModalProps = {
open: boolean; open: boolean;
@ -87,7 +88,10 @@ export const AuthModal: FC<AuthModalProps> = ({
setIsLoading(true); setIsLoading(true);
getAuth(login, password, paths[1]) getAuth(login, password, paths[1])
.then(({ data }) => { .then(({ data }) => {
console.log('jwt', data.jwtToken) if (data.jwtToken) {
setAuthToken(data.jwtToken);
handleCancel();
}
}) })
.catch((error) => { .catch((error) => {
notification.error({ notification.error({

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import Link from 'next/link';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { Link } from '../../../navigation'; import { Link as IntlLink } from '../../../navigation';
import { FOOTER_ROUTES } from '../../../constants/routes';
export const Footer = () => { export const Footer = () => {
const t = useTranslations('Footer'); const t = useTranslations('Footer');
@ -35,9 +35,8 @@ export const Footer = () => {
</div> </div>
<div className="b-footer__coll-2"> <div className="b-footer__coll-2">
<div className="b-footer__coll-2__item"> <div className="b-footer__coll-2__item">
{FOOTER_ROUTES.map((item) => ( <IntlLink href={'/faq' as any} className="b-footer__link">{t('menu.faq')}</IntlLink>
<Link key={item} href={`/${item}` as any} className="b-footer__link">{t(`menu.${item}`)}</Link> <Link href={'/docs/BBUDDY_privacy_policy_fin.docx' as any} className="b-footer__link">{t(`menu.privacy-policy`)}</Link>
))}
</div> </div>
<div className="b-footer__social"> <div className="b-footer__social">
<a href="#" className="b-footer__link"> <a href="#" className="b-footer__link">

View File

@ -0,0 +1,45 @@
import React from 'react';
type GeneralTopSectionProps = {
title: string;
description?: string;
mainImage: string;
};
export const GeneralTopSection = ({
title,
description,
mainImage
}: GeneralTopSectionProps) => (
<div className="main-top">
<div className="b-inner">
<h1 className="title-h1">{title}</h1>
<div className="main-top__wrap-text">
{description && <p className="main-top__text">{description}</p>}
</div>
<div className="main-top__wrap-btn">
<a
href="https://apps.apple.com/us/app/bbuddy/id6443601377?platform=iphone"
className="main-top__btn main-top__btn--apple"
target="_blank"
>
<img className="" src="/images/logo-apple.svg" alt=""/>
<span className="line" />
AppStore
</a>
<a
href="https://play.google.com/store/apps/details?id=com.bbuddy.whistle"
className="main-top__btn main-top__btn--android"
target="_blank"
>
<img className="" src="/images/logo-android.svg" alt=""/>
<span className="line" />
GooglePlay
</a>
</div>
<div className="main-top__img">
<img className="" src={`/images/${mainImage}`} alt=""/>
</div>
</div>
</div>
);

View File

@ -4,10 +4,13 @@ import React, { FC, useState, useEffect } from 'react';
import { Button } from 'antd'; import { Button } from 'antd';
import { styled } from 'styled-components'; import { styled } from 'styled-components';
import { AuthModal } from '../../Modals/AuthModal'; import { AuthModal } from '../../Modals/AuthModal';
import {checkAuthToken} from "../../../utils/storage/auth";
import {Link} from "../../../navigation";
type HeaderAuthLinksProps = { type HeaderAuthLinksProps = {
enterTitle: string; enterTitle: string;
registerTitle: string; registerTitle: string;
accountTitle: string;
separatorClass?: string; separatorClass?: string;
}; };
@ -24,6 +27,7 @@ const LinkButton = styled(Button)`
export const HeaderAuthLinks: FC<HeaderAuthLinksProps> = ({ export const HeaderAuthLinks: FC<HeaderAuthLinksProps> = ({
enterTitle, enterTitle,
registerTitle, registerTitle,
accountTitle,
separatorClass = 'b-header__nav__list__line' separatorClass = 'b-header__nav__list__line'
}) => { }) => {
const [isOpenModal, setIsOpenModal] = useState<boolean>(false); const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
@ -40,7 +44,13 @@ export const HeaderAuthLinks: FC<HeaderAuthLinksProps> = ({
setIsOpenModal(true); setIsOpenModal(true);
}; };
return ( return checkAuthToken()
? (
<li>
<Link href={'/sessions' as any}>{accountTitle}</Link>
</li>
)
: (
<> <>
<li> <li>
<LinkButton <LinkButton

View File

@ -5,14 +5,15 @@ type HeaderMenuProps = {
children: ReactNode; children: ReactNode;
enterTitle: string; enterTitle: string;
registerTitle: string; registerTitle: string;
accountTitle: string;
}; };
export const HeaderMenu = ({ children, enterTitle, registerTitle }: HeaderMenuProps) => ( export const HeaderMenu = ({ children, enterTitle, registerTitle, accountTitle }: HeaderMenuProps) => (
<div className="b-header__nav"> <div className="b-header__nav">
<nav> <nav>
<ul className="b-header__nav__list "> <ul className="b-header__nav__list ">
{children} {children}
<HeaderAuthLinks enterTitle={enterTitle} registerTitle={registerTitle} /> <HeaderAuthLinks enterTitle={enterTitle} registerTitle={registerTitle} accountTitle={accountTitle} />
</ul> </ul>
</nav> </nav>
</div> </div>

View File

@ -7,9 +7,10 @@ type HeaderMenuMobileProps = {
children: ReactNode; children: ReactNode;
enterTitle: string; enterTitle: string;
registerTitle: string; registerTitle: string;
accountTitle: string;
}; };
export const HeaderMobileMenu: FC<HeaderMenuMobileProps> = ({ children, enterTitle, registerTitle }) => { export const HeaderMobileMenu: FC<HeaderMenuMobileProps> = ({ children, enterTitle, registerTitle, accountTitle }) => {
const [showMobileMenu, setShowMobileMenu] = useState<boolean>(false); const [showMobileMenu, setShowMobileMenu] = useState<boolean>(false);
return ( return (
@ -33,6 +34,7 @@ export const HeaderMobileMenu: FC<HeaderMenuMobileProps> = ({ children, enterTit
separatorClass="menu-mobile__header__nav__line" separatorClass="menu-mobile__header__nav__line"
enterTitle={enterTitle} enterTitle={enterTitle}
registerTitle={registerTitle} registerTitle={registerTitle}
accountTitle={accountTitle}
/> />
</ul> </ul>
</div> </div>

View File

@ -30,13 +30,13 @@ export const Header: FC<HeaderProps> = ({ locale }) => {
alt="" alt=""
/> />
</Link> </Link>
<HeaderMenu enterTitle={t('enter')} registerTitle={t('registration')}> <HeaderMenu enterTitle={t('enter')} registerTitle={t('registration')} accountTitle={t('account')}>
{routes} {routes}
</HeaderMenu> </HeaderMenu>
<LanguageSwitcher locale={locale} /> <LanguageSwitcher locale={locale} />
</div> </div>
</header> </header>
<HeaderMobileMenu enterTitle={t('enter')} registerTitle={t('registration')}> <HeaderMobileMenu enterTitle={t('enter')} registerTitle={t('registration')} accountTitle={t('account')}>
{routes} {routes}
</HeaderMobileMenu> </HeaderMobileMenu>
</> </>

View File

@ -0,0 +1,88 @@
'use client';
import React, { useState } from 'react';
import Slider from 'react-slick';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
const ScreenList = [
{
id: '1',
title: 'Plan your working hours',
screen: '/images/slider-1.png'
},
{
id: '2',
title: 'Accept customers applications',
screen: '/images/slider-2.png'
},
{
id: '3',
title: 'Make video calls',
screen: '/images/slider-3.png'
},
{
id: '4',
title: 'Keep statistics',
screen: '/images/slider-4.png'
},
{
id: '5',
title: 'Get paid',
screen: '/images/slider-5.png'
},
{
id: '6',
title: 'Plan your working hours',
screen: '/images/slider-1.png'
},
{
id: '7',
title: 'Accept customers applications',
screen: '/images/slider-2.png'
},
{
id: '8',
title: 'Make video calls',
screen: '/images/slider-3.png'
},
{
id: '9',
title: 'Keep statistics',
screen: '/images/slider-4.png'
},
{
id: '10',
title: 'Get paid',
screen: '/images/slider-5.png'
}
];
export const ScreenCarousel = () => {
const [title, setTitle] = useState<string>(ScreenList[0].title);
const settings = {
dots: false,
autoplay: true,
infinite: true,
speed: 500,
slidesToShow: 5,
slidesToScroll: 1,
arrows: false,
afterChange: (current: number) => {
setTitle(ScreenList[current].title);
}
};
return (
<div className="b-inner">
<h2 id="slider-title" className="title-h2">{title}</h2>
<Slider {...settings} className="slider">
{ScreenList.map(({ id, screen }) => (
<div key={id} className="slider-item">
<img src={screen} alt="" className="slider-img" />
</div>
))}
</Slider>
</div>
);
};

View File

@ -1,2 +1,3 @@
export * from './Header'; export * from './Header';
export * from './Footer'; export * from './Footer';
export * from './GeneralTopSection';

View File

@ -1 +1,2 @@
export const BASE_URL = process.env.NEXT_PUBLIC_SERVER_BASE_URL || 'https://api.bbuddy.expert/api'; export const BASE_URL = process.env.NEXT_PUBLIC_SERVER_BASE_URL || 'https://api.bbuddy.expert/api';
export const AUTH_TOKEN_KEY = 'bbuddy_token';

View File

@ -1,33 +0,0 @@
import { AdditionalFilter, Filter } from '../types/experts';
import { LOCALES } from './locale';
export enum FilterType {
'WorkLifeBalance' = 'workLifeBalance',
'StrategicSession' = 'strategicSession',
'PersonalGrowth' = 'personalGrowth',
'CareerPlaning' = 'careerPlaning',
'ExecutiveCoaching' = 'executiveCoaching',
'CareerDevelopment' = 'careerDevelopment',
'Networking' = 'networking',
'BusinessModelReview' = 'businessModelReview',
'Price' = 'price',
'Duration' = 'duration'
}
export const INITIAL_FILTER: Filter = {
[FilterType.WorkLifeBalance]: false,
[FilterType.StrategicSession]: false,
[FilterType.PersonalGrowth]: false,
[FilterType.CareerPlaning]: false,
[FilterType.ExecutiveCoaching]: false,
[FilterType.CareerDevelopment]: false,
[FilterType.Networking]: false,
[FilterType.BusinessModelReview]: false,
[FilterType.Price]: [65, 170],
[FilterType.Duration]: [60, 120]
};
export const INITIAL_ADD_FILTER: AdditionalFilter = {
sort: 'By top views',
language: Object.keys(LOCALES)
};

View File

@ -1,2 +1 @@
export const HEAD_ROUTES = ['faq', 'experts', 'news']; export const HEAD_ROUTES = ['bb-client', 'bb-expert', 'blog'];
export const FOOTER_ROUTES = ['faq', 'privacy-policy'];

136
src/styles/_bb-clients.scss Normal file
View File

@ -0,0 +1,136 @@
.bb-client {
margin-top: 140px
}
.tag-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.tag-item {
background: #fff;
border-radius: 62px;
margin: 12px 8px;
color: #5aadcc;
padding: 16px;
font-size: 1.5rem;
}
.b2 {
padding: 120px 0 80px;
h2 {
margin-bottom: 40px;
}
}
.b4 {
margin-bottom: 30px;
&-list {
margin-top: 200px;
margin-bottom: 200px;
}
&-item {
margin-bottom: 30px;
}
&-title {
font-size: 2rem;
color: #2c7873;
}
&-descr {
color: #003b46;
font-size: 1rem;
}
}
.b4-title, .b5-item {
margin-bottom: 20px;
}
.b5 {
position: relative;
margin-top: -65px;
&-item {
background: #fff;
height: calc(100% - 120px);
margin-top: 100px;
position: relative;
text-align: center;
padding: 120px 16px 16px;
border-radius: 16px;
margin-bottom: 20px;
}
&-img {
height: 204px;
width: auto;
top: 0;
left: 50%;
position: absolute;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
&-text {
font-size: 1.5rem;
color: #5aadcc;
}
h3 {
margin: 36px 0;
font-size: 2rem;
color: #2c7873;
text-align: center;
}
}
.b6 {
margin: 60px 0 40px;
&-banner {
background: #ffd213;
border-radius: 16px;
padding: 0 32px;
display: flex;
-ms-flex-align: center;
align-items: center;
position: relative;
&:after {
position: absolute;
right: -70px;
top: -70px;
content: "";
width: 170px;
height: 170px;
display: block;
background: url(/images/white-ball.svg) center center no-repeat;
background-size: contain;
}
}
&-content {
width: 45%;
color: #2C7873;
}
&-title {
font-size: 2rem;
margin-bottom: 10px;
}
&-descr {
font-size: 1rem;
}
&-img {
width: 55%;
}
}

120
src/styles/_bb-experts.scss Normal file
View File

@ -0,0 +1,120 @@
.b-slider {
margin-top: -105px;
h2 {
margin: 40px 0 20px;
text-align: center;
}
}
.slider {
position: relative;
padding-bottom: 80px;
padding-top: 20px;
text-align: center;
overflow: hidden;
&:after {
content: "";
background: url(/images/slider-nahd.png) center center no-repeat;
left: 50%;
top: 20px;
width: 522px;
height: 522px;
position: absolute;
-webkit-transform: translate(-50%, -5px);
-ms-transform: translate(-50%, -5px);
transform: translate(-50%, -5px);
pointer-events: none;
}
&-item {
display: inline-flex !important;
justify-content: center;
}
}
.waves-top {
position: relative;
background: #DFF5FB;
margin-bottom: 24px;
&:before {
content: "";
position: absolute;
background-image: url(/images/wave-footer.svg);
background-position: center bottom;
background-size: contain;
background-repeat: repeat-x;
top: -59px;
width: 100%;
height: 59px;
}
}
.advice-list {
margin: 24px 0 40px;
}
.advice-item {
background: #E4F5FA;
border-radius: 16px;
margin-bottom: 24px;
padding: 16px;
font-size: 1.5rem;
}
.advice-item--with-img {
display: flex;
align-items: center;
.advice-img {
width: 148px;
min-width: 148px;
height: auto;
margin-right: 1rem;
}
.advice-title {
font-size: 2rem;
margin-bottom: 0.5rem;
}
}
.b3 {
margin-bottom: 40px;
&-item {
background: #e0f3f1;
height: calc(100% - 120px);
margin-bottom: 20px;
margin-top: 100px;
position: relative;
text-align: center;
padding: 120px 16px 16px;
border-radius: 16px;
}
&-img {
position: absolute;
height: 204px;
width: auto;
top: 0;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
&-title {
color: #000;
font-size: 1.5rem;
text-transform: uppercase;
margin-bottom: 14px;
}
&-text {
font-size: 1rem;
}
}

View File

@ -287,6 +287,10 @@ body{
align-items: flex-start; align-items: flex-start;
width: 100%; width: 100%;
& > div {
width: 100%;
}
&__inner { &__inner {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -321,6 +325,7 @@ body{
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
line-height: 160%; line-height: 160%;
text-transform: capitalize;
} }
} }
@ -440,6 +445,10 @@ body{
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
line-height: 160%; line-height: 160%;
&:hover, &:active {
color: #003B46 !important;
}
} }
.btn-back { .btn-back {

100
src/styles/_faq.scss Normal file
View File

@ -0,0 +1,100 @@
.banner {
text-align: center;
margin-bottom: -80px;
&-faq {
h1 {
font-size: 2.93rem;
margin-top: 120px;
color: #2C7873;
}
}
&-note {
font-size: 1.5rem;
color: #4e7c86;
}
}
.mb-4 {
margin-bottom: 1.5rem!important;
}
.store-buttons {
display: flex;
margin-bottom: 24px;
}
.store-button {
color: #fff;
font-size: 1.5rem;
line-height: 1.5rem;
text-decoration: none;
background: #5AADCC;
border-radius: 62px;
padding: 21px 15px 21px 73px;
margin: 0 12px;
transition: .3s all ease;
animation: pulse;
animation-duration: 2s;
position: relative;
&:before {
width: 32px;
height: 32px;
position: absolute;
left: 24px;
transform: translate(0, -50%);
}
&:after {
width: 1px;
height: 42px;
position: absolute;
left: 64px;
background: #fff;
transform: translate(0, -50%);
}
&:after, &:before {
display: block;
top: 50%;
content: "";
}
&.phone:before {
background: url(/images/call-outline-white.svg) center center no-repeat;
background-size: contain;
}
}
.mb-5 {
margin-bottom: 3rem!important;
}
.advice {
&-item {
background: #E4F5FA;
border-radius: 16px;
margin-bottom: 24px;
padding: 16px;
font-size: 1.5rem;
&--with-img {
display: flex;
align-items: center;
}
}
&-img {
width: 148px;
min-width: 148px;
height: auto;
margin-right: 1rem;
}
&-title {
font-size: 2rem;
margin-bottom: 0.5rem;
}
}

View File

@ -4,6 +4,9 @@
@import "_header.scss"; @import "_header.scss";
@import "_menu-mobile.scss"; @import "_menu-mobile.scss";
@import "_main.scss"; @import "_main.scss";
@import "_bb-experts.scss";
@import "_bb-clients.scss";
@import "_faq.scss";
@import "_footer.scss"; @import "_footer.scss";
@import "_cols.scss"; @import "_cols.scss";
@import "_pages.scss"; @import "_pages.scss";

View File

@ -1,12 +1,16 @@
export type Filter = Record<string, any>; import { Tag } from './tags';
// export type Filter = { // export type Filter = Record<string, any>;
// themesTagIds: number[];
// priceFrom?: number | null; export type GeneralFilter = Filter & AdditionalFilter;
// priceTo?: number | null;
// durationFrom?: number | null; export type Filter = {
// durationTo?: number | null; themesTagIds?: number[];
// }; priceFrom?: number | null;
priceTo?: number | null;
durationFrom?: number | null;
durationTo?: number | null;
};
export type AdditionalFilter = { export type AdditionalFilter = {
text?: string; text?: string;
@ -14,14 +18,6 @@ export type AdditionalFilter = {
language?: string[]; language?: string[];
}; };
export type Tag = {
id: number;
groupId: number;
name: string
couchCount?: number;
group?: string | null;
};
export type File = { export type File = {
id: number; id: number;
fileType: string; fileType: string;

21
src/types/tags.ts Normal file
View File

@ -0,0 +1,21 @@
export type Tag = {
id: number;
groupId: number;
name: string
couchCount?: number;
group?: string | null;
};
export type ThemeGroups = {
id: number;
name: string;
tags: Tag[];
};
export type SearchData = {
themesGroups: ThemeGroups[],
sessionCostMin: number;
sessionCostMax: number;
sessionDurationMin: number;
sessionDurationMax: number;
};

103
src/utils/filter.ts Normal file
View File

@ -0,0 +1,103 @@
import { SearchData } from '../types/tags';
import { AdditionalFilter, Filter } from '../types/experts';
export const getDefaultFilter = (searchData: SearchData | null): Filter => {
const themesTagIds = searchData?.themesGroups?.reduce<number[]>((result, { tags }) => {
const t = tags?.map(({ id }) => id) || [];
return t ? [
...result,
...t
] : result;
}, []);
return {
themesTagIds,
priceFrom: null,
priceTo: null,
durationFrom: null,
durationTo: null
} as Filter;
};
export const getFilter = (searchData: SearchData | null, searchParams?: { [key: string]: string | string[] | undefined }): Filter => {
const filter = getDefaultFilter(searchData);
if (searchParams) {
const { themesTagIds, priceFrom, priceTo, durationFrom, durationTo } = searchParams;
if (themesTagIds) {
if (Array.isArray(themesTagIds) && themesTagIds?.length > 0) {
filter.themesTagIds = themesTagIds.map((id) => +id);
}
if (typeof themesTagIds === 'string') {
filter.themesTagIds = [+themesTagIds];
}
}
if (priceFrom) {
filter.priceFrom = +priceFrom;
}
if (priceTo) {
filter.priceTo = +priceTo;
}
if (durationFrom) {
filter.durationFrom = +durationFrom;
}
if (durationTo) {
filter.durationTo = +durationTo;
}
}
return filter;
};
export const getObjectByFilter = (searchParams?: any): Filter | undefined => {
const filter: Filter = {};
let tags = searchParams?.getAll('themesTagIds');
if (tags && tags.length > 0) {
filter.themesTagIds = tags.map((id) => Number(id));
}
if (searchParams?.has('priceFrom')) {
filter.priceFrom = Number(searchParams.get('priceFrom'));
}
if (searchParams?.has('priceTo')) {
filter.priceTo = Number(searchParams.get('priceTo'));
}
if (searchParams?.has('durationFrom')) {
filter.durationFrom = Number(searchParams.get('durationFrom'));
}
if (searchParams?.has('durationTo')) {
filter.durationTo = Number(searchParams.get('durationTo'));
}
return filter;
};
export const getObjectByAdditionalFilter = (searchParams?: any): AdditionalFilter => {
const additionalFilter: AdditionalFilter = {};
if (searchParams?.has('text')) {
additionalFilter.text = searchParams.get('text');
}
if (searchParams?.has('sort')) {
additionalFilter.sort = searchParams.get('sort');
}
if (searchParams?.has('language')) {
additionalFilter.language = searchParams.getAll('language');
}
return additionalFilter;
};

23
src/utils/storage/auth.ts Normal file
View File

@ -0,0 +1,23 @@
'use client';
import { AUTH_TOKEN_KEY } from '../../constants/common';
export function checkAuthToken() {
return !!getAuthToken();
}
export function getAuthToken() {
return 'tr';
}
// export function getAuthToken() {
// return localStorage.getItem(AUTH_TOKEN_KEY);
// }
//
// export function removeAuthToken() {
// localStorage.removeItem(AUTH_TOKEN_KEY);
// }
//
// export function setAuthToken(token: string) {
// localStorage.setItem(AUTH_TOKEN_KEY, token);
// }